diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 0eb92d5a7e..ef415ec57c 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -1,10 +1,10 @@
using MediaBrowser.Api.Playback;
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using System;
@@ -39,6 +39,7 @@ namespace MediaBrowser.Api
private readonly IServerConfigurationManager _config;
private readonly ISessionManager _sessionManager;
+ private readonly IFileSystem _fileSystem;
public readonly SemaphoreSlim TranscodingStartLock = new SemaphoreSlim(1, 1);
@@ -48,11 +49,12 @@ namespace MediaBrowser.Api
/// The logger.
/// The session manager.
/// The configuration.
- public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config)
+ public ApiEntryPoint(ILogger logger, ISessionManager sessionManager, IServerConfigurationManager config, IFileSystem fileSystem)
{
Logger = logger;
_sessionManager = sessionManager;
_config = config;
+ _fileSystem = fileSystem;
Instance = this;
}
@@ -86,12 +88,12 @@ namespace MediaBrowser.Api
///
private void DeleteEncodedMediaCache()
{
- var path = Path.Combine(_config.ApplicationPaths.TranscodingTempPath, EncodingContext.Streaming.ToString().ToLower());
+ var path = _config.ApplicationPaths.TranscodingTempPath;
foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
.ToList())
{
- File.Delete(file);
+ _fileSystem.DeleteFile(file);
}
}
@@ -462,7 +464,7 @@ namespace MediaBrowser.Api
/// The output file path.
private void DeleteProgressivePartialStreamFiles(string outputFilePath)
{
- File.Delete(outputFilePath);
+ _fileSystem.DeleteFile(outputFilePath);
}
///
@@ -479,13 +481,13 @@ namespace MediaBrowser.Api
.ToList();
Exception e = null;
-
+
foreach (var file in filesToDelete)
{
try
{
Logger.Info("Deleting HLS file {0}", file);
- File.Delete(file);
+ _fileSystem.DeleteFile(file);
}
catch (DirectoryNotFoundException)
{
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 297a131557..dff433c9dc 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -1,9 +1,12 @@
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
+using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
@@ -21,7 +24,7 @@ namespace MediaBrowser.Api
///
/// The logger.
public ILogger Logger { get; set; }
-
+
///
/// Gets or sets the HTTP result factory.
///
@@ -35,6 +38,7 @@ namespace MediaBrowser.Api
public IRequest Request { get; set; }
public ISessionContext SessionContext { get; set; }
+ public IAuthorizationContext AuthorizationContext { get; set; }
public string GetHeader(string name)
{
@@ -109,6 +113,37 @@ namespace MediaBrowser.Api
private readonly char[] _dashReplaceChars = { '?', '/', '&' };
private const char SlugChar = '-';
+ protected DtoOptions GetDtoOptions(object request)
+ {
+ var options = new DtoOptions();
+
+ options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
+
+ var hasFields = request as IHasItemFields;
+ if (hasFields != null)
+ {
+ options.Fields = hasFields.GetItemFields().ToList();
+ }
+
+ var hasDtoOptions = request as IHasDtoOptions;
+ if (hasDtoOptions != null)
+ {
+ options.EnableImages = hasDtoOptions.EnableImages ?? true;
+
+ if (hasDtoOptions.ImageTypeLimit.HasValue)
+ {
+ options.ImageTypeLimit = hasDtoOptions.ImageTypeLimit.Value;
+ }
+
+ if (!string.IsNullOrWhiteSpace(hasDtoOptions.EnableImageTypes))
+ {
+ options.ImageTypes = (hasDtoOptions.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToList();
+ }
+ }
+
+ return options;
+ }
+
protected MusicArtist GetArtist(string name, ILibraryManager libraryManager)
{
return libraryManager.GetArtist(DeSlugArtistName(name, libraryManager));
@@ -139,11 +174,11 @@ namespace MediaBrowser.Api
return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager));
}
- protected IEnumerable GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId = null)
+ protected IList GetAllLibraryItems(Guid? userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func filter)
{
if (!string.IsNullOrEmpty(parentId))
{
- var folder = (Folder) libraryManager.GetItemById(new Guid(parentId));
+ var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
if (userId.HasValue)
{
@@ -154,10 +189,13 @@ namespace MediaBrowser.Api
throw new ArgumentException("User not found");
}
- return folder.GetRecursiveChildren(user);
+ return folder
+ .GetRecursiveChildren(user, filter)
+ .ToList();
}
- return folder.GetRecursiveChildren();
+ return folder
+ .GetRecursiveChildren(filter);
}
if (userId.HasValue)
{
@@ -168,10 +206,16 @@ namespace MediaBrowser.Api
throw new ArgumentException("User not found");
}
- return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user);
+ return userManager
+ .GetUserById(userId.Value)
+ .RootFolder
+ .GetRecursiveChildren(user, filter)
+ .ToList();
}
- return libraryManager.RootFolder.GetRecursiveChildren();
+ return libraryManager
+ .RootFolder
+ .GetRecursiveChildren(filter);
}
///
@@ -187,8 +231,9 @@ namespace MediaBrowser.Api
return name;
}
- return libraryManager.RootFolder.RecursiveChildren
- .OfType
public class AudioService : BaseProgressiveStreamingService
{
- public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, imageProcessor, httpClient)
+ public AudioService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager, imageProcessor, httpClient)
{
}
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index 6bec387d42..9dbe3389e6 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -1,8 +1,8 @@
-using System.Linq;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
@@ -15,6 +15,7 @@ using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -28,8 +29,7 @@ namespace MediaBrowser.Api.Playback.Progressive
protected readonly IImageProcessor ImageProcessor;
protected readonly IHttpClient HttpClient;
- protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IImageProcessor imageProcessor, IHttpClient httpClient)
- : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder)
+ protected BaseProgressiveStreamingService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager)
{
ImageProcessor = imageProcessor;
HttpClient = httpClient;
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 5ef72a4952..7e86b867f6 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -2,6 +2,7 @@ using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Library;
@@ -62,7 +63,7 @@ namespace MediaBrowser.Api.Playback.Progressive
///
public class VideoService : BaseProgressiveStreamingService
{
- public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, imageProcessor, httpClient)
+ public VideoService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, ILiveTvManager liveTvManager, IDlnaManager dlnaManager, IChannelManager channelManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IImageProcessor imageProcessor, IHttpClient httpClient) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, liveTvManager, dlnaManager, channelManager, subtitleEncoder, deviceManager, imageProcessor, httpClient)
{
}
@@ -96,6 +97,7 @@ namespace MediaBrowser.Api.Playback.Progressive
if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase))
{
+ // Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
format = " -f mp4 -movflags frag_keyframe+empty_moov";
}
diff --git a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
index ada2a98a15..fc94d070a0 100644
--- a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
@@ -40,7 +40,10 @@ namespace MediaBrowser.Api.Playback
/// The response stream.
public void WriteTo(Stream responseStream)
{
- _response.Content.CopyTo(responseStream, 819200);
+ using (_response)
+ {
+ _response.Content.CopyTo(responseStream, 819200);
+ }
}
}
}
diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs
index 7f7717f71b..e16ca1ee8f 100644
--- a/MediaBrowser.Api/PlaylistService.cs
+++ b/MediaBrowser.Api/PlaylistService.cs
@@ -151,9 +151,10 @@ namespace MediaBrowser.Api
{
items = items.Take(request.Limit.Value).ToArray();
}
-
- var dtos = items
- .Select(i => _dtoService.GetBaseItemDto(i.Item2, request.GetItemFields().ToList(), user))
+
+ var dtoOptions = GetDtoOptions(request);
+
+ var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2), dtoOptions, user)
.ToArray();
var index = 0;
diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs
index 62aa1e755a..f5c8935f80 100644
--- a/MediaBrowser.Api/PluginService.cs
+++ b/MediaBrowser.Api/PluginService.cs
@@ -1,5 +1,4 @@
-using System.Threading;
-using MediaBrowser.Common;
+using MediaBrowser.Common;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Security;
using MediaBrowser.Common.Updates;
@@ -8,12 +7,12 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
using ServiceStack;
-using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Api
@@ -236,8 +235,7 @@ namespace MediaBrowser.Api
{
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
- var pathInfo = PathInfo.Parse(Request.PathInfo);
- var id = new Guid(pathInfo.GetArgumentValue(1));
+ var id = new Guid(GetPathValue(1));
var plugin = _appHost.Plugins.First(p => p.Id == id);
diff --git a/MediaBrowser.Api/Reports/ReportFieldType.cs b/MediaBrowser.Api/Reports/ReportFieldType.cs
new file mode 100644
index 0000000000..d35c5cb2da
--- /dev/null
+++ b/MediaBrowser.Api/Reports/ReportFieldType.cs
@@ -0,0 +1,9 @@
+
+namespace MediaBrowser.Api.Reports
+{
+ public enum ReportFieldType
+ {
+ String,
+ Boolean
+ }
+}
diff --git a/MediaBrowser.Api/Reports/ReportRequests.cs b/MediaBrowser.Api/Reports/ReportRequests.cs
new file mode 100644
index 0000000000..8dea003814
--- /dev/null
+++ b/MediaBrowser.Api/Reports/ReportRequests.cs
@@ -0,0 +1,33 @@
+using ServiceStack;
+
+namespace MediaBrowser.Api.Reports
+{
+ public class BaseReportRequest : IReturn
+ {
+ ///
+ /// Specify this to localize the search to a specific item or folder. Omit to use the root.
+ ///
+ /// The parent id.
+ [ApiMember(Name = "ParentId", Description = "Specify this to localize the search to a specific item or folder. Omit to use the root", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ParentId { get; set; }
+
+ ///
+ /// Skips over a given number of items within the results. Use for paging.
+ ///
+ /// The start index.
+ [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? StartIndex { get; set; }
+
+ ///
+ /// The maximum number of items to return
+ ///
+ /// 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; }
+ }
+
+ [Route("/Reports/Items", "GET", Summary = "Gets reports based on library items")]
+ public class GetItemReport : BaseReportRequest
+ {
+ }
+}
diff --git a/MediaBrowser.Api/Reports/ReportResult.cs b/MediaBrowser.Api/Reports/ReportResult.cs
new file mode 100644
index 0000000000..c033ae8fb7
--- /dev/null
+++ b/MediaBrowser.Api/Reports/ReportResult.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Api.Reports
+{
+ public class ReportResult
+ {
+ public List> Rows { get; set; }
+ public List Columns { get; set; }
+
+ public ReportResult()
+ {
+ Rows = new List>();
+ Columns = new List();
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs
new file mode 100644
index 0000000000..45bc4a8893
--- /dev/null
+++ b/MediaBrowser.Api/Reports/ReportsService.cs
@@ -0,0 +1,64 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Querying;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Reports
+{
+ public class ReportsService : BaseApiService
+ {
+ private readonly ILibraryManager _libraryManager;
+
+ public ReportsService(ILibraryManager libraryManager)
+ {
+ _libraryManager = libraryManager;
+ }
+
+ public async Task Get(GetItemReport request)
+ {
+ var queryResult = await GetQueryResult(request).ConfigureAwait(false);
+
+ var reportResult = GetReportResult(queryResult);
+
+ return ToOptimizedResult(reportResult);
+ }
+
+ private ReportResult GetReportResult(QueryResult queryResult)
+ {
+ var reportResult = new ReportResult();
+
+ // Fill rows and columns
+
+ return reportResult;
+ }
+
+ private Task> GetQueryResult(BaseReportRequest request)
+ {
+ // Placeholder in case needed later
+ User user = null;
+
+ var parentItem = string.IsNullOrEmpty(request.ParentId) ?
+ (user == null ? _libraryManager.RootFolder : user.RootFolder) :
+ _libraryManager.GetItemById(request.ParentId);
+
+ return ((Folder)parentItem).GetItems(GetItemsQuery(request, user));
+ }
+
+ private InternalItemsQuery GetItemsQuery(BaseReportRequest request, User user)
+ {
+ var query = new InternalItemsQuery
+ {
+ User = user,
+ CollapseBoxSetItems = false
+ };
+
+ // Set query values based on request
+
+ // Example
+ //query.IncludeItemTypes = new[] {"Movie"};
+
+
+ return query;
+ }
+ }
+}
diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
index a05be024e0..e3722b4a75 100644
--- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
+++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
@@ -32,6 +32,9 @@ namespace MediaBrowser.Api.ScheduledTasks
{
[ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsHidden { get; set; }
+
+ [ApiMember(Name = "IsEnabled", Description = "Optional filter tasks that are enabled, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+ public bool? IsEnabled { get; set; }
}
///
@@ -132,6 +135,25 @@ namespace MediaBrowser.Api.ScheduledTasks
});
}
+ if (request.IsEnabled.HasValue)
+ {
+ var val = request.IsEnabled.Value;
+
+ result = result.Where(i =>
+ {
+ var isEnabled = true;
+
+ var configurableTask = i.ScheduledTask as IConfigurableScheduledTask;
+
+ if (configurableTask != null)
+ {
+ isEnabled = configurableTask.IsEnabled;
+ }
+
+ return isEnabled == val;
+ });
+ }
+
var infos = result
.Select(ScheduledTaskHelpers.GetTaskInfo)
.ToList();
@@ -202,8 +224,7 @@ namespace MediaBrowser.Api.ScheduledTasks
{
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
- var pathInfo = PathInfo.Parse(Request.PathInfo);
- var id = pathInfo.GetArgumentValue(1);
+ var id = GetPathValue(1);
var task = TaskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, id));
diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs
index 61c7305b5d..ee48946d5d 100644
--- a/MediaBrowser.Api/SearchService.cs
+++ b/MediaBrowser.Api/SearchService.cs
@@ -194,29 +194,24 @@ namespace MediaBrowser.Api
{
result.Series = season.Series.Name;
- result.EpisodeCount = season.GetRecursiveChildren().Count(i => i is Episode);
+ result.EpisodeCount = season.GetRecursiveChildren(i => i is Episode).Count;
}
var series = item as Series;
if (series != null)
{
- result.EpisodeCount = series.GetRecursiveChildren().Count(i => i is Episode);
+ result.EpisodeCount = series.GetRecursiveChildren(i => i is Episode).Count;
}
var album = item as MusicAlbum;
if (album != null)
{
- var songs = album.GetRecursiveChildren().OfType
/// The request.
- /// The items.
+ /// The f.
+ /// The exclude item types.
+ /// The include item types.
+ /// The media types.
/// IEnumerable{BaseItem}.
- protected virtual IEnumerable FilterItems(GetItemsByName request, IEnumerable items)
+ protected bool FilterItem(GetItemsByName request, BaseItem f, string[] excludeItemTypes, string[] includeItemTypes, string[] mediaTypes)
{
// Exclude item types
- if (!string.IsNullOrEmpty(request.ExcludeItemTypes))
+ if (excludeItemTypes.Length > 0)
{
- var vals = request.ExcludeItemTypes.Split(',');
- items = items.Where(f => !vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
+ if (excludeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
}
// Include item types
- if (!string.IsNullOrEmpty(request.IncludeItemTypes))
+ if (includeItemTypes.Length > 0)
{
- var vals = request.IncludeItemTypes.Split(',');
- items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
+ if (!includeItemTypes.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
}
// Include MediaTypes
- if (!string.IsNullOrEmpty(request.MediaTypes))
+ if (mediaTypes.Length > 0)
{
- var vals = request.MediaTypes.Split(',');
-
- items = items.Where(f => vals.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase));
+ if (!mediaTypes.Contains(f.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
}
- return items;
+ return true;
}
///
diff --git a/MediaBrowser.Api/UserLibrary/GameGenresService.cs b/MediaBrowser.Api/UserLibrary/GameGenresService.cs
index 2f1c73acef..3063e19c72 100644
--- a/MediaBrowser.Api/UserLibrary/GameGenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GameGenresService.cs
@@ -69,7 +69,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var item = GetGameGenre(request.Name, LibraryManager);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue)
{
diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs
index db0b0fe612..c659852de8 100644
--- a/MediaBrowser.Api/UserLibrary/GenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GenresService.cs
@@ -74,7 +74,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var item = GetGenre(request.Name, LibraryManager);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue)
{
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index cf9b0b4387..9b5ef3a981 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -169,8 +169,6 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "ExcludeLocationTypes", Description = "Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string ExcludeLocationTypes { get; set; }
- public bool IncludeIndexContainers { get; set; }
-
[ApiMember(Name = "IsMissing", Description = "Optional filter by items that are missing episodes or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? IsMissing { get; set; }
@@ -321,14 +319,14 @@ namespace MediaBrowser.Api.UserLibrary
var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
var isFiltered = result.Item2;
- var dtoOptions = request.GetDtoOptions();
+ var dtoOptions = GetDtoOptions(request);
if (isFiltered)
{
return new ItemsResult
{
TotalRecordCount = result.Item1.TotalRecordCount,
- Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray()
+ Items = _dtoService.GetBaseItemDtos(result.Item1.Items, dtoOptions, user).ToArray()
};
}
@@ -362,7 +360,7 @@ namespace MediaBrowser.Api.UserLibrary
var pagedItems = ApplyPaging(request, itemsArray);
- var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
+ var returnItems = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user).ToArray();
return new ItemsResult
{
@@ -396,52 +394,29 @@ namespace MediaBrowser.Api.UserLibrary
else if (request.Recursive)
{
- if (user == null)
- {
- items = ((Folder)item).RecursiveChildren;
+ var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
- items = _libraryManager.ReplaceVideosWithPrimaryVersions(items);
- }
- else
- {
- var result = await ((Folder)item).GetItems(GetItemsQuery(request, user));
-
- return new Tuple, bool>(result, true);
- }
+ return new Tuple, bool>(result, true);
}
else
{
if (user == null)
{
- items = ((Folder)item).Children;
+ var result = await ((Folder)item).GetItems(GetItemsQuery(request, null)).ConfigureAwait(false);
- items = _libraryManager.ReplaceVideosWithPrimaryVersions(items);
+ return new Tuple, bool>(result, true);
}
- else
- {
- var userRoot = item as UserRootFolder;
- if (userRoot == null)
- {
- var result = await ((Folder)item).GetItems(GetItemsQuery(request, user));
+ var userRoot = item as UserRootFolder;
- return new Tuple, bool>(result, true);
- }
+ if (userRoot == null)
+ {
+ var result = await ((Folder)item).GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
- items = ((Folder)item).GetChildren(user, true);
+ return new Tuple, bool>(result, true);
}
- }
-
- if (request.IncludeIndexContainers)
- {
- var list = items.ToList();
-
- var containers = list.Select(i => i.IndexContainer)
- .Where(i => i != null);
-
- list.AddRange(containers);
- items = list.Distinct();
+ items = ((Folder)item).GetChildren(user, true);
}
return new Tuple, bool>(new QueryResult
@@ -464,7 +439,7 @@ namespace MediaBrowser.Api.UserLibrary
SortBy = request.GetOrderBy(),
SortOrder = request.SortOrder ?? SortOrder.Ascending,
- Filter = (i, u) => ApplyAdditionalFilters(request, i, u, true, _libraryManager),
+ Filter = i => ApplyAdditionalFilters(request, i, user, true, _libraryManager),
Limit = request.Limit,
StartIndex = request.StartIndex,
diff --git a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
index f8575aa7c7..3733128f04 100644
--- a/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/MusicGenresService.cs
@@ -69,7 +69,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var item = GetMusicGenre(request.Name, LibraryManager);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue)
{
diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs
index 33ce6cd803..e9b3fa402f 100644
--- a/MediaBrowser.Api/UserLibrary/PersonsService.cs
+++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
using System.Collections.Generic;
@@ -86,7 +85,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var item = GetPerson(request.Name, LibraryManager);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue)
{
diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs
index 272134b70f..a4ebef6846 100644
--- a/MediaBrowser.Api/UserLibrary/StudiosService.cs
+++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs
@@ -73,7 +73,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var item = GetStudio(request.Name, LibraryManager);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue)
{
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 45a330a501..cdfd00ce9e 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -228,7 +228,7 @@ namespace MediaBrowser.Api.UserLibrary
///
/// The user id.
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public Guid UserId { get; set; }
+ public string UserId { get; set; }
[ApiMember(Name = "Limit", Description = "Limit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int Limit { get; set; }
@@ -259,7 +259,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
-
+
public GetLatestMedia()
{
Limit = 20;
@@ -304,74 +304,17 @@ namespace MediaBrowser.Api.UserLibrary
{
var user = _userManager.GetUserById(request.UserId);
- // Avoid implicitly captured closure
- var libraryItems = string.IsNullOrEmpty(request.ParentId) && user != null ?
- GetItemsConfiguredForLatest(user) :
- GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId);
-
- libraryItems = libraryItems.OrderByDescending(i => i.DateCreated)
- .Where(i => i.LocationType != LocationType.Virtual);
-
-
- //if (request.IsFolder.HasValue)
- //{
- //var val = request.IsFolder.Value;
- libraryItems = libraryItems.Where(f => f.IsFolder == false);
- //}
-
- if (!string.IsNullOrEmpty(request.IncludeItemTypes))
- {
- var vals = request.IncludeItemTypes.Split(',');
- libraryItems = libraryItems.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
- }
-
- var currentUser = user;
-
- if (request.IsPlayed.HasValue)
- {
- var takeLimit = request.Limit * 20;
-
- var val = request.IsPlayed.Value;
- libraryItems = libraryItems.Where(f => f.IsPlayed(currentUser) == val)
- .Take(takeLimit);
- }
-
- // Avoid implicitly captured closure
- var items = libraryItems
- .ToList();
-
- var list = new List>>();
-
- foreach (var item in items)
+ var list = _userViewManager.GetLatestItems(new LatestItemsQuery
{
- // Only grab the index container for media
- var container = item.IsFolder || !request.GroupItems ? null : item.LatestItemsIndexContainer;
-
- if (container == null)
- {
- list.Add(new Tuple>(null, new List { 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>(container, new List { item }));
- }
- }
-
- if (list.Count >= request.Limit)
- {
- break;
- }
- }
+ GroupItems = request.GroupItems,
+ IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
+ IsPlayed = request.IsPlayed,
+ Limit = request.Limit,
+ ParentId = request.ParentId,
+ UserId = request.UserId
+ });
- var options = request.GetDtoOptions();
+ var options = GetDtoOptions(request);
var dtos = list.Select(i =>
{
@@ -394,15 +337,6 @@ namespace MediaBrowser.Api.UserLibrary
return ToOptimizedResult(dtos.ToList());
}
- private IEnumerable GetItemsConfiguredForLatest(User user)
- {
- return user.RootFolder.GetChildren(user, true)
- .OfType()
- .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
- .SelectMany(i => i.GetRecursiveChildren(user))
- .DistinctBy(i => i.Id);
- }
-
public async Task Get(GetUserViews request)
{
var user = _userManager.GetUserById(request.UserId);
@@ -420,7 +354,7 @@ namespace MediaBrowser.Api.UserLibrary
var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
.ToArray();
@@ -447,14 +381,13 @@ namespace MediaBrowser.Api.UserLibrary
// Get them from the child tree
if (series != null)
{
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
// Avoid implicitly captured closure
var currentUser = user;
var dtos = series
- .GetRecursiveChildren()
- .Where(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
+ .GetRecursiveChildren(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
.OrderBy(i =>
{
if (i.PremiereDate.HasValue)
@@ -479,7 +412,7 @@ namespace MediaBrowser.Api.UserLibrary
// Get them from the db
if (movie != null)
{
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
var dtos = movie.SpecialFeatureIds
.Select(_libraryManager.GetItemById)
@@ -518,11 +451,10 @@ namespace MediaBrowser.Api.UserLibrary
trailerIds = hasTrailers.GetTrailerIds();
}
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
var dtos = trailerIds
.Select(_libraryManager.GetItemById)
- .OrderBy(i => i.SortName)
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
return dtos.ToList();
@@ -539,7 +471,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
@@ -557,7 +489,7 @@ namespace MediaBrowser.Api.UserLibrary
var item = user.RootFolder;
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
@@ -577,7 +509,7 @@ namespace MediaBrowser.Api.UserLibrary
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
.ToArray();
diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs
index b1b0aeb636..d95496333a 100644
--- a/MediaBrowser.Api/UserLibrary/YearsService.cs
+++ b/MediaBrowser.Api/UserLibrary/YearsService.cs
@@ -73,7 +73,7 @@ namespace MediaBrowser.Api.UserLibrary
{
var item = LibraryManager.GetYear(request.Year);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
if (request.UserId.HasValue)
{
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 51a7584b8d..c34924f3cc 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -11,7 +11,6 @@ using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Users;
using ServiceStack;
-using ServiceStack.Text.Controller;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -56,6 +55,21 @@ namespace MediaBrowser.Api
public string Id { get; set; }
}
+ ///
+ /// Class GetUser
+ ///
+ [Route("/Users/{Id}/Offline", "GET", Summary = "Gets an offline user record by Id")]
+ [Authenticated]
+ public class GetOfflineUser : IReturn
+ {
+ ///
+ /// Gets or sets the id.
+ ///
+ /// The id.
+ [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
///
/// Class DeleteUser
///
@@ -148,6 +162,32 @@ namespace MediaBrowser.Api
public bool ResetPassword { get; set; }
}
+ ///
+ /// Class UpdateUserEasyPassword
+ ///
+ [Route("/Users/{Id}/EasyPassword", "POST", Summary = "Updates a user's easy password")]
+ [Authenticated]
+ public class UpdateUserEasyPassword : IReturnVoid
+ {
+ ///
+ /// Gets or sets the id.
+ ///
+ /// The id.
+ public string Id { get; set; }
+
+ ///
+ /// Gets or sets the new password.
+ ///
+ /// The new password.
+ public string NewPassword { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether [reset password].
+ ///
+ /// true if [reset password]; otherwise, false.
+ public bool ResetPassword { get; set; }
+ }
+
///
/// Class UpdateUser
///
@@ -294,7 +334,7 @@ namespace MediaBrowser.Api
.Select(i => _userManager.GetUserDto(i, Request.RemoteIp))
.ToList();
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
}
///
@@ -313,7 +353,23 @@ namespace MediaBrowser.Api
var result = _userManager.GetUserDto(user, Request.RemoteIp);
- return ToOptimizedSerializedResultUsingCache(result);
+ return ToOptimizedResult(result);
+ }
+
+ public object Get(GetOfflineUser request)
+ {
+ var user = _userManager.GetUserById(request.Id);
+
+ if (user == null)
+ {
+ throw new ResourceNotFoundException("User not found");
+ }
+
+ var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+
+ var result = _userManager.GetOfflineUserDto(user, auth.DeviceId);
+
+ return ToOptimizedResult(result);
}
///
@@ -410,6 +466,8 @@ namespace MediaBrowser.Api
public async Task PostAsync(UpdateUserPassword request)
{
+ AssertCanUpdateUser(request.Id);
+
var user = _userManager.GetUserById(request.Id);
if (user == null)
@@ -434,6 +492,33 @@ namespace MediaBrowser.Api
}
}
+ public void Post(UpdateUserEasyPassword request)
+ {
+ var task = PostAsync(request);
+ Task.WaitAll(task);
+ }
+
+ public async Task PostAsync(UpdateUserEasyPassword request)
+ {
+ AssertCanUpdateUser(request.Id);
+
+ var user = _userManager.GetUserById(request.Id);
+
+ if (user == null)
+ {
+ throw new ResourceNotFoundException("User not found");
+ }
+
+ if (request.ResetPassword)
+ {
+ await _userManager.ResetEasyPassword(user).ConfigureAwait(false);
+ }
+ else
+ {
+ await _userManager.ChangeEasyPassword(user, request.NewPassword).ConfigureAwait(false);
+ }
+ }
+
///
/// Posts the specified request.
///
@@ -449,14 +534,15 @@ namespace MediaBrowser.Api
{
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
- var pathInfo = PathInfo.Parse(Request.PathInfo);
- var id = new Guid(pathInfo.GetArgumentValue(1));
+ var id = GetPathValue(1);
+
+ AssertCanUpdateUser(id);
var dtoUser = request;
var user = _userManager.GetUserById(id);
- var task = user.Name.Equals(dtoUser.Name, StringComparison.Ordinal) ?
+ var task = string.Equals(user.Name, dtoUser.Name, StringComparison.Ordinal) ?
_userManager.UpdateUser(user) :
_userManager.RenameUser(user, dtoUser.Name);
@@ -500,11 +586,29 @@ namespace MediaBrowser.Api
public void Post(UpdateUserConfiguration request)
{
+ AssertCanUpdateUser(request.Id);
+
var task = _userManager.UpdateConfiguration(request.Id, request);
Task.WaitAll(task);
}
+ private void AssertCanUpdateUser(string userId)
+ {
+ var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+
+ // If they're going to update the record of another user, they must be an administrator
+ if (!string.Equals(userId, auth.UserId, StringComparison.OrdinalIgnoreCase))
+ {
+ var authenticatedUser = _userManager.GetUserById(auth.UserId);
+
+ if (!authenticatedUser.Policy.IsAdministrator)
+ {
+ throw new SecurityException("Unauthorized access.");
+ }
+ }
+ }
+
public void Post(UpdateUserPolicy request)
{
var task = UpdateUserPolicy(request);
diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs
index 28db46b986..d1b0eb05fd 100644
--- a/MediaBrowser.Api/VideosService.cs
+++ b/MediaBrowser.Api/VideosService.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
@@ -80,7 +79,7 @@ namespace MediaBrowser.Api
: _libraryManager.RootFolder)
: _libraryManager.GetItemById(request.Id);
- var dtoOptions = new DtoOptions();
+ var dtoOptions = GetDtoOptions(request);
var video = (Video)item;
diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
index c98a6bd6cc..0f89bd1a6f 100644
--- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
+++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
@@ -475,7 +475,7 @@ namespace MediaBrowser.Common.Implementations
SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager);
RegisterSingleInstance(SecurityManager);
- InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager);
+ InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager);
RegisterSingleInstance(InstallationManager);
ZipClient = new ZipClient();
diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
index 1f82c5eb0c..b925649fce 100644
--- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
@@ -690,7 +690,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
{
try
{
- File.Delete(file);
+ _fileSystem.DeleteFile(file);
}
catch (IOException)
{
diff --git a/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs b/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
index 68df0e52ac..ea4a61e25a 100644
--- a/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
+++ b/MediaBrowser.Common.Implementations/IO/CommonFileSystem.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Model.Logging;
using System;
@@ -270,8 +270,8 @@ namespace MediaBrowser.Common.Implementations.IO
File.Copy(temp1, file2, true);
File.Copy(temp2, file1, true);
- File.Delete(temp1);
- File.Delete(temp2);
+ DeleteFile(temp1);
+ DeleteFile(temp2);
}
///
@@ -409,5 +409,25 @@ namespace MediaBrowser.Common.Implementations.IO
//return Path.IsPathRooted(path);
}
+
+ public void DeleteFile(string path, bool sendToRecycleBin)
+ {
+ File.Delete(path);
+ }
+
+ public void DeleteDirectory(string path, bool recursive, bool sendToRecycleBin)
+ {
+ Directory.Delete(path, recursive);
+ }
+
+ public void DeleteFile(string path)
+ {
+ DeleteFile(path, false);
+ }
+
+ public void DeleteDirectory(string path, bool recursive)
+ {
+ DeleteDirectory(path, recursive, false);
+ }
}
}
diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
index ff08c31bce..d7e5886d59 100644
--- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
+++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
@@ -48,21 +48,21 @@
Always
-
+
False
- ..\packages\NLog.3.1.0.0\lib\net45\NLog.dll
+ ..\packages\NLog.3.2.0.0\lib\net45\NLog.dll
False
..\ThirdParty\SharpCompress\SharpCompress.dll
-
+
False
- ..\packages\SimpleInjector.2.6.1\lib\net45\SimpleInjector.dll
+ ..\packages\SimpleInjector.2.7.0\lib\net45\SimpleInjector.dll
-
+
False
- ..\packages\SimpleInjector.2.6.1\lib\net45\SimpleInjector.Diagnostics.dll
+ ..\packages\SimpleInjector.2.7.0\lib\net45\SimpleInjector.Diagnostics.dll
diff --git a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs
index bf6ebf7ef7..6deda12932 100644
--- a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs
+++ b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs
@@ -18,11 +18,64 @@ namespace MediaBrowser.Common.Implementations.Networking
Logger = logger;
}
+ private volatile List _localIpAddresses;
+ private readonly object _localIpAddressSyncLock = new object();
+
///
/// Gets the machine's local ip address
///
/// IPAddress.
public IEnumerable GetLocalIpAddresses()
+ {
+ if (_localIpAddresses == null)
+ {
+ lock (_localIpAddressSyncLock)
+ {
+ if (_localIpAddresses == null)
+ {
+ var addresses = GetLocalIpAddressesInternal().ToList();
+
+ _localIpAddresses = addresses;
+ BindEvents();
+
+ return addresses;
+ }
+ }
+ }
+
+ return _localIpAddresses;
+ }
+
+ private void BindEvents()
+ {
+ NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged;
+ NetworkChange.NetworkAvailabilityChanged -= NetworkChange_NetworkAvailabilityChanged;
+
+ NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
+ NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
+ }
+
+ void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
+ {
+ Logger.Debug("NetworkAvailabilityChanged fired. Resetting cached network info.");
+
+ lock (_localIpAddressSyncLock)
+ {
+ _localIpAddresses = null;
+ }
+ }
+
+ void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
+ {
+ Logger.Debug("NetworkAddressChanged fired. Resetting cached network info.");
+
+ lock (_localIpAddressSyncLock)
+ {
+ _localIpAddresses = null;
+ }
+ }
+
+ private IEnumerable GetLocalIpAddressesInternal()
{
var list = GetIPsDefault()
.Where(i => !IPAddress.IsLoopback(i))
@@ -53,6 +106,11 @@ namespace MediaBrowser.Common.Implementations.Networking
// Private address space:
// http://en.wikipedia.org/wiki/Private_network
+ if (endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase))
+ {
+ return Is172AddressPrivate(endpoint);
+ }
+
return
// If url was requested with computer name, we may see this
@@ -61,11 +119,23 @@ namespace MediaBrowser.Common.Implementations.Networking
endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
- endpoint.StartsWith("192.", StringComparison.OrdinalIgnoreCase) ||
- endpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase) ||
+ endpoint.StartsWith("192.168", StringComparison.OrdinalIgnoreCase) ||
endpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase);
}
+ private bool Is172AddressPrivate(string endpoint)
+ {
+ for (var i = 16; i <= 31; i++)
+ {
+ if (endpoint.StartsWith("172." + i.ToString(CultureInfo.InvariantCulture) + ".", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public bool IsInLocalNetwork(string endpoint)
{
return IsInLocalNetworkInternal(endpoint, true);
@@ -122,7 +192,7 @@ namespace MediaBrowser.Common.Implementations.Networking
return false;
}
-
+
public IEnumerable GetIpAddresses(string hostName)
{
return Dns.GetHostAddresses(hostName);
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index 0dc67f8c00..4d6cc16089 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -108,13 +108,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
///
private TaskResult _lastExecutionResult;
///
- /// The _last execution resultinitialized
- ///
- private bool _lastExecutionResultinitialized;
- ///
/// The _last execution result sync lock
///
- private object _lastExecutionResultSyncLock = new object();
+ private readonly object _lastExecutionResultSyncLock = new object();
///
/// Gets the last execution result.
///
@@ -123,38 +119,39 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
{
get
{
- LazyInitializer.EnsureInitialized(ref _lastExecutionResult, ref _lastExecutionResultinitialized, ref _lastExecutionResultSyncLock, () =>
+ if (_lastExecutionResult == null)
{
- var path = GetHistoryFilePath();
-
- try
- {
- return JsonSerializer.DeserializeFromFile(path);
- }
- catch (DirectoryNotFoundException)
- {
- // File doesn't exist. No biggie
- return null;
- }
- catch (FileNotFoundException)
+ lock (_lastExecutionResultSyncLock)
{
- // File doesn't exist. No biggie
- return null;
+ if (_lastExecutionResult == null)
+ {
+ var path = GetHistoryFilePath();
+
+ try
+ {
+ return JsonSerializer.DeserializeFromFile(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 (Exception ex)
- {
- Logger.ErrorException("Error deserializing {0}", ex, path);
- return null;
- }
- });
+ }
return _lastExecutionResult;
}
private set
{
_lastExecutionResult = value;
-
- _lastExecutionResultinitialized = value != null;
}
}
@@ -227,13 +224,9 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
///
private IEnumerable _triggers;
///
- /// The _triggers initialized
- ///
- private bool _triggersInitialized;
- ///
/// The _triggers sync lock
///
- private object _triggersSyncLock = new object();
+ private readonly object _triggersSyncLock = new object();
///
/// Gets the triggers that define when the task will run
///
@@ -243,7 +236,16 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
{
get
{
- LazyInitializer.EnsureInitialized(ref _triggers, ref _triggersInitialized, ref _triggersSyncLock, LoadTriggers);
+ if (_triggers == null)
+ {
+ lock (_triggersSyncLock)
+ {
+ if (_triggers == null)
+ {
+ _triggers = LoadTriggers();
+ }
+ }
+ }
return _triggers;
}
@@ -262,8 +264,6 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
_triggers = value.ToList();
- _triggersInitialized = true;
-
ReloadTriggerEvents(false);
SaveTriggers(_triggers);
@@ -335,12 +335,30 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
trigger.Start(false);
}
+ private Task _currentTask;
+
///
/// Executes the task
///
/// Task.
/// Cannot execute a Task that is already running
public async Task Execute()
+ {
+ var task = ExecuteInternal();
+
+ _currentTask = task;
+
+ try
+ {
+ await task.ConfigureAwait(false);
+ }
+ finally
+ {
+ _currentTask = null;
+ }
+ }
+
+ private async Task ExecuteInternal()
{
// Cancel the current execution, if any
if (CurrentCancellationTokenSource != null)
@@ -544,6 +562,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
Id = Id
};
+ var hasKey = ScheduledTask as IHasKey;
+ if (hasKey != null)
+ {
+ result.Key = hasKey.Key;
+ }
+
if (ex != null)
{
result.ErrorMessage = ex.Message;
@@ -579,14 +603,60 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
{
DisposeTriggers();
- if (State == TaskState.Running)
+ var wassRunning = State == TaskState.Running;
+ var startTime = CurrentExecutionStartTime;
+
+ var token = CurrentCancellationTokenSource;
+ if (token != null)
{
- OnTaskCompleted(CurrentExecutionStartTime, DateTime.UtcNow, TaskCompletionStatus.Aborted, null);
+ try
+ {
+ Logger.Debug(Name + ": Cancelling");
+ token.Cancel();
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error calling CancellationToken.Cancel();", ex);
+ }
+ }
+ var task = _currentTask;
+ if (task != null)
+ {
+ try
+ {
+ Logger.Debug(Name + ": Waiting on Task");
+ var exited = Task.WaitAll(new[] { task }, 2000);
+
+ if (exited)
+ {
+ Logger.Debug(Name + ": Task exited");
+ }
+ else
+ {
+ Logger.Debug(Name + ": Timed out waiting for task to stop");
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error calling Task.WaitAll();", ex);
+ }
}
- if (CurrentCancellationTokenSource != null)
+ if (token != null)
+ {
+ try
+ {
+ Logger.Debug(Name + ": Disposing CancellationToken");
+ token.Dispose();
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error calling CancellationToken.Dispose();", ex);
+ }
+ }
+ if (wassRunning)
{
- CurrentCancellationTokenSource.Dispose();
+ OnTaskCompleted(startTime, DateTime.UtcNow, TaskCompletionStatus.Aborted, null);
}
}
}
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index fcb7b159b8..779f992d32 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -125,7 +125,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
{
try
{
- File.Delete(path);
+ _fileSystem.DeleteFile(path);
}
catch (IOException ex)
{
diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index 3b0c02dc6b..6b9bcbfc1a 100644
--- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -76,7 +76,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
cancellationToken.ThrowIfCancellationRequested();
- File.Delete(file.FullName);
+ _fileSystem.DeleteFile(file.FullName);
index++;
}
diff --git a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
index 47215aacf1..264e63b47f 100644
--- a/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
+++ b/MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Implementations.Security;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Progress;
@@ -106,6 +107,7 @@ namespace MediaBrowser.Common.Implementations.Updates
private readonly IJsonSerializer _jsonSerializer;
private readonly ISecurityManager _securityManager;
private readonly IConfigurationManager _config;
+ private readonly IFileSystem _fileSystem;
///
/// Gets the application host.
@@ -113,7 +115,7 @@ namespace MediaBrowser.Common.Implementations.Updates
/// The application host.
private readonly IApplicationHost _applicationHost;
- public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config)
+ public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem)
{
if (logger == null)
{
@@ -129,6 +131,7 @@ namespace MediaBrowser.Common.Implementations.Updates
_jsonSerializer = jsonSerializer;
_securityManager = securityManager;
_config = config;
+ _fileSystem = fileSystem;
_logger = logger;
}
@@ -570,7 +573,7 @@ namespace MediaBrowser.Common.Implementations.Updates
try
{
- File.Delete(tempFile);
+ _fileSystem.DeleteFile(tempFile);
}
catch (IOException e)
{
@@ -591,7 +594,7 @@ namespace MediaBrowser.Common.Implementations.Updates
// Remove it the quick way for now
_applicationHost.RemovePlugin(plugin);
- File.Delete(plugin.AssemblyFilePath);
+ _fileSystem.DeleteFile(plugin.AssemblyFilePath);
OnPluginUninstalled(plugin);
diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config
index 63d288bbbb..825b6662b2 100644
--- a/MediaBrowser.Common.Implementations/packages.config
+++ b/MediaBrowser.Common.Implementations/packages.config
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs
index 4c94f3aa20..22dfa378fc 100644
--- a/MediaBrowser.Common/Extensions/BaseExtensions.cs
+++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs
@@ -26,36 +26,6 @@ namespace MediaBrowser.Common.Extensions
return Regex.Replace(htmlString, pattern, string.Empty).Trim();
}
- ///
- /// Replaces the specified STR.
- ///
- /// The STR.
- /// The old value.
- /// The new value.
- /// The comparison.
- /// System.String.
- public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
- {
- var sb = new StringBuilder();
-
- var previousIndex = 0;
- var index = str.IndexOf(oldValue, comparison);
-
- while (index != -1)
- {
- sb.Append(str.Substring(previousIndex, index - previousIndex));
- sb.Append(newValue);
- index += oldValue.Length;
-
- previousIndex = index;
- index = str.IndexOf(oldValue, index, comparison);
- }
-
- sb.Append(str.Substring(previousIndex));
-
- return sb.ToString();
- }
-
public static string RemoveDiacritics(this string text)
{
return String.Concat(
diff --git a/MediaBrowser.Common/IO/IFileSystem.cs b/MediaBrowser.Common/IO/IFileSystem.cs
index 3bcec98ce6..5ce84f4360 100644
--- a/MediaBrowser.Common/IO/IFileSystem.cs
+++ b/MediaBrowser.Common/IO/IFileSystem.cs
@@ -133,5 +133,33 @@ namespace MediaBrowser.Common.IO
/// The path.
/// true if [is path file] [the specified path]; otherwise, false.
bool IsPathFile(string path);
+
+ ///
+ /// Deletes the file.
+ ///
+ /// The path.
+ /// if set to true [send to recycle bin].
+ void DeleteFile(string path, bool sendToRecycleBin);
+
+ ///
+ /// Deletes the directory.
+ ///
+ /// The path.
+ /// if set to true [recursive].
+ /// if set to true [send to recycle bin].
+ void DeleteDirectory(string path, bool recursive, bool sendToRecycleBin);
+
+ ///
+ /// Deletes the file.
+ ///
+ /// The path.
+ void DeleteFile(string path);
+
+ ///
+ /// Deletes the directory.
+ ///
+ /// The path.
+ /// if set to true [recursive].
+ void DeleteDirectory(string path, bool recursive);
}
}
diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs
index 979dae49c9..8fc947e8fe 100644
--- a/MediaBrowser.Common/Net/INetworkManager.cs
+++ b/MediaBrowser.Common/Net/INetworkManager.cs
@@ -51,5 +51,12 @@ namespace MediaBrowser.Common.Net
/// The endpoint.
/// true if [is in local network] [the specified endpoint]; otherwise, false.
bool IsInLocalNetwork(string endpoint);
+
+ ///
+ /// Generates a self signed certificate at the locatation specified by .
+ ///
+ /// The path to generate the certificate.
+ /// The common name for the certificate.
+ void GenerateSelfSignedSslCertificate(string certificatePath, string hostname);
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs
index 5a9fc33227..b6514ca0a6 100644
--- a/MediaBrowser.Controller/Channels/Channel.cs
+++ b/MediaBrowser.Controller/Channels/Channel.cs
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -14,9 +15,19 @@ namespace MediaBrowser.Controller.Channels
public override bool IsVisible(User user)
{
- if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
+ if (user.Policy.BlockedChannels != null)
{
- return false;
+ if (user.Policy.BlockedChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!user.Policy.EnableAllChannels && !user.Policy.EnabledChannels.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
}
return base.IsVisible(user);
@@ -50,7 +61,22 @@ namespace MediaBrowser.Controller.Channels
protected override string GetInternalMetadataPath(string basePath)
{
- return System.IO.Path.Combine(basePath, "channels", Id.ToString("N"), "metadata");
+ return GetInternalMetadataPath(basePath, Id);
+ }
+
+ public static string GetInternalMetadataPath(string basePath, Guid id)
+ {
+ return System.IO.Path.Combine(basePath, "channels", id.ToString("N"), "metadata");
+ }
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
+ protected override bool IsAllowTagFilterEnforced()
+ {
+ return false;
}
}
}
diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs
index 896d598bb0..91b2407bee 100644
--- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs
+++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs
@@ -3,10 +3,10 @@ using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -32,7 +32,7 @@ namespace MediaBrowser.Controller.Channels
return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
}
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return ExternalId;
}
@@ -89,5 +89,10 @@ namespace MediaBrowser.Controller.Channels
return list;
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs
index 8482e38dff..7ba73d126c 100644
--- a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs
+++ b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs
@@ -1,11 +1,10 @@
-using System;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Users;
+using System;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -40,7 +39,7 @@ namespace MediaBrowser.Controller.Channels
return false;
}
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return ExternalId;
}
@@ -76,5 +75,10 @@ namespace MediaBrowser.Controller.Channels
{
return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs
index f0eafcbdff..d7d4483cd1 100644
--- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs
+++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs
@@ -4,11 +4,11 @@ using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Channels
{
@@ -28,8 +28,8 @@ namespace MediaBrowser.Controller.Channels
public string OriginalImageUrl { get; set; }
public List ChannelMediaSources { get; set; }
-
- public override string GetUserDataKey()
+
+ protected override string CreateUserDataKey()
{
if (ContentType == ChannelMediaContentType.MovieExtra)
{
@@ -119,5 +119,10 @@ namespace MediaBrowser.Controller.Channels
{
return System.IO.Path.Combine(basePath, "channels", ChannelId, Id.ToString("N"));
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs
index 9130f68d43..89e505579d 100644
--- a/MediaBrowser.Controller/Collections/ICollectionManager.cs
+++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs
@@ -60,5 +60,12 @@ namespace MediaBrowser.Controller.Collections
/// The user identifier.
/// Folder.
Folder GetCollectionsFolder(string userId);
+
+ ///
+ /// Gets the collections.
+ ///
+ /// The user.
+ /// IEnumerable<BoxSet>.
+ IEnumerable GetCollections(User user);
}
}
diff --git a/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs b/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs
new file mode 100644
index 0000000000..b3f3bb9025
--- /dev/null
+++ b/MediaBrowser.Controller/Devices/CameraImageUploadInfo.cs
@@ -0,0 +1,10 @@
+using MediaBrowser.Model.Devices;
+
+namespace MediaBrowser.Controller.Devices
+{
+ public class CameraImageUploadInfo
+ {
+ public LocalFileInfo FileInfo { get; set; }
+ public DeviceInfo Device { get; set; }
+ }
+}
diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs
index f5010bb455..78eebd9942 100644
--- a/MediaBrowser.Controller/Devices/IDeviceManager.cs
+++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs
@@ -3,7 +3,6 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session;
using System;
-using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
@@ -15,6 +14,10 @@ namespace MediaBrowser.Controller.Devices
/// Occurs when [device options updated].
///
event EventHandler> DeviceOptionsUpdated;
+ ///
+ /// Occurs when [camera image uploaded].
+ ///
+ event EventHandler> CameraImageUploaded;
///
/// Registers the device.
diff --git a/MediaBrowser.Controller/Dlna/IDlnaManager.cs b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
index 34464f6a2d..2f64cd1946 100644
--- a/MediaBrowser.Controller/Dlna/IDlnaManager.cs
+++ b/MediaBrowser.Controller/Dlna/IDlnaManager.cs
@@ -62,8 +62,9 @@ namespace MediaBrowser.Controller.Dlna
///
/// The headers.
/// The server uu identifier.
+ /// The server address.
/// System.String.
- string GetServerDescriptionXml(IDictionary headers, string serverUuId);
+ string GetServerDescriptionXml(IDictionary headers, string serverUuId, string serverAddress);
///
/// Gets the icon.
diff --git a/MediaBrowser.Controller/Dlna/IMediaReceiverRegistrar.cs b/MediaBrowser.Controller/Dlna/IMediaReceiverRegistrar.cs
new file mode 100644
index 0000000000..6b76783a5d
--- /dev/null
+++ b/MediaBrowser.Controller/Dlna/IMediaReceiverRegistrar.cs
@@ -0,0 +1,7 @@
+
+namespace MediaBrowser.Controller.Dlna
+{
+ public interface IMediaReceiverRegistrar : IEventManager, IUpnpService
+ {
+ }
+}
diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs
index eeb4fc1143..a8d1b1862b 100644
--- a/MediaBrowser.Controller/Dto/DtoOptions.cs
+++ b/MediaBrowser.Controller/Dto/DtoOptions.cs
@@ -17,6 +17,7 @@ namespace MediaBrowser.Controller.Dto
public List ImageTypes { get; set; }
public int ImageTypeLimit { get; set; }
public bool EnableImages { get; set; }
+ public string DeviceId { get; set; }
public DtoOptions()
{
diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs
index 7c7ec56d50..ea311d9937 100644
--- a/MediaBrowser.Controller/Dto/IDtoService.cs
+++ b/MediaBrowser.Controller/Dto/IDtoService.cs
@@ -44,6 +44,17 @@ namespace MediaBrowser.Controller.Dto
/// The owner.
/// BaseItemDto.
BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null);
+
+ ///
+ /// Gets the base item dtos.
+ ///
+ /// The items.
+ /// The options.
+ /// The user.
+ /// The owner.
+ /// IEnumerable<BaseItemDto>.
+ IEnumerable GetBaseItemDtos(IEnumerable items, DtoOptions options, User user = null,
+ BaseItem owner = null);
///
/// Gets the chapter information dto.
diff --git a/MediaBrowser.Controller/Entities/AdultVideo.cs b/MediaBrowser.Controller/Entities/AdultVideo.cs
deleted file mode 100644
index 6c3f7851ea..0000000000
--- a/MediaBrowser.Controller/Entities/AdultVideo.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Entities
-{
- [Obsolete]
- public class AdultVideo : Video, IHasProductionLocations, IHasTaglines
- {
- public List ProductionLocations { get; set; }
-
- public List Taglines { get; set; }
-
- public AdultVideo()
- {
- Taglines = new List();
- ProductionLocations = new List();
- }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index 362096b5e3..66a0d551b2 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -32,6 +32,11 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// The _virtual children
///
@@ -66,7 +71,7 @@ namespace MediaBrowser.Controller.Entities
{
var path = ContainingFolderPath;
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager, directoryService)
+ var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths , directoryService)
{
FileInfo = new DirectoryInfo(path),
Path = path,
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index c5ed09016c..9024479999 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -4,11 +4,11 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -80,6 +80,15 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ [IgnoreDataMember]
+ protected override bool SupportsOwnedItems
+ {
+ get
+ {
+ return false;
+ }
+ }
+
[IgnoreDataMember]
public override Folder LatestItemsIndexContainer
{
@@ -104,6 +113,13 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ public override bool CanDownload()
+ {
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
///
/// Gets or sets the artist.
///
@@ -169,7 +185,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
var parent = FindParent();
@@ -186,7 +202,7 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
- return base.GetUserDataKey();
+ return base.CreateUserDataKey();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
@@ -223,7 +239,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{
Id = i.Id.ToString("N"),
Protocol = locationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File,
- MediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
+ MediaStreams = MediaSourceManager.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList(),
Name = i.Name,
Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
RunTimeTicks = i.RunTimeTicks,
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index 90edfcce70..e3f523b5a7 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -1,11 +1,11 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -52,14 +52,14 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
- public List AlbumArtists { get; set; }
-
[IgnoreDataMember]
public string AlbumArtist
{
get { return AlbumArtists.FirstOrDefault(); }
}
+ public List AlbumArtists { get; set; }
+
///
/// Gets the tracks.
///
@@ -68,7 +68,7 @@ namespace MediaBrowser.Controller.Entities.Audio
{
get
{
- return RecursiveChildren.OfType
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
var id = this.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
@@ -152,7 +152,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return "MusicAlbum-Musicbrainz-" + id;
}
- return base.GetUserDataKey();
+ return base.CreateUserDataKey();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
@@ -173,17 +173,12 @@ namespace MediaBrowser.Controller.Entities.Audio
id.ArtistProviderIds = artist.ProviderIds;
}
- id.SongInfos = RecursiveChildren.OfType()
+ id.SongInfos = GetRecursiveChildren(i => i is Audio)
+ .Cast()
.Select(i => i.GetLookupInfo())
.ToList();
return id;
}
}
-
- [Obsolete]
- public class MusicAlbumDisc : Folder
- {
-
- }
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index a60258d1a7..e65d3c0e78 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -1,14 +1,13 @@
-using System.Runtime.Serialization;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Audio
{
@@ -19,7 +18,8 @@ namespace MediaBrowser.Controller.Entities.Audio
{
public bool IsAccessedByName { get; set; }
public List ProductionLocations { get; set; }
-
+
+ [IgnoreDataMember]
public override bool IsFolder
{
get
@@ -34,6 +34,11 @@ namespace MediaBrowser.Controller.Entities.Audio
get { return true; }
}
+ public override bool CanDelete()
+ {
+ return !IsAccessedByName;
+ }
+
protected override IEnumerable ActualChildren
{
get
@@ -68,7 +73,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return GetUserDataKey(this);
}
@@ -78,6 +83,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// If the item is a folder, it returns the folder itself
///
/// The containing folder path.
+ [IgnoreDataMember]
public override string ContainingFolderPath
{
get
@@ -90,6 +96,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -122,89 +129,53 @@ namespace MediaBrowser.Controller.Entities.Audio
public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress progress, CancellationToken cancellationToken)
{
- var items = RecursiveChildren.ToList();
+ var items = GetRecursiveChildren().ToList();
var songs = items.OfType().ToList();
var others = items.Except(songs).ToList();
var totalItems = songs.Count + others.Count;
- var percentages = new Dictionary(totalItems);
-
- var tasks = new List();
+ var numComplete = 0;
// Refresh songs
foreach (var item in songs)
{
- if (tasks.Count >= 2)
- {
- await Task.WhenAll(tasks).ConfigureAwait(false);
- tasks.Clear();
- }
-
cancellationToken.ThrowIfCancellationRequested();
- var innerProgress = new ActionableProgress();
- // Avoid implicitly captured closure
- var currentChild = item;
- innerProgress.RegisterAction(p =>
- {
- lock (percentages)
- {
- percentages[currentChild.Id] = p / 100;
-
- var percent = percentages.Values.Sum();
- percent /= totalItems;
- percent *= 100;
- progress.Report(percent);
- }
- });
-
- var taskChild = item;
- tasks.Add(Task.Run(async () => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken));
- }
+ await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- await Task.WhenAll(tasks).ConfigureAwait(false);
- tasks.Clear();
+ numComplete++;
+ double percent = numComplete;
+ percent /= totalItems;
+ progress.Report(percent * 100);
+ }
// Refresh current item
await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
+
// Refresh all non-songs
foreach (var item in others)
{
cancellationToken.ThrowIfCancellationRequested();
- // Avoid implicitly captured closure
- var currentChild = item;
-
await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
- lock (percentages)
- {
- percentages[currentChild.Id] = 1;
- var percent = percentages.Values.Sum();
- percent /= totalItems;
- percent *= 100;
- progress.Report(percent);
- }
+ numComplete++;
+ double percent = numComplete;
+ percent /= totalItems;
+ progress.Report(percent * 100);
}
progress.Report(100);
}
- private async Task RefreshItem(BaseItem item, MetadataRefreshOptions refreshOptions, IProgress progress, CancellationToken cancellationToken)
- {
- await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
- progress.Report(100);
- }
-
public ArtistInfo GetLookupInfo()
{
var info = GetItemLookupInfo();
- info.SongInfos = RecursiveChildren.OfType()
+ info.SongInfos = GetRecursiveChildren(i => i is Audio)
+ .Cast()
.Select(i => i.GetLookupInfo())
.ToList();
@@ -213,9 +184,16 @@ namespace MediaBrowser.Controller.Entities.Audio
public IEnumerable GetTaggedItems(IEnumerable inputItems)
{
- return inputItems.OfType()
- .Where(i => i.HasArtist(Name))
- .Cast();
+ return inputItems.Where(GetItemFilter());
+ }
+
+ public Func GetItemFilter()
+ {
+ return i =>
+ {
+ var hasArtist = i as IHasArtist;
+ return hasArtist != null && hasArtist.HasArtist(Name);
+ };
}
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index 928eb64630..ed09560732 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return "MusicGenre-" + Name;
}
@@ -30,6 +30,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// If the item is a folder, it returns the folder itself
///
/// The containing folder path.
+ [IgnoreDataMember]
public override string ContainingFolderPath
{
get
@@ -38,10 +39,16 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -52,7 +59,12 @@ namespace MediaBrowser.Controller.Entities.Audio
public IEnumerable GetTaggedItems(IEnumerable inputItems)
{
- return inputItems.Where(i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
+ return inputItems.Where(GetItemFilter());
+ }
+
+ public Func GetItemFilter()
+ {
+ return i => (i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
}
}
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index ee562d8b49..3ab02ea21a 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -239,6 +239,38 @@ namespace MediaBrowser.Controller.Entities
get { return this.GetImagePath(ImageType.Primary); }
}
+ public virtual bool CanDelete()
+ {
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
+ public virtual bool IsAuthorizedToDelete(User user)
+ {
+ return user.Policy.EnableContentDeletion;
+ }
+
+ public bool CanDelete(User user)
+ {
+ return CanDelete() && IsAuthorizedToDelete(user);
+ }
+
+ public virtual bool CanDownload()
+ {
+ return false;
+ }
+
+ public virtual bool IsAuthorizedToDownload(User user)
+ {
+ return user.Policy.EnableContentDownloading;
+ }
+
+ public bool CanDownload(User user)
+ {
+ return CanDownload() && IsAuthorizedToDownload(user);
+ }
+
///
/// Gets or sets the date created.
///
@@ -268,6 +300,7 @@ namespace MediaBrowser.Controller.Entities
public static IChannelManager ChannelManager { get; set; }
public static ICollectionManager CollectionManager { get; set; }
public static IImageProcessor ImageProcessor { get; set; }
+ public static IMediaSourceManager MediaSourceManager { get; set; }
///
/// Returns a that represents this instance.
@@ -359,7 +392,7 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- if (!string.IsNullOrEmpty(ForcedSortName))
+ if (!string.IsNullOrWhiteSpace(ForcedSortName))
{
return ForcedSortName;
}
@@ -379,21 +412,19 @@ namespace MediaBrowser.Controller.Entities
public string GetInternalMetadataPath()
{
- return GetInternalMetadataPath(ConfigurationManager.ApplicationPaths.InternalMetadataPath);
+ var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
+
+ return GetInternalMetadataPath(basePath);
}
protected virtual string GetInternalMetadataPath(string basePath)
{
var idString = Id.ToString("N");
- return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
- }
-
- public static string GetInternalMetadataPathForId(Guid id)
- {
- var idString = id.ToString("N");
-
- var basePath = ConfigurationManager.ApplicationPaths.InternalMetadataPath;
+ if (ConfigurationManager.Configuration.EnableLibraryMetadataSubFolder)
+ {
+ basePath = System.IO.Path.Combine(basePath, "library");
+ }
return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
}
@@ -692,7 +723,7 @@ namespace MediaBrowser.Controller.Entities
var requiresSave = false;
- if (IsFolder || Parent != null)
+ if (SupportsOwnedItems)
{
try
{
@@ -724,6 +755,12 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ protected virtual bool SupportsOwnedItems
+ {
+ get { return IsFolder || Parent != null; }
+ }
+
///
/// Refreshes owned items such as trailers, theme videos, special features, etc.
/// Returns true or false indicating if changes were found.
@@ -889,11 +926,24 @@ namespace MediaBrowser.Controller.Entities
get { return null; }
}
+ private string _userDataKey;
///
/// Gets the user data key.
///
/// System.String.
- public virtual string GetUserDataKey()
+ public string GetUserDataKey()
+ {
+ if (string.IsNullOrWhiteSpace(_userDataKey))
+ {
+ var key = CreateUserDataKey();
+ _userDataKey = key;
+ return key;
+ }
+
+ return _userDataKey;
+ }
+
+ protected virtual string CreateUserDataKey()
{
return Id.ToString();
}
@@ -905,6 +955,12 @@ namespace MediaBrowser.Controller.Entities
return current.IsInMixedFolder == newItem.IsInMixedFolder;
}
+ public void AfterMetadataRefresh()
+ {
+ _sortName = null;
+ _userDataKey = null;
+ }
+
///
/// Gets the preferred metadata language.
///
@@ -1024,7 +1080,8 @@ namespace MediaBrowser.Controller.Entities
if (hasTags != null)
{
- if (user.Policy.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
+ var policy = user.Policy;
+ if (policy.BlockedTags.Any(i => hasTags.Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
{
return false;
}
@@ -1033,6 +1090,11 @@ namespace MediaBrowser.Controller.Entities
return true;
}
+ protected virtual bool IsAllowTagFilterEnforced()
+ {
+ return true;
+ }
+
///
/// Gets the block unrated value.
///
@@ -1060,6 +1122,23 @@ namespace MediaBrowser.Controller.Entities
return IsParentalAllowed(user);
}
+ public virtual bool IsVisibleStandalone(User user)
+ {
+ if (!IsVisible(user))
+ {
+ return false;
+ }
+
+ if (Parents.Any(i => !i.IsVisible(user)))
+ {
+ return false;
+ }
+
+ // TODO: Need some work here, e.g. is in user library, for channels, can user access channel, etc.
+
+ return true;
+ }
+
///
/// Gets a value indicating whether this instance is folder.
///
@@ -1146,7 +1225,7 @@ namespace MediaBrowser.Controller.Entities
if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType))
{
- return LibraryManager.RootFolder.RecursiveChildren.FirstOrDefault(i =>
+ return LibraryManager.RootFolder.GetRecursiveChildren(i =>
{
if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase))
{
@@ -1164,7 +1243,8 @@ namespace MediaBrowser.Controller.Entities
}
return false;
- });
+
+ }).FirstOrDefault();
}
return null;
@@ -1458,7 +1538,7 @@ namespace MediaBrowser.Controller.Entities
currentFile.Attributes &= ~FileAttributes.Hidden;
}
- currentFile.Delete();
+ FileSystem.DeleteFile(currentFile.FullName);
}
return UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
@@ -1703,6 +1783,9 @@ namespace MediaBrowser.Controller.Entities
///
public virtual bool BeforeMetadataRefresh()
{
+ _userDataKey = null;
+ _sortName = null;
+
var hasChanges = false;
if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Path))
diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
index b30bd81b96..785c441d37 100644
--- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs
+++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
@@ -11,5 +11,10 @@ namespace MediaBrowser.Controller.Entities
{
get { return null; }
}
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index 381b2101d2..e59db67a6a 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Model.Configuration;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
@@ -37,6 +38,13 @@ namespace MediaBrowser.Controller.Entities
Tags = new List();
}
+ public override bool CanDownload()
+ {
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Book);
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index f47a439a7e..a39357f2b0 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -35,6 +35,11 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
public string CollectionType { get; set; }
///
@@ -86,7 +91,7 @@ namespace MediaBrowser.Controller.Entities
{
var path = ContainingFolderPath;
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager, directoryService)
+ var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
{
FileInfo = new DirectoryInfo(path),
Path = path,
@@ -121,12 +126,6 @@ namespace MediaBrowser.Controller.Entities
return args;
}
- // Cache this since it will be used a lot
- ///
- /// The null task result
- ///
- private static readonly Task NullTaskResult = Task.FromResult(null);
-
///
/// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes
/// ***Currently does not contain logic to maintain items that are unavailable in the file system***
@@ -138,7 +137,7 @@ namespace MediaBrowser.Controller.Entities
/// The refresh options.
/// The directory service.
/// Task.
- protected override async Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
+ protected override Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{
var list = PhysicalLocationsList.ToList();
@@ -146,8 +145,10 @@ namespace MediaBrowser.Controller.Entities
if (!list.SequenceEqual(PhysicalLocationsList))
{
- await UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+ return UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
}
+
+ return Task.FromResult(true);
}
///
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 2761aa5d7e..0d9bb03acf 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -6,7 +6,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
-using MoreLinq;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -15,13 +14,14 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
///
/// Class Folder
///
- public class Folder : BaseItem, IHasThemeMedia, IHasTags
+ public class Folder : BaseItem, IHasThemeMedia, IHasTags, IHasPreferredMetadataLanguage
{
public static IUserManager UserManager { get; set; }
public static IUserViewManager UserViewManager { get; set; }
@@ -30,6 +30,14 @@ namespace MediaBrowser.Controller.Entities
public List ThemeVideoIds { get; set; }
public List Tags { get; set; }
+ public string PreferredMetadataLanguage { get; set; }
+
+ ///
+ /// Gets or sets the preferred metadata country code.
+ ///
+ /// The preferred metadata country code.
+ public string PreferredMetadataCountryCode { get; set; }
+
public Folder()
{
LinkedChildren = new List();
@@ -72,6 +80,19 @@ namespace MediaBrowser.Controller.Entities
}
}
+ protected override bool IsAllowTagFilterEnforced()
+ {
+ if (this is ICollectionFolder)
+ {
+ return false;
+ }
+ if (this is UserView)
+ {
+ return false;
+ }
+ return true;
+ }
+
///
/// Gets or sets a value indicating whether this instance is physical root.
///
@@ -98,6 +119,7 @@ namespace MediaBrowser.Controller.Entities
public virtual List LinkedChildren { get; set; }
+ [IgnoreDataMember]
protected virtual bool SupportsShortcutChildren
{
get { return true; }
@@ -237,14 +259,13 @@ namespace MediaBrowser.Controller.Entities
protected virtual IEnumerable GetIndexByOptions()
{
return new List {
- {LocalizedStrings.Instance.GetString("NoneDispPref")},
- {LocalizedStrings.Instance.GetString("PerformerDispPref")},
- {LocalizedStrings.Instance.GetString("GenreDispPref")},
- {LocalizedStrings.Instance.GetString("DirectorDispPref")},
- {LocalizedStrings.Instance.GetString("YearDispPref")},
- {LocalizedStrings.Instance.GetString("StudioDispPref")}
+ {"None"},
+ {"Performer"},
+ {"Genre"},
+ {"Director"},
+ {"Year"},
+ {"Studio"}
};
-
}
///
@@ -275,7 +296,17 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- return _children ?? (_children = LoadChildrenInternal());
+ if (_children == null)
+ {
+ lock (_childrenSyncLock)
+ {
+ if (_children == null)
+ {
+ _children = LoadChildrenInternal();
+ }
+ }
+ }
+ return _children;
}
}
@@ -301,14 +332,24 @@ namespace MediaBrowser.Controller.Entities
public override bool IsVisible(User user)
{
- if (this is ICollectionFolder)
+ if (this is ICollectionFolder && !(this is BasePluginFolder))
{
- if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) ||
+ if (user.Policy.BlockedMediaFolders != null)
+ {
+ if (user.Policy.BlockedMediaFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase) ||
- // Backwards compatibility
- user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase))
+ // Backwards compatibility
+ user.Policy.BlockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+ else
{
- return false;
+ if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Contains(Id.ToString("N"), StringComparer.OrdinalIgnoreCase))
+ {
+ return false;
+ }
}
}
@@ -345,12 +386,7 @@ namespace MediaBrowser.Controller.Entities
/// Task.
public Task ValidateChildren(IProgress progress, CancellationToken cancellationToken, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true)
{
- return ValidateChildrenWithCancellationSupport(progress, cancellationToken, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService);
- }
-
- private Task ValidateChildrenWithCancellationSupport(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
- {
- return ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
+ return ValidateChildrenInternal(progress, cancellationToken, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService);
}
private Dictionary GetActualChildrenDictionary()
@@ -540,50 +576,49 @@ namespace MediaBrowser.Controller.Entities
var children = ActualChildren.ToList();
var percentages = new Dictionary(children.Count);
-
- var tasks = new List();
+ var numComplete = 0;
+ var count = children.Count;
foreach (var child in children)
{
- if (tasks.Count >= 2)
- {
- await Task.WhenAll(tasks).ConfigureAwait(false);
- tasks.Clear();
- }
-
cancellationToken.ThrowIfCancellationRequested();
- var innerProgress = new ActionableProgress();
- // Avoid implicitly captured closure
- var currentChild = child;
- innerProgress.RegisterAction(p =>
+ if (child.IsFolder)
{
- lock (percentages)
+ var innerProgress = new ActionableProgress();
+
+ // Avoid implicitly captured closure
+ var currentChild = child;
+ innerProgress.RegisterAction(p =>
{
- percentages[currentChild.Id] = p / 100;
+ lock (percentages)
+ {
+ percentages[currentChild.Id] = p / 100;
- var percent = percentages.Values.Sum();
- percent /= children.Count;
- percent *= 100;
- progress.Report(percent);
- }
- });
+ var innerPercent = percentages.Values.Sum();
+ innerPercent /= count;
+ innerPercent *= 100;
+ progress.Report(innerPercent);
+ }
+ });
- if (child.IsFolder)
- {
await RefreshChildMetadata(child, refreshOptions, recursive, innerProgress, cancellationToken)
.ConfigureAwait(false);
}
else
{
- // Avoid implicitly captured closure
- var taskChild = child;
-
- tasks.Add(Task.Run(async () => await RefreshChildMetadata(taskChild, refreshOptions, false, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken));
+ await RefreshChildMetadata(child, refreshOptions, false, new Progress(), cancellationToken)
+ .ConfigureAwait(false);
}
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= count;
+ percent *= 100;
+
+ progress.Report(percent);
}
- await Task.WhenAll(tasks).ConfigureAwait(false);
progress.Report(100);
}
@@ -648,7 +683,7 @@ namespace MediaBrowser.Controller.Entities
}
});
- await child.ValidateChildrenWithCancellationSupport(innerProgress, cancellationToken, true, false, null, directoryService)
+ await child.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService)
.ConfigureAwait(false);
}
}
@@ -678,12 +713,12 @@ namespace MediaBrowser.Controller.Entities
path = System.IO.Path.GetDirectoryName(path);
}
- if (ContainsPath(LibraryManager.GetDefaultVirtualFolders(), originalPath))
+ if (ContainsPath(LibraryManager.GetVirtualFolders(), originalPath))
{
return true;
}
- return UserManager.Users.Any(user => ContainsPath(LibraryManager.GetVirtualFolders(user), originalPath));
+ return ContainsPath(LibraryManager.GetVirtualFolders(), originalPath);
}
///
@@ -731,28 +766,6 @@ namespace MediaBrowser.Controller.Entities
return childrenItems;
}
- ///
- /// Retrieves the child.
- ///
- /// The child.
- /// BaseItem.
- private BaseItem RetrieveChild(Guid child)
- {
- var item = LibraryManager.GetItemById(child);
-
- if (item != null)
- {
- if (item is IByReferenceItem)
- {
- return LibraryManager.GetOrAddByReferenceItem(item);
- }
-
- item.Parent = this;
- }
-
- return item;
- }
-
private BaseItem RetrieveChild(BaseItem child)
{
if (child.Id == Guid.Empty)
@@ -786,18 +799,31 @@ namespace MediaBrowser.Controller.Entities
{
var user = query.User;
- var items = query.Recursive
- ? GetRecursiveChildren(user)
- : GetChildren(user, true);
+ Func filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
+
+ IEnumerable items;
+
+ if (query.User == null)
+ {
+ items = query.Recursive
+ ? GetRecursiveChildren(filter)
+ : Children.Where(filter);
+ }
+ else
+ {
+ items = query.Recursive
+ ? GetRecursiveChildren(user, filter)
+ : GetChildren(user, true).Where(filter);
+ }
- var result = SortAndFilter(items, query);
+ var result = PostFilterAndSort(items, query);
return Task.FromResult(result);
}
- protected QueryResult SortAndFilter(IEnumerable items, InternalItemsQuery query)
+ protected QueryResult PostFilterAndSort(IEnumerable items, InternalItemsQuery query)
{
- return UserViewBuilder.SortAndFilter(items, this, null, query, LibraryManager, UserDataManager);
+ return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager);
}
///
@@ -822,11 +848,11 @@ namespace MediaBrowser.Controller.Entities
//the true root should return our users root folder children
if (IsPhysicalRoot) return user.RootFolder.GetChildren(user, includeLinkedChildren);
- var list = new List();
+ var result = new Dictionary();
- var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, includeHidden, false);
+ AddChildren(user, includeLinkedChildren, result, includeHidden, false, null);
- return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
+ return result.Values;
}
protected virtual IEnumerable GetEligibleChildrenForRecursiveChildren(User user)
@@ -839,31 +865,30 @@ namespace MediaBrowser.Controller.Entities
///
/// The user.
/// if set to true [include linked children].
- /// The list.
+ /// The result.
/// if set to true [include hidden].
/// if set to true [recursive].
+ /// The filter.
/// true if XXXX, false otherwise
- private bool AddChildrenToList(User user, bool includeLinkedChildren, List list, bool includeHidden, bool recursive)
+ private void AddChildren(User user, bool includeLinkedChildren, Dictionary result, bool includeHidden, bool recursive, Func filter)
{
- var hasLinkedChildren = false;
-
foreach (var child in GetEligibleChildrenForRecursiveChildren(user))
{
if (child.IsVisible(user))
{
if (includeHidden || !child.IsHiddenFromUser(user))
{
- list.Add(child);
+ if (filter == null || filter(child))
+ {
+ result[child.Id] = child;
+ }
}
if (recursive && child.IsFolder)
{
var folder = (Folder)child;
- if (folder.AddChildrenToList(user, includeLinkedChildren, list, includeHidden, true))
- {
- hasLinkedChildren = true;
- }
+ folder.AddChildren(user, includeLinkedChildren, result, includeHidden, true, filter);
}
}
}
@@ -874,14 +899,13 @@ namespace MediaBrowser.Controller.Entities
{
if (child.IsVisible(user))
{
- hasLinkedChildren = true;
-
- list.Add(child);
+ if (filter == null || filter(child))
+ {
+ result[child.Id] = child;
+ }
}
}
}
-
- return hasLinkedChildren;
}
///
@@ -891,18 +915,23 @@ namespace MediaBrowser.Controller.Entities
/// if set to true [include linked children].
/// IEnumerable{BaseItem}.
///
- public virtual IEnumerable GetRecursiveChildren(User user, bool includeLinkedChildren = true)
+ public IEnumerable GetRecursiveChildren(User user, bool includeLinkedChildren = true)
+ {
+ return GetRecursiveChildren(user, i => true);
+ }
+
+ public virtual IEnumerable GetRecursiveChildren(User user, Func filter)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
- var list = new List();
+ var result = new Dictionary();
- var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, true);
+ AddChildren(user, true, result, false, true, filter);
- return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
+ return result.Values;
}
///
@@ -910,10 +939,15 @@ namespace MediaBrowser.Controller.Entities
///
/// IList{BaseItem}.
public IList GetRecursiveChildren()
+ {
+ return GetRecursiveChildren(i => true);
+ }
+
+ public IList GetRecursiveChildren(Func filter)
{
var list = new List();
- AddChildrenToList(list, true, null);
+ AddChildrenToList(list, true, filter);
return list;
}
@@ -1022,6 +1056,15 @@ namespace MediaBrowser.Controller.Entities
.Where(i => i.Item2 != null);
}
+ [IgnoreDataMember]
+ protected override bool SupportsOwnedItems
+ {
+ get
+ {
+ return base.SupportsOwnedItems || SupportsShortcutChildren;
+ }
+ }
+
protected override async Task RefreshedOwnedItems(MetadataRefreshOptions options, List fileSystemChildren, CancellationToken cancellationToken)
{
var changesFound = false;
@@ -1126,8 +1169,7 @@ namespace MediaBrowser.Controller.Entities
bool resetPosition)
{
// Sweep through recursively and update status
- var tasks = GetRecursiveChildren(user, true)
- .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
+ var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.Select(c => c.MarkPlayed(user, datePlayed, resetPosition));
await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -1141,8 +1183,7 @@ namespace MediaBrowser.Controller.Entities
public override async Task MarkUnplayed(User user)
{
// Sweep through recursively and update status
- var tasks = GetRecursiveChildren(user, true)
- .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
+ var tasks = GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.Select(c => c.MarkUnplayed(user));
await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -1171,15 +1212,15 @@ namespace MediaBrowser.Controller.Entities
return this;
}
- return RecursiveChildren.FirstOrDefault(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) ||
+ return GetRecursiveChildren(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) ||
(!i.IsFolder && !i.IsInMixedFolder && string.Equals(i.ContainingFolderPath, path, StringComparison.OrdinalIgnoreCase)) ||
- i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase));
+ i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
+ .FirstOrDefault();
}
public override bool IsPlayed(User user)
{
- return GetRecursiveChildren(user)
- .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual)
+ return GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual)
.All(i => i.IsPlayed(user));
}
@@ -1206,8 +1247,7 @@ namespace MediaBrowser.Controller.Entities
}
else
{
- children = folder.GetRecursiveChildren(user)
- .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual);
+ children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual);
}
// Loop through each recursive child
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index bf32d3e634..899e5628f1 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -1,10 +1,10 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -38,6 +38,13 @@ namespace MediaBrowser.Controller.Entities
public List LocalTrailerIds { get; set; }
public List RemoteTrailerIds { get; set; }
+ public override bool CanDownload()
+ {
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
///
/// Gets or sets the tags.
///
@@ -88,7 +95,7 @@ namespace MediaBrowser.Controller.Entities
///
public List MultiPartGameFiles { get; set; }
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
var id = this.GetProviderId(MetadataProviders.Gamesdb);
@@ -96,7 +103,7 @@ namespace MediaBrowser.Controller.Entities
{
return "Game-Gamesdb-" + id;
}
- return base.GetUserDataKey();
+ return base.CreateUserDataKey();
}
public override IEnumerable GetDeletePaths()
diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs
index 8254689540..b246b9388f 100644
--- a/MediaBrowser.Controller/Entities/GameGenre.cs
+++ b/MediaBrowser.Controller/Entities/GameGenre.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities
{
@@ -10,7 +11,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return "GameGenre-" + Name;
}
@@ -20,6 +21,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself
///
/// The containing folder path.
+ [IgnoreDataMember]
public override string ContainingFolderPath
{
get
@@ -32,6 +34,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -40,9 +43,19 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
public IEnumerable GetTaggedItems(IEnumerable inputItems)
{
- return inputItems.Where(i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
+ return inputItems.Where(GetItemFilter());
+ }
+
+ public Func GetItemFilter()
+ {
+ return i => (i is Game) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
}
}
}
diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs
index 7584989779..cf69167638 100644
--- a/MediaBrowser.Controller/Entities/GameSystem.cs
+++ b/MediaBrowser.Controller/Entities/GameSystem.cs
@@ -35,13 +35,13 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
if (!string.IsNullOrEmpty(GameSystemName))
{
return "GameSystem-" + GameSystemName;
}
- return base.GetUserDataKey();
+ return base.CreateUserDataKey();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index 05442f2b7f..e17a5c1d8b 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities.Audio;
+using System.Runtime.Serialization;
+using MediaBrowser.Controller.Entities.Audio;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,7 +15,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return "Genre-" + Name;
}
@@ -24,6 +25,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself
///
/// The containing folder path.
+ [IgnoreDataMember]
public override string ContainingFolderPath
{
get
@@ -32,10 +34,16 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -46,7 +54,12 @@ namespace MediaBrowser.Controller.Entities
public IEnumerable GetTaggedItems(IEnumerable inputItems)
{
- return inputItems.Where(i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase));
+ return inputItems.Where(GetItemFilter());
+ }
+
+ public Func GetItemFilter()
+ {
+ return i => !(i is Game) && !(i is IHasMusicGenres) && i.Genres.Contains(Name, StringComparer.OrdinalIgnoreCase);
}
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs
index 10ddaa474a..3643c58b3e 100644
--- a/MediaBrowser.Controller/Entities/IHasMetadata.cs
+++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs
@@ -54,5 +54,10 @@ namespace MediaBrowser.Controller.Entities
/// Gets the item identities.
///
List Identities { get; set; }
+
+ ///
+ /// Afters the metadata refresh.
+ ///
+ void AfterMetadataRefresh();
}
}
diff --git a/MediaBrowser.Controller/Entities/IItemByName.cs b/MediaBrowser.Controller/Entities/IItemByName.cs
index 70d5b840fd..14b69b8fde 100644
--- a/MediaBrowser.Controller/Entities/IItemByName.cs
+++ b/MediaBrowser.Controller/Entities/IItemByName.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
@@ -13,6 +14,12 @@ namespace MediaBrowser.Controller.Entities
/// The input items.
/// IEnumerable{BaseItem}.
IEnumerable GetTaggedItems(IEnumerable inputItems);
+
+ ///
+ /// Gets the item filter.
+ ///
+ /// Func<BaseItem, System.Boolean>.
+ Func GetItemFilter();
}
public interface IHasDualAccess : IItemByName
diff --git a/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs b/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs
index 0fd463155f..fbe5a06d0e 100644
--- a/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs
+++ b/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs
@@ -1,6 +1,4 @@
-using System;
-using System.Collections.Generic;
-
+
namespace MediaBrowser.Controller.Entities
{
///
@@ -10,10 +8,5 @@ namespace MediaBrowser.Controller.Entities
///
public interface ISupportsBoxSetGrouping
{
- ///
- /// Gets or sets the box set identifier list.
- ///
- /// The box set identifier list.
- List BoxSetIdList { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 30043682dd..e99c11e87d 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Entities
public User User { get; set; }
- public Func Filter { get; set; }
+ public Func Filter { get; set; }
public bool? IsFolder { get; set; }
public bool? IsFavorite { get; set; }
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 4483c7b0f2..d874046efd 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -1,22 +1,22 @@
-using MediaBrowser.Common.Progress;
+using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Movies
{
///
/// Class BoxSet
///
- public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo, IMetadataContainer, IHasShares
+ public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo, IMetadataContainer, IHasShares
{
public List Shares { get; set; }
@@ -54,14 +54,6 @@ namespace MediaBrowser.Controller.Entities.Movies
/// The tags.
public List Keywords { get; set; }
- public string PreferredMetadataLanguage { get; set; }
-
- ///
- /// Gets or sets the preferred metadata country code.
- ///
- /// The preferred metadata country code.
- public string PreferredMetadataCountryCode { get; set; }
-
///
/// Gets or sets the display order.
///
@@ -82,6 +74,11 @@ namespace MediaBrowser.Controller.Entities.Movies
}
}
+ public override bool IsAuthorizedToDelete(User user)
+ {
+ return true;
+ }
+
///
/// Gets the trailer ids.
///
@@ -93,6 +90,31 @@ namespace MediaBrowser.Controller.Entities.Movies
return list;
}
+ ///
+ /// Updates the official rating based on content and returns true or false indicating if it changed.
+ ///
+ ///
+ public bool UpdateRatingToContent()
+ {
+ var currentOfficialRating = OfficialRating;
+
+ // Gather all possible ratings
+ var ratings = GetRecursiveChildren()
+ .Concat(GetLinkedChildren())
+ .Where(i => i is Movie || i is Series)
+ .Select(i => i.OfficialRating)
+ .Where(i => !string.IsNullOrEmpty(i))
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Select(i => new Tuple(i, LocalizationManager.GetRatingLevel(i)))
+ .OrderBy(i => i.Item2 ?? 1000)
+ .Select(i => i.Item1);
+
+ OfficialRating = ratings.FirstOrDefault() ?? currentOfficialRating;
+
+ return !string.Equals(currentOfficialRating ?? string.Empty, OfficialRating ?? string.Empty,
+ StringComparison.OrdinalIgnoreCase);
+ }
+
public override IEnumerable GetChildren(User user, bool includeLinkedChildren)
{
var children = base.GetChildren(user, includeLinkedChildren);
@@ -122,34 +144,22 @@ namespace MediaBrowser.Controller.Entities.Movies
{
// Refresh bottom up, children first, then the boxset
// By then hopefully the movies within will have Tmdb collection values
- var items = RecursiveChildren.ToList();
+ var items = GetRecursiveChildren().ToList();
var totalItems = items.Count;
- var percentages = new Dictionary(totalItems);
+ var numComplete = 0;
// Refresh songs
foreach (var item in items)
{
cancellationToken.ThrowIfCancellationRequested();
- var innerProgress = new ActionableProgress();
- // Avoid implicitly captured closure
- var currentChild = item;
- innerProgress.RegisterAction(p =>
- {
- lock (percentages)
- {
- percentages[currentChild.Id] = p / 100;
-
- var percent = percentages.Values.Sum();
- percent /= totalItems;
- percent *= 100;
- progress.Report(percent);
- }
- });
-
- // Avoid implicitly captured closure
- await RefreshItem(item, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false);
+ await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= totalItems;
+ progress.Report(percent * 100);
}
// Refresh current item
@@ -158,13 +168,6 @@ namespace MediaBrowser.Controller.Entities.Movies
progress.Report(100);
}
- private async Task RefreshItem(BaseItem item, MetadataRefreshOptions refreshOptions, IProgress progress, CancellationToken cancellationToken)
- {
- await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
- progress.Report(100);
- }
-
public override bool IsVisible(User user)
{
if (base.IsVisible(user))
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index b3774cfe02..cfe008bd7c 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -25,12 +25,6 @@ namespace MediaBrowser.Controller.Entities.Movies
public List ThemeVideoIds { get; set; }
public List ProductionLocations { get; set; }
- ///
- /// This is just a cache to enable quick access by Id
- ///
- [IgnoreDataMember]
- public List BoxSetIdList { get; set; }
-
public Movie()
{
SpecialFeatureIds = new List();
@@ -40,7 +34,6 @@ namespace MediaBrowser.Controller.Entities.Movies
RemoteTrailerIds = new List();
ThemeSongIds = new List();
ThemeVideoIds = new List();
- BoxSetIdList = new List();
Taglines = new List();
Keywords = new List();
ProductionLocations = new List();
@@ -107,9 +100,9 @@ namespace MediaBrowser.Controller.Entities.Movies
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
- return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.GetUserDataKey();
+ return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey();
}
protected override async Task RefreshedOwnedItems(MetadataRefreshOptions options, List fileSystemChildren, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index 4ca8cf1c5a..771c62fd6d 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -47,21 +47,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- ///
- /// TODO: Remove
- ///
- public string Artist
- {
- get { return Artists.FirstOrDefault(); }
- set
- {
- if (!string.IsNullOrEmpty(value) && !Artists.Contains(value, StringComparer.OrdinalIgnoreCase))
- {
- Artists.Add(value);
- }
- }
- }
-
///
/// Determines whether the specified name has artist.
///
@@ -76,9 +61,9 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
- return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.GetUserDataKey();
+ return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index fe8d618362..d8cb69ca17 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Providers;
+using System.Runtime.Serialization;
+using MediaBrowser.Controller.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -15,12 +16,12 @@ namespace MediaBrowser.Controller.Entities
///
/// The place of birth.
public string PlaceOfBirth { get; set; }
-
+
///
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return "Person-" + Name;
}
@@ -35,6 +36,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself
///
/// The containing folder path.
+ [IgnoreDataMember]
public override string ContainingFolderPath
{
get
@@ -43,10 +45,16 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -57,7 +65,13 @@ namespace MediaBrowser.Controller.Entities
public IEnumerable GetTaggedItems(IEnumerable inputItems)
{
- return inputItems.Where(i => i.People.Any(p => string.Equals(p.Name, Name, StringComparison.OrdinalIgnoreCase)));
+ return inputItems.Where(GetItemFilter());
+ }
+
+
+ public Func GetItemFilter()
+ {
+ return i => i.People.Any(p => string.Equals(p.Name, Name, StringComparison.OrdinalIgnoreCase));
}
}
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index 0d934ad0a5..31bbaf422e 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities
{
@@ -15,12 +16,12 @@ namespace MediaBrowser.Controller.Entities
{
Tags = new List();
}
-
+
///
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return "Studio-" + Name;
}
@@ -30,6 +31,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself
///
/// The containing folder path.
+ [IgnoreDataMember]
public override string ContainingFolderPath
{
get
@@ -38,10 +40,16 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -52,7 +60,13 @@ namespace MediaBrowser.Controller.Entities
public IEnumerable GetTaggedItems(IEnumerable inputItems)
{
- return inputItems.Where(i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase));
+ return inputItems.Where(GetItemFilter());
+ }
+
+
+ public Func GetItemFilter()
+ {
+ return i => i.Studios.Contains(Name, StringComparer.OrdinalIgnoreCase);
}
}
}
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 6b67cebc88..c8408365d3 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -55,6 +55,15 @@ namespace MediaBrowser.Controller.Entities.TV
get { return true; }
}
+ [IgnoreDataMember]
+ protected override bool SupportsOwnedItems
+ {
+ get
+ {
+ return IsStacked || MediaSourceCount > 1;
+ }
+ }
+
[IgnoreDataMember]
public int? AiredSeasonNumber
{
@@ -117,7 +126,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
var series = Series;
@@ -126,7 +135,7 @@ namespace MediaBrowser.Controller.Entities.TV
return series.GetUserDataKey() + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000");
}
- return base.GetUserDataKey();
+ return base.CreateUserDataKey();
}
///
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 54db12b6f8..a99b8c659a 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -81,10 +81,10 @@ namespace MediaBrowser.Controller.Entities.TV
protected override IEnumerable GetIndexByOptions()
{
return new List {
- {LocalizedStrings.Instance.GetString("NoneDispPref")},
- {LocalizedStrings.Instance.GetString("PerformerDispPref")},
- {LocalizedStrings.Instance.GetString("DirectorDispPref")},
- {LocalizedStrings.Instance.GetString("YearDispPref")},
+ {"None"},
+ {"Performer"},
+ {"Director"},
+ {"Year"},
};
}
@@ -92,7 +92,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
if (Series != null)
{
@@ -100,7 +100,7 @@ namespace MediaBrowser.Controller.Entities.TV
return Series.GetUserDataKey() + seasonNo.ToString("000");
}
- return base.GetUserDataKey();
+ return base.CreateUserDataKey();
}
///
@@ -244,7 +244,7 @@ namespace MediaBrowser.Controller.Entities.TV
private IEnumerable GetEpisodes()
{
- var episodes = RecursiveChildren.OfType();
+ var episodes = GetRecursiveChildren().OfType();
var series = Series;
if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
@@ -254,12 +254,12 @@ namespace MediaBrowser.Controller.Entities.TV
if (seasonNumber.HasValue)
{
- list.AddRange(series.RecursiveChildren.OfType()
+ list.AddRange(series.GetRecursiveChildren().OfType()
.Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
}
else
{
- list.AddRange(series.RecursiveChildren.OfType()
+ list.AddRange(series.GetRecursiveChildren().OfType()
.Where(i => !i.ParentIndexNumber.HasValue));
}
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 55cfffeb26..619617e9fa 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -1,4 +1,6 @@
-using MediaBrowser.Controller.Localization;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -14,7 +16,7 @@ namespace MediaBrowser.Controller.Entities.TV
///
/// Class Series
///
- public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo, IHasSpecialFeatures
+ public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasDisplayOrder, IHasLookupInfo, IHasSpecialFeatures, IMetadataContainer
{
public List SpecialFeatureIds { get; set; }
public List SoundtrackIds { get; set; }
@@ -23,12 +25,6 @@ namespace MediaBrowser.Controller.Entities.TV
public int? AnimeSeriesIndex { get; set; }
- ///
- /// Gets or sets the preferred metadata country code.
- ///
- /// The preferred metadata country code.
- public string PreferredMetadataCountryCode { get; set; }
-
public Series()
{
AirDays = new List();
@@ -93,7 +89,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
get
{
- return RecursiveChildren.OfType()
+ return GetRecursiveChildren(i => i is Episode)
.Select(i => i.DateCreated)
.OrderByDescending(i => i)
.FirstOrDefault();
@@ -117,9 +113,9 @@ namespace MediaBrowser.Controller.Entities.TV
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
- return this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Tvcom) ?? base.GetUserDataKey();
+ return this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Tvcom) ?? base.CreateUserDataKey();
}
///
@@ -137,10 +133,10 @@ namespace MediaBrowser.Controller.Entities.TV
protected override IEnumerable GetIndexByOptions()
{
return new List {
- {LocalizedStrings.Instance.GetString("NoneDispPref")},
- {LocalizedStrings.Instance.GetString("PerformerDispPref")},
- {LocalizedStrings.Instance.GetString("DirectorDispPref")},
- {LocalizedStrings.Instance.GetString("YearDispPref")},
+ {"None"},
+ {"Performer"},
+ {"Director"},
+ {"Year"},
};
}
@@ -191,6 +187,85 @@ namespace MediaBrowser.Controller.Entities.TV
.Cast();
}
+ public IEnumerable GetEpisodes(User user)
+ {
+ var config = user.Configuration;
+
+ return GetEpisodes(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
+ }
+
+ public IEnumerable GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired)
+ {
+ var allEpisodes = GetSeasons(user, true, true)
+ .SelectMany(i => i.GetEpisodes(user, includeMissing, includeVirtualUnaired))
+ .Reverse()
+ .ToList();
+
+ // Specials could appear twice based on above - once in season 0, once in the aired season
+ // This depends on settings for that series
+ // When this happens, remove the duplicate from season 0
+ var returnList = new List();
+
+ foreach (var episode in allEpisodes)
+ {
+ if (!returnList.Contains(episode))
+ {
+ returnList.Insert(0, episode);
+ }
+ }
+
+ return returnList;
+ }
+
+ public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress progress, CancellationToken cancellationToken)
+ {
+ // Refresh bottom up, children first, then the boxset
+ // By then hopefully the movies within will have Tmdb collection values
+ var items = GetRecursiveChildren().ToList();
+
+ var seasons = items.OfType().ToList();
+ var otherItems = items.Except(seasons).ToList();
+
+ var totalItems = seasons.Count + otherItems.Count;
+ var numComplete = 0;
+
+ refreshOptions = new MetadataRefreshOptions(refreshOptions);
+ refreshOptions.IsPostRecursiveRefresh = true;
+
+ // Refresh songs
+ foreach (var item in seasons)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= totalItems;
+ progress.Report(percent * 100);
+ }
+
+ // Refresh current item
+ await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+
+ // Refresh all non-songs
+ foreach (var item in otherItems)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+
+ numComplete++;
+ double percent = numComplete;
+ percent /= totalItems;
+ progress.Report(percent * 100);
+ }
+
+ await ProviderManager.RefreshMetadata(this, refreshOptions, cancellationToken).ConfigureAwait(false);
+
+ progress.Report(100);
+ }
+
public IEnumerable GetEpisodes(User user, int seasonNumber)
{
var config = user.Configuration;
@@ -206,8 +281,8 @@ namespace MediaBrowser.Controller.Entities.TV
internal IEnumerable GetEpisodes(User user, int seasonNumber, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable additionalEpisodes)
{
- var episodes = GetRecursiveChildren(user)
- .OfType();
+ var episodes = GetRecursiveChildren(user, i => i is Episode)
+ .Cast();
episodes = FilterEpisodesBySeason(episodes, seasonNumber, DisplaySpecialsWithSeasons);
@@ -262,8 +337,6 @@ namespace MediaBrowser.Controller.Entities.TV
return config.BlockUnratedItems.Contains(UnratedItem.Series);
}
- public string PreferredMetadataLanguage { get; set; }
-
public SeriesInfo GetLookupInfo()
{
var info = GetItemLookupInfo();
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index 7a1eef8dbb..522e6edf69 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -1,12 +1,12 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities
{
@@ -79,7 +79,7 @@ namespace MediaBrowser.Controller.Entities
}
}
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
var key = this.GetProviderId(MetadataProviders.Imdb) ?? this.GetProviderId(MetadataProviders.Tmdb);
@@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Entities
return key;
}
- return base.GetUserDataKey();
+ return base.CreateUserDataKey();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
index 626afcfdfe..01a7486b31 100644
--- a/MediaBrowser.Controller/Entities/User.cs
+++ b/MediaBrowser.Controller/Entities/User.cs
@@ -32,7 +32,7 @@ namespace MediaBrowser.Controller.Entities
///
/// The password.
public string Password { get; set; }
- public string LocalPassword { get; set; }
+ public string EasyPassword { get; set; }
public string ConnectUserName { get; set; }
public string ConnectUserId { get; set; }
@@ -62,6 +62,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself
///
/// The containing folder path.
+ [IgnoreDataMember]
public override string ContainingFolderPath
{
get
@@ -74,6 +75,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -166,7 +168,7 @@ namespace MediaBrowser.Controller.Entities
}
// If only the casing is changing, leave the file system alone
- if (!UsesIdForConfigurationPath && !newName.Equals(Name, StringComparison.OrdinalIgnoreCase))
+ if (!UsesIdForConfigurationPath && !string.Equals(newName, Name, StringComparison.OrdinalIgnoreCase))
{
UsesIdForConfigurationPath = true;
@@ -177,7 +179,7 @@ namespace MediaBrowser.Controller.Entities
// Exceptions will be thrown if these paths already exist
if (Directory.Exists(newConfigDirectory))
{
- Directory.Delete(newConfigDirectory, true);
+ FileSystem.DeleteDirectory(newConfigDirectory, true);
}
if (Directory.Exists(oldConfigurationDirectory))
@@ -227,16 +229,16 @@ namespace MediaBrowser.Controller.Entities
/// System.String.
private string GetConfigurationDirectoryPath(string username)
{
- if (string.IsNullOrEmpty(username))
- {
- throw new ArgumentNullException("username");
- }
-
var parentPath = ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath;
// Legacy
if (!UsesIdForConfigurationPath)
{
+ if (string.IsNullOrEmpty(username))
+ {
+ throw new ArgumentNullException("username");
+ }
+
var safeFolderName = FileSystem.GetValidFilename(username);
return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, safeFolderName);
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index a7276e262c..b065ae1715 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -18,10 +18,13 @@ namespace MediaBrowser.Controller.Entities
{
public override async Task> GetItems(InternalItemsQuery query)
{
+ var user = query.User;
+ Func filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
+
if (query.Recursive)
{
- var items = query.User.RootFolder.GetRecursiveChildren(query.User);
- return SortAndFilter(items, query);
+ var items = query.User.RootFolder.GetRecursiveChildren(query.User, filter);
+ return PostFilterAndSort(items, query);
}
var result = await UserViewManager.GetUserViews(new UserViewQuery
@@ -30,7 +33,7 @@ namespace MediaBrowser.Controller.Entities
}, CancellationToken.None).ConfigureAwait(false);
- return SortAndFilter(result, query);
+ return PostFilterAndSort(result.Where(filter), query);
}
public override bool IsPreSorted
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 0364ff6782..5f7ca3d3f4 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -40,12 +40,18 @@ namespace MediaBrowser.Controller.Entities
return result.Items;
}
- public override IEnumerable GetRecursiveChildren(User user, bool includeLinkedChildren = true)
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
+ public override IEnumerable GetRecursiveChildren(User user, Func filter)
{
var result = GetItems(new InternalItemsQuery
{
User = user,
- Recursive = true
+ Recursive = true,
+ Filter = filter
}).Result;
@@ -63,8 +69,7 @@ namespace MediaBrowser.Controller.Entities
{
CollectionType.Books,
CollectionType.HomeVideos,
- CollectionType.Photos,
- string.Empty
+ CollectionType.Photos
};
var collectionFolder = folder as ICollectionFolder;
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 166d56c514..b5ca053ecd 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -18,6 +18,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MoreLinq;
namespace MediaBrowser.Controller.Entities
{
@@ -117,7 +118,7 @@ namespace MediaBrowser.Controller.Entities
return await GetGameView(user, queryParent, query).ConfigureAwait(false);
case CollectionType.BoxSets:
- return GetResult(GetMediaFolders(user).SelectMany(i => i.GetRecursiveChildren(user)).OfType(), queryParent, query);
+ return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false);
case CollectionType.TvShows:
return await GetTvView(queryParent, user, query).ConfigureAwait(false);
@@ -238,7 +239,9 @@ namespace MediaBrowser.Controller.Entities
{
if (query.Recursive)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
var list = new List();
@@ -392,27 +395,54 @@ namespace MediaBrowser.Controller.Entities
private QueryResult GetMusicAlbums(Folder parent, User user, InternalItemsQuery query)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }).Where(i => i is MusicAlbum), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => (i is MusicAlbum) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetMusicSongs(Folder parent, User user, InternalItemsQuery query)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }).Where(i => i is Audio.Audio), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }, i => (i is Audio.Audio) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetMusicLatest(Folder parent, User user, InternalItemsQuery query)
{
- query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
- query.SortOrder = SortOrder.Descending;
+ var items = _userViewManager.GetLatestItems(new LatestItemsQuery
+ {
+ UserId = user.Id.ToString("N"),
+ Limit = GetSpecialItemsLimit(),
+ IncludeItemTypes = new[] { typeof(Audio.Audio).Name },
+ ParentId = (parent == null ? null : parent.Id.ToString("N")),
+ GroupItems = true
+
+ }).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null);
+
+ query.SortBy = new string[] { };
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music, CollectionType.MusicVideos }).Where(i => i is MusicVideo || i is Audio.Audio), parent, GetSpecialItemsLimit(), query);
+ //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> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
{
if (query.Recursive)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie || i is BoxSet), parent, query);
+ var recursiveItems = GetRecursiveChildren(parent, user,
+ new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty })
+ .Where(i => i is Movie || i is BoxSet);
+
+ //var collections = _collectionManager.CollapseItemsWithinBoxSets(recursiveItems, user).ToList();
+
+ //if (collections.Count > 0)
+ //{
+ // recursiveItems.AddRange(_collectionManager.CollapseItemsWithinBoxSets(recursiveItems, user));
+ // recursiveItems = recursiveItems.DistinctBy(i => i.Id).ToList();
+ //}
+
+ return GetResult(recursiveItems, parent, query);
}
var list = new List();
@@ -431,45 +461,59 @@ namespace MediaBrowser.Controller.Entities
{
query.IsFavorite = true;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => (i is Movie) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetFavoriteSeries(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).Where(i => i is Series), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => (i is Series) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetFavoriteEpisodes(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).Where(i => i is Episode), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => (i is Episode) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }).Where(i => i is Audio.Audio), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is Audio.Audio) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Music }).Where(i => i is MusicAlbum), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is MusicAlbum) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetMovieMovies(Folder parent, User user, InternalItemsQuery query)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => (i is Movie) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetMovieCollections(Folder parent, User user, InternalItemsQuery query)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is BoxSet), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => (i is BoxSet) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetMovieLatest(Folder parent, User user, InternalItemsQuery query)
@@ -477,7 +521,9 @@ namespace MediaBrowser.Controller.Entities
query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
query.SortOrder = SortOrder.Descending;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), parent, GetSpecialItemsLimit(), query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => i is Movie && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
}
private QueryResult GetMovieResume(Folder parent, User user, InternalItemsQuery query)
@@ -486,7 +532,9 @@ namespace MediaBrowser.Controller.Entities
query.SortOrder = SortOrder.Descending;
query.IsResumable = true;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }).Where(i => i is Movie), parent, GetSpecialItemsLimit(), query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => i is Movie && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
}
private async Task> GetMovieGenres(Folder parent, User user, InternalItemsQuery query)
@@ -526,11 +574,30 @@ namespace MediaBrowser.Controller.Entities
return GetResult(items, queryParent, query);
}
+ private async Task> GetBoxsetView(Folder parent, User user, InternalItemsQuery query)
+ {
+ return GetResult(GetMediaFolders(user).SelectMany(i =>
+ {
+ var hasCollectionType = i as ICollectionFolder;
+ Func filter = b => b is BoxSet;
+
+ if (hasCollectionType != null && string.Equals(hasCollectionType.CollectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
+ {
+ return i.GetChildren(user, true).Where(filter);
+ }
+
+ return i.GetRecursiveChildren(user, filter);
+
+ }), parent, query);
+ }
+
private async Task> GetTvView(Folder parent, User user, InternalItemsQuery query)
{
if (query.Recursive)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).Where(i => i is Series || i is Season || i is Episode), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => (i is Series || i is Season || i is Episode) && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
var list = new List();
@@ -550,7 +617,8 @@ namespace MediaBrowser.Controller.Entities
{
if (query.Recursive)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => FilterItem(i, query));
+ return PostFilterAndSort(items, parent, null, query);
}
var list = new List();
@@ -569,7 +637,9 @@ namespace MediaBrowser.Controller.Entities
query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
query.SortOrder = SortOrder.Descending;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType(), parent, GetSpecialItemsLimit(), query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
}
private QueryResult GetRecentlyPlayedGames(Folder parent, User user, InternalItemsQuery query)
@@ -578,14 +648,17 @@ namespace MediaBrowser.Controller.Entities
query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName };
query.SortOrder = SortOrder.Descending;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType(), parent, GetSpecialItemsLimit(), query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
}
private QueryResult GetFavoriteGames(Folder parent, User user, InternalItemsQuery query)
{
query.IsFavorite = true;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType(), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
+ return PostFilterAndSort(items, parent, null, query);
}
private QueryResult GetTvLatest(Folder parent, User user, InternalItemsQuery query)
@@ -593,7 +666,9 @@ namespace MediaBrowser.Controller.Entities
query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
query.SortOrder = SortOrder.Descending;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType(), parent, GetSpecialItemsLimit(), query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Episode && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
}
private QueryResult GetTvNextUp(Folder parent, InternalItemsQuery query)
@@ -617,12 +692,16 @@ namespace MediaBrowser.Controller.Entities
query.SortOrder = SortOrder.Descending;
query.IsResumable = true;
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType(), parent, GetSpecialItemsLimit(), query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Episode && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
}
private QueryResult GetTvSeries(Folder parent, User user, InternalItemsQuery query)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }).OfType(), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.TvShows, string.Empty }, i => i is Series && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private async Task> GetTvGenres(Folder parent, User user, InternalItemsQuery query)
@@ -664,14 +743,15 @@ namespace MediaBrowser.Controller.Entities
private QueryResult GetGameSystems(Folder parent, User user, InternalItemsQuery query)
{
- return GetResult(GetRecursiveChildren(parent, user, new[] { CollectionType.Games }).OfType(), parent, query);
+ var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is GameSystem && FilterItem(i, query));
+
+ return PostFilterAndSort(items, parent, null, query);
}
private async Task> GetGameGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
{
- var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Games })
- .OfType()
- .Where(i => i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase));
+ var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Games },
+ i => i is Game && i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase));
return GetResult(items, queryParent, query);
}
@@ -719,29 +799,32 @@ namespace MediaBrowser.Controller.Entities
InternalItemsQuery query)
where T : BaseItem
{
- return GetResult(items, queryParent, null, query);
+ items = items.Where(i => Filter(i, query.User, query, _userDataManager, _libraryManager));
+
+ return PostFilterAndSort(items, queryParent, null, query, _libraryManager);
}
- private QueryResult GetResult(IEnumerable items,
+ public bool FilterItem(BaseItem item, InternalItemsQuery query)
+ {
+ return Filter(item, query.User, query, _userDataManager, _libraryManager);
+ }
+
+ private QueryResult PostFilterAndSort(IEnumerable items,
BaseItem queryParent,
int? totalRecordLimit,
InternalItemsQuery query)
- where T : BaseItem
{
- return SortAndFilter(items, queryParent, totalRecordLimit, query, _libraryManager, _userDataManager);
+ return PostFilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager);
}
- public static QueryResult SortAndFilter(IEnumerable items,
+ public static QueryResult PostFilterAndSort(IEnumerable items,
BaseItem queryParent,
int? totalRecordLimit,
InternalItemsQuery query,
- ILibraryManager libraryManager,
- IUserDataManager userDataManager)
+ ILibraryManager libraryManager)
{
var user = query.User;
- items = items.Where(i => Filter(i, user, query, userDataManager, libraryManager));
-
items = FilterVirtualEpisodes(items,
query.IsMissing,
query.IsVirtualUnaired,
@@ -763,6 +846,11 @@ namespace MediaBrowser.Controller.Entities
BaseItem queryParent,
User user)
{
+ if (items == null)
+ {
+ throw new ArgumentNullException("items");
+ }
+
if (CollapseBoxSetItems(query, queryParent, user))
{
items = BaseItem.CollectionManager.CollapseItemsWithinBoxSets(items, user);
@@ -1140,7 +1228,7 @@ namespace MediaBrowser.Controller.Entities
};
}
- private static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
+ public static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
{
if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
@@ -1162,7 +1250,7 @@ namespace MediaBrowser.Controller.Entities
return false;
}
- if (query.Filter != null && !query.Filter(item, user))
+ if (query.Filter != null && !query.Filter(item))
{
return false;
}
@@ -1612,6 +1700,16 @@ namespace MediaBrowser.Controller.Entities
return parent.GetRecursiveChildren(user);
}
+ private IEnumerable GetRecursiveChildren(Folder parent, User user, IEnumerable viewTypes, Func filter)
+ {
+ if (parent == null || parent is UserView)
+ {
+ return GetMediaFolders(user, viewTypes).SelectMany(i => i.GetRecursiveChildren(user, filter));
+ }
+
+ return parent.GetRecursiveChildren(user, filter);
+ }
+
private async Task> GetLiveTvFolders(User user)
{
var list = new List();
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 3abaf095c8..d4507bc337 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -64,6 +64,19 @@ namespace MediaBrowser.Controller.Entities
LinkedAlternateVersions = new List();
}
+ public override bool CanDownload()
+ {
+ if (VideoType == VideoType.HdDvd || VideoType == VideoType.Dvd ||
+ VideoType == VideoType.BluRay)
+ {
+ return false;
+ }
+
+ var locationType = LocationType;
+ return locationType != LocationType.Remote &&
+ locationType != LocationType.Virtual;
+ }
+
[IgnoreDataMember]
public override bool SupportsAddingToPlaylist
{
@@ -409,7 +422,7 @@ namespace MediaBrowser.Controller.Entities
public virtual IEnumerable GetMediaStreams()
{
- return ItemRepository.GetMediaStreams(new MediaStreamQuery
+ return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
{
ItemId = Id
});
@@ -422,7 +435,7 @@ namespace MediaBrowser.Controller.Entities
return null;
}
- return ItemRepository.GetMediaStreams(new MediaStreamQuery
+ return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
{
ItemId = Id,
Index = DefaultVideoStreamIndex.Value
@@ -461,7 +474,8 @@ namespace MediaBrowser.Controller.Entities
private static MediaSourceInfo GetVersionInfo(bool enablePathSubstitution, Video i, MediaSourceType type)
{
- var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id }).ToList();
+ var mediaStreams = MediaSourceManager.GetMediaStreams(new MediaStreamQuery { ItemId = i.Id })
+ .ToList();
var locationType = i.LocationType;
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index 8deb930e8b..cf3ad3b6ab 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -1,6 +1,8 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities
{
@@ -13,7 +15,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return "Year-" + Name;
}
@@ -23,6 +25,7 @@ namespace MediaBrowser.Controller.Entities
/// If the item is a folder, it returns the folder itself
///
/// The containing folder path.
+ [IgnoreDataMember]
public override string ContainingFolderPath
{
get
@@ -31,10 +34,16 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override bool CanDelete()
+ {
+ return false;
+ }
+
///
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -47,8 +56,8 @@ namespace MediaBrowser.Controller.Entities
{
int year;
- var usCulture = new CultureInfo("en-US");
-
+ var usCulture = new CultureInfo("en-US");
+
if (!int.TryParse(Name, NumberStyles.Integer, usCulture, out year))
{
return inputItems;
@@ -56,5 +65,23 @@ namespace MediaBrowser.Controller.Entities
return inputItems.Where(i => i.ProductionYear.HasValue && i.ProductionYear.Value == year);
}
+
+ public int? GetYearValue()
+ {
+ int i;
+
+ if (int.TryParse(Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out i))
+ {
+ return i;
+ }
+
+ return null;
+ }
+
+ public Func GetItemFilter()
+ {
+ var val = GetYearValue();
+ return i => i.ProductionYear.HasValue && val.HasValue && i.ProductionYear.Value == val.Value;
+ }
}
}
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index d1a9b386cc..105e4e2f03 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -1,7 +1,7 @@
-using System.Collections.Generic;
-using MediaBrowser.Common;
+using MediaBrowser.Common;
using MediaBrowser.Model.System;
using System;
+using System.Collections.Generic;
namespace MediaBrowser.Controller
{
@@ -18,12 +18,6 @@ namespace MediaBrowser.Controller
/// SystemInfo.
SystemInfo GetSystemInfo();
- ///
- /// Gets the name of the web application.
- ///
- /// The name of the web application.
- string WebApplicationName { get; }
-
///
/// Gets a value indicating whether [supports automatic run at startup].
///
@@ -34,7 +28,19 @@ namespace MediaBrowser.Controller
/// Gets the HTTP server port.
///
/// The HTTP server port.
- int HttpServerPort { get; }
+ int HttpPort { get; }
+
+ ///
+ /// Gets the HTTPS port.
+ ///
+ /// The HTTPS port.
+ int HttpsPort { get; }
+
+ ///
+ /// Gets a value indicating whether [supports HTTPS].
+ ///
+ /// true if [supports HTTPS]; otherwise, false.
+ bool EnableHttps { get; }
///
/// Gets a value indicating whether this instance has update available.
@@ -49,9 +55,15 @@ namespace MediaBrowser.Controller
string FriendlyName { get; }
///
- /// Gets the HTTP server ip addresses.
+ /// Gets the local ip address.
+ ///
+ /// The local ip address.
+ string LocalIpAddress { get; }
+
+ ///
+ /// Gets the local API URL.
///
- /// The HTTP server ip addresses.
- IEnumerable HttpServerIpAddresses { get; }
+ /// The local API URL.
+ string LocalApiUrl { get; }
}
}
diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs
index e3438c3d25..c07934d0b1 100644
--- a/MediaBrowser.Controller/IServerApplicationPaths.cs
+++ b/MediaBrowser.Controller/IServerApplicationPaths.cs
@@ -59,12 +59,6 @@ namespace MediaBrowser.Controller
/// The game genre path.
string GameGenrePath { get; }
- ///
- /// Gets the artists path.
- ///
- /// The artists path.
- string ArtistsPath { get; }
-
///
/// Gets the path to the Studio directory
///
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 2ebd1cab93..9871ef3c5f 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -123,14 +123,7 @@ namespace MediaBrowser.Controller.Library
/// Gets the default view.
///
/// IEnumerable{VirtualFolderInfo}.
- IEnumerable GetDefaultVirtualFolders();
-
- ///
- /// Gets the view.
- ///
- /// The user.
- /// IEnumerable{VirtualFolderInfo}.
- IEnumerable GetVirtualFolders(User user);
+ IEnumerable GetVirtualFolders();
///
/// Gets the item by id.
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
new file mode 100644
index 0000000000..4378bc85d9
--- /dev/null
+++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
@@ -0,0 +1,11 @@
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Entities;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.Library
+{
+ public interface IMediaSourceManager
+ {
+ IEnumerable GetMediaStreams(MediaStreamQuery query);
+ }
+}
diff --git a/MediaBrowser.Controller/Library/IMusicManager.cs b/MediaBrowser.Controller/Library/IMusicManager.cs
index f66f18401d..0ce0687cc1 100644
--- a/MediaBrowser.Controller/Library/IMusicManager.cs
+++ b/MediaBrowser.Controller/Library/IMusicManager.cs
@@ -1,6 +1,5 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Playlists;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Library
@@ -13,7 +12,7 @@ namespace MediaBrowser.Controller.Library
/// The item.
/// The user.
/// IEnumerable{Audio}.
- IEnumerable GetInstantMixFromSong(Audio item, User user);
+ IEnumerable GetInstantMixFromItem(BaseItem item, User user);
///
/// Gets the instant mix from artist.
///
@@ -22,20 +21,6 @@ namespace MediaBrowser.Controller.Library
/// IEnumerable{Audio}.
IEnumerable GetInstantMixFromArtist(string name, User user);
///
- /// Gets the instant mix from album.
- ///
- /// The item.
- /// The user.
- /// IEnumerable{Audio}.
- IEnumerable GetInstantMixFromAlbum(MusicAlbum item, User user);
- ///
- /// Gets the instant mix from playlist.
- ///
- /// The item.
- /// The user.
- /// IEnumerable<Audio>.
- IEnumerable GetInstantMixFromPlaylist(Playlist item, User user);
- ///
/// Gets the instant mix from genre.
///
/// The genres.
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index f5846973eb..97a3cced9a 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -117,6 +117,21 @@ namespace MediaBrowser.Controller.Library
/// Task.
Task ResetPassword(User user);
+ ///
+ /// Gets the offline user dto.
+ ///
+ /// The user.
+ /// The device identifier.
+ /// UserDto.
+ UserDto GetOfflineUserDto(User user, string deviceId);
+
+ ///
+ /// Resets the easy password.
+ ///
+ /// The user.
+ /// Task.
+ Task ResetEasyPassword(User user);
+
///
/// Changes the password.
///
@@ -125,6 +140,14 @@ namespace MediaBrowser.Controller.Library
/// Task.
Task ChangePassword(User user, string newPasswordSha1);
+ ///
+ /// Changes the easy password.
+ ///
+ /// The user.
+ /// The new password sha1.
+ /// Task.
+ Task ChangeEasyPassword(User user, string newPasswordSha1);
+
///
/// Gets the user dto.
///
diff --git a/MediaBrowser.Controller/Library/IUserViewManager.cs b/MediaBrowser.Controller/Library/IUserViewManager.cs
index 727ccd00a6..f55c179248 100644
--- a/MediaBrowser.Controller/Library/IUserViewManager.cs
+++ b/MediaBrowser.Controller/Library/IUserViewManager.cs
@@ -1,5 +1,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Library;
+using MediaBrowser.Model.Querying;
+using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -16,5 +18,7 @@ namespace MediaBrowser.Controller.Library
Task GetUserView(string type, string sortName, CancellationToken cancellationToken);
Task GetUserView(string category, string type, User user, string sortName, CancellationToken cancellationToken);
+
+ List>> GetLatestItems(LatestItemsQuery request);
}
}
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index 4fa07421cc..db441d285a 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -17,7 +17,6 @@ namespace MediaBrowser.Controller.Library
/// The _app paths
///
private readonly IServerApplicationPaths _appPaths;
- private readonly ILibraryManager _libraryManager;
public IDirectoryService DirectoryService { get; private set; }
@@ -25,11 +24,10 @@ namespace MediaBrowser.Controller.Library
/// Initializes a new instance of the class.
///
/// The app paths.
- /// The library manager.
- public ItemResolveArgs(IServerApplicationPaths appPaths, ILibraryManager libraryManager, IDirectoryService directoryService)
+ /// The directory service.
+ public ItemResolveArgs(IServerApplicationPaths appPaths, IDirectoryService directoryService)
{
_appPaths = appPaths;
- _libraryManager = libraryManager;
DirectoryService = directoryService;
}
@@ -136,18 +134,6 @@ namespace MediaBrowser.Controller.Library
}
}
- ///
- /// Gets a value indicating whether this instance is root.
- ///
- /// true if this instance is root; otherwise, false.
- public bool IsRoot
- {
- get
- {
- return Parent == null;
- }
- }
-
///
/// Gets or sets the additional locations.
///
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
index ba1cb30436..784cb6ea18 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
@@ -25,5 +25,9 @@ namespace MediaBrowser.Controller.LiveTv
Task RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken);
PlayAccess GetPlayAccess(User user);
+
+ bool CanDelete();
+
+ bool CanDelete(User user);
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
index b95d67ad82..9815066efc 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
@@ -1,8 +1,10 @@
-using MediaBrowser.Controller.Entities.Audio;
+using System.Runtime.Serialization;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
-using System.Linq;
using MediaBrowser.Model.Users;
+using System.Linq;
namespace MediaBrowser.Controller.LiveTv
{
@@ -12,7 +14,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
var name = GetClientTypeName();
@@ -32,6 +34,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -40,6 +43,7 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ [IgnoreDataMember]
public override string MediaType
{
get
@@ -48,6 +52,7 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ [IgnoreDataMember]
public override LocationType LocationType
{
get
@@ -71,6 +76,7 @@ namespace MediaBrowser.Controller.LiveTv
return false;
}
+ [IgnoreDataMember]
public override bool SupportsLocalMetadata
{
get
@@ -83,5 +89,15 @@ namespace MediaBrowser.Controller.LiveTv
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
}
+
+ protected override string GetInternalMetadataPath(string basePath)
+ {
+ return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"));
+ }
+
+ public override bool IsAuthorizedToDelete(User user)
+ {
+ return user.Policy.EnableLiveTvManagement;
+ }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index de72accff8..eaea6cfa45 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -4,9 +4,10 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Users;
using System.Collections.Generic;
using System.Linq;
-using MediaBrowser.Model.Users;
+using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
@@ -16,24 +17,11 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return GetClientTypeName() + "-" + Name;
}
- ///
- /// Returns the folder containing the item.
- /// If the item is a folder, it returns the folder itself
- ///
- /// The containing folder path.
- public override string ContainingFolderPath
- {
- get
- {
- return Path;
- }
- }
-
protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvChannel);
@@ -43,6 +31,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -51,11 +40,6 @@ namespace MediaBrowser.Controller.LiveTv
}
}
- public override bool IsSaveLocalMetadataEnabled()
- {
- return true;
- }
-
///
/// Gets or sets the number.
///
@@ -106,6 +90,7 @@ namespace MediaBrowser.Controller.LiveTv
return number.ToString("000-") + (Name ?? string.Empty);
}
+ [IgnoreDataMember]
public override string MediaType
{
get
@@ -145,5 +130,15 @@ namespace MediaBrowser.Controller.LiveTv
return list;
}
+
+ protected override string GetInternalMetadataPath(string basePath)
+ {
+ return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"), "metadata");
+ }
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 29b23a551e..ee85ce20b0 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -2,11 +2,12 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Model.Users;
using System;
+using System.Linq;
+using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
-using System.Linq;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.LiveTv
{
@@ -16,7 +17,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
return GetClientTypeName() + "-" + Name;
}
@@ -138,6 +139,7 @@ namespace MediaBrowser.Controller.LiveTv
/// If the item is a folder, it returns the folder itself
///
/// The containing folder path.
+ [IgnoreDataMember]
public override string ContainingFolderPath
{
get
@@ -150,6 +152,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -158,6 +161,7 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ [IgnoreDataMember]
public override string MediaType
{
get
@@ -166,6 +170,7 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ [IgnoreDataMember]
public bool IsAiring
{
get
@@ -176,6 +181,7 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ [IgnoreDataMember]
public bool HasAired
{
get
@@ -204,5 +210,15 @@ namespace MediaBrowser.Controller.LiveTv
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
}
+
+ protected override string GetInternalMetadataPath(string basePath)
+ {
+ return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"));
+ }
+
+ public override bool CanDelete()
+ {
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
index 6fc985643a..207684d55d 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using System.Runtime.Serialization;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System.Linq;
@@ -12,7 +13,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets the user data key.
///
/// System.String.
- public override string GetUserDataKey()
+ protected override string CreateUserDataKey()
{
var name = GetClientTypeName();
@@ -28,6 +29,7 @@ namespace MediaBrowser.Controller.LiveTv
public string ServiceName { get; set; }
+ [IgnoreDataMember]
public override string MediaType
{
get
@@ -36,6 +38,7 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ [IgnoreDataMember]
public override LocationType LocationType
{
get
@@ -53,6 +56,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets a value indicating whether this instance is owned item.
///
/// true if this instance is owned item; otherwise, false.
+ [IgnoreDataMember]
public override bool IsOwnedItem
{
get
@@ -83,5 +87,15 @@ namespace MediaBrowser.Controller.LiveTv
{
return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
}
+
+ protected override string GetInternalMetadataPath(string basePath)
+ {
+ return System.IO.Path.Combine(basePath, "livetv", Id.ToString("N"));
+ }
+
+ public override bool IsAuthorizedToDelete(User user)
+ {
+ return user.Policy.EnableLiveTvManagement;
+ }
}
}
diff --git a/MediaBrowser.Controller/Localization/BaseStrings.cs b/MediaBrowser.Controller/Localization/BaseStrings.cs
deleted file mode 100644
index 22486d90dc..0000000000
--- a/MediaBrowser.Controller/Localization/BaseStrings.cs
+++ /dev/null
@@ -1,287 +0,0 @@
-
-namespace MediaBrowser.Controller.Localization
-{
- public class BaseStrings : LocalizedStringData
- {
- public BaseStrings()
- {
- ThisVersion = "1.0002";
- Prefix = LocalizedStrings.BasePrefix;
- }
-
-
-
- //Config Panel
- public string ConfigConfig = "Configuration";
- public string VersionConfig = "Version";
- public string MediaOptionsConfig = "Media Options";
- public string ThemesConfig = "Theme Options";
- public string ParentalControlConfig = "Parental Control";
- public string ContinueConfig = "Continue";
- public string ResetDefaultsConfig = "Reset Defaults";
- public string ClearCacheConfig = "Clear Cache";
- public string UnlockConfig = "Unlock";
- public string GeneralConfig = "General";
- public string EnableScreenSaverConfig = "Screen Saver";
- public string SSTimeOutConfig = "Timeout (mins)";
- public string TrackingConfig = "Tracking";
- public string AssumeWatchedIfOlderThanConfig = "Assume Played If Older Than";
- public string MetadataConfig = "Metadata";
- public string EnableInternetProvidersConfig = "Allow Internet Providers";
- public string UpdatesConfig = "Updates";
- public string AutomaticUpdatesConfig = "Check For Updates";
- public string LoggingConfig = "Logging";
- public string BetaUpdatesConfig = "Beta Updates";
- public string GlobalConfig = "Global";
- public string EnableEHSConfig = "Enable EHS";
- public string ShowClockConfig = "Show Clock";
- public string DimUnselectedPostersConfig = "Dim Unselected Posters";
- public string HideFocusFrameConfig = "Hide Focus Frame";
- public string AlwaysShowDetailsConfig = "Always Show Details";
- public string ExcludeRemoteContentInSearchesConfig = "Exclude Remote Content In Searches";
- public string EnhancedMouseSupportConfig = "Enhanced Mouse Support";
- public string ViewsConfig = "Views";
- public string PosterGridSpacingConfig = "Poster Grid Spacing";
- public string ThumbWidthSplitConfig = "Thumb Width Split";
- public string BreadcrumbCountConfig = "Breadcrumb Count";
- public string ShowFanArtonViewsConfig = "Show Fan Art on Views";
- public string ShowInitialFolderBackgroundConfig = "Show Initial Folder Background";
- public string ShowThemeBackgroundConfig = "Show Theme Background";
- public string ShowHDOverlayonPostersConfig = "Show HD Overlay on Posters";
- public string ShowIcononRemoteContentConfig = "Show Icon on Remote Content";
- public string EnableAdvancedCmdsConfig = "Enable Advanced Commands";
- public string MediaTrackingConfig = "Media Tracking";
- public string RememberFolderIndexingConfig = "Remember Folder Indexing";
- public string ShowUnwatchedCountConfig = "Show Unplayed Count";
- public string WatchedIndicatoronFoldersConfig = "Played Indicator on Folders";
- public string HighlightUnwatchedItemsConfig = "Highlight Unplayed Items";
- public string WatchedIndicatoronVideosConfig = "Played Indicator on Items";
- public string WatchedIndicatorinDetailViewConfig = "Played Indicator in Detail View";
- public string DefaultToFirstUnwatchedItemConfig = "Default To First Unplayed Item";
- public string GeneralBehaviorConfig = "General Behavior";
- public string AllowNestedMovieFoldersConfig = "Allow Nested Movie Folders";
- public string AutoEnterSingleFolderItemsConfig = "Auto Enter Single Folder Items";
- public string MultipleFileBehaviorConfig = "Multiple File Behavior";
- public string TreatMultipleFilesAsSingleMovieConfig = "Treat Multiple Files As Single Movie";
- public string MultipleFileSizeLimitConfig = "Multiple File Size Limit";
- public string MBThemeConfig = "Media Browser Theme";
- public string VisualThemeConfig = "Visual Theme";
- public string ColorSchemeConfig = "Color Scheme *";
- public string FontSizeConfig = "Font Size *";
- public string RequiresRestartConfig = "* Requires a restart to take effect.";
- public string ThemeSettingsConfig = "Theme Specific Settings";
- public string ShowConfigButtonConfig = "Show Config Button";
- public string AlphaBlendingConfig = "Alpha Blending";
- public string SecurityPINConfig = "Security PIN";
- public string PCUnlockedTxtConfig = "Parental Controls are Temporarily Unlocked. You cannot change values unless you re-lock.";
- public string RelockBtnConfig = "Re-Lock";
- public string EnableParentalBlocksConfig = "Enable Parental Blocks";
- public string MaxAllowedRatingConfig = "Max Allowed Rating ";
- public string BlockUnratedContentConfig = "Block Unrated Content";
- public string HideBlockedContentConfig = "Hide Blocked Content";
- public string UnlockonPINEntryConfig = "Unlock on PIN Entry";
- public string UnlockPeriodHoursConfig = "Unlock Period (Hours)";
- public string EnterNewPINConfig = "Enter New PIN";
- public string RandomizeBackdropConfig = "Randomize";
- public string RotateBackdropConfig = "Rotate";
- public string UpdateLibraryConfig = "Update Library";
- public string BackdropSettingsConfig = "Backdrop Settings";
- public string BackdropRotationIntervalConfig = "Rotation Time";
- public string BackdropTransitionIntervalConfig = "Transition Time";
- public string BackdropLoadDelayConfig = "Load Delay";
- public string AutoScrollTextConfig = "Auto Scroll Overview";
- public string SortYearsAscConfig = "Sort by Year in Ascending Order";
- public string AutoValidateConfig = "Automatically Validate Items";
- public string SaveLocalMetaConfig = "Save Locally";
- public string HideEmptyFoldersConfig = "Hide Empty TV Folders";
-
-
- //EHS
- public string RecentlyWatchedEHS = "last played";
- public string RecentlyAddedEHS = "last added";
- public string RecentlyAddedUnwatchedEHS = "last added unplayed";
- public string WatchedEHS = "Played";
- public string AddedEHS = "Added";
- public string UnwatchedEHS = "Unplayed";
- public string AddedOnEHS = "Added on";
- public string OnEHS = "on";
- public string OfEHS = "of";
- public string NoItemsEHS = "No Items To Show";
- public string VariousEHS = "(various)";
-
- //Context menu
- public string CloseCMenu = "Close";
- public string PlayMenuCMenu = "Play Menu";
- public string ItemMenuCMenu = "Item Menu";
- public string PlayAllCMenu = "Play All";
- public string PlayAllFromHereCMenu = "Play All From Here";
- public string ResumeCMenu = "Resume";
- public string MarkUnwatchedCMenu = "Mark Unplayed";
- public string MarkWatchedCMenu = "Mark Played";
- public string ShufflePlayCMenu = "Shuffle Play";
-
- //Media Detail Page
- public string GeneralDetail = "General";
- public string ActorsDetail = "Actors";
- public string ArtistsDetail = "Artists";
- public string PlayDetail = "Play";
- public string ResumeDetail = "Resume";
- public string RefreshDetail = "Refresh";
- public string PlayTrailersDetail = "Trailer";
- public string CacheDetail = "Cache 2 xml";
- public string DeleteDetail = "Delete";
- public string TMDBRatingDetail = "TMDb Rating";
- public string OutOfDetail = "out of";
- public string DirectorDetail = "Director";
- public string ComposerDetail = "Composer";
- public string HostDetail = "Host";
- public string RuntimeDetail = "Runtime";
- public string NextItemDetail = "Next";
- public string PreviousItemDetail = "Previous";
- public string FirstAiredDetail = "First aired";
- public string LastPlayedDetail = "Last played";
- public string TrackNumberDetail = "Track";
-
- public string DirectedByDetail = "Directed By: ";
- public string WrittenByDetail = "Written By: ";
- public string ComposedByDetail = "Composed By: ";
-
- //Display Prefs
- public string ViewDispPref = "View";
- public string ViewSearch = "Search";
- public string CoverFlowDispPref = "Cover Flow";
- public string DetailDispPref = "Detail";
- public string PosterDispPref = "Poster";
- public string ThumbDispPref = "Thumb";
- public string ThumbStripDispPref = "Thumb Strip";
- public string ShowLabelsDispPref = "Show Labels";
- public string VerticalScrollDispPref = "Vertical Scroll";
- public string UseBannersDispPref = "Use Banners";
- public string UseCoverflowDispPref = "Use Coverflow Style";
- public string ThumbSizeDispPref = "Thumb Size";
- public string NameDispPref = "Name";
- public string DateDispPref = "Date";
- public string RatingDispPref = "User Rating";
- public string OfficialRatingDispPref = "Rating";
- public string RuntimeDispPref = "Runtime";
- public string UnWatchedDispPref = "Unplayed";
- public string YearDispPref = "Year";
- public string NoneDispPref = "None";
- public string PerformerDispPref = "Performer";
- public string ActorDispPref = "Actor";
- public string GenreDispPref = "Genre";
- public string DirectorDispPref = "Director";
- public string StudioDispPref = "Studio";
-
- //Dialog boxes
- //public string BrokenEnvironmentDial = "Application will now close due to broken MediaCenterEnvironment object, possibly due to 5 minutes of idle time and/or running with TVPack installed.";
- //public string InitialConfigDial = "Initial configuration is complete, please restart Media Browser";
- //public string DeleteMediaDial = "Are you sure you wish to delete this media item?";
- //public string DeleteMediaCapDial = "Delete Confirmation";
- //public string NotDeletedDial = "Item NOT Deleted.";
- //public string NotDeletedCapDial = "Delete Cancelled by User";
- //public string NotDelInvalidPathDial = "The selected media item cannot be deleted due to an invalid path. Or you may not have sufficient access rights to perform this command.";
- //public string DelFailedDial = "Delete Failed";
- //public string NotDelUnknownDial = "The selected media item cannot be deleted due to an unknown error.";
- //public string NotDelTypeDial = "The selected media item cannot be deleted due to its Item-Type or you have not enabled this feature in the configuration file.";
- //public string FirstTimeDial = "As this is the first time you have run Media Browser please setup the inital configuration";
- //public string FirstTimeCapDial = "Configure";
- //public string EntryPointErrorDial = "Media Browser could not launch directly into ";
- //public string EntryPointErrorCapDial = "Entrypoint Error";
- //public string CriticalErrorDial = "Media Browser encountered a critical error and had to shut down: ";
- //public string CriticalErrorCapDial = "Critical Error";
- //public string ClearCacheErrorDial = "An error occured during the clearing of the cache, you may wish to manually clear it from {0} before restarting Media Browser";
- //public string RestartMBDial = "Please restart Media Browser";
- //public string ClearCacheDial = "Are you sure you wish to clear the cache?\nThis will erase all cached and downloaded information and images.";
- //public string ClearCacheCapDial = "Clear Cache";
- //public string CacheClearedDial = "Cache Cleared";
- //public string ResetConfigDial = "Are you sure you wish to reset all configuration to defaults?";
- //public string ResetConfigCapDial = "Reset Configuration";
- //public string ConfigResetDial = "Configuration Reset";
- //public string UpdateMBDial = "Please visit www.mediabrowser.tv/download to install the new version.";
- //public string UpdateMBCapDial = "Update Available";
- //public string UpdateMBExtDial = "There is an update available for Media Browser. Please update Media Browser next time you are at your MediaCenter PC.";
- //public string DLUpdateFailDial = "Media Browser will operate normally and prompt you again the next time you load it.";
- //public string DLUpdateFailCapDial = "Update Download Failed";
- //public string UpdateSuccessDial = "Media Browser must now exit to apply the update. It will restart automatically when it is done";
- //public string UpdateSuccessCapDial = "Update Downloaded";
- //public string CustomErrorDial = "Customisation Error";
- //public string ConfigErrorDial = "Reset to default?";
- //public string ConfigErrorCapDial = "Error in configuration file";
- //public string ContentErrorDial = "There was a problem playing the content. Check location exists";
- //public string ContentErrorCapDial = "Content Error";
- //public string CannotMaximizeDial = "We can not maximize the window! This is a known bug with Windows 7 and TV Pack, you will have to restart Media Browser!";
- //public string IncorrectPINDial = "Incorrect PIN Entered";
- //public string ContentProtected = "Content Protected";
- //public string CantChangePINDial = "Cannot Change PIN";
- //public string LibraryUnlockedDial = "Library Temporarily Unlocked. Will Re-Lock in {0} Hour(s) or on Application Re-Start";
- //public string LibraryUnlockedCapDial = "Unlock";
- //public string PINChangedDial = "PIN Successfully Changed";
- //public string PINChangedCapDial = "PIN Change";
- //public string EnterPINToViewDial = "Please Enter PIN to View Protected Content";
- //public string EnterPINToPlayDial = "Please Enter PIN to Play Protected Content";
- //public string EnterCurrentPINDial = "Please Enter CURRENT PIN.";
- //public string EnterNewPINDial = "Please Enter NEW PIN (exactly 4 digits).";
- //public string EnterPINDial = "Please Enter PIN to Unlock Library";
- //public string NoContentDial = "No Content that can be played in this context.";
- //public string FontsMissingDial = "CustomFonts.mcml as been patched with missing values";
- //public string StyleMissingDial = "{0} has been patched with missing values";
- //public string ManualRefreshDial = "Library Update Started. Will proceed in the background.";
- //public string ForcedRebuildDial = "Your library is currently being migrated by the service. The service will re-start when it is finished and you may then run Media Browser.";
- //public string ForcedRebuildCapDial = "Library Migration";
- //public string RefreshFailedDial = "The last service refresh process failed. Please run a manual refresh from the service.";
- //public string RefreshFailedCapDial = "Service Refresh Failed";
- //public string RebuildNecDial = "This version of Media Browser requires a re-build of your library. It has started automatically in the service. Some information may be incomplete until this process finishes.";
- //public string MigrateNecDial = "This version of Media Browser requires a migration of your library. It has started automatically in the service. The service will restart when it is complete and you may then run Media Browser.";
- //public string RebuildFailedDial = "There was an error attempting to tell the service to re-build your library. Please run the service and do a manual refresh with the cache clear options selected.";
- //public string MigrateFailedDial = "There was an error attempting to tell the service to re-build your library. Please run the service and do a manual refresh with the cache clear options selected.";
- //public string RefreshFolderDial = "Refresh all contents too?";
- //public string RefreshFolderCapDial = "Refresh Folder";
-
- //Generic
- public string Restartstr = "Restart";
- public string Errorstr = "Error";
- public string Playstr = "Play";
- public string MinutesStr = "mins"; //Minutes abbreviation
- public string HoursStr = "hrs"; //Hours abbreviation
- public string EndsStr = "Ends";
- public string KBsStr = "Kbps"; //Kilobytes per second
- public string FrameRateStr = "fps"; //Frames per second
- public string AtStr = "at"; //x at y, e.g. 1920x1080 at 25 fps
- public string Rated = "Rated";
- public string Or = "Or ";
- public string Lower = "Lower";
- public string Higher = "Higher";
- public string Search = "Search";
- public string Cancel = "Cancel";
- public string TitleContains = "Title Contains ";
- public string Any = "Any";
-
- //Search
- public string IncludeNested = "Include Subfolders";
- public string UnwatchedOnly = "Include Only Unwatched";
- public string FilterByRated = "Filter by Rating";
-
- //Profiler
- public string WelcomeProf = "Welcome to Media Browser";
- public string ProfilerTimeProf = "{1} took {2} seconds.";
- public string RefreshProf = "Refresh";
- public string SetWatchedProf = "Set Played {0}";
- public string RefreshFolderProf = "Refresh Folder and all Contents of";
- public string ClearWatchedProf = "Clear Played {0}";
- public string FullRefreshProf = "Full Library Refresh";
- public string FullValidationProf = "Full Library Validation";
- public string FastRefreshProf = "Fast Metadata refresh";
- public string SlowRefresh = "Slow Metadata refresh";
- public string ImageRefresh = "Image refresh";
- public string PluginUpdateProf = "An update is available for plug-in {0}";
- public string NoPluginUpdateProf = "No Plugin Updates Currently Available.";
- public string LibraryUnLockedProf = "Library Temporarily UnLocked. Will Re-Lock in {0} Hour(s)";
- public string LibraryReLockedProf = "Library Re-Locked";
-
- //Messages
- public string FullRefreshMsg = "Updating Media Library...";
- public string FullRefreshFinishedMsg = "Library update complete";
-
- }
-}
diff --git a/MediaBrowser.Controller/Localization/LocalizedStringData.cs b/MediaBrowser.Controller/Localization/LocalizedStringData.cs
deleted file mode 100644
index 71200b8c84..0000000000
--- a/MediaBrowser.Controller/Localization/LocalizedStringData.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System.IO;
-using System.Xml.Serialization;
-
-namespace MediaBrowser.Controller.Localization
-{
- ///
- /// Class LocalizedStringData
- ///
- public class LocalizedStringData
- {
- ///
- /// The this version
- ///
- [XmlIgnore]
- public string ThisVersion = "1.0000";
- ///
- /// The prefix
- ///
- [XmlIgnore]
- public string Prefix = "";
- ///
- /// The file name
- ///
- public string FileName; //this is public so it will serialize and we know where to save ourselves
- ///
- /// The version
- ///
- public string Version = ""; //this will get saved so we can check it against us for changes
-
- ///
- /// Saves this instance.
- ///
- public void Save()
- {
- Save(FileName);
- }
-
- ///
- /// Saves the specified file.
- ///
- /// The file.
- public void Save(string file)
- {
- var xs = new XmlSerializer(GetType());
- using (var fs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))
- {
- xs.Serialize(fs, this);
- }
- }
- }
-}
diff --git a/MediaBrowser.Controller/Localization/LocalizedStrings.cs b/MediaBrowser.Controller/Localization/LocalizedStrings.cs
deleted file mode 100644
index 94ac902716..0000000000
--- a/MediaBrowser.Controller/Localization/LocalizedStrings.cs
+++ /dev/null
@@ -1,166 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-
-namespace MediaBrowser.Controller.Localization
-{
- ///
- /// Class LocalizedStrings
- ///
- public class LocalizedStrings
- {
- public static IServerApplicationPaths ApplicationPaths;
-
- ///
- /// Gets the list of Localized string files
- ///
- /// The string files.
- public static IEnumerable StringFiles { get; set; }
-
- ///
- /// The base prefix
- ///
- public const string BasePrefix = "base-";
- ///
- /// The local strings
- ///
- protected ConcurrentDictionary LocalStrings = new ConcurrentDictionary();
- ///
- /// The _instance
- ///
- private static LocalizedStrings _instance;
-
- private readonly IServerApplicationPaths _appPaths;
-
- ///
- /// Gets the instance.
- ///
- /// The instance.
- public static LocalizedStrings Instance { get { return _instance ?? (_instance = new LocalizedStrings(ApplicationPaths)); } }
-
- ///
- /// Initializes a new instance of the class.
- ///
- public LocalizedStrings(IServerApplicationPaths appPaths)
- {
- _appPaths = appPaths;
-
- foreach (var stringObject in StringFiles)
- {
- AddStringData(LoadFromFile(GetFileName(stringObject),stringObject.GetType()));
- }
- }
-
- ///
- /// Gets the name of the file.
- ///
- /// The string object.
- /// System.String.
- protected string GetFileName(LocalizedStringData stringObject)
- {
- var path = _appPaths.LocalizationPath;
- var name = Path.Combine(path, stringObject.Prefix + "strings-" + CultureInfo.CurrentCulture + ".xml");
- if (File.Exists(name))
- {
- return name;
- }
-
- name = Path.Combine(path, stringObject.Prefix + "strings-" + CultureInfo.CurrentCulture.Parent + ".xml");
- if (File.Exists(name))
- {
- return name;
- }
-
- //just return default
- return Path.Combine(path, stringObject.Prefix + "strings-en.xml");
- }
-
- ///
- /// Loads from file.
- ///
- /// The file.
- /// The t.
- /// LocalizedStringData.
- protected LocalizedStringData LoadFromFile(string file, Type t)
- {
- return new BaseStrings {FileName = file};
- //var xs = new XmlSerializer(t);
- //var strings = (LocalizedStringData)Activator.CreateInstance(t);
- //strings.FileName = file;
- //Logger.Info("Using String Data from {0}", file);
- //if (File.Exists(file))
- //{
- // using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read))
- // {
- // strings = (LocalizedStringData)xs.Deserialize(fs);
- // }
- //}
- //else
- //{
- // strings.Save(); //brand new - save it
- //}
-
- //if (strings.ThisVersion != strings.Version && file.ToLower().Contains("-en.xml"))
- //{
- // //only re-save the english version as that is the one defined internally
- // strings = new BaseStrings {FileName = file};
- // strings.Save();
- //}
- //return strings;
-
- }
-
- ///
- /// Adds the string data.
- ///
- /// The string data.
- public void AddStringData(object stringData )
- {
- //translate our object definition into a dictionary for lookups
- // and a reverse dictionary so we can lookup keys by value
- foreach (var field in stringData.GetType().GetFields().Where(f => f != null && f.FieldType == typeof(string)))
- {
- string value;
-
- try
- {
- value = field.GetValue(stringData) as string;
- }
- catch (TargetException)
- {
- //Logger.ErrorException("Error getting value for field: {0}", ex, field.Name);
- continue;
- }
- catch (FieldAccessException)
- {
- //Logger.ErrorException("Error getting value for field: {0}", ex, field.Name);
- continue;
- }
- catch (NotSupportedException)
- {
- //Logger.ErrorException("Error getting value for field: {0}", ex, field.Name);
- continue;
- }
-
- LocalStrings.TryAdd(field.Name, value);
- }
- }
-
- ///
- /// Gets the string.
- ///
- /// The key.
- /// System.String.
- public string GetString(string key)
- {
- string value;
-
- LocalStrings.TryGetValue(key, out value);
- return value;
- }
- }
-}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 0667730fdd..e9531e0571 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -101,6 +101,7 @@
+
@@ -110,6 +111,7 @@
+
@@ -117,7 +119,6 @@
-
@@ -169,6 +170,7 @@
+
@@ -312,9 +314,6 @@
-
-
-
@@ -343,6 +342,7 @@
+
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
index a988c2f974..fe0fb32955 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
@@ -5,6 +5,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public class EncodingJobOptions
{
public string OutputContainer { get; set; }
+ public string OutputDirectory { get; set; }
public long? StartTimeTicks { get; set; }
public int? Width { get; set; }
@@ -80,12 +81,17 @@ namespace MediaBrowser.Controller.MediaEncoding
VideoCodec = info.VideoCodec;
VideoBitRate = info.VideoBitrate;
AudioStreamIndex = info.AudioStreamIndex;
- SubtitleStreamIndex = info.SubtitleStreamIndex;
MaxRefFrames = info.MaxRefFrames;
MaxVideoBitDepth = info.MaxVideoBitDepth;
SubtitleMethod = info.SubtitleDeliveryMethod;
Cabac = info.Cabac;
Context = info.Context;
+
+ if (info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode ||
+ info.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed)
+ {
+ SubtitleStreamIndex = info.SubtitleStreamIndex;
+ }
}
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
index 9e32fc32b0..37c2bf4d2e 100644
--- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
@@ -47,9 +47,8 @@ namespace MediaBrowser.Controller.MediaEncoding
/// Gets the subtitle language encoding parameter.
///
/// The path.
- /// The language.
/// System.String.
- string GetSubtitleFileCharacterSet(string path, string language);
+ string GetSubtitleFileCharacterSet(string path);
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
index 4a807df7ac..57fddb2b1f 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaStreamSelector.cs
@@ -38,7 +38,8 @@ namespace MediaBrowser.Controller.MediaEncoding
SubtitlePlaybackMode mode,
string audioTrackLanguage)
{
- streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages).ToList();
+ streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages)
+ .ToList();
var full = streams.Where(s => !s.IsForced);
@@ -81,21 +82,24 @@ namespace MediaBrowser.Controller.MediaEncoding
private static IEnumerable GetSortedStreams(IEnumerable streams, MediaStreamType type, List languagePreferences)
{
- var orderStreams = streams
- .Where(i => i.Type == type);
-
// Give some preferance to external text subs for better performance
- return orderStreams.OrderBy(i =>
+ return streams.Where(i => i.Type == type)
+ .OrderBy(i =>
{
var index = languagePreferences.FindIndex(l => string.Equals(i.Language, l, StringComparison.OrdinalIgnoreCase));
return index == -1 ? 100 : index;
})
- .ThenBy(i => i.IsDefault)
- .ThenBy(i => i.IsTextSubtitleStream)
- .ThenBy(i => i.IsExternal)
- .ThenBy(i => i.Index)
- .ToList();
+ .ThenBy(i => GetBooleanOrderBy(i.IsDefault))
+ .ThenBy(i => GetBooleanOrderBy(i.SupportsExternalStream))
+ .ThenBy(i => GetBooleanOrderBy(i.IsTextSubtitleStream))
+ .ThenBy(i => GetBooleanOrderBy(i.IsExternal))
+ .ThenBy(i => i.Index);
+ }
+
+ private static int GetBooleanOrderBy(bool value)
+ {
+ return value ? 0 : 1;
}
}
}
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
index f1e371c1a7..990d23970a 100644
--- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
+++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
@@ -93,6 +93,11 @@ namespace MediaBrowser.Controller.Net
}
}
+ protected virtual void ParseMessageParams(string[] values)
+ {
+
+ }
+
///
/// Starts sending messages over a web socket
///
@@ -104,6 +109,11 @@ namespace MediaBrowser.Controller.Net
var dueTimeMs = long.Parse(vals[0], UsCulture);
var periodMs = long.Parse(vals[1], UsCulture);
+ if (vals.Length > 2)
+ {
+ ParseMessageParams(vals.Skip(2).ToArray());
+ }
+
var cancellationTokenSource = new CancellationTokenSource();
Logger.Info("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name);
diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs
index 5b179d479a..315b48b837 100644
--- a/MediaBrowser.Controller/Net/IHttpServer.cs
+++ b/MediaBrowser.Controller/Net/IHttpServer.cs
@@ -1,4 +1,3 @@
-using MediaBrowser.Common.Net;
using System;
using System.Collections.Generic;
@@ -15,11 +14,19 @@ namespace MediaBrowser.Controller.Net
/// The URL prefix.
IEnumerable UrlPrefixes { get; }
+ ///
+ /// Gets the certificate path.
+ ///
+ /// The certificate path.
+ string CertificatePath { get; }
+
///
/// Starts the specified server name.
///
/// The URL prefixes.
- void StartServer(IEnumerable urlPrefixes);
+ /// If an https prefix is specified,
+ /// the ssl certificate localtion on the file system.
+ void StartServer(IEnumerable urlPrefixes, string certificatePath);
///
/// Gets the local end points.
diff --git a/MediaBrowser.Controller/Net/IServerManager.cs b/MediaBrowser.Controller/Net/IServerManager.cs
index dff0863478..d90a0f8ed6 100644
--- a/MediaBrowser.Controller/Net/IServerManager.cs
+++ b/MediaBrowser.Controller/Net/IServerManager.cs
@@ -15,7 +15,9 @@ namespace MediaBrowser.Controller.Net
/// Starts this instance.
///
/// The URL prefixes.
- void Start(IEnumerable urlPrefixes);
+ /// If an https prefix is specified,
+ /// the ssl certificate localtion on the file system.
+ void Start(IEnumerable urlPrefixes, string certificatePath);
///
/// Sends a message to all clients currently connected via a web socket
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index e48cddaaa6..3479902cbd 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -1,7 +1,5 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using System;
@@ -40,6 +38,11 @@ namespace MediaBrowser.Controller.Playlists
}
}
+ public override bool IsAuthorizedToDelete(User user)
+ {
+ return true;
+ }
+
public override bool IsSaveLocalMetadataEnabled()
{
return true;
@@ -50,9 +53,16 @@ namespace MediaBrowser.Controller.Playlists
return GetPlayableItems(user);
}
- public override IEnumerable GetRecursiveChildren(User user, bool includeLinkedChildren = true)
+ public override IEnumerable GetRecursiveChildren(User user, Func filter)
{
- return GetPlayableItems(user);
+ var items = GetPlayableItems(user);
+
+ if (filter != null)
+ {
+ items = items.Where(filter);
+ }
+
+ return items;
}
public IEnumerable> GetManageableItems()
@@ -76,75 +86,51 @@ namespace MediaBrowser.Controller.Playlists
.Where(m => string.Equals(m.MediaType, playlistMediaType, StringComparison.OrdinalIgnoreCase));
}
- private static IEnumerable GetPlaylistItems(BaseItem i, User user)
+ private static IEnumerable GetPlaylistItems(BaseItem item, User user)
{
- var musicGenre = i as MusicGenre;
+ var musicGenre = item as MusicGenre;
if (musicGenre != null)
{
- var items = user == null
- ? LibraryManager.RootFolder.GetRecursiveChildren()
- : user.RootFolder.GetRecursiveChildren(user, true);
-
- var songs = items
- .OfType()
- .Where(a => a.Genres.Contains(musicGenre.Name, StringComparer.OrdinalIgnoreCase));
+ Func filter = i => i is Audio && i.Genres.Contains(musicGenre.Name, StringComparer.OrdinalIgnoreCase);
- return LibraryManager.Sort(songs, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending);
- }
-
- var musicArtist = i as MusicArtist;
- if (musicArtist != null)
- {
var items = user == null
- ? LibraryManager.RootFolder.GetRecursiveChildren()
- : user.RootFolder.GetRecursiveChildren(user, true);
+ ? LibraryManager.RootFolder.GetRecursiveChildren(filter)
+ : user.RootFolder.GetRecursiveChildren(user, filter);
- var songs = items
- .OfType()
- .Where(a => a.HasArtist(musicArtist.Name));
-
- return LibraryManager.Sort(songs, 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 = i as BoxSet;
- if (collection != null)
+ var musicArtist = item as MusicArtist;
+ if (musicArtist != null)
{
- var items = user == null
- ? collection.Children
- : collection.GetChildren(user, true);
+ Func filter = i =>
+ {
+ var audio = i as Audio;
+ return audio != null && audio.HasArtist(musicArtist.Name);
+ };
- return items
- .Where(m => !m.IsFolder);
- }
-
- // Grab these explicitly to avoid the sorting that will happen below
- var season = i as Season;
- if (season != null)
- {
var items = user == null
- ? season.Children
- : season.GetChildren(user, true);
+ ? LibraryManager.RootFolder.GetRecursiveChildren(filter)
+ : user.RootFolder.GetRecursiveChildren(user, filter);
- return items
- .Where(m => !m.IsFolder);
+ return LibraryManager.Sort(items, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending);
}
- var folder = i as Folder;
-
+ var folder = item as Folder;
if (folder != null)
{
var items = user == null
- ? folder.GetRecursiveChildren()
- : folder.GetRecursiveChildren(user, true);
-
- items = items
- .Where(m => !m.IsFolder);
+ ? folder.GetRecursiveChildren(m => !m.IsFolder)
+ : folder.GetRecursiveChildren(user, m => !m.IsFolder);
+ if (folder.IsPreSorted)
+ {
+ return items;
+ }
return LibraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
}
- return new[] { i };
+ return new[] { item };
}
[IgnoreDataMember]
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
index a9e1555098..2cd119cf51 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
@@ -11,6 +11,8 @@ namespace MediaBrowser.Controller.Providers
///
public bool ReplaceAllMetadata { get; set; }
+ public bool IsPostRecursiveRefresh { get; set; }
+
public MetadataRefreshMode MetadataRefreshMode { get; set; }
public bool ForceSave { get; set; }
diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs
index d6dd7698e6..a4badee473 100644
--- a/MediaBrowser.Controller/Session/ISessionController.cs
+++ b/MediaBrowser.Controller/Session/ISessionController.cs
@@ -105,5 +105,15 @@ namespace MediaBrowser.Controller.Session
/// The cancellation token.
/// Task.
Task SendServerRestartNotification(CancellationToken cancellationToken);
+
+ ///
+ /// Sends the message.
+ ///
+ ///
+ /// The name.
+ /// The data.
+ /// The cancellation token.
+ /// Task.
+ Task SendMessage(string name, T data, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index f0272b3350..4082f56008 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -170,6 +170,29 @@ namespace MediaBrowser.Controller.Session
/// Task.
Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken);
+ ///
+ /// Sends the message to user sessions.
+ ///
+ ///
+ /// The user identifier.
+ /// The name.
+ /// The data.
+ /// The cancellation token.
+ /// Task.
+ Task SendMessageToUserSessions(string userId, string name, T data, CancellationToken cancellationToken);
+
+ ///
+ /// Sends the message to user device sessions.
+ ///
+ ///
+ /// The device identifier.
+ /// The name.
+ /// The data.
+ /// The cancellation token.
+ /// Task.
+ Task SendMessageToUserDeviceSessions(string deviceId, string name, T data,
+ CancellationToken cancellationToken);
+
///
/// Sends the restart required message.
///
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index d8a2464d64..078d4d70ff 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -160,6 +160,11 @@ namespace MediaBrowser.Controller.Session
}
}
+ public bool ContainsUser(string userId)
+ {
+ return ContainsUser(new Guid(userId));
+ }
+
public bool ContainsUser(Guid userId)
{
return (UserId ?? Guid.Empty) == userId || AdditionalUsers.Any(i => userId == new Guid(i.UserId));
diff --git a/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs b/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs
index f93360c641..f9327a71c3 100644
--- a/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs
+++ b/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs
@@ -1,4 +1,6 @@
-
+using MediaBrowser.Model.Sync;
+using System.Collections.Generic;
+
namespace MediaBrowser.Controller.Sync
{
public interface ICloudSyncProvider
@@ -8,5 +10,12 @@ namespace MediaBrowser.Controller.Sync
///
/// The name.
string Name { get; }
+
+ ///
+ /// Gets the synchronize targets.
+ ///
+ /// The user identifier.
+ /// IEnumerable<SyncTarget>.
+ IEnumerable GetSyncTargets(string userId);
}
}
diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
new file mode 100644
index 0000000000..8ef54fd432
--- /dev/null
+++ b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
@@ -0,0 +1,42 @@
+using MediaBrowser.Model.Sync;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Sync
+{
+ public interface IServerSyncProvider : ISyncProvider
+ {
+ ///
+ /// Gets the server item ids.
+ ///
+ /// The server identifier.
+ /// The target.
+ /// The cancellation token.
+ /// Task<List<System.String>>.
+ Task> GetServerItemIds(string serverId, SyncTarget target, CancellationToken cancellationToken);
+
+ ///
+ /// Removes the item.
+ ///
+ /// The server identifier.
+ /// The item identifier.
+ /// The target.
+ /// The cancellation token.
+ /// Task.
+ Task DeleteItem(string serverId, string itemId, SyncTarget target, CancellationToken cancellationToken);
+
+ ///
+ /// Transfers the file.
+ ///
+ /// The server identifier.
+ /// The item identifier.
+ /// The path parts.
+ /// The name.
+ /// Type of the file.
+ /// The target.
+ /// The cancellation token.
+ /// Task.
+ Task TransferItemFile(string serverId, string itemId, string[] pathParts, string name, ItemFileType fileType, SyncTarget target, CancellationToken cancellationToken);
+ }
+}
diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs
index 59136c0e6f..4d654575ee 100644
--- a/MediaBrowser.Controller/Sync/ISyncManager.cs
+++ b/MediaBrowser.Controller/Sync/ISyncManager.cs
@@ -1,8 +1,10 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Sync;
using MediaBrowser.Model.Users;
+using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -10,6 +12,12 @@ namespace MediaBrowser.Controller.Sync
{
public interface ISyncManager
{
+ event EventHandler> SyncJobCreated;
+ event EventHandler> SyncJobCancelled;
+ event EventHandler> SyncJobUpdated;
+ event EventHandler> SyncJobItemUpdated;
+ event EventHandler> SyncJobItemCreated;
+
///
/// Creates the job.
///
@@ -29,7 +37,7 @@ namespace MediaBrowser.Controller.Sync
/// The query.
/// QueryResult<SyncJobItem>.
QueryResult GetJobItems(SyncJobItemQuery query);
-
+
///
/// Gets the job.
///
@@ -44,6 +52,20 @@ namespace MediaBrowser.Controller.Sync
/// Task.
Task UpdateJob(SyncJob job);
+ ///
+ /// Res the enable job item.
+ ///
+ /// The identifier.
+ /// Task.
+ Task ReEnableJobItem(string id);
+
+ ///
+ /// Cnacels the job item.
+ ///
+ /// The identifier.
+ /// Task.
+ Task CancelJobItem(string id);
+
///
/// Cancels the job.
///
@@ -109,5 +131,55 @@ namespace MediaBrowser.Controller.Sync
/// The request.
/// Task<SyncDataResponse>.
Task SyncData(SyncDataRequest request);
+
+ ///
+ /// Marks the job item for removal.
+ ///
+ /// The identifier.
+ /// Task.
+ Task MarkJobItemForRemoval(string id);
+
+ ///
+ /// Unmarks the job item for removal.
+ ///
+ /// The identifier.
+ /// Task.
+ Task UnmarkJobItemForRemoval(string id);
+
+ ///
+ /// Gets the library item ids.
+ ///
+ /// The query.
+ /// QueryResult<System.String>.
+ QueryResult GetLibraryItemIds(SyncJobItemQuery query);
+
+ ///
+ /// Gets the audio options.
+ ///
+ /// The job item.
+ /// AudioOptions.
+ AudioOptions GetAudioOptions(SyncJobItem jobItem);
+
+ ///
+ /// Gets the video options.
+ ///
+ /// The job item.
+ /// The job.
+ /// VideoOptions.
+ VideoOptions GetVideoOptions(SyncJobItem jobItem, SyncJob job);
+
+ ///
+ /// Reports the synchronize job item transfer beginning.
+ ///
+ /// The identifier.
+ /// Task.
+ Task ReportSyncJobItemTransferBeginning(string id);
+
+ ///
+ /// Reports the synchronize job item transfer failed.
+ ///
+ /// The identifier.
+ /// Task.
+ Task ReportSyncJobItemTransferFailed(string id);
}
}
diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs
index af08edb5ea..6f24eac1ae 100644
--- a/MediaBrowser.Controller/Sync/ISyncProvider.cs
+++ b/MediaBrowser.Controller/Sync/ISyncProvider.cs
@@ -12,12 +12,6 @@ namespace MediaBrowser.Controller.Sync
/// The name.
string Name { get; }
- ///
- /// Gets the synchronize targets.
- ///
- /// IEnumerable<SyncTarget>.
- IEnumerable GetSyncTargets();
-
///
/// Gets the synchronize targets.
///
diff --git a/MediaBrowser.Controller/Sync/ISyncRepository.cs b/MediaBrowser.Controller/Sync/ISyncRepository.cs
index f1bcd7f07e..315f5f5414 100644
--- a/MediaBrowser.Controller/Sync/ISyncRepository.cs
+++ b/MediaBrowser.Controller/Sync/ISyncRepository.cs
@@ -68,5 +68,12 @@ namespace MediaBrowser.Controller.Sync
/// The query.
/// IEnumerable<SyncJobItem>.
QueryResult GetJobItems(SyncJobItemQuery query);
+
+ ///
+ /// Gets the library item ids.
+ ///
+ /// The query.
+ /// QueryResult<System.String>.
+ QueryResult GetLibraryItemIds(SyncJobItemQuery query);
}
}
diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
index 1faf690c93..e480326e9b 100644
--- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
+++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
@@ -185,13 +185,15 @@ namespace MediaBrowser.Dlna.ContentDirectory
var provided = 0;
- int? requested = 0;
+ // Default to null instead of 0
+ // Upnp inspector sends 0 as requestedCount when it wants everything
+ int? requestedCount = null;
int? start = 0;
int requestedVal;
if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
{
- requested = requestedVal;
+ requestedCount = requestedVal;
}
int startVal;
@@ -221,7 +223,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
if (item.IsFolder || serverItem.StubType.HasValue)
{
- var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false));
+ var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id));
}
@@ -234,7 +236,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
}
else
{
- var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false));
+ var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
totalCount = childrenResult.TotalRecordCount;
provided = childrenResult.Items.Length;
@@ -277,13 +279,15 @@ namespace MediaBrowser.Dlna.ContentDirectory
// sort example: dc:title, dc:date
- int? requested = 0;
+ // Default to null instead of 0
+ // Upnp inspector sends 0 as requestedCount when it wants everything
+ int? requestedCount = null;
int? start = 0;
int requestedVal;
if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
{
- requested = requestedVal;
+ requestedCount = requestedVal;
}
int startVal;
@@ -311,7 +315,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
var item = serverItem.Item;
- var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requested).ConfigureAwait(false));
+ var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requestedCount).ConfigureAwait(false));
var totalCount = childrenResult.TotalRecordCount;
@@ -479,9 +483,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
private async Task> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
{
- var items = user.RootFolder.GetRecursiveChildren(user)
- .Where(i => i is Movie || i is Series)
- .Where(i => i.ContainsPerson(person.Name))
+ var items = user.RootFolder.GetRecursiveChildren(user, i => i is Movie || i is Series && i.ContainsPerson(person.Name))
.ToList();
var trailerResult = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery
@@ -595,7 +597,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
});
}
- private bool FilterUnsupportedContent(BaseItem i, User user)
+ private bool FilterUnsupportedContent(BaseItem i)
{
// Unplayable
if (i.LocationType == LocationType.Virtual && !i.IsFolder)
diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
index d1941c8563..59dcbdecbb 100644
--- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs
+++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
@@ -13,12 +12,12 @@ using MediaBrowser.Dlna.ContentDirectory;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Net;
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
-using MediaBrowser.Model.Net;
namespace MediaBrowser.Dlna.Didl
{
@@ -801,7 +800,10 @@ namespace MediaBrowser.Dlna.Didl
if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)
|| string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
- return;
+ if (!stubType.HasValue)
+ {
+ return;
+ }
}
}
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index 7517cea50d..f0f295c831 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
+using MediaBrowser.Controller;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Plugins;
@@ -27,18 +28,20 @@ namespace MediaBrowser.Dlna
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
private readonly IJsonSerializer _jsonSerializer;
+ private readonly IServerApplicationHost _appHost;
public DlnaManager(IXmlSerializer xmlSerializer,
IFileSystem fileSystem,
IApplicationPaths appPaths,
ILogger logger,
- IJsonSerializer jsonSerializer)
+ IJsonSerializer jsonSerializer, IServerApplicationHost appHost)
{
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
_appPaths = appPaths;
_logger = logger;
_jsonSerializer = jsonSerializer;
+ _appHost = appHost;
}
public IEnumerable GetProfiles()
@@ -132,61 +135,74 @@ namespace MediaBrowser.Dlna
{
if (!string.IsNullOrWhiteSpace(profileInfo.DeviceDescription))
{
- if (deviceInfo.DeviceDescription == null || !Regex.IsMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
+ if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.FriendlyName))
{
- if (deviceInfo.FriendlyName == null || !Regex.IsMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
+ if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.Manufacturer))
{
- if (deviceInfo.Manufacturer == null || !Regex.IsMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
+ if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ManufacturerUrl))
{
- if (deviceInfo.ManufacturerUrl == null || !Regex.IsMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
+ if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelDescription))
{
- if (deviceInfo.ModelDescription == null || !Regex.IsMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
+ if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelName))
{
- if (deviceInfo.ModelName == null || !Regex.IsMatch(deviceInfo.ModelName, profileInfo.ModelName))
+ if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelNumber))
{
- if (deviceInfo.ModelNumber == null || !Regex.IsMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
+ if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelUrl))
{
- if (deviceInfo.ModelUrl == null || !Regex.IsMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
+ if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.SerialNumber))
{
- if (deviceInfo.SerialNumber == null || !Regex.IsMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
+ if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
return false;
}
return true;
}
+ private bool IsRegexMatch(string input, string pattern)
+ {
+ try
+ {
+ return Regex.IsMatch(input, pattern);
+ }
+ catch (ArgumentException ex)
+ {
+ _logger.ErrorException("Error evaluating regex pattern {0}", ex, pattern);
+ return false;
+ }
+ }
+
public DeviceProfile GetProfile(IDictionary headers)
{
if (headers == null)
@@ -393,7 +409,7 @@ namespace MediaBrowser.Dlna
throw new ArgumentException("System profiles cannot be deleted.");
}
- File.Delete(info.Path);
+ _fileSystem.DeleteFile(info.Path);
}
public void CreateProfile(DeviceProfile profile)
@@ -432,9 +448,9 @@ namespace MediaBrowser.Dlna
if (!string.Equals(path, current.Path, StringComparison.Ordinal) &&
current.Info.Type != DeviceProfileType.System)
{
- File.Delete(current.Path);
+ _fileSystem.DeleteFile(current.Path);
}
-
+
_xmlSerializer.SerializeToFile(profile, path);
}
@@ -462,12 +478,12 @@ namespace MediaBrowser.Dlna
internal string Path { get; set; }
}
- public string GetServerDescriptionXml(IDictionary headers, string serverUuId)
+ public string GetServerDescriptionXml(IDictionary headers, string serverUuId, string serverAddress)
{
var profile = GetProfile(headers) ??
GetDefaultProfile();
- return new DescriptionXmlBuilder(profile, serverUuId, "").GetXml();
+ return new DescriptionXmlBuilder(profile, serverUuId, serverAddress, _appHost.FriendlyName).GetXml();
}
public ImageStream GetIcon(string filename)
diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
index 810b1e5684..f6c04cdbe9 100644
--- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
+++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
@@ -165,15 +165,17 @@ namespace MediaBrowser.Dlna.Main
{
var guid = address.GetMD5();
- var descriptorURI = "/mediabrowser/dlna/" + guid.ToString("N") + "/description.xml";
+ var descriptorURI = "/dlna/" + guid.ToString("N") + "/description.xml";
- var uri = new Uri(string.Format("http://{0}:{1}{2}", address, _config.Configuration.HttpServerPortNumber, descriptorURI));
+ var uri = new Uri(string.Format("http://{0}:{1}{2}", address, _appHost.HttpPort, descriptorURI));
var services = new List
{
"upnp:rootdevice",
"urn:schemas-upnp-org:device:MediaServer:1",
"urn:schemas-upnp-org:service:ContentDirectory:1",
+ "urn:schemas-upnp-org:service:ConnectionManager:1",
+ "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1",
"uuid:" + guid.ToString("N")
};
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index 7871946cba..aed4ef4219 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -64,6 +64,10 @@
+
+
+
+
Code
diff --git a/MediaBrowser.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/MediaBrowser.Dlna/MediaReceiverRegistrar/ControlHandler.cs
new file mode 100644
index 0000000000..d1f701711a
--- /dev/null
+++ b/MediaBrowser.Dlna/MediaReceiverRegistrar/ControlHandler.cs
@@ -0,0 +1,43 @@
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Dlna.Server;
+using MediaBrowser.Dlna.Service;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Dlna.MediaReceiverRegistrar
+{
+ public class ControlHandler : BaseControlHandler
+ {
+ public ControlHandler(IServerConfigurationManager config, ILogger logger) : base(config, logger)
+ {
+ }
+
+ protected override IEnumerable> GetResult(string methodName, Headers methodParams)
+ {
+ if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
+ return HandleIsAuthorized();
+ if (string.Equals(methodName, "IsValidated", StringComparison.OrdinalIgnoreCase))
+ return HandleIsValidated();
+
+ throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
+ }
+
+ private IEnumerable> HandleIsAuthorized()
+ {
+ return new Headers(true)
+ {
+ { "Result", "1" }
+ };
+ }
+
+ private IEnumerable> HandleIsValidated()
+ {
+ return new Headers(true)
+ {
+ { "Result", "1" }
+ };
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
new file mode 100644
index 0000000000..a3b2bcee0c
--- /dev/null
+++ b/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs
@@ -0,0 +1,39 @@
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Dlna.Service;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Dlna.MediaReceiverRegistrar
+{
+ public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar, IDisposable
+ {
+ private readonly IServerConfigurationManager _config;
+
+ public MediaReceiverRegistrar(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config)
+ : base(logger, httpClient)
+ {
+ _config = config;
+ }
+
+ public string GetServiceXml(IDictionary headers)
+ {
+ return new MediaReceiverRegistrarXmlBuilder().GetXml();
+ }
+
+ public ControlResponse ProcessControlRequest(ControlRequest request)
+ {
+ return new ControlHandler(
+ _config,
+ Logger)
+ .ProcessControlRequest(request);
+ }
+
+ public void Dispose()
+ {
+
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
new file mode 100644
index 0000000000..cb1fdcecbf
--- /dev/null
+++ b/MediaBrowser.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
@@ -0,0 +1,78 @@
+using MediaBrowser.Dlna.Common;
+using MediaBrowser.Dlna.Service;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Dlna.MediaReceiverRegistrar
+{
+ public class MediaReceiverRegistrarXmlBuilder
+ {
+ public string GetXml()
+ {
+ return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(),
+ GetStateVariables());
+ }
+
+ private IEnumerable GetStateVariables()
+ {
+ var list = new List();
+
+ list.Add(new StateVariable
+ {
+ Name = "AuthorizationGrantedUpdateID",
+ DataType = "ui4",
+ SendsEvents = true
+ });
+
+ list.Add(new StateVariable
+ {
+ Name = "A_ARG_TYPE_DeviceID",
+ DataType = "string",
+ SendsEvents = false
+ });
+
+ list.Add(new StateVariable
+ {
+ Name = "AuthorizationDeniedUpdateID",
+ DataType = "ui4",
+ SendsEvents = true
+ });
+
+ list.Add(new StateVariable
+ {
+ Name = "ValidationSucceededUpdateID",
+ DataType = "ui4",
+ SendsEvents = true
+ });
+
+ list.Add(new StateVariable
+ {
+ Name = "A_ARG_TYPE_RegistrationRespMsg",
+ DataType = "bin.base64",
+ SendsEvents = false
+ });
+
+ list.Add(new StateVariable
+ {
+ Name = "A_ARG_TYPE_RegistrationReqMsg",
+ DataType = "bin.base64",
+ SendsEvents = false
+ });
+
+ list.Add(new StateVariable
+ {
+ Name = "ValidationRevokedUpdateID",
+ DataType = "ui4",
+ SendsEvents = true
+ });
+
+ list.Add(new StateVariable
+ {
+ Name = "A_ARG_TYPE_Result",
+ DataType = "int",
+ SendsEvents = false
+ });
+
+ return list;
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs b/MediaBrowser.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
new file mode 100644
index 0000000000..7e19805db3
--- /dev/null
+++ b/MediaBrowser.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
@@ -0,0 +1,154 @@
+using MediaBrowser.Dlna.Common;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Dlna.MediaReceiverRegistrar
+{
+ public class ServiceActionListBuilder
+ {
+ public IEnumerable GetActions()
+ {
+ var list = new List
+ {
+ GetIsValidated(),
+ GetIsAuthorized(),
+ GetRegisterDevice(),
+ GetGetAuthorizationDeniedUpdateID(),
+ GetGetAuthorizationGrantedUpdateID(),
+ GetGetValidationRevokedUpdateID(),
+ GetGetValidationSucceededUpdateID()
+ };
+
+ return list;
+ }
+
+ private ServiceAction GetIsValidated()
+ {
+ var action = new ServiceAction
+ {
+ Name = "IsValidated"
+ };
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "DeviceID",
+ Direction = "in"
+ });
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "Result",
+ Direction = "out"
+ });
+
+ return action;
+ }
+
+ private ServiceAction GetIsAuthorized()
+ {
+ var action = new ServiceAction
+ {
+ Name = "IsAuthorized"
+ };
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "DeviceID",
+ Direction = "in"
+ });
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "Result",
+ Direction = "out"
+ });
+
+ return action;
+ }
+
+ private ServiceAction GetRegisterDevice()
+ {
+ var action = new ServiceAction
+ {
+ Name = "RegisterDevice"
+ };
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "RegistrationReqMsg",
+ Direction = "in"
+ });
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "RegistrationRespMsg",
+ Direction = "out"
+ });
+
+ return action;
+ }
+
+ private ServiceAction GetGetValidationSucceededUpdateID()
+ {
+ var action = new ServiceAction
+ {
+ Name = "GetValidationSucceededUpdateID"
+ };
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "ValidationSucceededUpdateID",
+ Direction = "out"
+ });
+
+ return action;
+ }
+
+ private ServiceAction GetGetAuthorizationDeniedUpdateID()
+ {
+ var action = new ServiceAction
+ {
+ Name = "GetAuthorizationDeniedUpdateID"
+ };
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "AuthorizationDeniedUpdateID",
+ Direction = "out"
+ });
+
+ return action;
+ }
+
+ private ServiceAction GetGetValidationRevokedUpdateID()
+ {
+ var action = new ServiceAction
+ {
+ Name = "GetValidationRevokedUpdateID"
+ };
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "ValidationRevokedUpdateID",
+ Direction = "out"
+ });
+
+ return action;
+ }
+
+ private ServiceAction GetGetAuthorizationGrantedUpdateID()
+ {
+ var action = new ServiceAction
+ {
+ Name = "GetAuthorizationGrantedUpdateID"
+ };
+
+ action.ArgumentList.Add(new Argument
+ {
+ Name = "AuthorizationGrantedUpdateID",
+ Direction = "out"
+ });
+
+ return action;
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs
index c43efcd1da..00cb34be3e 100644
--- a/MediaBrowser.Dlna/PlayTo/Device.cs
+++ b/MediaBrowser.Dlna/PlayTo/Device.cs
@@ -712,14 +712,10 @@ namespace MediaBrowser.Dlna.PlayTo
if (avService == null)
return;
- var url = avService.ScpdUrl;
- if (!url.Contains("/"))
- url = "/dmr/" + url;
- if (!url.StartsWith("/"))
- url = "/" + url;
+ string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClient, _config);
- var document = await httpClient.GetDataAsync(Properties.BaseUrl + url);
+ var document = await httpClient.GetDataAsync(url);
AvCommands = TransportCommands.Create(document);
}
@@ -730,16 +726,28 @@ namespace MediaBrowser.Dlna.PlayTo
if (avService == null)
return;
- string url = avService.ScpdUrl;
+ string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
+
+ var httpClient = new SsdpHttpClient(_httpClient, _config);
+ var document = await httpClient.GetDataAsync(url);
+
+ RendererCommands = TransportCommands.Create(document);
+ }
+
+ private string NormalizeUrl(string baseUrl, string url)
+ {
+ // If it's already a complete url, don't stick anything onto the front of it
+ if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ return url;
+ }
+
if (!url.Contains("/"))
url = "/dmr/" + url;
if (!url.StartsWith("/"))
url = "/" + url;
- var httpClient = new SsdpHttpClient(_httpClient, _config);
- var document = await httpClient.GetDataAsync(Properties.BaseUrl + url);
-
- RendererCommands = TransportCommands.Create(document);
+ return baseUrl + url;
}
private TransportCommands AvCommands
diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs
index 1989c437aa..bd2b169ad9 100644
--- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs
@@ -272,7 +272,7 @@ namespace MediaBrowser.Dlna.PlayTo
return new PlaybackStartInfo
{
- ItemId = mediaInfo.Id,
+ ItemId = info.ItemId,
SessionId = _session.Id,
PositionTicks = ticks,
IsMuted = _device.IsMuted,
@@ -899,5 +899,11 @@ namespace MediaBrowser.Dlna.PlayTo
return request;
}
}
+
+ public Task SendMessage(string name, T data, CancellationToken cancellationToken)
+ {
+ // Not supported or needed right now
+ return Task.FromResult(true);
+ }
}
}
diff --git a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
index a60b5efa48..24e1ef1133 100644
--- a/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlayToManager.cs
@@ -148,11 +148,11 @@ namespace MediaBrowser.Dlna.PlayTo
private string GetServerAddress(IPAddress localIp)
{
- return string.Format("{0}://{1}:{2}/mediabrowser",
+ return string.Format("{0}://{1}:{2}",
"http",
localIp,
- _appHost.HttpServerPort
+ _appHost.HttpPort
);
}
diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
index 83d7f322d1..317ec09699 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -20,6 +20,7 @@ namespace MediaBrowser.Dlna.PlayTo
{
ItemId = item.Id.ToString("N"),
MediaType = DlnaProfileType.Photo,
+ DeviceProfile = profile
},
Profile = profile
diff --git a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
index ccc7d46e6e..f0689751c5 100644
--- a/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
+++ b/MediaBrowser.Dlna/PlayTo/SsdpHttpClient.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Net;
+using System;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Dlna.Common;
using System.Globalization;
@@ -29,11 +30,7 @@ namespace MediaBrowser.Dlna.PlayTo
string postData,
string header = null)
{
- var serviceUrl = service.ControlUrl;
- if (!serviceUrl.StartsWith("/"))
- serviceUrl = "/" + serviceUrl;
-
- var response = await PostSoapDataAsync(baseUrl + serviceUrl, "\"" + service.ServiceType + "#" + command + "\"", postData, header)
+ var response = await PostSoapDataAsync(NormalizeServiceUrl(baseUrl, service.ControlUrl), "\"" + service.ServiceType + "#" + command + "\"", postData, header)
.ConfigureAwait(false);
using (var stream = response.Content)
@@ -45,6 +42,20 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
+ private string NormalizeServiceUrl(string baseUrl, string serviceUrl)
+ {
+ // If it's already a complete url, don't stick anything onto the front of it
+ if (serviceUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ return serviceUrl;
+ }
+
+ if (!serviceUrl.StartsWith("/"))
+ serviceUrl = "/" + serviceUrl;
+
+ return baseUrl + serviceUrl;
+ }
+
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public async Task SubscribeAsync(string url,
diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
index 1499d0e745..970995b7d9 100644
--- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
@@ -336,6 +336,13 @@ namespace MediaBrowser.Dlna.Profiles
Container = "mkv",
MimeType = "video/x-mkv",
Type = DlnaProfileType.Video
+ },
+
+ new ResponseProfile
+ {
+ Container = "flac",
+ MimeType = "audio/x-flac",
+ Type = DlnaProfileType.Audio
}
};
diff --git a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs
index 7b04ae6d73..e3dd832248 100644
--- a/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs
+++ b/MediaBrowser.Dlna/Profiles/Xbox360Profile.cs
@@ -4,6 +4,9 @@ using MediaBrowser.Model.Dlna.Profiles;
namespace MediaBrowser.Dlna.Profiles
{
+ ///
+ /// Good info on xbox 360 requirements: https://code.google.com/p/jems/wiki/XBox360Notes
+ ///
[XmlRoot("Profile")]
public class Xbox360Profile : DefaultProfile
{
@@ -11,8 +14,13 @@ namespace MediaBrowser.Dlna.Profiles
{
Name = "Xbox 360";
+ // Required according to above
ModelName = "Windows Media Player Sharing";
+
ModelNumber = "12.0";
+
+ FriendlyName = "${HostName} : 1";
+
ModelUrl = "http://www.microsoft.com/";
Manufacturer = "Microsoft Corporation";
ManufacturerUrl = "http://www.microsoft.com/";
@@ -21,6 +29,7 @@ namespace MediaBrowser.Dlna.Profiles
TimelineOffsetSeconds = 40;
RequiresPlainFolders = true;
RequiresPlainVideoItems = true;
+ EnableMSMediaReceiverRegistrar = true;
Identification = new DeviceIdentification
{
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml
index fd4246dec9..7062afc69a 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml
@@ -26,8 +26,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
index b62580f69d..9ff1ae8336 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
@@ -31,8 +31,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
index 603ec554ed..746b7f5c40 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
@@ -32,8 +32,7 @@
10
true
true
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
index c8eff5b1d1..4a70e3d4c5 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
@@ -33,8 +33,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
index e625555bde..471917a13f 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
@@ -32,8 +32,7 @@
10
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
index 09165349e4..5fe441945b 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
@@ -30,8 +30,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
index c57a368b34..35775892c8 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
@@ -32,8 +32,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
index 515492c7fa..36c5289298 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
@@ -33,8 +33,7 @@
10
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml b/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml
index 4fa4db30bb..0749dbeac7 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml
@@ -26,8 +26,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
index 27d8e00ec8..9e61df43ed 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
@@ -32,8 +32,7 @@
0
false
false
- false
- false
+ false
@@ -110,6 +109,9 @@
+
+
+
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
index 1d0ea66bbc..91aa767bee 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
@@ -32,8 +32,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
index 2a883ac67c..a6ea108408 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
@@ -34,8 +34,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
index d9727ecd08..0c3bdc4c5f 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
@@ -34,8 +34,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
index 1e3b48452e..0269c05c16 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
@@ -34,8 +34,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
index 509f533b97..9aa614faae 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
@@ -34,8 +34,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
index 35e645422d..231db2091f 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
@@ -34,8 +34,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
index 939f953b9d..153b66be81 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
@@ -34,8 +34,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
index 015dbbc1fa..d51669cfb8 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
@@ -33,8 +33,7 @@
5
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml
index 17100466f8..da55724075 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml
@@ -8,7 +8,7 @@
- Media Browser
+ ${HostName} : 1
Microsoft Corporation
http://www.microsoft.com/
Windows Media Player Sharing
@@ -33,8 +33,7 @@
40
true
true
- false
- false
+ true
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
index f666dfd27f..063cebebc3 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
@@ -33,8 +33,7 @@
40
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
index 2afdcc6f88..de7c03c481 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
@@ -32,8 +32,7 @@
0
false
false
- false
- false
+ false
diff --git a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
index f4f724d07a..e302fe902f 100644
--- a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
+++ b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs
@@ -1,8 +1,10 @@
using MediaBrowser.Dlna.Common;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Extensions;
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
using System.Security;
using System.Text;
@@ -15,17 +17,29 @@ namespace MediaBrowser.Dlna.Server
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly string _serverUdn;
private readonly string _serverAddress;
+ private readonly string _serverName;
- public DescriptionXmlBuilder(DeviceProfile profile, string serverUdn, string serverAddress)
+ public DescriptionXmlBuilder(DeviceProfile profile, string serverUdn, string serverAddress, string serverName)
{
if (string.IsNullOrWhiteSpace(serverUdn))
{
throw new ArgumentNullException("serverUdn");
}
+ if (string.IsNullOrWhiteSpace(serverAddress))
+ {
+ throw new ArgumentNullException("serverAddress");
+ }
+
_profile = profile;
_serverUdn = serverUdn;
_serverAddress = serverAddress;
+ _serverName = serverName;
+ }
+
+ private bool EnableAbsoluteUrls
+ {
+ get { return false; }
}
public string GetXml()
@@ -71,7 +85,7 @@ namespace MediaBrowser.Dlna.Server
builder.Append("M-DMS-1.50");
builder.Append("" + SecurityElement.Escape(_profile.XDlnaDoc ?? string.Empty) + "");
- builder.Append("" + SecurityElement.Escape(_profile.FriendlyName ?? string.Empty) + "");
+ builder.Append("" + SecurityElement.Escape(GetFriendlyName()) + "");
builder.Append("urn:schemas-upnp-org:device:MediaServer:1");
builder.Append("" + SecurityElement.Escape(_profile.Manufacturer ?? string.Empty) + "");
builder.Append("" + SecurityElement.Escape(_profile.ManufacturerUrl ?? string.Empty) + "");
@@ -81,14 +95,30 @@ namespace MediaBrowser.Dlna.Server
builder.Append("" + SecurityElement.Escape(_profile.ModelUrl ?? string.Empty) + "");
builder.Append("" + SecurityElement.Escape(_profile.SerialNumber ?? string.Empty) + "");
- //builder.Append("" + SecurityElement.Escape(_serverAddress) + "");
-
+ builder.Append("" + SecurityElement.Escape(_serverAddress) + "");
+
+ if (!EnableAbsoluteUrls)
+ {
+ //builder.Append("" + SecurityElement.Escape(_serverAddress) + "");
+ }
+
if (!string.IsNullOrWhiteSpace(_profile.SonyAggregationFlags))
{
builder.Append("" + SecurityElement.Escape(_profile.SonyAggregationFlags) + "");
}
}
+ private string GetFriendlyName()
+ {
+ var characters = _serverName.Where(c => (char.IsLetterOrDigit(c) || c == '-')).ToArray();
+
+ var serverName = new string(characters);
+
+ var name = (_profile.FriendlyName ?? string.Empty).Replace("${HostName}", serverName, StringComparison.OrdinalIgnoreCase);
+
+ return name;
+ }
+
private void AppendIconList(StringBuilder builder)
{
builder.Append("");
@@ -101,7 +131,7 @@ namespace MediaBrowser.Dlna.Server
builder.Append("" + SecurityElement.Escape(icon.Width.ToString(_usCulture)) + "");
builder.Append("" + SecurityElement.Escape(icon.Height.ToString(_usCulture)) + "");
builder.Append("" + SecurityElement.Escape(icon.Depth ?? string.Empty) + "");
- builder.Append("" + SecurityElement.Escape(icon.Url ?? string.Empty) + "");
+ builder.Append("" + BuildUrl(icon.Url) + "");
builder.Append("");
}
@@ -119,9 +149,9 @@ namespace MediaBrowser.Dlna.Server
builder.Append("" + SecurityElement.Escape(service.ServiceType ?? string.Empty) + "");
builder.Append("" + SecurityElement.Escape(service.ServiceId ?? string.Empty) + "");
- builder.Append("" + SecurityElement.Escape(service.ScpdUrl ?? string.Empty) + "");
- builder.Append("" + SecurityElement.Escape(service.ControlUrl ?? string.Empty) + "");
- builder.Append("" + SecurityElement.Escape(service.EventSubUrl ?? string.Empty) + "");
+ builder.Append("" + BuildUrl(service.ScpdUrl) + "");
+ builder.Append("" + BuildUrl(service.ControlUrl) + "");
+ builder.Append("" + BuildUrl(service.EventSubUrl) + "");
builder.Append("");
}
@@ -129,6 +159,25 @@ namespace MediaBrowser.Dlna.Server
builder.Append("");
}
+ private string BuildUrl(string url)
+ {
+ if (string.IsNullOrWhiteSpace(url))
+ {
+ return string.Empty;
+ }
+
+ url = url.TrimStart('/');
+
+ url = "/dlna/" + _serverUdn + "/" + url;
+
+ if (EnableAbsoluteUrls)
+ {
+ url = _serverAddress.TrimEnd('/') + url;
+ }
+
+ return SecurityElement.Escape(url);
+ }
+
private IEnumerable GetIcons()
{
var list = new List();
@@ -139,7 +188,7 @@ namespace MediaBrowser.Dlna.Server
Depth = "24",
Width = 240,
Height = 240,
- Url = "/mediabrowser/dlna/icons/logo240.png"
+ Url = "icons/logo240.png"
});
list.Add(new DeviceIcon
@@ -148,7 +197,7 @@ namespace MediaBrowser.Dlna.Server
Depth = "24",
Width = 240,
Height = 240,
- Url = "/mediabrowser/dlna/icons/logo240.jpg"
+ Url = "icons/logo240.jpg"
});
list.Add(new DeviceIcon
@@ -157,7 +206,7 @@ namespace MediaBrowser.Dlna.Server
Depth = "24",
Width = 120,
Height = 120,
- Url = "/mediabrowser/dlna/icons/logo120.png"
+ Url = "icons/logo120.png"
});
list.Add(new DeviceIcon
@@ -166,7 +215,7 @@ namespace MediaBrowser.Dlna.Server
Depth = "24",
Width = 120,
Height = 120,
- Url = "/mediabrowser/dlna/icons/logo120.jpg"
+ Url = "icons/logo120.jpg"
});
list.Add(new DeviceIcon
@@ -175,7 +224,7 @@ namespace MediaBrowser.Dlna.Server
Depth = "24",
Width = 48,
Height = 48,
- Url = "/mediabrowser/dlna/icons/logo48.png"
+ Url = "icons/logo48.png"
});
list.Add(new DeviceIcon
@@ -184,7 +233,7 @@ namespace MediaBrowser.Dlna.Server
Depth = "24",
Width = 48,
Height = 48,
- Url = "/mediabrowser/dlna/icons/logo48.jpg"
+ Url = "icons/logo48.jpg"
});
return list;
@@ -198,20 +247,32 @@ namespace MediaBrowser.Dlna.Server
{
ServiceType = "urn:schemas-upnp-org:service:ContentDirectory:1",
ServiceId = "urn:upnp-org:serviceId:ContentDirectory",
- ScpdUrl = "/mediabrowser/dlna/contentdirectory/contentdirectory.xml",
- ControlUrl = "/mediabrowser/dlna/contentdirectory/" + _serverUdn + "/control",
- EventSubUrl = "/mediabrowser/dlna/contentdirectory/" + _serverUdn + "/events"
+ ScpdUrl = "contentdirectory/contentdirectory.xml",
+ ControlUrl = "contentdirectory/control",
+ EventSubUrl = "contentdirectory/events"
});
list.Add(new DeviceService
{
ServiceType = "urn:schemas-upnp-org:service:ConnectionManager:1",
ServiceId = "urn:upnp-org:serviceId:ConnectionManager",
- ScpdUrl = "/mediabrowser/dlna/connectionmanager/connectionmanager.xml",
- ControlUrl = "/mediabrowser/dlna/connectionmanager/" + _serverUdn + "/control",
- EventSubUrl = "/mediabrowser/dlna/connectionmanager/" + _serverUdn + "/events"
+ ScpdUrl = "connectionmanager/connectionmanager.xml",
+ ControlUrl = "connectionmanager/control",
+ EventSubUrl = "connectionmanager/events"
});
+ if (_profile.EnableMSMediaReceiverRegistrar)
+ {
+ list.Add(new DeviceService
+ {
+ ServiceType = "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1",
+ ServiceId = "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar",
+ ScpdUrl = "mediareceiverregistrar/mediareceiverregistrar.xml",
+ ControlUrl = "mediareceiverregistrar/control",
+ EventSubUrl = "mediareceiverregistrar/events"
+ });
+ }
+
return list;
}
diff --git a/MediaBrowser.Dlna/Ssdp/Datagram.cs b/MediaBrowser.Dlna/Ssdp/Datagram.cs
index f16464209b..ae79ab44f1 100644
--- a/MediaBrowser.Dlna/Ssdp/Datagram.cs
+++ b/MediaBrowser.Dlna/Ssdp/Datagram.cs
@@ -11,26 +11,15 @@ namespace MediaBrowser.Dlna.Ssdp
public EndPoint ToEndPoint { get; private set; }
public EndPoint FromEndPoint { get; private set; }
public string Message { get; private set; }
-
- ///
- /// The number of times to send the message
- ///
- public int TotalSendCount { get; private set; }
public bool IgnoreBindFailure { get; private set; }
- ///
- /// The number of times the message has been sent
- ///
- public int SendCount { get; private set; }
-
private readonly ILogger _logger;
- public Datagram(EndPoint toEndPoint, EndPoint fromEndPoint, ILogger logger, string message, int totalSendCount, bool ignoreBindFailure)
+ public Datagram(EndPoint toEndPoint, EndPoint fromEndPoint, ILogger logger, string message, bool ignoreBindFailure)
{
Message = message;
_logger = logger;
IgnoreBindFailure = ignoreBindFailure;
- TotalSendCount = totalSendCount;
FromEndPoint = fromEndPoint;
ToEndPoint = toEndPoint;
}
@@ -83,7 +72,6 @@ namespace MediaBrowser.Dlna.Ssdp
{
_logger.ErrorException("Error sending Datagram to {0} from {1}: " + Message, ex, ToEndPoint, FromEndPoint == null ? "" : FromEndPoint.ToString());
}
- ++SendCount;
}
private Socket CreateSocket()
diff --git a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs b/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs
index adc2d731ac..a90c6dc01e 100644
--- a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs
+++ b/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs
@@ -86,7 +86,7 @@ namespace MediaBrowser.Dlna.Ssdp
try
{
- var ip = _appHost.HttpServerIpAddresses.FirstOrDefault();
+ var ip = _appHost.LocalIpAddress;
if (!string.IsNullOrWhiteSpace(ip))
{
diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
index bd3098ecd6..8ca16832da 100644
--- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
+++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
@@ -62,20 +62,14 @@ namespace MediaBrowser.Dlna.Ssdp
{
if (string.Equals(args.Method, "M-SEARCH", StringComparison.OrdinalIgnoreCase))
{
- string mx = null;
- args.Headers.TryGetValue("mx", out mx);
- int delaySeconds;
- if (!string.IsNullOrWhiteSpace(mx) &&
- int.TryParse(mx, NumberStyles.Any, CultureInfo.InvariantCulture, out delaySeconds)
- && delaySeconds > 0)
+ TimeSpan delay = GetSearchDelay(args.Headers);
+
+ if (_config.GetDlnaConfiguration().EnableDebugLogging)
{
- if (_config.GetDlnaConfiguration().EnableDebugLogging)
- {
- _logger.Debug("Delaying search response by {0} seconds", delaySeconds);
- }
-
- await Task.Delay(delaySeconds * 1000).ConfigureAwait(false);
+ _logger.Debug("Delaying search response by {0} seconds", delay.TotalSeconds);
}
+
+ await Task.Delay(delay).ConfigureAwait(false);
RespondToSearch(args.EndPoint, args.Headers["st"]);
}
@@ -135,17 +129,50 @@ namespace MediaBrowser.Dlna.Ssdp
int sendCount = 1)
{
var msg = new SsdpMessageBuilder().BuildMessage(header, values);
+ var queued = false;
- var dgram = new Datagram(endpoint, localAddress, _logger, msg, sendCount, ignoreBindFailure);
+ for (var i = 0; i < sendCount; i++)
+ {
+ var dgram = new Datagram(endpoint, localAddress, _logger, msg, ignoreBindFailure);
- if (_messageQueue.Count == 0)
+ if (_messageQueue.Count == 0)
+ {
+ dgram.Send();
+ }
+ else
+ {
+ _messageQueue.Enqueue(dgram);
+ queued = true;
+ }
+ }
+
+ if (queued)
{
- dgram.Send();
- return;
+ StartQueueTimer();
}
+ }
- _messageQueue.Enqueue(dgram);
- StartQueueTimer();
+ ///
+ /// According to the spec: http://www.upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0-20080424.pdf
+ /// Device responses should be delayed a random duration between 0 and this many seconds to balance
+ /// load for the control point when it processes responses. In my testing kodi times out after mx
+ /// so we will generate from mx - 1
+ ///
+ /// The mx headers
+ /// A timepsan for the amount to delay before returning search result.
+ private TimeSpan GetSearchDelay(Dictionary headers)
+ {
+ string mx;
+ headers.TryGetValue("mx", out mx);
+ int delaySeconds = 0;
+ if (!string.IsNullOrWhiteSpace(mx)
+ && int.TryParse(mx, NumberStyles.Any, CultureInfo.InvariantCulture, out delaySeconds)
+ && delaySeconds > 1)
+ {
+ delaySeconds = new Random().Next(delaySeconds - 1);
+ }
+
+ return TimeSpan.FromSeconds(delaySeconds);
}
private void RespondToSearch(EndPoint endpoint, string deviceType)
@@ -172,8 +199,8 @@ namespace MediaBrowser.Dlna.Ssdp
values["ST"] = d.Type;
values["USN"] = d.USN;
- SendDatagram(header, values, endpoint, null, true);
- SendDatagram(header, values, endpoint, new IPEndPoint(d.Address, 0), true);
+ SendDatagram(header, values, endpoint, null, true, 1);
+ SendDatagram(header, values, endpoint, new IPEndPoint(d.Address, 0), true, 1);
//SendDatagram(header, values, endpoint, null, true);
if (_config.GetDlnaConfiguration().EnableDebugLogging)
@@ -191,36 +218,21 @@ namespace MediaBrowser.Dlna.Ssdp
{
if (_queueTimer == null)
{
- _queueTimer = new Timer(QueueTimerCallback, null, 1000, Timeout.Infinite);
+ _queueTimer = new Timer(QueueTimerCallback, null, 500, Timeout.Infinite);
}
else
{
- _queueTimer.Change(1000, Timeout.Infinite);
+ _queueTimer.Change(500, Timeout.Infinite);
}
}
}
private void QueueTimerCallback(object state)
{
- while (_messageQueue.Count != 0)
+ Datagram msg;
+ while (_messageQueue.TryDequeue(out msg))
{
- Datagram msg;
- if (!_messageQueue.TryPeek(out msg))
- {
- continue;
- }
-
- if (msg != null && (!_isDisposed || msg.TotalSendCount > 1))
- {
- msg.Send();
- if (msg.SendCount > msg.TotalSendCount)
- {
- _messageQueue.TryDequeue(out msg);
- }
- break;
- }
-
- _messageQueue.TryDequeue(out msg);
+ msg.Send();
}
_datagramPosted.Set();
diff --git a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
index 7c60df4cbd..afe4b5799b 100644
--- a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.LocalMetadata
{
- public abstract class BaseXmlProvider : ILocalMetadataProvider, IHasChangeMonitor
+ public abstract class BaseXmlProvider : ILocalMetadataProvider, IHasChangeMonitor, IHasOrder
where T : IHasMetadata, new()
{
protected IFileSystem FileSystem;
diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
index cb9c23494a..894be87994 100644
--- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
+++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
@@ -61,7 +61,6 @@
-
@@ -74,7 +73,6 @@
-
diff --git a/MediaBrowser.LocalMetadata/Providers/ChannelXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/ChannelXmlProvider.cs
deleted file mode 100644
index 78845487a2..0000000000
--- a/MediaBrowser.LocalMetadata/Providers/ChannelXmlProvider.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System.IO;
-using System.Threading;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.LocalMetadata.Providers
-{
- public class ChannelXmlProvider : BaseXmlProvider
- {
- private readonly ILogger _logger;
-
- public ChannelXmlProvider(IFileSystem fileSystem, ILogger logger)
- : base(fileSystem)
- {
- _logger = logger;
- }
-
- protected override void Fetch(LocalMetadataResult result, string path, CancellationToken cancellationToken)
- {
- new BaseItemXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
- }
-
- protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
- {
- return directoryService.GetFile(Path.Combine(info.Path, "channel.xml"));
- }
- }
-}
diff --git a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
index dff3c1c07c..d800093300 100644
--- a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
@@ -1,16 +1,16 @@
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
namespace MediaBrowser.LocalMetadata.Providers
{
- public class EpisodeXmlProvider : BaseXmlProvider, IHasOrder
+ public class EpisodeXmlProvider : BaseXmlProvider
{
private readonly ILogger _logger;
@@ -40,14 +40,5 @@ namespace MediaBrowser.LocalMetadata.Providers
return directoryService.GetFile(metadataFile);
}
-
- public int Order
- {
- get
- {
- // After Xbmc
- return 1;
- }
- }
}
}
diff --git a/MediaBrowser.LocalMetadata/Savers/ChannelXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/ChannelXmlSaver.cs
deleted file mode 100644
index 03fdf2bc80..0000000000
--- a/MediaBrowser.LocalMetadata/Savers/ChannelXmlSaver.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Threading;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
- ///
- /// Class PersonXmlSaver
- ///
- public class ChannelXmlSaver : IMetadataFileSaver
- {
- private readonly IServerConfigurationManager _config;
-
- public ChannelXmlSaver(IServerConfigurationManager config)
- {
- _config = config;
- }
-
- ///
- /// Determines whether [is enabled for] [the specified item].
- ///
- /// The item.
- /// Type of the update.
- /// true if [is enabled for] [the specified item]; otherwise, false.
- public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
- {
- if (!item.SupportsLocalMetadata)
- {
- return false;
- }
-
- return item is LiveTvChannel && updateType >= ItemUpdateType.MetadataDownload;
- }
-
- public string Name
- {
- get
- {
- return XmlProviderUtils.Name;
- }
- }
-
- ///
- /// Saves the specified item.
- ///
- /// The item.
- /// The cancellation token.
- /// Task.
- public void Save(IHasMetadata item, CancellationToken cancellationToken)
- {
- var builder = new StringBuilder();
-
- builder.Append("- ");
-
- XmlSaverHelpers.AddCommonNodes((LiveTvChannel)item, builder);
-
- builder.Append("
");
-
- var xmlFilePath = GetSavePath(item);
-
- XmlSaverHelpers.Save(builder, xmlFilePath, new List
- {
- }, _config);
- }
-
- ///
- /// Gets the save path.
- ///
- /// The item.
- /// System.String.
- public string GetSavePath(IHasMetadata item)
- {
- return Path.Combine(item.Path, "channel.xml");
- }
- }
-}
diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
index ecf5d72d5a..5a00c3d3fe 100644
--- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
@@ -248,7 +248,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
protected virtual void DeleteFiles(EncodingJob job)
{
- File.Delete(job.OutputFilePath);
+ FileSystem.DeleteFile(job.OutputFilePath);
}
private void OnTranscodeBeginning(EncodingJob job)
@@ -280,13 +280,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string GetOutputFilePath(EncodingJob state)
{
- var folder = ConfigurationManager.ApplicationPaths.TranscodingTempPath;
+ var folder = string.IsNullOrWhiteSpace(state.Options.OutputDirectory) ?
+ ConfigurationManager.ApplicationPaths.TranscodingTempPath :
+ state.Options.OutputDirectory;
var outputFileExtension = GetOutputFileExtension(state);
- var context = state.Options.Context;
var filename = state.Id + (outputFileExtension ?? string.Empty).ToLower();
- return Path.Combine(folder, context.ToString().ToLower(), filename);
+ return Path.Combine(folder, filename);
}
protected virtual string GetOutputFileExtension(EncodingJob state)
@@ -460,7 +461,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// {
// if (SupportsThrottleWithStream)
// {
- // var url = "http://localhost:" + ServerConfigurationManager.Configuration.HttpServerPortNumber.ToString(UsCulture) + "/mediabrowser/videos/" + job.Request.Id + "/stream?static=true&Throttle=true&mediaSourceId=" + job.Request.MediaSourceId;
+ // var url = "http://localhost:" + ServerConfigurationManager.Configuration.HttpServerPortNumber.ToString(UsCulture) + "/videos/" + job.Request.Id + "/stream?static=true&Throttle=true&mediaSourceId=" + job.Request.MediaSourceId;
// url += "&transcodingJobId=" + transcodingJobId;
@@ -630,13 +631,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
switch (qualitySetting)
{
case EncodingQuality.HighSpeed:
- param += " -crf 23";
+ param += " -subq 0 -crf 23";
break;
case EncodingQuality.HighQuality:
- param += " -crf 20";
+ param += " -subq 3 -crf 20";
break;
case EncodingQuality.MaxQuality:
- param += " -crf 18";
+ param += " -subq 6 -crf 18";
break;
}
}
@@ -739,7 +740,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
param += " -level " + state.Options.Level.Value.ToString(UsCulture);
}
- return param;
+ return "-pix_fmt yuv420p " + param;
}
protected string GetVideoBitrateParam(EncodingJob state, string videoCodec, bool isHls)
@@ -1014,7 +1015,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!string.IsNullOrEmpty(state.SubtitleStream.Language))
{
- var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath, state.SubtitleStream.Language);
+ var charenc = SubtitleEncoder.GetSubtitleFileCharacterSet(subtitlePath);
if (!string.IsNullOrEmpty(charenc))
{
diff --git a/MediaBrowser.MediaEncoding/Encoder/JobLogger.cs b/MediaBrowser.MediaEncoding/Encoder/JobLogger.cs
index 6be8705192..cb6e58f176 100644
--- a/MediaBrowser.MediaEncoding/Encoder/JobLogger.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/JobLogger.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Logging;
using System;
using System.Globalization;
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index a4ab1c5514..b75d7bee30 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -36,6 +36,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// The json serializer.
private readonly IJsonSerializer _jsonSerializer;
+ ///
+ /// The _thumbnail resource pool
+ ///
+ private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1);
+
///
/// The video image resource pool
///
@@ -326,8 +331,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
// -f image2 -f webp
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
- var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf) :
- string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf);
+ var args = useIFrame ? string.Format("-i {0} -threads 1 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf) :
+ string.Format("-i {0} -threads 1 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf);
var probeSize = GetProbeSizeArgument(new[] { inputPath }, protocol);
@@ -357,6 +362,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
};
+ _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
+
await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
process.Start();
@@ -456,7 +463,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
int? maxWidth,
CancellationToken cancellationToken)
{
- var resourcePool = _videoImageResourcePool;
+ var resourcePool = _thumbnailResourcePool;
var inputArgument = GetInputArgument(inputFiles, protocol);
@@ -472,7 +479,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
Directory.CreateDirectory(targetDirectory);
var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
- var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
+ var args = string.Format("-i {0} -threads 1 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
var probeSize = GetProbeSizeArgument(new[] { inputArgument }, protocol);
@@ -499,41 +506,52 @@ namespace MediaBrowser.MediaEncoding.Encoder
await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
- process.Start();
-
- // Need to give ffmpeg enough time to make all the thumbnails, which could be a while,
- // but we still need to detect if the process hangs.
- // Making the assumption that as long as new jpegs are showing up, everything is good.
+ bool ranToCompletion;
- bool isResponsive = true;
- int lastCount = 0;
-
- while (isResponsive && !process.WaitForExit(120000))
+ try
{
- int jpegCount = Directory.GetFiles(targetDirectory, "*.jpg").Count();
- isResponsive = (jpegCount > lastCount);
- lastCount = jpegCount;
- }
+ process.Start();
- bool ranToCompletion = process.HasExited;
+ // Need to give ffmpeg enough time to make all the thumbnails, which could be a while,
+ // but we still need to detect if the process hangs.
+ // Making the assumption that as long as new jpegs are showing up, everything is good.
- if (!ranToCompletion)
- {
- try
+ bool isResponsive = true;
+ int lastCount = 0;
+
+ while (isResponsive && !process.WaitForExit(30000))
{
- _logger.Info("Killing ffmpeg process");
+ cancellationToken.ThrowIfCancellationRequested();
- process.StandardInput.WriteLine("q");
+ int jpegCount = Directory.GetFiles(targetDirectory)
+ .Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));
- process.WaitForExit(1000);
+ isResponsive = (jpegCount > lastCount);
+ lastCount = jpegCount;
}
- catch (Exception ex)
+
+ ranToCompletion = process.HasExited;
+
+ if (!ranToCompletion)
{
- _logger.ErrorException("Error killing process", ex);
+ try
+ {
+ _logger.Info("Killing ffmpeg process");
+
+ process.StandardInput.WriteLine("q");
+
+ process.WaitForExit(1000);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error killing process", ex);
+ }
}
}
-
- resourcePool.Release();
+ finally
+ {
+ resourcePool.Release();
+ }
var exitCode = ranToCompletion ? process.ExitCode : -1;
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
index aaaafc2265..f289449452 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Model.Extensions;
using System;
using System.Collections.Generic;
using System.Globalization;
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
index ea565f70a5..9751176cb6 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
@@ -1,16 +1,25 @@
-using System;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Logging;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
-using MediaBrowser.Common.Extensions;
namespace MediaBrowser.MediaEncoding.Subtitles
{
public class SrtParser : ISubtitleParser
{
+ private readonly ILogger _logger;
+
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public SrtParser(ILogger logger)
+ {
+ _logger = logger;
+ }
+
public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
{
var trackInfo = new SubtitleTrackInfo();
@@ -34,6 +43,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
var time = Regex.Split(line, @"[\t ]*-->[\t ]*");
+
+ if (time.Length < 2)
+ {
+ // This occurs when subtitle text has an empty line as part of the text.
+ // Need to adjust the break statement below to resolve this.
+ _logger.Warn("Unrecognized line in srt: {0}", line);
+ continue;
+ }
subEvent.StartPositionTicks = GetTicks(time[0]);
var endTime = time[1];
var idx = endTime.IndexOf(" ", StringComparison.Ordinal);
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
index d82ef4e24a..358251625c 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
@@ -1,4 +1,4 @@
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Model.Extensions;
using System;
using System.IO;
using System.Text;
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 4f8b6c6ac6..b9cad27e0d 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -149,22 +149,22 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, subtitleStream, cancellationToken).ConfigureAwait(false);
- var stream = await GetSubtitleStream(fileInfo.Item1, subtitleStream.Language, fileInfo.Item3).ConfigureAwait(false);
+ var stream = await GetSubtitleStream(fileInfo.Item1, fileInfo.Item3).ConfigureAwait(false);
return new Tuple(stream, fileInfo.Item2);
}
- private async Task GetSubtitleStream(string path, string language, bool requiresCharset)
+ private async Task GetSubtitleStream(string path, bool requiresCharset)
{
- if (requiresCharset && !string.IsNullOrEmpty(language))
+ if (requiresCharset)
{
- var charset = GetSubtitleFileCharacterSet(path, language);
+ var charset = GetSubtitleFileCharacterSet(path);
if (!string.IsNullOrEmpty(charset))
{
using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
{
- using (var reader = new StreamReader(fs, Encoding.GetEncoding(charset)))
+ using (var reader = new StreamReader(fs, GetEncoding(charset)))
{
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
@@ -179,6 +179,23 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return File.OpenRead(path);
}
+ private Encoding GetEncoding(string charset)
+ {
+ if (string.IsNullOrWhiteSpace(charset))
+ {
+ throw new ArgumentNullException("charset");
+ }
+
+ try
+ {
+ return Encoding.GetEncoding(charset);
+ }
+ catch (ArgumentException)
+ {
+ return Encoding.GetEncoding(charset.Replace("-", string.Empty));
+ }
+ }
+
private async Task> GetReadableFile(string mediaPath,
string[] inputFiles,
MediaProtocol protocol,
@@ -227,8 +244,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
// Convert
var outputPath = GetSubtitleCachePath(mediaPath, subtitleStream.Index, ".srt");
- await ConvertTextSubtitleToSrt(subtitleStream.Path, outputPath, subtitleStream.Language, cancellationToken)
- .ConfigureAwait(false);
+ await ConvertTextSubtitleToSrt(subtitleStream.Path, outputPath, cancellationToken).ConfigureAwait(false);
return new Tuple(outputPath, "srt", true);
}
@@ -254,7 +270,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase))
{
- return new SrtParser();
+ return new SrtParser(_logger);
}
if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase))
{
@@ -321,11 +337,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
///
/// The input path.
/// The output path.
- /// The language.
/// The cancellation token.
/// Task.
- public async Task ConvertTextSubtitleToSrt(string inputPath, string outputPath, string language,
- CancellationToken cancellationToken)
+ public async Task ConvertTextSubtitleToSrt(string inputPath, string outputPath, CancellationToken cancellationToken)
{
var semaphore = GetLock(outputPath);
@@ -335,7 +349,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
if (!File.Exists(outputPath))
{
- await ConvertTextSubtitleToSrtInternal(inputPath, outputPath, language).ConfigureAwait(false);
+ await ConvertTextSubtitleToSrtInternal(inputPath, outputPath).ConfigureAwait(false);
}
}
finally
@@ -349,15 +363,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
///
/// The input path.
/// The output path.
- /// The language.
/// Task.
- ///
- /// inputPath
+ /// inputPath
/// or
- /// outputPath
- ///
+ /// outputPath
///
- private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string outputPath, string language)
+ private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string outputPath)
{
if (string.IsNullOrEmpty(inputPath))
{
@@ -371,9 +382,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
- var encodingParam = string.IsNullOrEmpty(language)
- ? string.Empty
- : GetSubtitleFileCharacterSet(inputPath, language);
+ var encodingParam = GetSubtitleFileCharacterSet(inputPath);
if (!string.IsNullOrEmpty(encodingParam))
{
@@ -459,7 +468,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
try
{
_logger.Info("Deleting converted subtitle due to failure: ", outputPath);
- File.Delete(outputPath);
+ _fileSystem.DeleteFile(outputPath);
}
catch (IOException ex)
{
@@ -608,7 +617,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
try
{
_logger.Info("Deleting extracted subtitle due to failure: {0}", outputPath);
- File.Delete(outputPath);
+ _fileSystem.DeleteFile(outputPath);
}
catch (FileNotFoundException)
{
@@ -696,27 +705,31 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// Gets the subtitle language encoding param.
///
/// The path.
- /// The language.
/// System.String.
- public string GetSubtitleFileCharacterSet(string path, string language)
+ public string GetSubtitleFileCharacterSet(string path)
{
- //var charset = DetectCharset(path);
-
- //if (!string.IsNullOrWhiteSpace(charset))
- //{
- // if (string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase))
- // {
- // return null;
- // }
-
- // return charset;
- //}
-
if (GetFileEncoding(path).Equals(Encoding.UTF8))
{
return string.Empty;
}
+ var charset = DetectCharset(path);
+
+ if (!string.IsNullOrWhiteSpace(charset))
+ {
+ if (string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ return charset;
+ }
+
+ return null;
+ }
+
+ public string GetSubtitleFileCharacterSetFromLanguage(string language)
+ {
switch (language.ToLower())
{
case "pol":
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index c49e3e303f..ba3065bc9d 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -122,6 +122,9 @@
ApiClient\ServerInfo.cs
+
+ ApiClient\ServerUserInfo.cs
+
ApiClient\SessionUpdatesEventArgs.cs
@@ -194,6 +197,9 @@
Configuration\EncodingQuality.cs
+
+ Configuration\FanartOptions.cs
+
Configuration\ImageOption.cs
@@ -227,6 +233,12 @@
Configuration\SubtitlePlaybackMode.cs
+
+ Configuration\TheMovieDbOptions.cs
+
+
+ Configuration\TvdbOptions.cs
+
Configuration\UnratedItem.cs
@@ -488,9 +500,6 @@
Dto\UserItemDataDto.cs
-
- Dto\VideoStreamOptions.cs
-
Entities\BaseItemInfo.cs
@@ -812,9 +821,6 @@
Net\WebSocketState.cs
-
- News\NewsChannel.cs
-
News\NewsItem.cs
@@ -1037,6 +1043,9 @@
Session\UserDataChangeInfo.cs
+
+ Sync\CompleteSyncJobInfo.cs
+
Sync\DeviceFileInfo.cs
@@ -1094,6 +1103,9 @@
Sync\SyncOptions.cs
+
+ Sync\SyncParameter.cs
+
Sync\SyncQuality.cs
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index d8a29e8da4..8d22f25a90 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -87,6 +87,9 @@
ApiClient\ServerInfo.cs
+
+ ApiClient\ServerUserInfo.cs
+
ApiClient\SessionUpdatesEventArgs.cs
@@ -159,6 +162,9 @@
Configuration\EncodingQuality.cs
+
+ Configuration\FanartOptions.cs
+
Configuration\ImageOption.cs
@@ -192,6 +198,12 @@
Configuration\SubtitlePlaybackMode.cs
+
+ Configuration\TheMovieDbOptions.cs
+
+
+ Configuration\TvdbOptions.cs
+
Configuration\UnratedItem.cs
@@ -453,9 +465,6 @@
Dto\UserItemDataDto.cs
-
- Dto\VideoStreamOptions.cs
-
Entities\BaseItemInfo.cs
@@ -771,9 +780,6 @@
Net\WebSocketState.cs
-
- News\NewsChannel.cs
-
News\NewsItem.cs
@@ -996,6 +1002,9 @@
Session\UserDataChangeInfo.cs
+
+ Sync\CompleteSyncJobInfo.cs
+
Sync\DeviceFileInfo.cs
@@ -1053,6 +1062,9 @@
Sync\SyncOptions.cs
+
+ Sync\SyncParameter.cs
+
Sync\SyncQuality.cs
diff --git a/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs b/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs
index 4b0effa55d..b5bf299907 100644
--- a/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs
+++ b/MediaBrowser.Model/ApiClient/ApiClientExtensions.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Sync;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -31,5 +32,10 @@ namespace MediaBrowser.Model.ApiClient
{
return apiClient.GetItemsAsync(query, CancellationToken.None);
}
+
+ public static Task GetSyncOptions(this IApiClient apiClient, SyncJob job)
+ {
+ return apiClient.GetSyncOptions(job.RequestedItemIds, job.UserId, job.ParentId, job.Category);
+ }
}
}
diff --git a/MediaBrowser.Model/ApiClient/ConnectionResult.cs b/MediaBrowser.Model/ApiClient/ConnectionResult.cs
index 12a80ee159..32a80d1a3e 100644
--- a/MediaBrowser.Model/ApiClient/ConnectionResult.cs
+++ b/MediaBrowser.Model/ApiClient/ConnectionResult.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Model.Connect;
+using MediaBrowser.Model.Dto;
using System.Collections.Generic;
namespace MediaBrowser.Model.ApiClient
@@ -9,6 +10,7 @@ namespace MediaBrowser.Model.ApiClient
public List Servers { get; set; }
public IApiClient ApiClient { get; set; }
public ConnectUser ConnectUser { get; set; }
+ public UserDto OfflineUser { get; set; }
public ConnectionResult()
{
diff --git a/MediaBrowser.Model/ApiClient/ConnectionState.cs b/MediaBrowser.Model/ApiClient/ConnectionState.cs
index 5e47d688e1..9b753c7bb6 100644
--- a/MediaBrowser.Model/ApiClient/ConnectionState.cs
+++ b/MediaBrowser.Model/ApiClient/ConnectionState.cs
@@ -6,6 +6,8 @@ namespace MediaBrowser.Model.ApiClient
ServerSignIn = 2,
SignedIn = 3,
ServerSelection = 4,
- ConnectSignIn = 5
+ ConnectSignIn = 5,
+ OfflineSignIn = 6,
+ OfflineSignedIn = 7
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index 9faa8fceda..ca49c6c5ad 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -41,7 +41,7 @@ namespace MediaBrowser.Model.ApiClient
/// Occurs when [authenticated].
///
event EventHandler> Authenticated;
-
+
///
/// Gets the API URL.
///
@@ -201,7 +201,7 @@ namespace MediaBrowser.Model.ApiClient
/// The cancellation token.
/// Task<HttpResponse>.
Task GetResponse(string url, CancellationToken cancellationToken = default(CancellationToken));
-
+
///
/// Updates the user configuration.
///
@@ -225,7 +225,7 @@ namespace MediaBrowser.Model.ApiClient
/// The query.
/// Task<QueryResult<BaseItemDto>>.
Task GetLatestItems(LatestItemsQuery query);
-
+
///
/// Gets the intros async.
///
@@ -324,7 +324,7 @@ namespace MediaBrowser.Model.ApiClient
/// The cancellation token.
/// Task<ItemsResult>.
Task GetUserViews(string userId, CancellationToken cancellationToken = default(CancellationToken));
-
+
///
/// Gets the instant mix from song async.
///
@@ -557,6 +557,13 @@ namespace MediaBrowser.Model.ApiClient
/// id
Task GetUserAsync(string id);
+ ///
+ /// Gets the offline user asynchronous.
+ ///
+ /// The identifier.
+ /// Task<UserDto>.
+ Task GetOfflineUserAsync(string id);
+
///
/// Gets the parental ratings async.
///
@@ -754,7 +761,7 @@ namespace MediaBrowser.Model.ApiClient
/// The password.
/// Task.
/// userId
- Task AuthenticateUserAsync(string username,
+ Task AuthenticateUserAsync(string username,
string password);
///
@@ -867,7 +874,7 @@ namespace MediaBrowser.Model.ApiClient
/// The access token.
/// The user identifier.
void SetAuthenticationInfo(string accessToken, string userId);
-
+
///
/// Sets the authentication information.
///
@@ -886,6 +893,35 @@ namespace MediaBrowser.Model.ApiClient
/// if set to true [keep existing authentication].
void ChangeServerLocation(string address, bool keepExistingAuth = false);
+ ///
+ /// Starts the receiving synchronize job updates.
+ ///
+ /// The interval ms.
+ /// The job identifier.
+ /// Task.
+ Task StartReceivingSyncJobUpdates(int intervalMs, string jobId);
+
+ ///
+ /// Stops the receiving synchronize job updates.
+ ///
+ /// Task.
+ Task StopReceivingSyncJobUpdates();
+
+ ///
+ /// Starts the receiving synchronize jobs updates.
+ ///
+ /// The interval ms.
+ /// The user identifier.
+ /// The target identifier.
+ /// Task.
+ Task StartReceivingSyncJobsUpdates(int intervalMs, string userId, string targetId);
+
+ ///
+ /// Stops the receiving synchronize jobs updates.
+ ///
+ /// Task.
+ Task StopReceivingSyncJobsUpdates();
+
///
/// Starts the receiving session updates.
///
@@ -898,7 +934,7 @@ namespace MediaBrowser.Model.ApiClient
///
/// Task.
Task StopReceivingSessionUpdates();
-
+
///
/// Gets the image URL.
///
@@ -1316,24 +1352,6 @@ namespace MediaBrowser.Model.ApiClient
/// Task<QueryResult<BaseItemDto>>.
Task> GetPlaylistItems(PlaylistItemQuery query);
- ///
- /// Gets the url needed to stream a video file
- ///
- /// The options.
- /// System.String.
- /// options
- [Obsolete]
- string GetVideoStreamUrl(VideoStreamOptions options);
-
- ///
- /// Formulates a url for streaming video using the HLS protocol
- ///
- /// The options.
- /// System.String.
- /// options
- [Obsolete]
- string GetHlsVideoStreamUrl(VideoStreamOptions options);
-
///
/// Sends the context message asynchronous.
///
@@ -1360,7 +1378,7 @@ namespace MediaBrowser.Model.ApiClient
/// The file.
/// The cancellation token.
/// Task.
- Task UploadFile(Stream stream,
+ Task UploadFile(Stream stream,
LocalFileInfo file,
CancellationToken cancellationToken);
@@ -1384,6 +1402,13 @@ namespace MediaBrowser.Model.ApiClient
/// Task<SyncJob>.
Task CreateSyncJob(SyncJobRequest request);
+ ///
+ /// Updates the synchronize job.
+ ///
+ /// The job.
+ /// Task.
+ Task UpdateSyncJob(SyncJob job);
+
///
/// Gets the synchronize jobs.
///
@@ -1413,6 +1438,15 @@ namespace MediaBrowser.Model.ApiClient
/// Task<Stream>.
Task GetSyncJobItemFile(string id, CancellationToken cancellationToken);
+ ///
+ /// Gets the synchronize job item additional file.
+ ///
+ /// The identifier.
+ /// The name.
+ /// The cancellation token.
+ /// Task<Stream>.
+ Task GetSyncJobItemAdditionalFile(string id, string name, CancellationToken cancellationToken);
+
///
/// Opens the web socket.
///
@@ -1440,5 +1474,56 @@ namespace MediaBrowser.Model.ApiClient
/// The request.
/// Task<SyncDataResponse>.
Task SyncData(SyncDataRequest request);
+ ///
+ /// Gets the synchronize job item file URL.
+ ///
+ /// The identifier.
+ /// System.String.
+ string GetSyncJobItemFileUrl(string id);
+ ///
+ /// Marks the synchronize job item for removal.
+ ///
+ /// The identifier.
+ /// Task.
+ Task MarkSyncJobItemForRemoval(string id);
+ ///
+ /// Unmarks the synchronize job item for removal.
+ ///
+ /// The identifier.
+ /// Task.
+ Task UnmarkSyncJobItemForRemoval(string id);
+ ///
+ /// Queues the failed synchronize job item for retry.
+ ///
+ /// The identifier.
+ /// Task.
+ Task QueueFailedSyncJobItemForRetry(string id);
+ ///
+ /// Cancels the synchronize job.
+ ///
+ /// The identifier.
+ /// Task.
+ Task CancelSyncJob(string id);
+ ///
+ /// Cancels the synchronize job item.
+ ///
+ /// The identifier.
+ /// Task.
+ Task CancelSyncJobItem(string id);
+ ///
+ /// Enables the cancelled synchronize job item.
+ ///
+ /// The identifier.
+ /// Task.
+ Task EnableCancelledSyncJobItem(string id);
+ ///
+ /// Gets the synchronize options.
+ ///
+ /// The user identifier.
+ /// The item ids.
+ /// The parent identifier.
+ /// The category.
+ /// Task<SyncOptions>.
+ Task GetSyncOptions(IEnumerable itemIds, string userId, string parentId = null, SyncCategory? category = null);
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Model/ApiClient/IConnectionManager.cs b/MediaBrowser.Model/ApiClient/IConnectionManager.cs
index a54c330acd..658c71ac5d 100644
--- a/MediaBrowser.Model/ApiClient/IConnectionManager.cs
+++ b/MediaBrowser.Model/ApiClient/IConnectionManager.cs
@@ -136,5 +136,14 @@ namespace MediaBrowser.Model.ApiClient
///
/// The cancellation token.
Task> GetAvailableServers(CancellationToken cancellationToken);
+
+ ///
+ /// Authenticates an offline user with their password
+ ///
+ /// The user.
+ /// The password.
+ /// if set to true [remember credentials].
+ /// Task.
+ Task AuthenticateOffline(UserDto user, string password, bool rememberCredentials);
}
}
diff --git a/MediaBrowser.Model/ApiClient/IServerEvents.cs b/MediaBrowser.Model/ApiClient/IServerEvents.cs
index 88faad3884..ae2d5d3233 100644
--- a/MediaBrowser.Model/ApiClient/IServerEvents.cs
+++ b/MediaBrowser.Model/ApiClient/IServerEvents.cs
@@ -3,9 +3,11 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Session;
+using MediaBrowser.Model.Sync;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Updates;
using System;
+using System.Collections.Generic;
namespace MediaBrowser.Model.ApiClient
{
@@ -130,5 +132,21 @@ namespace MediaBrowser.Model.ApiClient
/// Occurs when [session ended].
///
event EventHandler> SessionEnded;
+ ///
+ /// Occurs when [synchronize job created].
+ ///
+ event EventHandler> SyncJobCreated;
+ ///
+ /// Occurs when [synchronize job cancelled].
+ ///
+ event EventHandler> SyncJobCancelled;
+ ///
+ /// Occurs when [synchronize jobs updated].
+ ///
+ event EventHandler>> SyncJobsUpdated;
+ ///
+ /// Occurs when [synchronize job updated].
+ ///
+ event EventHandler> SyncJobUpdated;
}
}
diff --git a/MediaBrowser.Model/ApiClient/ServerCredentials.cs b/MediaBrowser.Model/ApiClient/ServerCredentials.cs
index fbbc6c05de..d911f81211 100644
--- a/MediaBrowser.Model/ApiClient/ServerCredentials.cs
+++ b/MediaBrowser.Model/ApiClient/ServerCredentials.cs
@@ -70,6 +70,10 @@ namespace MediaBrowser.Model.ApiClient
{
existing.LastConnectionMode = server.LastConnectionMode;
}
+ foreach (ServerUserInfo user in server.Users)
+ {
+ existing.AddOrUpdate(user);
+ }
}
else
{
@@ -85,7 +89,7 @@ namespace MediaBrowser.Model.ApiClient
foreach (var server in servers)
{
- if (StringHelper.Equals(id, server.Id))
+ if (StringHelper.EqualsIgnoreCase(id, server.Id))
{
return index;
}
diff --git a/MediaBrowser.Model/ApiClient/ServerInfo.cs b/MediaBrowser.Model/ApiClient/ServerInfo.cs
index 46cc560af2..cc062f2f60 100644
--- a/MediaBrowser.Model/ApiClient/ServerInfo.cs
+++ b/MediaBrowser.Model/ApiClient/ServerInfo.cs
@@ -1,12 +1,16 @@
using MediaBrowser.Model.Connect;
+using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.System;
using System;
using System.Collections.Generic;
+using System.Linq;
namespace MediaBrowser.Model.ApiClient
{
public class ServerInfo
{
+ public List Users { get; set; }
+
public String Name { get; set; }
public String Id { get; set; }
public String LocalAddress { get; set; }
@@ -23,6 +27,7 @@ namespace MediaBrowser.Model.ApiClient
public ServerInfo()
{
WakeOnLanInfos = new List();
+ Users = new List();
}
public void ImportInfo(PublicSystemInfo systemInfo)
@@ -70,5 +75,48 @@ namespace MediaBrowser.Model.ApiClient
throw new ArgumentException("Unexpected ConnectionMode");
}
}
+
+ public void AddOrUpdate(ServerUserInfo user)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException("user");
+ }
+
+ var list = Users.ToList();
+
+ var index = FindIndex(list, user.Id);
+
+ if (index != -1)
+ {
+ var existing = list[index];
+
+ // Merge the data
+ existing.IsSignedInOffline = user.IsSignedInOffline;
+ }
+ else
+ {
+ list.Add(user);
+ }
+
+ Users = list;
+ }
+
+ private int FindIndex(List users, string id)
+ {
+ var index = 0;
+
+ foreach (var user in users)
+ {
+ if (StringHelper.EqualsIgnoreCase(id, user.Id))
+ {
+ return index;
+ }
+
+ index++;
+ }
+
+ return -1;
+ }
}
}
diff --git a/MediaBrowser.Model/ApiClient/ServerUserInfo.cs b/MediaBrowser.Model/ApiClient/ServerUserInfo.cs
new file mode 100644
index 0000000000..812da7402c
--- /dev/null
+++ b/MediaBrowser.Model/ApiClient/ServerUserInfo.cs
@@ -0,0 +1,9 @@
+
+namespace MediaBrowser.Model.ApiClient
+{
+ public class ServerUserInfo
+ {
+ public string Id { get; set; }
+ public bool IsSignedInOffline { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Branding/BrandingOptions.cs b/MediaBrowser.Model/Branding/BrandingOptions.cs
index 737cb5c48f..3b207d345c 100644
--- a/MediaBrowser.Model/Branding/BrandingOptions.cs
+++ b/MediaBrowser.Model/Branding/BrandingOptions.cs
@@ -8,5 +8,10 @@ namespace MediaBrowser.Model.Branding
///
/// The login disclaimer.
public string LoginDisclaimer { get; set; }
+ ///
+ /// Gets or sets the custom CSS.
+ ///
+ /// The custom CSS.
+ public string CustomCss { get; set; }
}
}
diff --git a/MediaBrowser.Model/Configuration/FanartOptions.cs b/MediaBrowser.Model/Configuration/FanartOptions.cs
new file mode 100644
index 0000000000..e992abe5de
--- /dev/null
+++ b/MediaBrowser.Model/Configuration/FanartOptions.cs
@@ -0,0 +1,17 @@
+
+namespace MediaBrowser.Model.Configuration
+{
+ public class FanartOptions
+ {
+ ///
+ /// Gets or sets a value indicating whether [enable automatic updates].
+ ///
+ /// true if [enable automatic updates]; otherwise, false.
+ public bool EnableAutomaticUpdates { get; set; }
+ ///
+ /// Gets or sets the user API key.
+ ///
+ /// The user API key.
+ public string UserApiKey { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index f9c5c65674..c15c76004e 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -20,6 +20,12 @@ namespace MediaBrowser.Model.Configuration
/// The public mapped port.
public int PublicPort { get; set; }
+ ///
+ /// Gets or sets the public HTTPS port.
+ ///
+ /// The public HTTPS port.
+ public int PublicHttpsPort { get; set; }
+
///
/// Gets or sets the HTTP server port number.
///
@@ -31,7 +37,19 @@ namespace MediaBrowser.Model.Configuration
///
/// The HTTPS server port number.
public int HttpsPortNumber { get; set; }
-
+
+ ///
+ /// Gets or sets a value indicating whether [use HTTPS].
+ ///
+ /// true if [use HTTPS]; otherwise, false.
+ public bool EnableHttps { get; set; }
+
+ ///
+ /// Gets or sets the value pointing to the file system where the ssl certiifcate is located..
+ ///
+ /// The value pointing to the file system where the ssl certiifcate is located..
+ public string CertificatePath { get; set; }
+
///
/// Gets or sets a value indicating whether [enable internet providers].
///
@@ -73,6 +91,12 @@ namespace MediaBrowser.Model.Configuration
///
/// true if [enable localized guids]; otherwise, false.
public bool EnableLocalizedGuids { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether [enable library metadata sub folder].
+ ///
+ /// true if [enable library metadata sub folder]; otherwise, false.
+ public bool EnableLibraryMetadataSubFolder { get; set; }
///
/// Gets or sets the preferred metadata language.
@@ -136,6 +160,7 @@ namespace MediaBrowser.Model.Configuration
///
/// true if [enable dashboard response caching]; otherwise, false.
public bool EnableDashboardResponseCaching { get; set; }
+ public bool EnableDashboardResourceMinification { get; set; }
///
/// Allows the dashboard to be served from a custom path.
@@ -143,13 +168,8 @@ namespace MediaBrowser.Model.Configuration
/// The dashboard source path.
public string DashboardSourcePath { get; set; }
- ///
- /// Gets or sets a value indicating whether [enable tv db updates].
- ///
- /// true if [enable tv db updates]; otherwise, false.
- public bool EnableTvDbUpdates { get; set; }
- public bool EnableTmdbUpdates { get; set; }
- public bool EnableFanArtUpdates { get; set; }
+ public bool MergeMetadataAndImagesByName { get; set; }
+ public bool EnableStandaloneMetadata { get; set; }
///
/// Gets or sets the image saving convention.
@@ -177,13 +197,14 @@ namespace MediaBrowser.Model.Configuration
public string[] InsecureApps8 { get; set; }
public bool SaveMetadataHidden { get; set; }
- public bool EnableWin8HttpListener { get; set; }
public NameValuePair[] ContentTypes { get; set; }
public bool EnableAudioArchiveFiles { get; set; }
public bool EnableVideoArchiveFiles { get; set; }
+ public bool EnableLegacyCollections { get; set; }
+
///
/// Initializes a new instance of the class.
///
@@ -192,12 +213,14 @@ namespace MediaBrowser.Model.Configuration
{
ImageSavingConvention = ImageSavingConvention.Compatible;
PublicPort = 8096;
+ PublicHttpsPort = 8920;
HttpServerPortNumber = 8096;
HttpsPortNumber = 8920;
+ EnableHttps = false;
EnableDashboardResponseCaching = true;
+ EnableDashboardResourceMinification = true;
EnableAutomaticRestart = true;
- EnableWin8HttpListener = true;
EnableUPnP = true;
diff --git a/MediaBrowser.Model/Configuration/TheMovieDbOptions.cs b/MediaBrowser.Model/Configuration/TheMovieDbOptions.cs
new file mode 100644
index 0000000000..9a73e34764
--- /dev/null
+++ b/MediaBrowser.Model/Configuration/TheMovieDbOptions.cs
@@ -0,0 +1,12 @@
+
+namespace MediaBrowser.Model.Configuration
+{
+ public class TheMovieDbOptions
+ {
+ ///
+ /// Gets or sets a value indicating whether [enable automatic updates].
+ ///
+ /// true if [enable automatic updates]; otherwise, false.
+ public bool EnableAutomaticUpdates { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Configuration/TvdbOptions.cs b/MediaBrowser.Model/Configuration/TvdbOptions.cs
new file mode 100644
index 0000000000..034af609c4
--- /dev/null
+++ b/MediaBrowser.Model/Configuration/TvdbOptions.cs
@@ -0,0 +1,12 @@
+
+namespace MediaBrowser.Model.Configuration
+{
+ public class TvdbOptions
+ {
+ ///
+ /// Gets or sets a value indicating whether [enable automatic updates].
+ ///
+ /// true if [enable automatic updates]; otherwise, false.
+ public bool EnableAutomaticUpdates { get; set; }
+ }
+}
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index 9e33c1c36f..aa49ee50d5 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -6,12 +6,6 @@ namespace MediaBrowser.Model.Configuration
///
public class UserConfiguration
{
- ///
- /// Gets or sets the max parental rating.
- ///
- /// The max parental rating.
- public int? MaxParentalRating { get; set; }
-
///
/// Gets or sets a value indicating whether this instance is administrator.
///
@@ -36,22 +30,8 @@ namespace MediaBrowser.Model.Configuration
/// The subtitle language preference.
public string SubtitleLanguagePreference { get; set; }
- ///
- /// Gets or sets a value indicating whether this instance is hidden.
- ///
- /// true if this instance is hidden; otherwise, false.
- public bool IsHidden { get; set; }
-
- ///
- /// Gets or sets a value indicating whether this instance is disabled.
- ///
- /// true if this instance is disabled; otherwise, false.
- public bool IsDisabled { get; set; }
-
public bool DisplayMissingEpisodes { get; set; }
public bool DisplayUnairedEpisodes { get; set; }
- public bool EnableRemoteControlOfOtherUsers { get; set; }
- public bool EnableSharedDeviceControl { get; set; }
public bool EnableLiveTvManagement { get; set; }
public bool EnableLiveTvAccess { get; set; }
@@ -61,9 +41,6 @@ namespace MediaBrowser.Model.Configuration
public bool GroupMoviesIntoBoxSets { get; set; }
- public string[] BlockedMediaFolders { get; set; }
- public string[] BlockedChannels { get; set; }
-
public string[] DisplayChannelsWithinViews { get; set; }
public string[] ExcludeFoldersFromGrouping { get; set; }
@@ -82,12 +59,7 @@ namespace MediaBrowser.Model.Configuration
public bool EnableCinemaMode { get; set; }
- public AccessSchedule[] AccessSchedules { get; set; }
-
- public bool EnableUserPreferenceAccess { get; set; }
-
public string[] LatestItemsExcludes { get; set; }
- public string[] BlockedTags { get; set; }
public bool HasMigratedToPolicy { get; set; }
@@ -100,14 +72,10 @@ namespace MediaBrowser.Model.Configuration
EnableLiveTvManagement = true;
EnableMediaPlayback = true;
EnableLiveTvAccess = true;
- EnableSharedDeviceControl = true;
LatestItemsExcludes = new string[] { };
OrderedViews = new string[] { };
- BlockedMediaFolders = new string[] { };
DisplayChannelsWithinViews = new string[] { };
- BlockedTags = new string[] { };
- BlockedChannels = new string[] { };
BlockUnratedItems = new UnratedItem[] { };
ExcludeFoldersFromGrouping = new string[] { };
@@ -115,9 +83,6 @@ namespace MediaBrowser.Model.Configuration
IncludeTrailersInSuggestions = true;
EnableCinemaMode = true;
- EnableUserPreferenceAccess = true;
-
- AccessSchedules = new AccessSchedule[] { };
}
}
}
diff --git a/MediaBrowser.Model/Connect/ConnectAuthorization.cs b/MediaBrowser.Model/Connect/ConnectAuthorization.cs
index 0c545f4bac..e8baf72692 100644
--- a/MediaBrowser.Model/Connect/ConnectAuthorization.cs
+++ b/MediaBrowser.Model/Connect/ConnectAuthorization.cs
@@ -7,14 +7,14 @@ namespace MediaBrowser.Model.Connect
public string UserName { get; set; }
public string ImageUrl { get; set; }
public string Id { get; set; }
- public string[] ExcludedLibraries { get; set; }
+ public string[] EnabledLibraries { get; set; }
public bool EnableLiveTv { get; set; }
- public string[] ExcludedChannels { get; set; }
+ public string[] EnabledChannels { get; set; }
public ConnectAuthorization()
{
- ExcludedLibraries = new string[] { };
- ExcludedChannels = new string[] { };
+ EnabledLibraries = new string[] { };
+ EnabledChannels = new string[] { };
}
}
}
diff --git a/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs b/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs
index d40d353f01..6baea15a9d 100644
--- a/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs
+++ b/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs
@@ -5,14 +5,14 @@ namespace MediaBrowser.Model.Connect
{
public string SendingUserId { get; set; }
public string ConnectUserName { get; set; }
- public string[] ExcludedLibraries { get; set; }
+ public string[] EnabledLibraries { get; set; }
public bool EnableLiveTv { get; set; }
- public string[] ExcludedChannels { get; set; }
+ public string[] EnabledChannels { get; set; }
public ConnectAuthorizationRequest()
{
- ExcludedLibraries = new string[] { };
- ExcludedChannels = new string[] { };
+ EnabledLibraries = new string[] { };
+ EnabledChannels = new string[] { };
}
}
}
diff --git a/MediaBrowser.Model/Devices/DeviceQuery.cs b/MediaBrowser.Model/Devices/DeviceQuery.cs
index 2cd2389d89..9ae4986062 100644
--- a/MediaBrowser.Model/Devices/DeviceQuery.cs
+++ b/MediaBrowser.Model/Devices/DeviceQuery.cs
@@ -12,7 +12,7 @@ namespace MediaBrowser.Model.Devices
/// Gets or sets a value indicating whether [supports unique identifier].
///
/// null if [supports unique identifier] contains no value, true if [supports unique identifier]; otherwise, false.
- public bool? SupportsUniqueIdentifier { get; set; }
+ public bool? SupportsPersistentIdentifier { get; set; }
///
/// Gets or sets a value indicating whether [supports synchronize].
///
diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs
index dd6dad2610..cddfd89559 100644
--- a/MediaBrowser.Model/Dlna/AudioOptions.cs
+++ b/MediaBrowser.Model/Dlna/AudioOptions.cs
@@ -47,6 +47,17 @@ namespace MediaBrowser.Model.Dlna
/// The audio transcoding bitrate.
public int? AudioTranscodingBitrate { get; set; }
+ ///
+ /// Gets or sets a value indicating whether [supports direct remote content].
+ ///
+ /// true if [supports direct remote content]; otherwise, false.
+ public bool SupportsDirectRemoteContent { get; set; }
+ ///
+ /// Gets or sets a value indicating whether [supports custom HTTP headers].
+ ///
+ /// true if [supports custom HTTP headers]; otherwise, false.
+ public bool SupportsCustomHttpHeaders { get; set; }
+
///
/// Gets the maximum bitrate.
///
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index 1fb553010e..4b137a268c 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -76,8 +76,7 @@ namespace MediaBrowser.Model.Dlna
public bool RequiresPlainVideoItems { get; set; }
public bool RequiresPlainFolders { get; set; }
- public bool SupportsDirectRemoteContent { get; set; }
- public bool SupportsCustomHttpHeaders { get; set; }
+ public bool EnableMSMediaReceiverRegistrar { get; set; }
public XmlAttribute[] XmlRootAttributes { get; set; }
diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
index 5101bbe5af..ffff0f27d5 100644
--- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
+++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
@@ -109,7 +109,7 @@ namespace MediaBrowser.Model.Dlna
return new List { MediaFormatProfile.AVC_TS_HD_DTS_T };
}
- if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3"))
+ if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2"))
{
if (timestampType == TransportStreamTimestamp.None)
{
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 7c47b0d444..6fa29a533b 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -10,8 +10,6 @@ namespace MediaBrowser.Model.Dlna
{
public class StreamBuilder
{
- private readonly string[] _serverTextSubtitleOutputs = { "srt", "vtt", "ttml" };
-
public StreamInfo BuildAudioItem(AudioOptions options)
{
ValidateAudioInput(options);
@@ -110,7 +108,8 @@ namespace MediaBrowser.Model.Dlna
MediaType = DlnaProfileType.Audio,
MediaSource = item,
RunTimeTicks = item.RunTimeTicks,
- Context = options.Context
+ Context = options.Context,
+ DeviceProfile = options.Profile
};
int? maxBitrateSetting = options.GetMaxBitrate();
@@ -242,7 +241,8 @@ namespace MediaBrowser.Model.Dlna
MediaType = DlnaProfileType.Video,
MediaSource = item,
RunTimeTicks = item.RunTimeTicks,
- Context = options.Context
+ Context = options.Context,
+ DeviceProfile = options.Profile
};
int? audioStreamIndex = options.AudioStreamIndex ?? item.DefaultAudioStreamIndex;
@@ -258,7 +258,7 @@ namespace MediaBrowser.Model.Dlna
if (IsEligibleForDirectPlay(item, maxBitrateSetting, subtitleStream, options))
{
// See if it can be direct played
- var directPlay = GetVideoDirectPlayProfile(options.Profile, item, videoStream, audioStream);
+ var directPlay = GetVideoDirectPlayProfile(options, options.Profile, item, videoStream, audioStream);
if (directPlay != null)
{
@@ -267,7 +267,10 @@ namespace MediaBrowser.Model.Dlna
if (subtitleStream != null)
{
- playlistItem.SubtitleDeliveryMethod = GetSubtitleDeliveryMethod(subtitleStream, options);
+ SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile);
+
+ playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
+ playlistItem.SubtitleFormat = subtitleProfile.Format;
}
return playlistItem;
@@ -289,7 +292,10 @@ namespace MediaBrowser.Model.Dlna
{
if (subtitleStream != null)
{
- playlistItem.SubtitleDeliveryMethod = GetSubtitleDeliveryMethod(subtitleStream, options);
+ SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile);
+
+ playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
+ playlistItem.SubtitleFormat = subtitleProfile.Format;
}
playlistItem.PlayMethod = PlayMethod.Transcode;
@@ -374,7 +380,8 @@ namespace MediaBrowser.Model.Dlna
return 128000;
}
- private PlayMethod? GetVideoDirectPlayProfile(DeviceProfile profile,
+ private PlayMethod? GetVideoDirectPlayProfile(VideoOptions options,
+ DeviceProfile profile,
MediaSourceInfo mediaSource,
MediaStream videoStream,
MediaStream audioStream)
@@ -498,12 +505,12 @@ namespace MediaBrowser.Model.Dlna
if (mediaSource.Protocol == MediaProtocol.Http)
{
- if (!profile.SupportsDirectRemoteContent)
+ if (!options.SupportsDirectRemoteContent)
{
return null;
}
- if (mediaSource.RequiredHttpHeaders.Count > 0 && !profile.SupportsCustomHttpHeaders)
+ if (mediaSource.RequiredHttpHeaders.Count > 0 && !options.SupportsCustomHttpHeaders)
{
return null;
}
@@ -520,14 +527,9 @@ namespace MediaBrowser.Model.Dlna
{
if (subtitleStream != null)
{
- if (!subtitleStream.IsTextSubtitleStream)
- {
- return false;
- }
-
- SubtitleDeliveryMethod subtitleMethod = GetSubtitleDeliveryMethod(subtitleStream, options);
+ SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile);
- if (subtitleMethod != SubtitleDeliveryMethod.External && subtitleMethod != SubtitleDeliveryMethod.Embed)
+ if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
{
return false;
}
@@ -536,43 +538,33 @@ namespace MediaBrowser.Model.Dlna
return IsAudioEligibleForDirectPlay(item, maxBitrate);
}
- private SubtitleDeliveryMethod GetSubtitleDeliveryMethod(MediaStream subtitleStream,
- VideoOptions options)
+ public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, DeviceProfile deviceProfile)
{
- if (subtitleStream.IsTextSubtitleStream)
+ // Look for an external profile that matches the stream type (text/graphical)
+ foreach (SubtitleProfile profile in deviceProfile.SubtitleProfiles)
{
- // See if the device can retrieve the subtitles externally
- bool supportsSubsExternally = options.Context == EncodingContext.Streaming &&
- ContainsSubtitleFormat(options.Profile.SubtitleProfiles, SubtitleDeliveryMethod.External, _serverTextSubtitleOutputs);
-
- if (supportsSubsExternally)
- {
- return SubtitleDeliveryMethod.External;
- }
-
- // See if the device can retrieve the subtitles externally
- bool supportsEmbedded = ContainsSubtitleFormat(options.Profile.SubtitleProfiles, SubtitleDeliveryMethod.Embed, _serverTextSubtitleOutputs);
-
- if (supportsEmbedded)
+ if (subtitleStream.SupportsExternalStream)
{
- return SubtitleDeliveryMethod.Embed;
+ if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
+ {
+ return profile;
+ }
}
}
- return SubtitleDeliveryMethod.Encode;
- }
-
- private bool ContainsSubtitleFormat(SubtitleProfile[] profiles, SubtitleDeliveryMethod method, string[] formats)
- {
- foreach (SubtitleProfile profile in profiles)
+ foreach (SubtitleProfile profile in deviceProfile.SubtitleProfiles)
{
- if (method == profile.Method && ListHelper.ContainsIgnoreCase(formats, profile.Format))
+ if (profile.Method == SubtitleDeliveryMethod.Embed && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))
{
- return true;
+ return profile;
}
}
- return false;
+ return new SubtitleProfile
+ {
+ Method = SubtitleDeliveryMethod.Encode,
+ Format = subtitleStream.Codec
+ };
}
private bool IsAudioEligibleForDirectPlay(MediaSourceInfo item, int? maxBitrate)
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 22eb0cf6ce..3c2f39b745 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -54,6 +54,7 @@ namespace MediaBrowser.Model.Dlna
public float? MaxFramerate { get; set; }
+ public DeviceProfile DeviceProfile { get; set; }
public string DeviceProfileId { get; set; }
public string DeviceId { get; set; }
@@ -160,11 +161,6 @@ namespace MediaBrowser.Model.Dlna
List list = new List();
- if (SubtitleDeliveryMethod != SubtitleDeliveryMethod.External)
- {
- return list;
- }
-
// HLS will preserve timestamps so we can just grab the full subtitle stream
long startPositionTicks = StringHelper.EqualsIgnoreCase(Protocol, "hls")
? 0
@@ -175,7 +171,7 @@ namespace MediaBrowser.Model.Dlna
{
foreach (MediaStream stream in MediaSource.MediaStreams)
{
- if (stream.Type == MediaStreamType.Subtitle && stream.IsTextSubtitleStream && stream.Index == SubtitleStreamIndex.Value)
+ if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
{
AddSubtitle(list, stream, baseUrl, startPositionTicks);
}
@@ -186,7 +182,7 @@ namespace MediaBrowser.Model.Dlna
{
foreach (MediaStream stream in MediaSource.MediaStreams)
{
- if (stream.Type == MediaStreamType.Subtitle && stream.IsTextSubtitleStream && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
+ if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
{
AddSubtitle(list, stream, baseUrl, startPositionTicks);
}
@@ -198,6 +194,13 @@ namespace MediaBrowser.Model.Dlna
private void AddSubtitle(List list, MediaStream stream, string baseUrl, long startPositionTicks)
{
+ var subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, DeviceProfile);
+
+ if (subtitleProfile.Method != SubtitleDeliveryMethod.External)
+ {
+ return;
+ }
+
string url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
baseUrl,
ItemId,
@@ -212,7 +215,8 @@ namespace MediaBrowser.Model.Dlna
IsForced = stream.IsForced,
Language = stream.Language,
Name = stream.Language ?? "Unknown",
- Format = SubtitleFormat
+ Format = SubtitleFormat,
+ Index = stream.Index
});
}
diff --git a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
index 1600408d60..a7a8da3ba2 100644
--- a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs
@@ -7,5 +7,6 @@ namespace MediaBrowser.Model.Dlna
public string Name { get; set; }
public bool IsForced { get; set; }
public string Format { get; set; }
+ public int Index { get; set; }
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 2383d4809a..be3cd99be8 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -56,6 +56,8 @@ namespace MediaBrowser.Model.Dto
public int? AirsBeforeEpisodeNumber { get; set; }
public int? AbsoluteEpisodeNumber { get; set; }
public bool? DisplaySpecialsWithSeasons { get; set; }
+ public bool? CanDelete { get; set; }
+ public bool? CanDownload { get; set; }
public string PreferredMetadataLanguage { get; set; }
public string PreferredMetadataCountryCode { get; set; }
@@ -69,7 +71,8 @@ namespace MediaBrowser.Model.Dto
public int? AnimeSeriesIndex { get; set; }
public bool? SupportsSync { get; set; }
-
+ public bool? HasSyncJob { get; set; }
+
///
/// Gets or sets the DVD season number.
///
@@ -223,12 +226,6 @@ namespace MediaBrowser.Model.Dto
/// The run time ticks.
public long? RunTimeTicks { get; set; }
- ///
- /// Gets or sets the recursive unplayed item count.
- ///
- /// The recursive unplayed item count.
- public int? RecursiveUnplayedItemCount { get; set; }
-
///
/// Gets or sets the play access.
///
diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs
index 9b6d920306..c66b34150d 100644
--- a/MediaBrowser.Model/Dto/UserDto.cs
+++ b/MediaBrowser.Model/Dto/UserDto.cs
@@ -26,7 +26,14 @@ namespace MediaBrowser.Model.Dto
///
/// The server identifier.
public string ServerId { get; set; }
-
+
+ ///
+ /// Gets or sets the name of the server.
+ /// This is not used by the server and is for client-side usage only.
+ ///
+ /// The name of the server.
+ public string ServerName { get; set; }
+
///
/// Gets or sets the name of the connect user.
///
@@ -48,7 +55,13 @@ namespace MediaBrowser.Model.Dto
///
/// The id.
public string Id { get; set; }
-
+
+ ///
+ /// Gets or sets the offline password.
+ ///
+ /// The offline password.
+ public string OfflinePassword { get; set; }
+
///
/// Gets or sets the primary image tag.
///
@@ -66,6 +79,12 @@ namespace MediaBrowser.Model.Dto
///
/// true if this instance has configured password; otherwise, false.
public bool HasConfiguredPassword { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether this instance has configured easy password.
+ ///
+ /// true if this instance has configured easy password; otherwise, false.
+ public bool HasConfiguredEasyPassword { get; set; }
///
/// Gets or sets the last login date.
@@ -102,7 +121,7 @@ namespace MediaBrowser.Model.Dto
///
/// The original primary image aspect ratio.
public double? OriginalPrimaryImageAspectRatio { get; set; }
-
+
///
/// Gets a value indicating whether this instance has primary image.
///
diff --git a/MediaBrowser.Model/Dto/VideoStreamOptions.cs b/MediaBrowser.Model/Dto/VideoStreamOptions.cs
deleted file mode 100644
index e9a83bd12a..0000000000
--- a/MediaBrowser.Model/Dto/VideoStreamOptions.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-using System;
-
-namespace MediaBrowser.Model.Dto
-{
- ///
- /// Class VideoStreamOptions
- ///
- [Obsolete]
- public class VideoStreamOptions
- {
- ///
- /// Gets or sets the audio bit rate.
- ///
- /// The audio bit rate.
- public int? AudioBitRate { get; set; }
-
- ///
- /// Gets or sets the audio codec.
- /// Omit to copy the original stream
- ///
- /// The audio encoding format.
- public string AudioCodec { get; set; }
-
- ///
- /// Gets or sets the item id.
- ///
- /// The item id.
- public string ItemId { get; set; }
-
- ///
- /// Gets or sets the max audio channels.
- ///
- /// The max audio channels.
- public int? MaxAudioChannels { get; set; }
-
- ///
- /// Gets or sets the max audio sample rate.
- ///
- /// The max audio sample rate.
- public int? MaxAudioSampleRate { get; set; }
-
- ///
- /// Gets or sets the start time ticks.
- ///
- /// The start time ticks.
- public long? StartTimeTicks { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the original media should be served statically
- /// Only used with progressive streaming
- ///
- /// true if static; otherwise, false.
- public bool? Static { get; set; }
-
- ///
- /// Gets or sets the output file extension.
- ///
- /// The output file extension.
- public string OutputFileExtension { get; set; }
-
- ///
- /// Gets or sets the device id.
- ///
- /// The device id.
- public string DeviceId { get; set; }
-
- ///
- /// Gets or sets the video codec.
- /// Omit to copy
- ///
- /// The video codec.
- public string VideoCodec { get; set; }
-
- ///
- /// Gets or sets the video bit rate.
- ///
- /// The video bit rate.
- public int? VideoBitRate { get; set; }
-
- ///
- /// Gets or sets the width.
- ///
- /// The width.
- public int? Width { get; set; }
-
- ///
- /// Gets or sets the height.
- ///
- /// The height.
- public int? Height { get; set; }
-
- ///
- /// Gets or sets the width of the max.
- ///
- /// The width of the max.
- public int? MaxWidth { get; set; }
-
- ///
- /// Gets or sets the height of the max.
- ///
- /// The height of the max.
- public int? MaxHeight { get; set; }
-
- ///
- /// Gets or sets the frame rate.
- ///
- /// The frame rate.
- public double? FrameRate { get; set; }
-
- ///
- /// Gets or sets the index of the audio stream.
- ///
- /// The index of the audio stream.
- public int? AudioStreamIndex { get; set; }
-
- ///
- /// Gets or sets the index of the video stream.
- ///
- /// The index of the video stream.
- public int? VideoStreamIndex { get; set; }
-
- ///
- /// Gets or sets the index of the subtitle stream.
- ///
- /// The index of the subtitle stream.
- public int? SubtitleStreamIndex { get; set; }
-
- ///
- /// Gets or sets the profile.
- ///
- /// The profile.
- public string Profile { get; set; }
-
- ///
- /// Gets or sets the level.
- ///
- /// The level.
- public string Level { get; set; }
-
- ///
- /// Gets or sets the baseline stream audio bit rate.
- ///
- /// The baseline stream audio bit rate.
- public int? BaselineStreamAudioBitRate { get; set; }
-
- ///
- /// Gets or sets a value indicating whether [append baseline stream].
- ///
- /// true if [append baseline stream]; otherwise, false.
- public bool AppendBaselineStream { get; set; }
-
- ///
- /// Gets or sets the time stamp offset ms. Only used with HLS.
- ///
- /// The time stamp offset ms.
- public int? TimeStampOffsetMs { get; set; }
- }
-}
\ No newline at end of file
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 4a9b765c23..4af32bb500 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -50,7 +50,7 @@ namespace MediaBrowser.Model.Entities
///
/// The reference frames.
public int? RefFrames { get; set; }
-
+
///
/// Gets or sets the length of the packet.
///
@@ -141,16 +141,26 @@ namespace MediaBrowser.Model.Entities
{
if (Type != MediaStreamType.Subtitle) return false;
- string codec = Codec ?? string.Empty;
+ return IsTextFormat(Codec);
+ }
+ }
+
+ public static bool IsTextFormat(string format)
+ {
+ string codec = format ?? string.Empty;
- // sub = external .sub file
+ // sub = external .sub file
- return StringHelper.IndexOfIgnoreCase(codec, "pgs") == -1 &&
- StringHelper.IndexOfIgnoreCase(codec, "dvd") == -1 &&
- !StringHelper.EqualsIgnoreCase(codec, "sub");
- }
+ return StringHelper.IndexOfIgnoreCase(codec, "pgs") == -1 &&
+ StringHelper.IndexOfIgnoreCase(codec, "dvd") == -1 &&
+ !StringHelper.EqualsIgnoreCase(codec, "sub");
}
+ ///
+ /// Gets or sets a value indicating whether [supports external stream].
+ ///
+ /// true if [supports external stream]; otherwise, false.
+ public bool SupportsExternalStream { get; set; }
///
/// Gets or sets the filename.
diff --git a/MediaBrowser.Model/Entities/MetadataProviders.cs b/MediaBrowser.Model/Entities/MetadataProviders.cs
index e867737898..7644b150aa 100644
--- a/MediaBrowser.Model/Entities/MetadataProviders.cs
+++ b/MediaBrowser.Model/Entities/MetadataProviders.cs
@@ -40,6 +40,7 @@ namespace MediaBrowser.Model.Entities
NesBoxRom = 14,
TvRage = 15,
AudioDbArtist = 16,
- AudioDbAlbum = 17
+ AudioDbAlbum = 17,
+ MusicBrainzTrack = 18
}
}
diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs
index 206723467c..99bec68a73 100644
--- a/MediaBrowser.Model/Extensions/StringHelper.cs
+++ b/MediaBrowser.Model/Extensions/StringHelper.cs
@@ -1,5 +1,6 @@
using System;
using System.Globalization;
+using System.Text;
using System.Text.RegularExpressions;
namespace MediaBrowser.Model.Extensions
@@ -94,5 +95,35 @@ namespace MediaBrowser.Model.Extensions
{
return new Regex(term).Split(str, limit);
}
+
+ ///
+ /// Replaces the specified STR.
+ ///
+ /// The STR.
+ /// The old value.
+ /// The new value.
+ /// The comparison.
+ /// System.String.
+ public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
+ {
+ var sb = new StringBuilder();
+
+ var previousIndex = 0;
+ var index = str.IndexOf(oldValue, comparison);
+
+ while (index != -1)
+ {
+ sb.Append(str.Substring(previousIndex, index - previousIndex));
+ sb.Append(newValue);
+ index += oldValue.Length;
+
+ previousIndex = index;
+ index = str.IndexOf(oldValue, index, comparison);
+ }
+
+ sb.Append(str.Substring(previousIndex));
+
+ return sb.ToString();
+ }
}
}
diff --git a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
index 15378a9af1..a6cd85d8d3 100644
--- a/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
+++ b/MediaBrowser.Model/LiveTv/RecordingInfoDto.cs
@@ -99,6 +99,12 @@ namespace MediaBrowser.Model.LiveTv
/// The path.
public string Path { get; set; }
+ ///
+ /// Gets or sets a value indicating whether this instance can delete.
+ ///
+ /// null if [can delete] contains no value, true if [can delete]; otherwise, false.
+ public bool? CanDelete { get; set; }
+
///
/// Overview of the recording.
///
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 2a1b0b6593..c8e09dd826 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -76,6 +76,7 @@
+
@@ -97,8 +98,11 @@
+
+
+
@@ -132,7 +136,6 @@
-
@@ -262,7 +265,6 @@
-
@@ -366,6 +368,7 @@
+
@@ -385,6 +388,7 @@
+
diff --git a/MediaBrowser.Model/News/NewsChannel.cs b/MediaBrowser.Model/News/NewsChannel.cs
deleted file mode 100644
index c3955b0a07..0000000000
--- a/MediaBrowser.Model/News/NewsChannel.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System.Collections.Generic;
-
-namespace MediaBrowser.Model.News
-{
- public class NewsChannel
- {
- public string Title { get; set; }
- public string Link { get; set; }
- public string Description { get; set; }
- public List Items { get; set; }
- }
-}
\ No newline at end of file
diff --git a/MediaBrowser.Model/Notifications/NotificationType.cs b/MediaBrowser.Model/Notifications/NotificationType.cs
index 34e4d32b53..269e27a4fc 100644
--- a/MediaBrowser.Model/Notifications/NotificationType.cs
+++ b/MediaBrowser.Model/Notifications/NotificationType.cs
@@ -18,6 +18,7 @@ namespace MediaBrowser.Model.Notifications
NewLibraryContent,
NewLibraryContentMultiple,
ServerRestartRequired,
- TaskFailed
+ TaskFailed,
+ CameraImageUploaded
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index 5018f8e516..7a91d77fff 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -1,5 +1,4 @@
-
-namespace MediaBrowser.Model.Querying
+namespace MediaBrowser.Model.Querying
{
///
/// Used to control the data that gets attached to DtoBaseItems
@@ -26,6 +25,16 @@ namespace MediaBrowser.Model.Querying
///
Budget,
+ ///
+ /// The can delete
+ ///
+ CanDelete,
+
+ ///
+ /// The can download
+ ///
+ CanDownload,
+
///
/// The chapters
///
diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs
index 5a806a8675..6ca4507c02 100644
--- a/MediaBrowser.Model/Session/ClientCapabilities.cs
+++ b/MediaBrowser.Model/Session/ClientCapabilities.cs
@@ -14,16 +14,22 @@ namespace MediaBrowser.Model.Session
public string MessageCallbackUrl { get; set; }
public bool SupportsContentUploading { get; set; }
- public bool SupportsUniqueIdentifier { get; set; }
+ public bool SupportsPersistentIdentifier { get; set; }
public bool SupportsSync { get; set; }
+ public bool SupportsOfflineAccess { get; set; }
public DeviceProfile DeviceProfile { get; set; }
+ ///
+ /// Usage should be migrated to SupportsPersistentIdentifier. Keeping this to preserve data.
+ ///
+ public bool? SupportsUniqueIdentifier { get; set; }
+
public ClientCapabilities()
{
PlayableMediaTypes = new List();
SupportedCommands = new List();
- SupportsUniqueIdentifier = true;
+ SupportsPersistentIdentifier = true;
}
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Model/Sync/CompleteSyncJobInfo.cs b/MediaBrowser.Model/Sync/CompleteSyncJobInfo.cs
new file mode 100644
index 0000000000..52d3fab3c0
--- /dev/null
+++ b/MediaBrowser.Model/Sync/CompleteSyncJobInfo.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.Sync
+{
+ public class CompleteSyncJobInfo
+ {
+ public SyncJob Job { get; set; }
+ public List JobItems { get; set; }
+
+ public CompleteSyncJobInfo()
+ {
+ JobItems = new List();
+ }
+ }
+}
diff --git a/MediaBrowser.Model/Sync/ItemFIleInfo.cs b/MediaBrowser.Model/Sync/ItemFIleInfo.cs
index b110af6b5a..e023572fd2 100644
--- a/MediaBrowser.Model/Sync/ItemFIleInfo.cs
+++ b/MediaBrowser.Model/Sync/ItemFIleInfo.cs
@@ -23,6 +23,11 @@ namespace MediaBrowser.Model.Sync
/// Gets or sets the type of the image.
///
/// The type of the image.
- public ImageType ImageType { get; set; }
+ public ImageType? ImageType { get; set; }
+ ///
+ /// Gets or sets the index.
+ ///
+ /// The index.
+ public int Index { get; set; }
}
}
diff --git a/MediaBrowser.Model/Sync/LocalItem.cs b/MediaBrowser.Model/Sync/LocalItem.cs
index ec4544524a..c6a10298fe 100644
--- a/MediaBrowser.Model/Sync/LocalItem.cs
+++ b/MediaBrowser.Model/Sync/LocalItem.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Model.Dto;
+using System.Collections.Generic;
namespace MediaBrowser.Model.Sync
{
@@ -29,5 +30,15 @@ namespace MediaBrowser.Model.Sync
///
/// The item identifier.
public string ItemId { get; set; }
+ ///
+ /// Gets or sets the user ids with access.
+ ///
+ /// The user ids with access.
+ public List UserIdsWithAccess { get; set; }
+
+ public LocalItem()
+ {
+ UserIdsWithAccess = new List();
+ }
}
}
diff --git a/MediaBrowser.Model/Sync/SyncDataRequest.cs b/MediaBrowser.Model/Sync/SyncDataRequest.cs
index 3eb447b3f2..dc33239a04 100644
--- a/MediaBrowser.Model/Sync/SyncDataRequest.cs
+++ b/MediaBrowser.Model/Sync/SyncDataRequest.cs
@@ -5,12 +5,14 @@ namespace MediaBrowser.Model.Sync
public class SyncDataRequest
{
public List LocalItemIds { get; set; }
+ public List OfflineUserIds { get; set; }
public string TargetId { get; set; }
public SyncDataRequest()
{
LocalItemIds = new List();
+ OfflineUserIds = new List();
}
}
}
diff --git a/MediaBrowser.Model/Sync/SyncDataResponse.cs b/MediaBrowser.Model/Sync/SyncDataResponse.cs
index ac7ff5c84a..3799e94557 100644
--- a/MediaBrowser.Model/Sync/SyncDataResponse.cs
+++ b/MediaBrowser.Model/Sync/SyncDataResponse.cs
@@ -5,10 +5,12 @@ namespace MediaBrowser.Model.Sync
public class SyncDataResponse
{
public List