aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api')
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs10
-rw-r--r--MediaBrowser.Api/BaseApiService.cs14
-rw-r--r--MediaBrowser.Api/ChannelService.cs7
-rw-r--r--MediaBrowser.Api/ConfigurationService.cs10
-rw-r--r--MediaBrowser.Api/Devices/DeviceService.cs65
-rw-r--r--MediaBrowser.Api/DisplayPreferencesService.cs8
-rw-r--r--MediaBrowser.Api/EnvironmentService.cs29
-rw-r--r--MediaBrowser.Api/FilterService.cs9
-rw-r--r--MediaBrowser.Api/IHasDtoOptions.cs1
-rw-r--r--MediaBrowser.Api/IHasItemFields.cs3
-rw-r--r--MediaBrowser.Api/Images/ImageByNameService.cs10
-rw-r--r--MediaBrowser.Api/Images/ImageRequest.cs12
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs203
-rw-r--r--MediaBrowser.Api/Images/RemoteImageService.cs19
-rw-r--r--MediaBrowser.Api/ItemLookupService.cs20
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs61
-rw-r--r--MediaBrowser.Api/Library/LibraryStructureService.cs8
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs39
-rw-r--r--MediaBrowser.Api/LocalizationService.cs12
-rw-r--r--MediaBrowser.Api/Movies/CollectionService.cs1
-rw-r--r--MediaBrowser.Api/Movies/MoviesService.cs29
-rw-r--r--MediaBrowser.Api/Movies/TrailersService.cs19
-rw-r--r--MediaBrowser.Api/Music/AlbumsService.cs16
-rw-r--r--MediaBrowser.Api/Music/InstantMixService.cs2
-rw-r--r--MediaBrowser.Api/PackageService.cs10
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs34
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs33
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs227
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs126
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs6
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs4
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs43
-rw-r--r--MediaBrowser.Api/Playback/Progressive/AudioService.cs6
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs12
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs6
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs5
-rw-r--r--MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs6
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs6
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs2
-rw-r--r--MediaBrowser.Api/Playback/UniversalAudioService.cs30
-rw-r--r--MediaBrowser.Api/PlaylistService.cs5
-rw-r--r--MediaBrowser.Api/PluginService.cs31
-rw-r--r--MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs12
-rw-r--r--MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs2
-rw-r--r--MediaBrowser.Api/SearchService.cs11
-rw-r--r--MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs61
-rw-r--r--MediaBrowser.Api/Sessions/SessionService.cs9
-rw-r--r--MediaBrowser.Api/SimilarItemsHelper.cs15
-rw-r--r--MediaBrowser.Api/Subtitles/SubtitleService.cs2
-rw-r--r--MediaBrowser.Api/SuggestionsService.cs6
-rw-r--r--MediaBrowser.Api/SyncPlay/SyncPlayService.cs302
-rw-r--r--MediaBrowser.Api/SyncPlay/TimeSyncService.cs52
-rw-r--r--MediaBrowser.Api/System/ActivityLogService.cs9
-rw-r--r--MediaBrowser.Api/System/ActivityLogWebSocketListener.cs20
-rw-r--r--MediaBrowser.Api/System/SystemService.cs13
-rw-r--r--MediaBrowser.Api/TranscodingJob.cs8
-rw-r--r--MediaBrowser.Api/TvShowsService.cs33
-rw-r--r--MediaBrowser.Api/UserLibrary/ArtistsService.cs6
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs11
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs25
-rw-r--r--MediaBrowser.Api/UserLibrary/GenresService.cs6
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs31
-rw-r--r--MediaBrowser.Api/UserLibrary/PersonsService.cs6
-rw-r--r--MediaBrowser.Api/UserLibrary/PlaystateService.cs12
-rw-r--r--MediaBrowser.Api/UserLibrary/StudiosService.cs6
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs22
-rw-r--r--MediaBrowser.Api/UserLibrary/UserViewsService.cs4
-rw-r--r--MediaBrowser.Api/UserLibrary/YearsService.cs6
-rw-r--r--MediaBrowser.Api/UserService.cs61
-rw-r--r--MediaBrowser.Api/VideosService.cs1
70 files changed, 1403 insertions, 548 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 6691080bc8..b041effb2e 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -31,7 +31,7 @@ namespace MediaBrowser.Api
/// <summary>
/// The logger.
/// </summary>
- private ILogger _logger;
+ private ILogger<ApiEntryPoint> _logger;
/// <summary>
/// The configuration manager.
@@ -43,7 +43,7 @@ namespace MediaBrowser.Api
private readonly IMediaSourceManager _mediaSourceManager;
/// <summary>
- /// The active transcoding jobs
+ /// The active transcoding jobs.
/// </summary>
private readonly List<TranscodingJob> _activeTranscodingJobs = new List<TranscodingJob>();
@@ -284,8 +284,8 @@ namespace MediaBrowser.Api
Width = state.OutputWidth,
Height = state.OutputHeight,
AudioChannels = state.OutputAudioChannels,
- IsAudioDirect = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase),
- IsVideoDirect = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase),
+ IsAudioDirect = EncodingHelper.IsCopyCodec(state.OutputAudioCodec),
+ IsVideoDirect = EncodingHelper.IsCopyCodec(state.OutputVideoCodec),
TranscodeReasons = state.TranscodeReasons
});
}
@@ -293,7 +293,7 @@ namespace MediaBrowser.Api
/// <summary>
/// <summary>
- /// The progressive
+ /// The progressive.
/// </summary>
/// Called when [transcode failed to start].
/// </summary>
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 1a1d86362a..63a31a7452 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -16,12 +17,12 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class BaseApiService
+ /// Class BaseApiService.
/// </summary>
public abstract class BaseApiService : IService, IRequiresRequest
{
public BaseApiService(
- ILogger logger,
+ ILogger<BaseApiService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory)
{
@@ -34,7 +35,7 @@ namespace MediaBrowser.Api
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
- protected ILogger Logger { get; }
+ protected ILogger<BaseApiService> Logger { get; }
/// <summary>
/// Gets or sets the server configuration manager.
@@ -94,8 +95,8 @@ namespace MediaBrowser.Api
var authenticatedUser = auth.User;
// If they're going to update the record of another user, they must be an administrator
- if ((!userId.Equals(auth.UserId) && !authenticatedUser.Policy.IsAdministrator)
- || (restrictUserPreferences && !authenticatedUser.Policy.EnableUserPreferenceAccess))
+ if ((!userId.Equals(auth.UserId) && !authenticatedUser.HasPermission(PermissionKind.IsAdministrator))
+ || (restrictUserPreferences && !authenticatedUser.EnableUserPreferenceAccess))
{
throw new SecurityException("Unauthorized access.");
}
@@ -267,7 +268,6 @@ namespace MediaBrowser.Api
Name = name.Replace(BaseItem.SlugChar, '&'),
IncludeItemTypes = new[] { typeof(T).Name },
DtoOptions = dtoOptions
-
}).OfType<T>().FirstOrDefault();
result ??= libraryManager.GetItemList(new InternalItemsQuery
@@ -275,7 +275,6 @@ namespace MediaBrowser.Api
Name = name.Replace(BaseItem.SlugChar, '/'),
IncludeItemTypes = new[] { typeof(T).Name },
DtoOptions = dtoOptions
-
}).OfType<T>().FirstOrDefault();
result ??= libraryManager.GetItemList(new InternalItemsQuery
@@ -283,7 +282,6 @@ namespace MediaBrowser.Api
Name = name.Replace(BaseItem.SlugChar, '?'),
IncludeItemTypes = new[] { typeof(T).Name },
DtoOptions = dtoOptions
-
}).OfType<T>().FirstOrDefault();
return result;
diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs
index fd9b8c3968..8c336b1c9d 100644
--- a/MediaBrowser.Api/ChannelService.cs
+++ b/MediaBrowser.Api/ChannelService.cs
@@ -36,7 +36,7 @@ namespace MediaBrowser.Api
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
@@ -90,7 +90,7 @@ namespace MediaBrowser.Api
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
@@ -149,7 +149,7 @@ namespace MediaBrowser.Api
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
@@ -240,7 +240,6 @@ namespace MediaBrowser.Api
{
Fields = request.GetItemFields()
}
-
};
foreach (var filter in request.GetFilters())
diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs
index 316be04a03..19369cccae 100644
--- a/MediaBrowser.Api/ConfigurationService.cs
+++ b/MediaBrowser.Api/ConfigurationService.cs
@@ -11,13 +11,12 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class GetConfiguration
+ /// Class GetConfiguration.
/// </summary>
[Route("/System/Configuration", "GET", Summary = "Gets application configuration")]
[Authenticated]
public class GetConfiguration : IReturn<ServerConfiguration>
{
-
}
[Route("/System/Configuration/{Key}", "GET", Summary = "Gets a named configuration")]
@@ -29,7 +28,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UpdateConfiguration
+ /// Class UpdateConfiguration.
/// </summary>
[Route("/System/Configuration", "POST", Summary = "Updates application configuration")]
[Authenticated(Roles = "Admin")]
@@ -51,7 +50,6 @@ namespace MediaBrowser.Api
[Authenticated(Roles = "Admin")]
public class GetDefaultMetadataOptions : IReturn<MetadataOptions>
{
-
}
[Route("/System/MediaEncoder/Path", "POST", Summary = "Updates the path to the media encoder")]
@@ -67,12 +65,12 @@ namespace MediaBrowser.Api
public class ConfigurationService : BaseApiService
{
/// <summary>
- /// The _json serializer
+ /// The _json serializer.
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
- /// The _configuration manager
+ /// The _configuration manager.
/// </summary>
private readonly IServerConfigurationManager _configurationManager;
diff --git a/MediaBrowser.Api/Devices/DeviceService.cs b/MediaBrowser.Api/Devices/DeviceService.cs
index 7004a2559e..18860983ec 100644
--- a/MediaBrowser.Api/Devices/DeviceService.cs
+++ b/MediaBrowser.Api/Devices/DeviceService.cs
@@ -1,5 +1,3 @@
-using System.IO;
-using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Net;
@@ -41,33 +39,6 @@ namespace MediaBrowser.Api.Devices
public string Id { get; set; }
}
- [Route("/Devices/CameraUploads", "GET", Summary = "Gets camera upload history for a device")]
- [Authenticated]
- public class GetCameraUploads : IReturn<ContentUploadHistory>
- {
- [ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
- public string DeviceId { get; set; }
- }
-
- [Route("/Devices/CameraUploads", "POST", Summary = "Uploads content")]
- [Authenticated]
- public class PostCameraUpload : IRequiresRequestStream, IReturnVoid
- {
- [ApiMember(Name = "DeviceId", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string DeviceId { get; set; }
-
- [ApiMember(Name = "Album", Description = "Album", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Album { get; set; }
-
- [ApiMember(Name = "Name", Description = "Name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Name { get; set; }
-
- [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Id { get; set; }
-
- public Stream RequestStream { get; set; }
- }
-
[Route("/Devices/Options", "POST", Summary = "Updates device options")]
[Authenticated(Roles = "Admin")]
public class PostDeviceOptions : DeviceOptions, IReturnVoid
@@ -116,17 +87,11 @@ namespace MediaBrowser.Api.Devices
return _deviceManager.GetDeviceOptions(request.Id);
}
- public object Get(GetCameraUploads request)
- {
- return ToOptimizedResult(_deviceManager.GetCameraUploadHistory(request.DeviceId));
- }
-
public void Delete(DeleteDevice request)
{
var sessions = _authRepo.Get(new AuthenticationInfoQuery
{
DeviceId = request.Id
-
}).Items;
foreach (var session in sessions)
@@ -134,35 +99,5 @@ namespace MediaBrowser.Api.Devices
_sessionManager.Logout(session);
}
}
-
- public Task Post(PostCameraUpload request)
- {
- var deviceId = Request.QueryString["DeviceId"];
- var album = Request.QueryString["Album"];
- var id = Request.QueryString["Id"];
- var name = Request.QueryString["Name"];
- var req = Request.Response.HttpContext.Request;
-
- if (req.HasFormContentType)
- {
- var file = req.Form.Files.Count == 0 ? null : req.Form.Files[0];
-
- return _deviceManager.AcceptCameraUpload(deviceId, file.OpenReadStream(), new LocalFileInfo
- {
- MimeType = file.ContentType,
- Album = album,
- Name = name,
- Id = id
- });
- }
-
- return _deviceManager.AcceptCameraUpload(deviceId, request.RequestStream, new LocalFileInfo
- {
- MimeType = Request.ContentType,
- Album = album,
- Name = name,
- Id = id
- });
- }
}
}
diff --git a/MediaBrowser.Api/DisplayPreferencesService.cs b/MediaBrowser.Api/DisplayPreferencesService.cs
index 62c4ff43f2..c3ed40ad3c 100644
--- a/MediaBrowser.Api/DisplayPreferencesService.cs
+++ b/MediaBrowser.Api/DisplayPreferencesService.cs
@@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class UpdateDisplayPreferences
+ /// Class UpdateDisplayPreferences.
/// </summary>
[Route("/DisplayPreferences/{DisplayPreferencesId}", "POST", Summary = "Updates a user's display preferences for an item")]
public class UpdateDisplayPreferences : DisplayPreferences, IReturnVoid
@@ -44,17 +44,17 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class DisplayPreferencesService
+ /// Class DisplayPreferencesService.
/// </summary>
[Authenticated]
public class DisplayPreferencesService : BaseApiService
{
/// <summary>
- /// The _display preferences manager
+ /// The _display preferences manager.
/// </summary>
private readonly IDisplayPreferencesRepository _displayPreferencesManager;
/// <summary>
- /// The _json serializer
+ /// The _json serializer.
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs
index d199ce1544..720a710258 100644
--- a/MediaBrowser.Api/EnvironmentService.cs
+++ b/MediaBrowser.Api/EnvironmentService.cs
@@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class GetDirectoryContents
+ /// Class GetDirectoryContents.
/// </summary>
[Route("/Environment/DirectoryContents", "GET", Summary = "Gets the contents of a given directory in the file system")]
public class GetDirectoryContents : IReturn<List<FileSystemEntryInfo>>
@@ -50,6 +50,7 @@ namespace MediaBrowser.Api
public string Path { get; set; }
public bool ValidateWriteable { get; set; }
+
public bool? IsFile { get; set; }
}
@@ -66,7 +67,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class GetDrives
+ /// Class GetDrives.
/// </summary>
[Route("/Environment/Drives", "GET", Summary = "Gets available drives from the server's file system")]
public class GetDrives : IReturn<List<FileSystemEntryInfo>>
@@ -74,7 +75,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class GetNetworkComputers
+ /// Class GetNetworkComputers.
/// </summary>
[Route("/Environment/NetworkDevices", "GET", Summary = "Gets a list of devices on the network")]
public class GetNetworkDevices : IReturn<List<FileSystemEntryInfo>>
@@ -100,11 +101,10 @@ namespace MediaBrowser.Api
[Route("/Environment/DefaultDirectoryBrowser", "GET", Summary = "Gets the parent path of a given path")]
public class GetDefaultDirectoryBrowser : IReturn<DefaultDirectoryBrowserInfo>
{
-
}
/// <summary>
- /// Class EnvironmentService
+ /// Class EnvironmentService.
/// </summary>
[Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)]
public class EnvironmentService : BaseApiService
@@ -113,7 +113,7 @@ namespace MediaBrowser.Api
private const string UncSeparatorString = "\\";
/// <summary>
- /// The _network manager
+ /// The _network manager.
/// </summary>
private readonly INetworkManager _networkManager;
private readonly IFileSystem _fileSystem;
@@ -221,17 +221,12 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Gets the list that is returned when an empty path is supplied
+ /// Gets the list that is returned when an empty path is supplied.
/// </summary>
/// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
private IEnumerable<FileSystemEntryInfo> GetDrives()
{
- return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo
- {
- Name = d.Name,
- Path = d.FullName,
- Type = FileSystemEntryType.Directory
- });
+ return _fileSystem.GetDrives().Select(d => new FileSystemEntryInfo(d.Name, d.FullName, FileSystemEntryType.Directory));
}
/// <summary>
@@ -261,13 +256,7 @@ namespace MediaBrowser.Api
return request.IncludeDirectories || !isDirectory;
});
- return entries.Select(f => new FileSystemEntryInfo
- {
- Name = f.Name,
- Path = f.FullName,
- Type = f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File
-
- });
+ return entries.Select(f => new FileSystemEntryInfo(f.Name, f.FullName, f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File));
}
public object Get(GetParentPath request)
diff --git a/MediaBrowser.Api/FilterService.cs b/MediaBrowser.Api/FilterService.cs
index 5eb72cdb19..1b736c77da 100644
--- a/MediaBrowser.Api/FilterService.cs
+++ b/MediaBrowser.Api/FilterService.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -72,11 +73,17 @@ namespace MediaBrowser.Api
}
public bool? IsAiring { get; set; }
+
public bool? IsMovie { get; set; }
+
public bool? IsSports { get; set; }
+
public bool? IsKids { get; set; }
+
public bool? IsNews { get; set; }
+
public bool? IsSeries { get; set; }
+
public bool? Recursive { get; set; }
}
@@ -149,7 +156,6 @@ namespace MediaBrowser.Api
{
Name = i.Item1.Name,
Id = i.Item1.Id
-
}).ToArray();
}
else
@@ -158,7 +164,6 @@ namespace MediaBrowser.Api
{
Name = i.Item1.Name,
Id = i.Item1.Id
-
}).ToArray();
}
diff --git a/MediaBrowser.Api/IHasDtoOptions.cs b/MediaBrowser.Api/IHasDtoOptions.cs
index 03d3b3692f..33d498e8bd 100644
--- a/MediaBrowser.Api/IHasDtoOptions.cs
+++ b/MediaBrowser.Api/IHasDtoOptions.cs
@@ -3,6 +3,7 @@ namespace MediaBrowser.Api
public interface IHasDtoOptions : IHasItemFields
{
bool? EnableImages { get; set; }
+
bool? EnableUserData { get; set; }
int? ImageTypeLimit { get; set; }
diff --git a/MediaBrowser.Api/IHasItemFields.cs b/MediaBrowser.Api/IHasItemFields.cs
index 85b4a7e2d2..ad4f1b4891 100644
--- a/MediaBrowser.Api/IHasItemFields.cs
+++ b/MediaBrowser.Api/IHasItemFields.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Model.Querying;
namespace MediaBrowser.Api
{
/// <summary>
- /// Interface IHasItemFields
+ /// Interface IHasItemFields.
/// </summary>
public interface IHasItemFields
{
@@ -43,7 +43,6 @@ namespace MediaBrowser.Api
}
return null;
-
}).Where(i => i.HasValue).Select(i => i.Value).ToArray();
}
}
diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs
index 45b7d0c100..2d405ac3d8 100644
--- a/MediaBrowser.Api/Images/ImageByNameService.cs
+++ b/MediaBrowser.Api/Images/ImageByNameService.cs
@@ -16,7 +16,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Images
{
/// <summary>
- /// Class GetGeneralImage
+ /// Class GetGeneralImage.
/// </summary>
[Route("/Images/General/{Name}/{Type}", "GET", Summary = "Gets a general image by name")]
public class GetGeneralImage
@@ -33,7 +33,7 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class GetRatingImage
+ /// Class GetRatingImage.
/// </summary>
[Route("/Images/Ratings/{Theme}/{Name}", "GET", Summary = "Gets a rating image by name")]
public class GetRatingImage
@@ -54,7 +54,7 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class GetMediaInfoImage
+ /// Class GetMediaInfoImage.
/// </summary>
[Route("/Images/MediaInfo/{Theme}/{Name}", "GET", Summary = "Gets a media info image by name")]
public class GetMediaInfoImage
@@ -93,12 +93,12 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class ImageByNameService
+ /// Class ImageByNameService.
/// </summary>
public class ImageByNameService : BaseApiService
{
/// <summary>
- /// The _app paths
+ /// The _app paths.
/// </summary>
private readonly IServerApplicationPaths _appPaths;
diff --git a/MediaBrowser.Api/Images/ImageRequest.cs b/MediaBrowser.Api/Images/ImageRequest.cs
index 71ff09b63f..0f3455548d 100644
--- a/MediaBrowser.Api/Images/ImageRequest.cs
+++ b/MediaBrowser.Api/Images/ImageRequest.cs
@@ -4,30 +4,30 @@ using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Images
{
/// <summary>
- /// Class ImageRequest
+ /// Class ImageRequest.
/// </summary>
public class ImageRequest : DeleteImageRequest
{
/// <summary>
- /// The max width
+ /// The max width.
/// </summary>
[ApiMember(Name = "MaxWidth", Description = "The maximum image width to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? MaxWidth { get; set; }
/// <summary>
- /// The max height
+ /// The max height.
/// </summary>
[ApiMember(Name = "MaxHeight", Description = "The maximum image height to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? MaxHeight { get; set; }
/// <summary>
- /// The width
+ /// The width.
/// </summary>
[ApiMember(Name = "Width", Description = "The fixed image width to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Width { get; set; }
/// <summary>
- /// The height
+ /// The height.
/// </summary>
[ApiMember(Name = "Height", Description = "The fixed image height to return.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? Height { get; set; }
@@ -79,7 +79,7 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class DeleteImageRequest
+ /// Class DeleteImageRequest.
/// </summary>
public class DeleteImageRequest
{
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index 2e9b3e6cb4..8426a9a4f8 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
@@ -17,9 +18,11 @@ using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Net;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
+using User = Jellyfin.Data.Entities.User;
namespace MediaBrowser.Api.Images
{
@@ -55,7 +58,7 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class UpdateItemImageIndex
+ /// Class UpdateItemImageIndex.
/// </summary>
[Route("/Items/{Id}/Images/{Type}/{Index}/Index", "POST", Summary = "Updates the index for an item image")]
[Authenticated(Roles = "admin")]
@@ -91,7 +94,7 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class GetPersonImage
+ /// Class GetPersonImage.
/// </summary>
[Route("/Artists/{Name}/Images/{Type}", "GET")]
[Route("/Artists/{Name}/Images/{Type}/{Index}", "GET")]
@@ -128,7 +131,7 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class GetUserImage
+ /// Class GetUserImage.
/// </summary>
[Route("/Users/{Id}/Images/{Type}", "GET")]
[Route("/Users/{Id}/Images/{Type}/{Index}", "GET")]
@@ -145,7 +148,7 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class DeleteItemImage
+ /// Class DeleteItemImage.
/// </summary>
[Route("/Items/{Id}/Images/{Type}", "DELETE")]
[Route("/Items/{Id}/Images/{Type}/{Index}", "DELETE")]
@@ -161,7 +164,7 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class DeleteUserImage
+ /// Class DeleteUserImage.
/// </summary>
[Route("/Users/{Id}/Images/{Type}", "DELETE")]
[Route("/Users/{Id}/Images/{Type}/{Index}", "DELETE")]
@@ -177,7 +180,7 @@ namespace MediaBrowser.Api.Images
}
/// <summary>
- /// Class PostUserImage
+ /// Class PostUserImage.
/// </summary>
[Route("/Users/{Id}/Images/{Type}", "POST")]
[Route("/Users/{Id}/Images/{Type}/{Index}", "POST")]
@@ -192,14 +195,14 @@ namespace MediaBrowser.Api.Images
public string Id { get; set; }
/// <summary>
- /// The raw Http Request Input Stream
+ /// The raw Http Request Input Stream.
/// </summary>
/// <value>The request stream.</value>
public Stream RequestStream { get; set; }
}
/// <summary>
- /// Class PostItemImage
+ /// Class PostItemImage.
/// </summary>
[Route("/Items/{Id}/Images/{Type}", "POST")]
[Route("/Items/{Id}/Images/{Type}/{Index}", "POST")]
@@ -214,14 +217,14 @@ namespace MediaBrowser.Api.Images
public string Id { get; set; }
/// <summary>
- /// The raw Http Request Input Stream
+ /// The raw Http Request Input Stream.
/// </summary>
/// <value>The request stream.</value>
public Stream RequestStream { get; set; }
}
/// <summary>
- /// Class ImageService
+ /// Class ImageService.
/// </summary>
public class ImageService : BaseApiService
{
@@ -280,9 +283,16 @@ namespace MediaBrowser.Api.Images
public List<ImageInfo> GetItemImageInfos(BaseItem item)
{
var list = new List<ImageInfo>();
-
var itemImages = item.ImageInfos;
+ if (itemImages.Length == 0)
+ {
+ // short-circuit
+ return list;
+ }
+
+ _libraryManager.UpdateImages(item); // this makes sure dimensions and hashes are correct
+
foreach (var image in itemImages)
{
if (!item.AllowsMultipleImages(image.Type))
@@ -323,6 +333,7 @@ namespace MediaBrowser.Api.Images
{
int? width = null;
int? height = null;
+ string blurhash = null;
long length = 0;
try
@@ -332,10 +343,9 @@ namespace MediaBrowser.Api.Images
var fileInfo = _fileSystem.GetFileInfo(info.Path);
length = fileInfo.Length;
- ImageDimensions size = _imageProcessor.GetImageDimensions(item, info);
- _libraryManager.UpdateImages(item);
- width = size.Width;
- height = size.Height;
+ blurhash = info.BlurHash;
+ width = info.Width;
+ height = info.Height;
if (width <= 0 || height <= 0)
{
@@ -358,6 +368,7 @@ namespace MediaBrowser.Api.Images
ImageType = info.Type,
ImageTag = _imageProcessor.GetImageCacheTag(item, info),
Size = length,
+ BlurHash = blurhash,
Width = width,
Height = height
};
@@ -399,14 +410,14 @@ namespace MediaBrowser.Api.Images
{
var item = _userManager.GetUserById(request.Id);
- return GetImage(request, Guid.Empty, item, false);
+ return GetImage(request, item, false);
}
public object Head(GetUserImage request)
{
var item = _userManager.GetUserById(request.Id);
- return GetImage(request, Guid.Empty, item, true);
+ return GetImage(request, item, true);
}
public object Get(GetItemByNameImage request)
@@ -439,9 +450,9 @@ namespace MediaBrowser.Api.Images
request.Type = Enum.Parse<ImageType>(GetPathValue(3).ToString(), true);
- var item = _userManager.GetUserById(id);
+ var user = _userManager.GetUserById(id);
- return PostImage(item, request.RequestStream, request.Type, Request.ContentType);
+ return PostImage(user, request.RequestStream, Request.ContentType);
}
/// <summary>
@@ -468,9 +479,17 @@ namespace MediaBrowser.Api.Images
var userId = request.Id;
AssertCanUpdateUser(_authContext, _userManager, userId, true);
- var item = _userManager.GetUserById(userId);
+ var user = _userManager.GetUserById(userId);
+ try
+ {
+ File.Delete(user.ProfileImage.Path);
+ }
+ catch (IOException e)
+ {
+ Logger.LogError(e, "Error deleting user profile image:");
+ }
- item.DeleteImage(request.Type, request.Index ?? 0);
+ _userManager.ClearProfileImage(user);
}
/// <summary>
@@ -555,18 +574,17 @@ namespace MediaBrowser.Api.Images
var imageInfo = GetImageInfo(request, item);
if (imageInfo == null)
{
- var displayText = item == null ? itemId.ToString() : item.Name;
- throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", displayText, request.Type));
+ throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type));
}
- bool cropwhitespace;
+ bool cropWhitespace;
if (request.CropWhitespace.HasValue)
{
- cropwhitespace = request.CropWhitespace.Value;
+ cropWhitespace = request.CropWhitespace.Value;
}
else
{
- cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
+ cropWhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
}
var outputFormats = GetOutputFormats(request);
@@ -589,13 +607,90 @@ namespace MediaBrowser.Api.Images
itemId,
request,
imageInfo,
- cropwhitespace,
+ cropWhitespace,
outputFormats,
cacheDuration,
responseHeaders,
isHeadRequest);
}
+ public Task<object> GetImage(ImageRequest request, User user, bool isHeadRequest)
+ {
+ var imageInfo = GetImageInfo(request, user);
+
+ TimeSpan? cacheDuration = null;
+
+ if (!string.IsNullOrEmpty(request.Tag))
+ {
+ cacheDuration = TimeSpan.FromDays(365);
+ }
+
+ var responseHeaders = new Dictionary<string, string>
+ {
+ {"transferMode.dlna.org", "Interactive"},
+ {"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"}
+ };
+
+ var outputFormats = GetOutputFormats(request);
+
+ return GetImageResult(user.Id,
+ request,
+ imageInfo,
+ outputFormats,
+ cacheDuration,
+ responseHeaders,
+ isHeadRequest);
+ }
+
+ private async Task<object> GetImageResult(
+ Guid itemId,
+ ImageRequest request,
+ ItemImageInfo info,
+ IReadOnlyCollection<ImageFormat> supportedFormats,
+ TimeSpan? cacheDuration,
+ IDictionary<string, string> headers,
+ bool isHeadRequest)
+ {
+ info.Type = ImageType.Profile;
+ var options = new ImageProcessingOptions
+ {
+ CropWhiteSpace = true,
+ Height = request.Height,
+ ImageIndex = request.Index ?? 0,
+ Image = info,
+ Item = null, // Hack alert
+ ItemId = itemId,
+ MaxHeight = request.MaxHeight,
+ MaxWidth = request.MaxWidth,
+ Quality = request.Quality ?? 100,
+ Width = request.Width,
+ AddPlayedIndicator = request.AddPlayedIndicator,
+ PercentPlayed = 0,
+ UnplayedCount = request.UnplayedCount,
+ Blur = request.Blur,
+ BackgroundColor = request.BackgroundColor,
+ ForegroundLayer = request.ForegroundLayer,
+ SupportedOutputFormats = supportedFormats
+ };
+
+ var imageResult = await _imageProcessor.ProcessImage(options).ConfigureAwait(false);
+
+ headers[HeaderNames.Vary] = HeaderNames.Accept;
+
+ return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
+ {
+ CacheDuration = cacheDuration,
+ ResponseHeaders = headers,
+ ContentType = imageResult.Item2,
+ DateLastModified = imageResult.Item3,
+ IsHeadRequest = isHeadRequest,
+ Path = imageResult.Item1,
+
+ FileShare = FileShare.Read
+
+ }).ConfigureAwait(false);
+ }
+
private async Task<object> GetImageResult(
BaseItem item,
Guid itemId,
@@ -648,7 +743,6 @@ namespace MediaBrowser.Api.Images
Path = imageResult.Item1,
FileShare = FileShare.Read
-
}).ConfigureAwait(false);
}
@@ -733,13 +827,35 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
- private ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item)
+ private static ItemImageInfo GetImageInfo(ImageRequest request, BaseItem item)
{
var index = request.Index ?? 0;
return item.GetImageInfo(request.Type, index);
}
+ private static ItemImageInfo GetImageInfo(ImageRequest request, User user)
+ {
+ var info = new ItemImageInfo
+ {
+ Path = user.ProfileImage.Path,
+ Type = ImageType.Primary,
+ DateModified = user.ProfileImage.LastModified,
+ };
+
+ if (request.Width.HasValue)
+ {
+ info.Width = request.Width.Value;
+ }
+
+ if (request.Height.HasValue)
+ {
+ info.Height = request.Height.Value;
+ }
+
+ return info;
+ }
+
/// <summary>
/// Posts the image.
/// </summary>
@@ -750,22 +866,41 @@ namespace MediaBrowser.Api.Images
/// <returns>Task.</returns>
public async Task PostImage(BaseItem entity, Stream inputStream, ImageType imageType, string mimeType)
{
+ var memoryStream = await GetMemoryStream(inputStream);
+
+ // Handle image/png; charset=utf-8
+ mimeType = mimeType.Split(';').FirstOrDefault();
+
+ await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
+
+ entity.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ }
+
+ private static async Task<MemoryStream> GetMemoryStream(Stream inputStream)
+ {
using var reader = new StreamReader(inputStream);
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
var bytes = Convert.FromBase64String(text);
-
- var memoryStream = new MemoryStream(bytes)
+ return new MemoryStream(bytes)
{
Position = 0
};
+ }
+
+ private async Task PostImage(User user, Stream inputStream, string mimeType)
+ {
+ var memoryStream = await GetMemoryStream(inputStream);
// Handle image/png; charset=utf-8
mimeType = mimeType.Split(';').FirstOrDefault();
+ var userDataPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username);
+ user.ProfileImage = new Jellyfin.Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType)));
- await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
-
- entity.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None);
+ await _providerManager
+ .SaveImage(user, memoryStream, mimeType, user.ProfileImage.Path)
+ .ConfigureAwait(false);
+ await _userManager.UpdateUserAsync(user);
}
}
}
diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs
index 222bb34d31..86464b4b9a 100644
--- a/MediaBrowser.Api/Images/RemoteImageService.cs
+++ b/MediaBrowser.Api/Images/RemoteImageService.cs
@@ -33,7 +33,7 @@ namespace MediaBrowser.Api.Images
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
@@ -147,13 +147,11 @@ namespace MediaBrowser.Api.Images
{
var item = _libraryManager.GetItemById(request.Id);
- var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery
+ var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery(request.ProviderName)
{
- ProviderName = request.ProviderName,
IncludeAllLanguages = request.IncludeAllLanguages,
IncludeDisabledProviders = true,
ImageType = request.Type
-
}, CancellationToken.None).ConfigureAwait(false);
var imagesList = images.ToArray();
@@ -265,17 +263,20 @@ namespace MediaBrowser.Api.Images
{
Url = url,
BufferContent = false
-
}).ConfigureAwait(false);
- var ext = result.ContentType.Split('/').Last();
+ var ext = result.ContentType.Split('/')[^1];
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
- using (var stream = result.Content)
+ var stream = result.Content;
+ await using (stream.ConfigureAwait(false))
{
- using var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
- await stream.CopyToAsync(filestream).ConfigureAwait(false);
+ var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
+ await using (filestream.ConfigureAwait(false))
+ {
+ await stream.CopyToAsync(filestream).ConfigureAwait(false);
+ }
}
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs
index 0bbe7e1cfa..8624112099 100644
--- a/MediaBrowser.Api/ItemLookupService.cs
+++ b/MediaBrowser.Api/ItemLookupService.cs
@@ -220,7 +220,7 @@ namespace MediaBrowser.Api
{
var item = _libraryManager.GetItemById(new Guid(request.Id));
- //foreach (var key in request.ProviderIds)
+ // foreach (var key in request.ProviderIds)
//{
// var value = key.Value;
@@ -233,8 +233,8 @@ namespace MediaBrowser.Api
// Since the refresh process won't erase provider Ids, we need to set this explicitly now.
item.ProviderIds = request.ProviderIds;
- //item.ProductionYear = request.ProductionYear;
- //item.Name = request.Name;
+ // item.ProductionYear = request.ProductionYear;
+ // item.Name = request.Name;
return _providerManager.RefreshFullItem(
item,
@@ -299,22 +299,26 @@ namespace MediaBrowser.Api
{
var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false);
- var ext = result.ContentType.Split('/').Last();
+ var ext = result.ContentType.Split('/')[^1];
var fullCachePath = GetFullCachePath(urlHash + "." + ext);
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
- using (var stream = result.Content)
+ var stream = result.Content;
+
+ await using (stream.ConfigureAwait(false))
{
- using var fileStream = new FileStream(
+ var fileStream = new FileStream(
fullCachePath,
FileMode.Create,
FileAccess.Write,
FileShare.Read,
IODefaults.FileStreamBufferSize,
true);
-
- await stream.CopyToAsync(fileStream).ConfigureAwait(false);
+ await using (fileStream.ConfigureAwait(false))
+ {
+ await stream.CopyToAsync(fileStream).ConfigureAwait(false);
+ }
}
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index a54640b2fd..6555864dc5 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -6,6 +6,7 @@ using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Api.Movies;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
@@ -14,7 +15,6 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
@@ -27,6 +27,12 @@ using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
+using Book = MediaBrowser.Controller.Entities.Book;
+using Episode = MediaBrowser.Controller.Entities.TV.Episode;
+using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
+using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace MediaBrowser.Api.Library
{
@@ -43,7 +49,7 @@ namespace MediaBrowser.Api.Library
}
/// <summary>
- /// Class GetCriticReviews
+ /// Class GetCriticReviews.
/// </summary>
[Route("/Items/{Id}/CriticReviews", "GET", Summary = "Gets critic reviews for an item")]
[Authenticated]
@@ -64,7 +70,7 @@ namespace MediaBrowser.Api.Library
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
@@ -72,7 +78,7 @@ namespace MediaBrowser.Api.Library
}
/// <summary>
- /// Class GetThemeSongs
+ /// Class GetThemeSongs.
/// </summary>
[Route("/Items/{Id}/ThemeSongs", "GET", Summary = "Gets theme songs for an item")]
[Authenticated]
@@ -97,7 +103,7 @@ namespace MediaBrowser.Api.Library
}
/// <summary>
- /// Class GetThemeVideos
+ /// Class GetThemeVideos.
/// </summary>
[Route("/Items/{Id}/ThemeVideos", "GET", Summary = "Gets theme videos for an item")]
[Authenticated]
@@ -122,7 +128,7 @@ namespace MediaBrowser.Api.Library
}
/// <summary>
- /// Class GetThemeVideos
+ /// Class GetThemeVideos.
/// </summary>
[Route("/Items/{Id}/ThemeMedia", "GET", Summary = "Gets theme videos and songs for an item")]
[Authenticated]
@@ -199,7 +205,7 @@ namespace MediaBrowser.Api.Library
}
/// <summary>
- /// Class GetPhyscialPaths
+ /// Class GetPhyscialPaths.
/// </summary>
[Route("/Library/PhysicalPaths", "GET", Summary = "Gets a list of physical paths from virtual folders")]
[Authenticated(Roles = "Admin")]
@@ -279,34 +285,43 @@ namespace MediaBrowser.Api.Library
public class GetLibraryOptionsInfo : IReturn<LibraryOptionsResult>
{
public string LibraryContentType { get; set; }
+
public bool IsNewLibrary { get; set; }
}
public class LibraryOptionInfo
{
public string Name { get; set; }
+
public bool DefaultEnabled { get; set; }
}
public class LibraryOptionsResult
{
public LibraryOptionInfo[] MetadataSavers { get; set; }
+
public LibraryOptionInfo[] MetadataReaders { get; set; }
+
public LibraryOptionInfo[] SubtitleFetchers { get; set; }
+
public LibraryTypeOptions[] TypeOptions { get; set; }
}
public class LibraryTypeOptions
{
public string Type { get; set; }
+
public LibraryOptionInfo[] MetadataFetchers { get; set; }
+
public LibraryOptionInfo[] ImageFetchers { get; set; }
+
public ImageType[] SupportedImageTypes { get; set; }
+
public ImageOption[] DefaultImageOptions { get; set; }
}
/// <summary>
- /// Class LibraryService
+ /// Class LibraryService.
/// </summary>
public class LibraryService : BaseApiService
{
@@ -319,11 +334,14 @@ namespace MediaBrowser.Api.Library
private readonly ILocalizationManager _localization;
private readonly ILibraryMonitor _libraryMonitor;
+ private readonly ILogger<MoviesService> _moviesServiceLogger;
+
/// <summary>
/// Initializes a new instance of the <see cref="LibraryService" /> class.
/// </summary>
public LibraryService(
ILogger<LibraryService> logger,
+ ILogger<MoviesService> moviesServiceLogger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IProviderManager providerManager,
@@ -344,8 +362,10 @@ namespace MediaBrowser.Api.Library
_activityManager = activityManager;
_localization = localization;
_libraryMonitor = libraryMonitor;
+ _moviesServiceLogger = moviesServiceLogger;
}
+ // Content Types available for each Library
private string[] GetRepresentativeItemTypes(string contentType)
{
return contentType switch
@@ -355,7 +375,7 @@ namespace MediaBrowser.Api.Library
CollectionType.Movies => new[] {"Movie"},
CollectionType.TvShows => new[] {"Series", "Season", "Episode"},
CollectionType.Books => new[] {"Book"},
- CollectionType.Music => new[] {"MusicAlbum", "MusicArtist", "Audio", "MusicVideo"},
+ CollectionType.Music => new[] {"MusicArtist", "MusicAlbum", "Audio", "MusicVideo"},
CollectionType.HomeVideos => new[] {"Video", "Photo"},
CollectionType.Photos => new[] {"Video", "Photo"},
CollectionType.MusicVideos => new[] {"MusicVideo"},
@@ -421,7 +441,6 @@ namespace MediaBrowser.Api.Library
return string.Equals(name, "TheTVDB", StringComparison.OrdinalIgnoreCase)
|| string.Equals(name, "Screen Grabber", StringComparison.OrdinalIgnoreCase)
|| string.Equals(name, "TheAudioDB", StringComparison.OrdinalIgnoreCase)
- || string.Equals(name, "Emby Designs", StringComparison.OrdinalIgnoreCase)
|| string.Equals(name, "Image Extractor", StringComparison.OrdinalIgnoreCase);
}
@@ -543,7 +562,7 @@ namespace MediaBrowser.Api.Library
if (item is Movie || (program != null && program.IsMovie) || item is Trailer)
{
return new MoviesService(
- Logger,
+ _moviesServiceLogger,
ServerConfigurationManager,
ResultFactory,
_userManager,
@@ -552,7 +571,6 @@ namespace MediaBrowser.Api.Library
_authContext)
{
Request = Request,
-
}.GetSimilarItemsResult(request);
}
@@ -650,8 +668,7 @@ namespace MediaBrowser.Api.Library
{
EnableImages = false
}
-
- }).Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+ }).Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProvider.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
foreach (var item in series)
{
@@ -679,16 +696,15 @@ namespace MediaBrowser.Api.Library
{
EnableImages = false
}
-
});
if (!string.IsNullOrWhiteSpace(request.ImdbId))
{
- movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).ToList();
+ movies = movies.Where(i => string.Equals(request.ImdbId, i.GetProviderId(MetadataProvider.Imdb), StringComparison.OrdinalIgnoreCase)).ToList();
}
else if (!string.IsNullOrWhiteSpace(request.TmdbId))
{
- movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase)).ToList();
+ movies = movies.Where(i => string.Equals(request.TmdbId, i.GetProviderId(MetadataProvider.Tmdb), StringComparison.OrdinalIgnoreCase)).ToList();
}
else
{
@@ -759,13 +775,12 @@ namespace MediaBrowser.Api.Library
{
try
{
- _activityManager.Create(new ActivityLogEntry
+ _activityManager.Create(new ActivityLog(
+ string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name),
+ "UserDownloadingContent",
+ auth.UserId)
{
- Name = string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name),
- Type = "UserDownloadingContent",
ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device),
- UserId = auth.UserId
-
});
}
catch
@@ -1030,6 +1045,7 @@ namespace MediaBrowser.Api.Library
{
break;
}
+
item = parent;
}
@@ -1087,6 +1103,7 @@ namespace MediaBrowser.Api.Library
{
break;
}
+
item = parent;
}
diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs
index 1e300814f6..b69550ed1a 100644
--- a/MediaBrowser.Api/Library/LibraryStructureService.cs
+++ b/MediaBrowser.Api/Library/LibraryStructureService.cs
@@ -19,7 +19,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Library
{
/// <summary>
- /// Class GetDefaultVirtualFolders
+ /// Class GetDefaultVirtualFolders.
/// </summary>
[Route("/Library/VirtualFolders", "GET")]
public class GetVirtualFolders : IReturn<List<VirtualFolderInfo>>
@@ -166,18 +166,18 @@ namespace MediaBrowser.Api.Library
}
/// <summary>
- /// Class LibraryStructureService
+ /// Class LibraryStructureService.
/// </summary>
[Authenticated(Roles = "Admin", AllowBeforeStartupWizard = true)]
public class LibraryStructureService : BaseApiService
{
/// <summary>
- /// The _app paths
+ /// The _app paths.
/// </summary>
private readonly IServerApplicationPaths _appPaths;
/// <summary>
- /// The _library manager
+ /// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILibraryMonitor _libraryMonitor;
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 5fe4c0cca3..830372dd8e 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -7,6 +7,7 @@ using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Api.UserLibrary;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
@@ -30,7 +31,7 @@ using Microsoft.Net.Http.Headers;
namespace MediaBrowser.Api.LiveTv
{
/// <summary>
- /// This is insecure right now to avoid windows phone refactoring
+ /// This is insecure right now to avoid windows phone refactoring.
/// </summary>
[Route("/LiveTv/Info", "GET", Summary = "Gets available live tv services.")]
[Authenticated]
@@ -71,7 +72,7 @@ namespace MediaBrowser.Api.LiveTv
public bool? IsSports { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
@@ -99,7 +100,7 @@ namespace MediaBrowser.Api.LiveTv
public string EnableImageTypes { get; set; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -187,7 +188,7 @@ namespace MediaBrowser.Api.LiveTv
public string EnableImageTypes { get; set; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -199,10 +200,15 @@ namespace MediaBrowser.Api.LiveTv
public bool? EnableUserData { get; set; }
public bool? IsMovie { get; set; }
+
public bool? IsSeries { get; set; }
+
public bool? IsKids { get; set; }
+
public bool? IsSports { get; set; }
+
public bool? IsNews { get; set; }
+
public bool? IsLibraryItem { get; set; }
public GetRecordings()
@@ -249,7 +255,7 @@ namespace MediaBrowser.Api.LiveTv
public string EnableImageTypes { get; set; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -347,6 +353,7 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool? HasAired { get; set; }
+
public bool? IsAiring { get; set; }
[ApiMember(Name = "MaxStartDate", Description = "Optional. The maximum premiere date. Format = ISO", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
@@ -406,10 +413,11 @@ namespace MediaBrowser.Api.LiveTv
public bool? EnableUserData { get; set; }
public string SeriesTimerId { get; set; }
+
public Guid LibrarySeriesId { get; set; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -472,7 +480,7 @@ namespace MediaBrowser.Api.LiveTv
public string GenreIds { get; set; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -600,7 +608,9 @@ namespace MediaBrowser.Api.LiveTv
public class AddListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo>
{
public bool ValidateLogin { get; set; }
+
public bool ValidateListings { get; set; }
+
public string Pw { get; set; }
}
@@ -649,15 +659,20 @@ namespace MediaBrowser.Api.LiveTv
{
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query")]
public string ProviderId { get; set; }
+
public string TunerChannelId { get; set; }
+
public string ProviderChannelId { get; set; }
}
public class ChannelMappingOptions
{
public List<TunerChannelMapping> TunerChannels { get; set; }
+
public List<NameIdPair> ProviderChannels { get; set; }
+
public NameValuePair[] Mappings { get; set; }
+
public string ProviderName { get; set; }
}
@@ -665,6 +680,7 @@ namespace MediaBrowser.Api.LiveTv
public class GetLiveStreamFile
{
public string Id { get; set; }
+
public string Container { get; set; }
}
@@ -678,7 +694,6 @@ namespace MediaBrowser.Api.LiveTv
[Authenticated]
public class GetTunerHostTypes : IReturn<List<NameIdPair>>
{
-
}
[Route("/LiveTv/Tuners/Discvover", "GET")]
@@ -825,7 +840,6 @@ namespace MediaBrowser.Api.LiveTv
{
Name = i.Name,
Id = i.Id
-
}).ToList(),
Mappings = mappings,
@@ -844,7 +858,6 @@ namespace MediaBrowser.Api.LiveTv
{
Url = "https://json.schedulesdirect.org/20141201/available/countries",
BufferContent = false
-
}).ConfigureAwait(false);
return ResultFactory.GetResult(Request, response, "application/json");
@@ -859,7 +872,7 @@ namespace MediaBrowser.Api.LiveTv
throw new SecurityException("Anonymous live tv management is not allowed.");
}
- if (!user.Policy.EnableLiveTvManagement)
+ if (!user.HasPermission(PermissionKind.EnableLiveTvManagement))
{
throw new SecurityException("The current user does not have permission to manage live tv.");
}
@@ -957,7 +970,6 @@ namespace MediaBrowser.Api.LiveTv
SortBy = request.GetOrderBy(),
SortOrder = request.SortOrder ?? SortOrder.Ascending,
AddCurrentProgram = request.AddCurrentProgram
-
}, options, CancellationToken.None);
var user = request.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(request.UserId);
@@ -1112,7 +1124,6 @@ namespace MediaBrowser.Api.LiveTv
Fields = request.GetItemFields(),
ImageTypeLimit = request.ImageTypeLimit,
EnableImages = request.EnableImages
-
}, options);
return ToOptimizedResult(result);
@@ -1151,7 +1162,6 @@ namespace MediaBrowser.Api.LiveTv
SeriesTimerId = request.SeriesTimerId,
IsActive = request.IsActive,
IsScheduled = request.IsScheduled
-
}, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
@@ -1187,7 +1197,6 @@ namespace MediaBrowser.Api.LiveTv
{
SortOrder = request.SortOrder,
SortBy = request.SortBy
-
}, CancellationToken.None).ConfigureAwait(false);
return ToOptimizedResult(result);
diff --git a/MediaBrowser.Api/LocalizationService.cs b/MediaBrowser.Api/LocalizationService.cs
index 6a69d26568..d6b5f51950 100644
--- a/MediaBrowser.Api/LocalizationService.cs
+++ b/MediaBrowser.Api/LocalizationService.cs
@@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class GetCultures
+ /// Class GetCultures.
/// </summary>
[Route("/Localization/Cultures", "GET", Summary = "Gets known cultures")]
public class GetCultures : IReturn<CultureDto[]>
@@ -16,7 +16,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class GetCountries
+ /// Class GetCountries.
/// </summary>
[Route("/Localization/Countries", "GET", Summary = "Gets known countries")]
public class GetCountries : IReturn<CountryInfo[]>
@@ -24,7 +24,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class ParentalRatings
+ /// Class ParentalRatings.
/// </summary>
[Route("/Localization/ParentalRatings", "GET", Summary = "Gets known parental ratings")]
public class GetParentalRatings : IReturn<ParentalRating[]>
@@ -32,7 +32,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class ParentalRatings
+ /// Class ParentalRatings.
/// </summary>
[Route("/Localization/Options", "GET", Summary = "Gets localization options")]
public class GetLocalizationOptions : IReturn<LocalizationOption[]>
@@ -40,13 +40,13 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class CulturesService
+ /// Class CulturesService.
/// </summary>
[Authenticated(AllowBeforeStartupWizard = true)]
public class LocalizationService : BaseApiService
{
/// <summary>
- /// The _localization
+ /// The _localization.
/// </summary>
private readonly ILocalizationManager _localization;
diff --git a/MediaBrowser.Api/Movies/CollectionService.cs b/MediaBrowser.Api/Movies/CollectionService.cs
index 95a37dfc56..e9629439de 100644
--- a/MediaBrowser.Api/Movies/CollectionService.cs
+++ b/MediaBrowser.Api/Movies/CollectionService.cs
@@ -79,7 +79,6 @@ namespace MediaBrowser.Api.Movies
ParentId = parentId,
ItemIdList = SplitValue(request.Ids, ','),
UserIds = new[] { userId }
-
});
var dtoOptions = GetDtoOptions(_authContext, request);
diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs
index 46da8b9099..34cccffa38 100644
--- a/MediaBrowser.Api/Movies/MoviesService.cs
+++ b/MediaBrowser.Api/Movies/MoviesService.cs
@@ -2,11 +2,11 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
@@ -15,6 +15,8 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
+using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
+using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
namespace MediaBrowser.Api.Movies
{
@@ -63,13 +65,13 @@ namespace MediaBrowser.Api.Movies
}
/// <summary>
- /// Class MoviesService
+ /// Class MoviesService.
/// </summary>
[Authenticated]
public class MoviesService : BaseApiService
{
/// <summary>
- /// The _user manager
+ /// The _user manager.
/// </summary>
private readonly IUserManager _userManager;
@@ -82,7 +84,7 @@ namespace MediaBrowser.Api.Movies
/// Initializes a new instance of the <see cref="MoviesService" /> class.
/// </summary>
public MoviesService(
- ILogger logger,
+ ILogger<MoviesService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
@@ -159,8 +161,8 @@ namespace MediaBrowser.Api.Movies
IncludeItemTypes = new[]
{
typeof(Movie).Name,
- //typeof(Trailer).Name,
- //typeof(LiveTvProgram).Name
+ // typeof(Trailer).Name,
+ // typeof(LiveTvProgram).Name
},
// IsMovie = true
OrderBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.Random }.Select(i => new ValueTuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
@@ -192,7 +194,6 @@ namespace MediaBrowser.Api.Movies
ParentId = parentIdGuid,
Recursive = true,
DtoOptions = dtoOptions
-
});
var mostRecentMovies = recentlyPlayedMovies.Take(6).ToList();
@@ -251,7 +252,12 @@ namespace MediaBrowser.Api.Movies
return categories.OrderBy(i => i.RecommendationType);
}
- private IEnumerable<RecommendationDto> GetWithDirector(User user, IEnumerable<string> names, int itemLimit, DtoOptions dtoOptions, RecommendationType type)
+ private IEnumerable<RecommendationDto> GetWithDirector(
+ User user,
+ IEnumerable<string> names,
+ int itemLimit,
+ DtoOptions dtoOptions,
+ RecommendationType type)
{
var itemTypes = new List<string> { typeof(Movie).Name };
if (ServerConfigurationManager.Configuration.EnableExternalContentInSuggestions)
@@ -272,8 +278,7 @@ namespace MediaBrowser.Api.Movies
IsMovie = true,
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
-
- }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
+ }).GroupBy(i => i.GetProviderId(MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
.Select(x => x.First())
.Take(itemLimit)
.ToList();
@@ -313,8 +318,7 @@ namespace MediaBrowser.Api.Movies
IsMovie = true,
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
-
- }).GroupBy(i => i.GetProviderId(MetadataProviders.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
+ }).GroupBy(i => i.GetProviderId(MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
.Select(x => x.First())
.Take(itemLimit)
.ToList();
@@ -353,7 +357,6 @@ namespace MediaBrowser.Api.Movies
SimilarTo = item,
EnableGroupByMetadataKey = true,
DtoOptions = dtoOptions
-
});
if (similar.Count > 0)
diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs
index 8adf9c6216..ca9f9d03b2 100644
--- a/MediaBrowser.Api/Movies/TrailersService.cs
+++ b/MediaBrowser.Api/Movies/TrailersService.cs
@@ -18,28 +18,33 @@ namespace MediaBrowser.Api.Movies
}
/// <summary>
- /// Class TrailersService
+ /// Class TrailersService.
/// </summary>
[Authenticated]
public class TrailersService : BaseApiService
{
/// <summary>
- /// The _user manager
+ /// The _user manager.
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
- /// The _library manager
+ /// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
+ /// <summary>
+ /// The logger for the created <see cref="ItemsService"/> instances.
+ /// </summary>
+ private readonly ILogger<ItemsService> _logger;
+
private readonly IDtoService _dtoService;
private readonly ILocalizationManager _localizationManager;
private readonly IJsonSerializer _json;
private readonly IAuthorizationContext _authContext;
public TrailersService(
- ILogger<TrailersService> logger,
+ ILoggerFactory loggerFactory,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
@@ -48,7 +53,7 @@ namespace MediaBrowser.Api.Movies
ILocalizationManager localizationManager,
IJsonSerializer json,
IAuthorizationContext authContext)
- : base(logger, serverConfigurationManager, httpResultFactory)
+ : base(loggerFactory.CreateLogger<TrailersService>(), serverConfigurationManager, httpResultFactory)
{
_userManager = userManager;
_libraryManager = libraryManager;
@@ -56,6 +61,7 @@ namespace MediaBrowser.Api.Movies
_localizationManager = localizationManager;
_json = json;
_authContext = authContext;
+ _logger = loggerFactory.CreateLogger<ItemsService>();
}
public object Get(Getrailers request)
@@ -66,7 +72,7 @@ namespace MediaBrowser.Api.Movies
getItems.IncludeItemTypes = "Trailer";
return new ItemsService(
- Logger,
+ _logger,
ServerConfigurationManager,
ResultFactory,
_userManager,
@@ -76,7 +82,6 @@ namespace MediaBrowser.Api.Movies
_authContext)
{
Request = Request,
-
}.Get(getItems);
}
}
diff --git a/MediaBrowser.Api/Music/AlbumsService.cs b/MediaBrowser.Api/Music/AlbumsService.cs
index 58c95d053e..74d3cce12a 100644
--- a/MediaBrowser.Api/Music/AlbumsService.cs
+++ b/MediaBrowser.Api/Music/AlbumsService.cs
@@ -27,16 +27,16 @@ namespace MediaBrowser.Api.Music
public class AlbumsService : BaseApiService
{
/// <summary>
- /// The _user manager
+ /// The _user manager.
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
- /// The _user data repository
+ /// The _user data repository.
/// </summary>
private readonly IUserDataManager _userDataRepository;
/// <summary>
- /// The _library manager
+ /// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
@@ -67,12 +67,13 @@ namespace MediaBrowser.Api.Music
{
var dtoOptions = GetDtoOptions(_authContext, request);
- var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
+ var result = SimilarItemsHelper.GetSimilarItemsResult(
+ dtoOptions,
+ _userManager,
_itemRepo,
_libraryManager,
_userDataRepository,
_dtoService,
- Logger,
request, new[] { typeof(MusicArtist) },
SimilarItemsHelper.GetSimiliarityScore);
@@ -88,12 +89,13 @@ namespace MediaBrowser.Api.Music
{
var dtoOptions = GetDtoOptions(_authContext, request);
- var result = SimilarItemsHelper.GetSimilarItemsResult(dtoOptions, _userManager,
+ var result = SimilarItemsHelper.GetSimilarItemsResult(
+ dtoOptions,
+ _userManager,
_itemRepo,
_libraryManager,
_userDataRepository,
_dtoService,
- Logger,
request, new[] { typeof(MusicAlbum) },
GetAlbumSimilarityScore);
diff --git a/MediaBrowser.Api/Music/InstantMixService.cs b/MediaBrowser.Api/Music/InstantMixService.cs
index cacec8d640..ebd3eb64a1 100644
--- a/MediaBrowser.Api/Music/InstantMixService.cs
+++ b/MediaBrowser.Api/Music/InstantMixService.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -191,6 +192,5 @@ namespace MediaBrowser.Api.Music
return result;
}
-
}
}
diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs
index 444354a992..a63d06ad5f 100644
--- a/MediaBrowser.Api/PackageService.cs
+++ b/MediaBrowser.Api/PackageService.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class GetPackage
+ /// Class GetPackage.
/// </summary>
[Route("/Packages/{Name}", "GET", Summary = "Gets a package, by name or assembly guid")]
[Authenticated]
@@ -36,7 +36,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class GetPackages
+ /// Class GetPackages.
/// </summary>
[Route("/Packages", "GET", Summary = "Gets available packages")]
[Authenticated]
@@ -45,7 +45,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class InstallPackage
+ /// Class InstallPackage.
/// </summary>
[Route("/Packages/Installed/{Name}", "POST", Summary = "Installs a package")]
[Authenticated(Roles = "Admin")]
@@ -74,7 +74,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class CancelPackageInstallation
+ /// Class CancelPackageInstallation.
/// </summary>
[Route("/Packages/Installing/{Id}", "DELETE", Summary = "Cancels a package installation")]
[Authenticated(Roles = "Admin")]
@@ -89,7 +89,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class PackageService
+ /// Class PackageService.
/// </summary>
public class PackageService : BaseApiService
{
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 7705393576..84ed5dcac4 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
@@ -27,7 +28,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Playback
{
/// <summary>
- /// Class BaseStreamingService
+ /// Class BaseStreamingService.
/// </summary>
public abstract class BaseStreamingService : BaseApiService
{
@@ -81,7 +82,7 @@ namespace MediaBrowser.Api.Playback
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
/// </summary>
protected BaseStreamingService(
- ILogger logger,
+ ILogger<BaseStreamingService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
@@ -193,10 +194,10 @@ namespace MediaBrowser.Api.Playback
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
- if (state.VideoRequest != null && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (state.VideoRequest != null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
- if (auth.User != null && !auth.User.Policy.EnableVideoPlaybackTranscoding)
+ if (auth.User != null && !auth.User.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding))
{
ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);
@@ -215,7 +216,7 @@ namespace MediaBrowser.Api.Playback
UseShellExecute = false,
// Must consume both stdout and stderr or deadlocks may occur
- //RedirectStandardOutput = true,
+ // RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
@@ -243,9 +244,9 @@ namespace MediaBrowser.Api.Playback
var logFilePrefix = "ffmpeg-transcode";
if (state.VideoRequest != null
- && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ && EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
- logFilePrefix = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase)
+ logFilePrefix = EncodingHelper.IsCopyCodec(state.OutputAudioCodec)
? "ffmpeg-remux" : "ffmpeg-directstream";
}
@@ -302,6 +303,7 @@ namespace MediaBrowser.Api.Playback
{
StartThrottler(state, transcodingJob);
}
+
Logger.LogDebug("StartFfMpeg() finished successfully");
return transcodingJob;
@@ -321,15 +323,16 @@ namespace MediaBrowser.Api.Playback
var encodingOptions = ServerConfigurationManager.GetEncodingOptions();
// enable throttling when NOT using hardware acceleration
- if (encodingOptions.HardwareAccelerationType == string.Empty)
+ if (string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType))
{
return state.InputProtocol == MediaProtocol.File &&
state.RunTimeTicks.HasValue &&
state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks &&
state.IsInputVideo &&
state.VideoType == VideoType.VideoFile &&
- !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase);
+ !EncodingHelper.IsCopyCodec(state.OutputVideoCodec);
}
+
return false;
}
@@ -606,6 +609,7 @@ namespace MediaBrowser.Api.Playback
{
throw new ArgumentException("Invalid timeseek header");
}
+
int index = value.IndexOf('-');
value = index == -1
? value.Substring(Npt.Length)
@@ -637,8 +641,10 @@ namespace MediaBrowser.Api.Playback
{
throw new ArgumentException("Invalid timeseek header");
}
+
timeFactor /= 60;
}
+
return TimeSpan.FromSeconds(secondsSum).Ticks;
}
@@ -683,7 +689,7 @@ namespace MediaBrowser.Api.Playback
state.User = UserManager.GetUserById(auth.UserId);
}
- //if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
+ // if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
// (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
// (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
//{
@@ -714,9 +720,9 @@ namespace MediaBrowser.Api.Playback
state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
- //var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ??
+ // var primaryImage = item.GetImageInfo(ImageType.Primary, 0) ??
// item.Parents.Select(i => i.GetImageInfo(ImageType.Primary, 0)).FirstOrDefault(i => i != null);
- //if (primaryImage != null)
+ // if (primaryImage != null)
//{
// state.AlbumCoverPath = primaryImage.Path;
//}
@@ -790,7 +796,7 @@ namespace MediaBrowser.Api.Playback
EncodingHelper.TryStreamCopy(state);
}
- if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (state.OutputVideoBitrate.HasValue && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
var resolution = ResolutionNormalizer.Normalize(
state.VideoStream?.BitRate,
@@ -883,7 +889,7 @@ namespace MediaBrowser.Api.Playback
if (transcodingProfile != null)
{
state.EstimateContentLength = transcodingProfile.EstimateContentLength;
- //state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
+ // state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
if (state.VideoRequest != null)
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 52962366c6..418cd92b3d 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -20,12 +20,12 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Playback.Hls
{
/// <summary>
- /// Class BaseHlsService
+ /// Class BaseHlsService.
/// </summary>
public abstract class BaseHlsService : BaseStreamingService
{
public BaseHlsService(
- ILogger logger,
+ ILogger<BaseHlsService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
@@ -146,6 +146,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
}
+
return ResultFactory.GetResult(GetLivePlaylistText(playlist, state.SegmentLength), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
}
@@ -178,7 +179,7 @@ namespace MediaBrowser.Api.Playback.Hls
var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(CultureInfo.InvariantCulture);
text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength - 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase);
- //text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase);
+ // text = text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(CultureInfo.InvariantCulture), newDuration, StringComparison.OrdinalIgnoreCase);
return text;
}
@@ -209,24 +210,28 @@ namespace MediaBrowser.Api.Playback.Hls
try
{
// Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written
- using var fileStream = GetPlaylistFileStream(playlist);
- using var reader = new StreamReader(fileStream);
- var count = 0;
-
- while (!reader.EndOfStream)
+ var fileStream = GetPlaylistFileStream(playlist);
+ await using (fileStream.ConfigureAwait(false))
{
- var line = reader.ReadLine();
+ using var reader = new StreamReader(fileStream);
+ var count = 0;
- if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
+ while (!reader.EndOfStream)
{
- count++;
- if (count >= segmentCount)
+ var line = await reader.ReadLineAsync().ConfigureAwait(false);
+
+ if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
{
- Logger.LogDebug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
- return;
+ count++;
+ if (count >= segmentCount)
+ {
+ Logger.LogDebug("Finished waiting for {0} segments in {1}", segmentCount, playlist);
+ return;
+ }
}
}
}
+
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
catch (IOException)
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 20e18cc265..fe5f980b18 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -94,7 +94,7 @@ namespace MediaBrowser.Api.Playback.Hls
public class DynamicHlsService : BaseHlsService
{
public DynamicHlsService(
- ILogger logger,
+ ILogger<DynamicHlsService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
@@ -234,6 +234,7 @@ namespace MediaBrowser.Api.Playback.Hls
Logger.LogDebug("Starting transcoding because segmentGap is {0} and max allowed gap is {1}. requestedIndex={2}", requestedIndex - currentTranscodingIndex.Value, segmentGapRequiringTranscodingChange, requestedIndex);
startTranscoding = true;
}
+
if (startTranscoding)
{
// If the playlist doesn't already exist, startup ffmpeg
@@ -257,7 +258,7 @@ namespace MediaBrowser.Api.Playback.Hls
throw;
}
- //await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
+ // await WaitForMinimumSegmentCount(playlistPath, 1, cancellationTokenSource.Token).ConfigureAwait(false);
}
else
{
@@ -277,8 +278,8 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
- //Logger.LogInformation("waiting for {0}", segmentPath);
- //while (!File.Exists(segmentPath))
+ // Logger.LogInformation("waiting for {0}", segmentPath);
+ // while (!File.Exists(segmentPath))
//{
// await Task.Delay(50, cancellationToken).ConfigureAwait(false);
//}
@@ -518,6 +519,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
Logger.LogDebug("serving {0} as it's on disk and transcoding stopped", segmentPath);
}
+
cancellationToken.ThrowIfCancellationRequested();
}
else
@@ -700,12 +702,12 @@ namespace MediaBrowser.Api.Playback.Hls
return false;
}
- if (string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec))
{
return false;
}
- if (string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(state.OutputAudioCodec))
{
return false;
}
@@ -717,25 +719,204 @@ namespace MediaBrowser.Api.Playback.Hls
// Having problems in android
return false;
- //return state.VideoRequest.VideoBitRate.HasValue;
+ // return state.VideoRequest.VideoBitRate.HasValue;
+ }
+
+ /// <summary>
+ /// Get the H.26X level of the output video stream.
+ /// </summary>
+ /// <param name="state">StreamState of the current stream.</param>
+ /// <returns>H.26X level of the output video stream.</returns>
+ private int? GetOutputVideoCodecLevel(StreamState state)
+ {
+ string levelString;
+ if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)
+ && state.VideoStream.Level.HasValue)
+ {
+ levelString = state.VideoStream?.Level.ToString();
+ }
+ else
+ {
+ levelString = state.GetRequestedLevel(state.ActualOutputVideoCodec);
+ }
+
+ if (int.TryParse(levelString, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedLevel))
+ {
+ return parsedLevel;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Gets a formatted string of the output audio codec, for use in the CODECS field.
+ /// </summary>
+ /// <seealso cref="AppendPlaylistCodecsField(StringBuilder, StreamState)"/>
+ /// <seealso cref="GetPlaylistVideoCodecs(StreamState, string, int)"/>
+ /// <param name="state">StreamState of the current stream.</param>
+ /// <returns>Formatted audio codec string.</returns>
+ private string GetPlaylistAudioCodecs(StreamState state)
+ {
+
+ if (string.Equals(state.ActualOutputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase))
+ {
+ string profile = state.GetRequestedProfiles("aac").FirstOrDefault();
+
+ return HlsCodecStringFactory.GetAACString(profile);
+ }
+ else if (string.Equals(state.ActualOutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
+ {
+ return HlsCodecStringFactory.GetMP3String();
+ }
+ else if (string.Equals(state.ActualOutputAudioCodec, "ac3", StringComparison.OrdinalIgnoreCase))
+ {
+ return HlsCodecStringFactory.GetAC3String();
+ }
+ else if (string.Equals(state.ActualOutputAudioCodec, "eac3", StringComparison.OrdinalIgnoreCase))
+ {
+ return HlsCodecStringFactory.GetEAC3String();
+ }
+
+ return string.Empty;
+ }
+
+ /// <summary>
+ /// Gets a formatted string of the output video codec, for use in the CODECS field.
+ /// </summary>
+ /// <seealso cref="AppendPlaylistCodecsField(StringBuilder, StreamState)"/>
+ /// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/>
+ /// <param name="state">StreamState of the current stream.</param>
+ /// <returns>Formatted video codec string.</returns>
+ private string GetPlaylistVideoCodecs(StreamState state, string codec, int level)
+ {
+ if (level == 0)
+ {
+ // This is 0 when there's no requested H.26X level in the device profile
+ // and the source is not encoded in H.26X
+ Logger.LogError("Got invalid H.26X level when building CODECS field for HLS master playlist");
+ return string.Empty;
+ }
+
+ if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
+ {
+ string profile = state.GetRequestedProfiles("h264").FirstOrDefault();
+
+ return HlsCodecStringFactory.GetH264String(profile, level);
+ }
+ else if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
+ {
+ string profile = state.GetRequestedProfiles("h265").FirstOrDefault();
+
+ return HlsCodecStringFactory.GetH265String(profile, level);
+ }
+
+ return string.Empty;
+ }
+
+ /// <summary>
+ /// Appends a CODECS field containing formatted strings of
+ /// the active streams output video and audio codecs.
+ /// </summary>
+ /// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/>
+ /// <seealso cref="GetPlaylistVideoCodecs(StreamState, string, int)"/>
+ /// <seealso cref="GetPlaylistAudioCodecs(StreamState)"/>
+ /// <param name="builder">StringBuilder to append the field to.</param>
+ /// <param name="state">StreamState of the current stream.</param>
+ private void AppendPlaylistCodecsField(StringBuilder builder, StreamState state)
+ {
+ // Video
+ string videoCodecs = string.Empty;
+ int? videoCodecLevel = GetOutputVideoCodecLevel(state);
+ if (!string.IsNullOrEmpty(state.ActualOutputVideoCodec) && videoCodecLevel.HasValue)
+ {
+ videoCodecs = GetPlaylistVideoCodecs(state, state.ActualOutputVideoCodec, videoCodecLevel.Value);
+ }
+
+ // Audio
+ string audioCodecs = string.Empty;
+ if (!string.IsNullOrEmpty(state.ActualOutputAudioCodec))
+ {
+ audioCodecs = GetPlaylistAudioCodecs(state);
+ }
+
+ StringBuilder codecs = new StringBuilder();
+
+ codecs.Append(videoCodecs)
+ .Append(',')
+ .Append(audioCodecs);
+
+ if (codecs.Length > 1)
+ {
+ builder.Append(",CODECS=\"")
+ .Append(codecs)
+ .Append('"');
+ }
+ }
+
+ /// <summary>
+ /// Appends a FRAME-RATE field containing the framerate of the output stream.
+ /// </summary>
+ /// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/>
+ /// <param name="builder">StringBuilder to append the field to.</param>
+ /// <param name="state">StreamState of the current stream.</param>
+ private void AppendPlaylistFramerateField(StringBuilder builder, StreamState state)
+ {
+ double? framerate = null;
+ if (state.TargetFramerate.HasValue)
+ {
+ framerate = Math.Round(state.TargetFramerate.GetValueOrDefault(), 3);
+ }
+ else if (state.VideoStream?.RealFrameRate != null)
+ {
+ framerate = Math.Round(state.VideoStream.RealFrameRate.GetValueOrDefault(), 3);
+ }
+
+ if (framerate.HasValue)
+ {
+ builder.Append(",FRAME-RATE=")
+ .Append(framerate.Value);
+ }
+ }
+
+ /// <summary>
+ /// Appends a RESOLUTION field containing the resolution of the output stream.
+ /// </summary>
+ /// <seealso cref="AppendPlaylist(StringBuilder, StreamState, string, int, string)"/>
+ /// <param name="builder">StringBuilder to append the field to.</param>
+ /// <param name="state">StreamState of the current stream.</param>
+ private void AppendPlaylistResolutionField(StringBuilder builder, StreamState state)
+ {
+ if (state.OutputWidth.HasValue && state.OutputHeight.HasValue)
+ {
+ builder.Append(",RESOLUTION=")
+ .Append(state.OutputWidth.GetValueOrDefault())
+ .Append('x')
+ .Append(state.OutputHeight.GetValueOrDefault());
+ }
}
private void AppendPlaylist(StringBuilder builder, StreamState state, string url, int bitrate, string subtitleGroup)
{
- var header = "#EXT-X-STREAM-INF:BANDWIDTH=" + bitrate.ToString(CultureInfo.InvariantCulture) + ",AVERAGE-BANDWIDTH=" + bitrate.ToString(CultureInfo.InvariantCulture);
+ builder.Append("#EXT-X-STREAM-INF:BANDWIDTH=")
+ .Append(bitrate.ToString(CultureInfo.InvariantCulture))
+ .Append(",AVERAGE-BANDWIDTH=")
+ .Append(bitrate.ToString(CultureInfo.InvariantCulture));
- // tvos wants resolution, codecs, framerate
- //if (state.TargetFramerate.HasValue)
- //{
- // header += string.Format(",FRAME-RATE=\"{0}\"", state.TargetFramerate.Value.ToString(CultureInfo.InvariantCulture));
- //}
+ AppendPlaylistCodecsField(builder, state);
+
+ AppendPlaylistResolutionField(builder, state);
+
+ AppendPlaylistFramerateField(builder, state);
if (!string.IsNullOrWhiteSpace(subtitleGroup))
{
- header += string.Format(",SUBTITLES=\"{0}\"", subtitleGroup);
+ builder.Append(",SUBTITLES=\"")
+ .Append(subtitleGroup)
+ .Append('"');
}
- builder.AppendLine(header);
+ builder.Append(Environment.NewLine);
builder.AppendLine(url);
}
@@ -793,7 +974,7 @@ namespace MediaBrowser.Api.Playback.Hls
var queryStringIndex = Request.RawUrl.IndexOf('?');
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
- //if ((Request.UserAgent ?? string.Empty).IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1)
+ // if ((Request.UserAgent ?? string.Empty).IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1)
//{
// queryString = string.Empty;
//}
@@ -827,7 +1008,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (!state.IsOutputVideo)
{
- if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(audioCodec))
{
return "-acodec copy";
}
@@ -855,11 +1036,11 @@ namespace MediaBrowser.Api.Playback.Hls
return string.Join(" ", audioTranscodeParams.ToArray());
}
- if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(audioCodec))
{
var videoCodec = EncodingHelper.GetVideoEncoder(state, encodingOptions);
- if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.EnableBreakOnNonKeyFrames(videoCodec))
+ if (EncodingHelper.IsCopyCodec(videoCodec) && state.EnableBreakOnNonKeyFrames(videoCodec))
{
return "-codec:a:0 copy -copypriorss:a:0 0";
}
@@ -910,7 +1091,7 @@ namespace MediaBrowser.Api.Playback.Hls
// }
// See if we can save come cpu cycles by avoiding encoding
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(codec))
{
if (state.VideoStream != null && !string.Equals(state.VideoStream.NalLengthSize, "0", StringComparison.OrdinalIgnoreCase))
{
@@ -921,7 +1102,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
- //args += " -flags -global_header";
+ // args += " -flags -global_header";
}
else
{
@@ -963,7 +1144,7 @@ namespace MediaBrowser.Api.Playback.Hls
args += " " + keyFrameArg + gopArg;
}
- //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
+ // args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@@ -985,7 +1166,7 @@ namespace MediaBrowser.Api.Playback.Hls
args += " -start_at_zero";
}
- //args += " -flags -global_header";
+ // args += " -flags -global_header";
}
if (!string.IsNullOrEmpty(state.OutputVideoSync))
diff --git a/MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs b/MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs
new file mode 100644
index 0000000000..3bbb77a65e
--- /dev/null
+++ b/MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Text;
+
+
+namespace MediaBrowser.Api.Playback
+{
+ /// <summary>
+ /// Get various codec strings for use in HLS playlists.
+ /// </summary>
+ static class HlsCodecStringFactory
+ {
+
+ /// <summary>
+ /// Gets a MP3 codec string.
+ /// </summary>
+ /// <returns>MP3 codec string.</returns>
+ public static string GetMP3String()
+ {
+ return "mp4a.40.34";
+ }
+
+ /// <summary>
+ /// Gets an AAC codec string.
+ /// </summary>
+ /// <param name="profile">AAC profile.</param>
+ /// <returns>AAC codec string.</returns>
+ public static string GetAACString(string profile)
+ {
+ StringBuilder result = new StringBuilder("mp4a", 9);
+
+ if (string.Equals(profile, "HE", StringComparison.OrdinalIgnoreCase))
+ {
+ result.Append(".40.5");
+ }
+ else
+ {
+ // Default to LC if profile is invalid
+ result.Append(".40.2");
+ }
+
+ return result.ToString();
+ }
+
+ /// <summary>
+ /// Gets a H.264 codec string.
+ /// </summary>
+ /// <param name="profile">H.264 profile.</param>
+ /// <param name="level">H.264 level.</param>
+ /// <returns>H.264 string.</returns>
+ public static string GetH264String(string profile, int level)
+ {
+ StringBuilder result = new StringBuilder("avc1", 11);
+
+ if (string.Equals(profile, "high", StringComparison.OrdinalIgnoreCase))
+ {
+ result.Append(".6400");
+ }
+ else if (string.Equals(profile, "main", StringComparison.OrdinalIgnoreCase))
+ {
+ result.Append(".4D40");
+ }
+ else if (string.Equals(profile, "baseline", StringComparison.OrdinalIgnoreCase))
+ {
+ result.Append(".42E0");
+ }
+ else
+ {
+ // Default to constrained baseline if profile is invalid
+ result.Append(".4240");
+ }
+
+ string levelHex = level.ToString("X2");
+ result.Append(levelHex);
+
+ return result.ToString();
+ }
+
+ /// <summary>
+ /// Gets a H.265 codec string.
+ /// </summary>
+ /// <param name="profile">H.265 profile.</param>
+ /// <param name="level">H.265 level.</param>
+ /// <returns>H.265 string.</returns>
+ public static string GetH265String(string profile, int level)
+ {
+ // The h265 syntax is a bit of a mystery at the time this comment was written.
+ // This is what I've found through various sources:
+ // FORMAT: [codecTag].[profile].[constraint?].L[level * 30].[UNKNOWN]
+ StringBuilder result = new StringBuilder("hev1", 16);
+
+ if (string.Equals(profile, "main10", StringComparison.OrdinalIgnoreCase))
+ {
+ result.Append(".2.6");
+ }
+ else
+ {
+ // Default to main if profile is invalid
+ result.Append(".1.6");
+ }
+
+ result.Append(".L")
+ .Append(level * 3)
+ .Append(".B0");
+
+ return result.ToString();
+ }
+
+ /// <summary>
+ /// Gets an AC-3 codec string.
+ /// </summary>
+ /// <returns>AC-3 codec string.</returns>
+ public static string GetAC3String()
+ {
+ return "mp4a.a5";
+ }
+
+ /// <summary>
+ /// Gets an E-AC-3 codec string.
+ /// </summary>
+ /// <returns>E-AC-3 codec string.</returns>
+ public static string GetEAC3String()
+ {
+ return "mp4a.a6";
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
index 87ccde2e04..8a3d00283f 100644
--- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
+++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
@@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Playback.Hls
{
/// <summary>
- /// Class GetHlsAudioSegment
+ /// Class GetHlsAudioSegment.
/// </summary>
// Can't require authentication just yet due to seeing some requests come from Chrome without full query string
//[Authenticated]
@@ -37,7 +37,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
/// <summary>
- /// Class GetHlsVideoSegment
+ /// Class GetHlsVideoSegment.
/// </summary>
[Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
[Authenticated]
@@ -66,7 +66,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
/// <summary>
- /// Class GetHlsVideoSegment
+ /// Class GetHlsVideoSegment.
/// </summary>
// Can't require authentication just yet due to seeing some requests come from Chrome without full query string
//[Authenticated]
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index d1c53c1c11..9562f9953a 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
/// <summary>
- /// Class VideoHlsService
+ /// Class VideoHlsService.
/// </summary>
[Authenticated]
public class VideoHlsService : BaseHlsService
@@ -72,7 +72,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
var codec = EncodingHelper.GetAudioEncoder(state);
- if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(codec))
{
return "-codec:a:0 copy";
}
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index db24eaca6e..b7ca1a031d 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
@@ -79,7 +80,7 @@ namespace MediaBrowser.Api.Playback
private readonly IAuthorizationContext _authContext;
public MediaInfoService(
- ILogger logger,
+ ILogger<MediaInfoService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IMediaSourceManager mediaSourceManager,
@@ -400,21 +401,24 @@ namespace MediaBrowser.Api.Playback
if (item is Audio)
{
- Logger.LogInformation("User policy for {0}. EnableAudioPlaybackTranscoding: {1}", user.Name, user.Policy.EnableAudioPlaybackTranscoding);
+ Logger.LogInformation(
+ "User policy for {0}. EnableAudioPlaybackTranscoding: {1}",
+ user.Username,
+ user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding));
}
else
{
Logger.LogInformation("User policy for {0}. EnablePlaybackRemuxing: {1} EnableVideoPlaybackTranscoding: {2} EnableAudioPlaybackTranscoding: {3}",
- user.Name,
- user.Policy.EnablePlaybackRemuxing,
- user.Policy.EnableVideoPlaybackTranscoding,
- user.Policy.EnableAudioPlaybackTranscoding);
+ user.Username,
+ user.HasPermission(PermissionKind.EnablePlaybackRemuxing),
+ user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding),
+ user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding));
}
// Beginning of Playback Determination: Attempt DirectPlay first
if (mediaSource.SupportsDirectPlay)
{
- if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
+ if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
mediaSource.SupportsDirectPlay = false;
}
@@ -428,14 +432,16 @@ namespace MediaBrowser.Api.Playback
if (item is Audio)
{
- if (!user.Policy.EnableAudioPlaybackTranscoding)
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
{
options.ForceDirectPlay = true;
}
}
else if (item is Video)
{
- if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
{
options.ForceDirectPlay = true;
}
@@ -463,7 +469,7 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsDirectStream)
{
- if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
+ if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
mediaSource.SupportsDirectStream = false;
}
@@ -473,14 +479,16 @@ namespace MediaBrowser.Api.Playback
if (item is Audio)
{
- if (!user.Policy.EnableAudioPlaybackTranscoding)
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding))
{
options.ForceDirectStream = true;
}
}
else if (item is Video)
{
- if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ if (!user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)
+ && !user.HasPermission(PermissionKind.EnablePlaybackRemuxing))
{
options.ForceDirectStream = true;
}
@@ -512,7 +520,7 @@ namespace MediaBrowser.Api.Playback
? streamBuilder.BuildAudioItem(options)
: streamBuilder.BuildVideoItem(options);
- if (mediaSource.IsRemote && user.Policy.ForceRemoteSourceTranscoding)
+ if (mediaSource.IsRemote && user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding))
{
if (streamInfo != null)
{
@@ -543,10 +551,12 @@ namespace MediaBrowser.Api.Playback
{
mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false";
}
+
if (!allowAudioStreamCopy)
{
mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false";
}
+
mediaSource.TranscodingContainer = streamInfo.Container;
mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol;
}
@@ -576,10 +586,10 @@ namespace MediaBrowser.Api.Playback
}
}
- private long? GetMaxBitrate(long? clientMaxBitrate, User user)
+ private long? GetMaxBitrate(long? clientMaxBitrate, Jellyfin.Data.Entities.User user)
{
var maxBitrate = clientMaxBitrate;
- var remoteClientMaxBitrate = user?.Policy.RemoteClientBitrateLimit ?? 0;
+ var remoteClientMaxBitrate = user?.RemoteClientBitrateLimit ?? 0;
if (remoteClientMaxBitrate <= 0)
{
@@ -638,7 +648,6 @@ namespace MediaBrowser.Api.Playback
}
return 1;
-
}).ThenBy(i =>
{
// Let's assume direct streaming a file is just as desirable as direct playing a remote url
@@ -648,7 +657,6 @@ namespace MediaBrowser.Api.Playback
}
return 1;
-
}).ThenBy(i =>
{
return i.Protocol switch
@@ -664,7 +672,6 @@ namespace MediaBrowser.Api.Playback
}
return 1;
-
}).ThenBy(originalList.IndexOf)
.ToArray();
}
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
index 8d1e3a3f23..d51787df27 100644
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
@@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Playback.Progressive
{
/// <summary>
- /// Class GetAudioStream
+ /// Class GetAudioStream.
/// </summary>
[Route("/Audio/{Id}/stream.{Container}", "GET", Summary = "Gets an audio stream")]
[Route("/Audio/{Id}/stream", "GET", Summary = "Gets an audio stream")]
@@ -26,14 +26,14 @@ namespace MediaBrowser.Api.Playback.Progressive
}
/// <summary>
- /// Class AudioService
+ /// Class AudioService.
/// </summary>
// TODO: In order to autheneticate this in the future, Dlna playback will require updating
//[Authenticated]
public class AudioService : BaseProgressiveStreamingService
{
public AudioService(
- ILogger logger,
+ ILogger<AudioService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IHttpClient httpClient,
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index ed68219c9f..2ebf0e420d 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -21,14 +21,14 @@ using Microsoft.Net.Http.Headers;
namespace MediaBrowser.Api.Playback.Progressive
{
/// <summary>
- /// Class BaseProgressiveStreamingService
+ /// Class BaseProgressiveStreamingService.
/// </summary>
public abstract class BaseProgressiveStreamingService : BaseStreamingService
{
protected IHttpClient HttpClient { get; private set; }
public BaseProgressiveStreamingService(
- ILogger logger,
+ ILogger<BaseProgressiveStreamingService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IHttpClient httpClient,
@@ -88,14 +88,17 @@ namespace MediaBrowser.Api.Playback.Progressive
{
return ".ts";
}
+
if (string.Equals(videoCodec, "theora", StringComparison.OrdinalIgnoreCase))
{
return ".ogv";
}
+
if (string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
{
return ".webm";
}
+
if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
{
return ".asf";
@@ -111,14 +114,17 @@ namespace MediaBrowser.Api.Playback.Progressive
{
return ".aac";
}
+
if (string.Equals("mp3", audioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".mp3";
}
+
if (string.Equals("vorbis", audioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".ogg";
}
+
if (string.Equals("wma", audioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".wma";
@@ -231,7 +237,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
//// Not static but transcode cache file exists
- //if (isTranscodeCached && state.VideoRequest == null)
+ // if (isTranscodeCached && state.VideoRequest == null)
//{
// var contentType = state.GetMimeType(outputPath);
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index a53b848f9e..b70fff128b 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -23,6 +23,7 @@ namespace MediaBrowser.Api.Playback.Progressive
private long _bytesWritten = 0;
public long StartPosition { get; set; }
+
public bool AllowEndOfFile = true;
private readonly IDirectStreamProvider _directStreamProvider;
@@ -96,8 +97,8 @@ namespace MediaBrowser.Api.Playback.Progressive
bytesRead = await CopyToInternalAsyncWithSyncRead(inputStream, outputStream, cancellationToken).ConfigureAwait(false);
}
- //var position = fs.Position;
- //_logger.LogDebug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
+ // var position = fs.Position;
+ // _logger.LogDebug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
if (bytesRead == 0)
{
@@ -105,6 +106,7 @@ namespace MediaBrowser.Api.Playback.Progressive
{
eofCount++;
}
+
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
else
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 4de81655ce..c3f6b905cb 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Playback.Progressive
{
/// <summary>
- /// Class GetVideoStream
+ /// Class GetVideoStream.
/// </summary>
[Route("/Videos/{Id}/stream.mpegts", "GET")]
[Route("/Videos/{Id}/stream.ts", "GET")]
@@ -59,11 +59,10 @@ namespace MediaBrowser.Api.Playback.Progressive
[Route("/Videos/{Id}/stream", "HEAD")]
public class GetVideoStream : VideoStreamRequest
{
-
}
/// <summary>
- /// Class VideoService
+ /// Class VideoService.
/// </summary>
// TODO: In order to autheneticate this in the future, Dlna playback will require updating
//[Authenticated]
diff --git a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
index 3b8b299957..7e2e337ad1 100644
--- a/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/StaticRemoteStreamWriter.cs
@@ -8,17 +8,17 @@ using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback
{
/// <summary>
- /// Class StaticRemoteStreamWriter
+ /// Class StaticRemoteStreamWriter.
/// </summary>
public class StaticRemoteStreamWriter : IAsyncStreamWriter, IHasHeaders
{
/// <summary>
- /// The _input stream
+ /// The _input stream.
/// </summary>
private readonly HttpResponseInfo _response;
/// <summary>
- /// The _options
+ /// The _options.
/// </summary>
private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index 9ba8eda91f..67c334e489 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -4,7 +4,7 @@ using MediaBrowser.Model.Services;
namespace MediaBrowser.Api.Playback
{
/// <summary>
- /// Class StreamRequest
+ /// Class StreamRequest.
/// </summary>
public class StreamRequest : BaseEncodingJobOptions
{
@@ -12,11 +12,15 @@ namespace MediaBrowser.Api.Playback
public string DeviceProfileId { get; set; }
public string Params { get; set; }
+
public string PlaySessionId { get; set; }
+
public string Tag { get; set; }
+
public string SegmentContainer { get; set; }
public int? SegmentLength { get; set; }
+
public int? MinSegments { get; set; }
}
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index d5d2f58c03..c244b00334 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -42,7 +42,7 @@ namespace MediaBrowser.Api.Playback
return Request.SegmentLength.Value;
}
- if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EncodingHelper.IsCopyCodec(OutputVideoCodec))
{
var userAgent = UserAgent ?? string.Empty;
diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs
index cebd4b49a1..d5d78cf37a 100644
--- a/MediaBrowser.Api/Playback/UniversalAudioService.cs
+++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs
@@ -37,10 +37,13 @@ namespace MediaBrowser.Api.Playback
public string DeviceId { get; set; }
public Guid UserId { get; set; }
+
public string AudioCodec { get; set; }
+
public string Container { get; set; }
public int? MaxAudioChannels { get; set; }
+
public int? TranscodingAudioChannels { get; set; }
public long? MaxStreamingBitrate { get; set; }
@@ -49,12 +52,17 @@ namespace MediaBrowser.Api.Playback
public long? StartTimeTicks { get; set; }
public string TranscodingContainer { get; set; }
+
public string TranscodingProtocol { get; set; }
+
public int? MaxAudioSampleRate { get; set; }
+
public int? MaxAudioBitDepth { get; set; }
public bool EnableRedirection { get; set; }
+
public bool EnableRemoteMedia { get; set; }
+
public bool BreakOnNonKeyFrames { get; set; }
public BaseUniversalRequest()
@@ -75,9 +83,11 @@ namespace MediaBrowser.Api.Playback
public class UniversalAudioService : BaseApiService
{
private readonly EncodingHelper _encodingHelper;
+ private readonly ILoggerFactory _loggerFactory;
public UniversalAudioService(
ILogger<UniversalAudioService> logger,
+ ILoggerFactory loggerFactory,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IHttpClient httpClient,
@@ -108,19 +118,31 @@ namespace MediaBrowser.Api.Playback
AuthorizationContext = authorizationContext;
NetworkManager = networkManager;
_encodingHelper = encodingHelper;
+ _loggerFactory = loggerFactory;
}
protected IHttpClient HttpClient { get; private set; }
+
protected IUserManager UserManager { get; private set; }
+
protected ILibraryManager LibraryManager { get; private set; }
+
protected IIsoManager IsoManager { get; private set; }
+
protected IMediaEncoder MediaEncoder { get; private set; }
+
protected IFileSystem FileSystem { get; private set; }
+
protected IDlnaManager DlnaManager { get; private set; }
+
protected IDeviceManager DeviceManager { get; private set; }
+
protected IMediaSourceManager MediaSourceManager { get; private set; }
+
protected IJsonSerializer JsonSerializer { get; private set; }
+
protected IAuthorizationContext AuthorizationContext { get; private set; }
+
protected INetworkManager NetworkManager { get; private set; }
public Task<object> Get(GetUniversalAudioStream request)
@@ -233,7 +255,7 @@ namespace MediaBrowser.Api.Playback
AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId;
var mediaInfoService = new MediaInfoService(
- Logger,
+ _loggerFactory.CreateLogger<MediaInfoService>(),
ServerConfigurationManager,
ResultFactory,
MediaSourceManager,
@@ -256,7 +278,6 @@ namespace MediaBrowser.Api.Playback
UserId = request.UserId,
DeviceProfile = deviceProfile,
MediaSourceId = request.MediaSourceId
-
}).ConfigureAwait(false);
var mediaSource = playbackInfoResult.MediaSources[0];
@@ -277,7 +298,7 @@ namespace MediaBrowser.Api.Playback
if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
{
var service = new DynamicHlsService(
- Logger,
+ _loggerFactory.CreateLogger<DynamicHlsService>(),
ServerConfigurationManager,
ResultFactory,
UserManager,
@@ -326,12 +347,13 @@ namespace MediaBrowser.Api.Playback
{
return await service.Head(newRequest).ConfigureAwait(false);
}
+
return await service.Get(newRequest).ConfigureAwait(false);
}
else
{
var service = new AudioService(
- Logger,
+ _loggerFactory.CreateLogger<AudioService>(),
ServerConfigurationManager,
ResultFactory,
HttpClient,
diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs
index 953b00e35a..5513c08922 100644
--- a/MediaBrowser.Api/PlaylistService.cs
+++ b/MediaBrowser.Api/PlaylistService.cs
@@ -95,14 +95,14 @@ namespace MediaBrowser.Api
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[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; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -161,7 +161,6 @@ namespace MediaBrowser.Api
ItemIdList = GetGuids(request.Ids),
UserId = request.UserId,
MediaType = request.MediaType
-
}).ConfigureAwait(false);
return ToOptimizedResult(result);
diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs
index 7f74511eec..7d976ceaac 100644
--- a/MediaBrowser.Api/PluginService.cs
+++ b/MediaBrowser.Api/PluginService.cs
@@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class Plugins
+ /// Class Plugins.
/// </summary>
[Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")]
[Authenticated]
@@ -25,7 +25,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UninstallPlugin
+ /// Class UninstallPlugin.
/// </summary>
[Route("/Plugins/{Id}", "DELETE", Summary = "Uninstalls a plugin")]
[Authenticated(Roles = "Admin")]
@@ -40,7 +40,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class GetPluginConfiguration
+ /// Class GetPluginConfiguration.
/// </summary>
[Route("/Plugins/{Id}/Configuration", "GET", Summary = "Gets a plugin's configuration")]
[Authenticated]
@@ -55,7 +55,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UpdatePluginConfiguration
+ /// Class UpdatePluginConfiguration.
/// </summary>
[Route("/Plugins/{Id}/Configuration", "POST", Summary = "Updates a plugin's configuration")]
[Authenticated]
@@ -69,13 +69,13 @@ namespace MediaBrowser.Api
public string Id { get; set; }
/// <summary>
- /// The raw Http Request Input Stream
+ /// The raw Http Request Input Stream.
/// </summary>
/// <value>The request stream.</value>
public Stream RequestStream { get; set; }
}
- //TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins,
+ // TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins,
// delete all these registration endpoints. They are only kept for compatibility.
[Route("/Registrations/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)]
[Authenticated]
@@ -86,7 +86,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class GetPluginSecurityInfo
+ /// Class GetPluginSecurityInfo.
/// </summary>
[Route("/Plugins/SecurityInfo", "GET", Summary = "Gets plugin registration information", IsHidden = true)]
[Authenticated]
@@ -95,7 +95,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UpdatePluginSecurityInfo
+ /// Class UpdatePluginSecurityInfo.
/// </summary>
[Route("/Plugins/SecurityInfo", "POST", Summary = "Updates plugin registration information", IsHidden = true)]
[Authenticated(Roles = "Admin")]
@@ -115,38 +115,47 @@ namespace MediaBrowser.Api
public class RegistrationInfo
{
public string Name { get; set; }
+
public DateTime ExpirationDate { get; set; }
+
public bool IsTrial { get; set; }
+
public bool IsRegistered { get; set; }
}
public class MBRegistrationRecord
{
public DateTime ExpirationDate { get; set; }
+
public bool IsRegistered { get; set; }
+
public bool RegChecked { get; set; }
+
public bool RegError { get; set; }
+
public bool TrialVersion { get; set; }
+
public bool IsValid { get; set; }
}
public class PluginSecurityInfo
{
public string SupporterKey { get; set; }
+
public bool IsMBSupporter { get; set; }
}
/// <summary>
- /// Class PluginsService
+ /// Class PluginsService.
/// </summary>
public class PluginService : BaseApiService
{
/// <summary>
- /// The _json serializer
+ /// The _json serializer.
/// </summary>
private readonly IJsonSerializer _jsonSerializer;
/// <summary>
- /// The _app host
+ /// The _app host.
/// </summary>
private readonly IApplicationHost _appHost;
private readonly IInstallationManager _installationManager;
diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
index e08a8482e0..86b00316ad 100644
--- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
+++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
@@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.ScheduledTasks
{
/// <summary>
- /// Class GetScheduledTask
+ /// Class GetScheduledTask.
/// </summary>
[Route("/ScheduledTasks/{Id}", "GET", Summary = "Gets a scheduled task, by Id")]
public class GetScheduledTask : IReturn<TaskInfo>
@@ -25,7 +25,7 @@ namespace MediaBrowser.Api.ScheduledTasks
}
/// <summary>
- /// Class GetScheduledTasks
+ /// Class GetScheduledTasks.
/// </summary>
[Route("/ScheduledTasks", "GET", Summary = "Gets scheduled tasks")]
public class GetScheduledTasks : IReturn<TaskInfo[]>
@@ -38,7 +38,7 @@ namespace MediaBrowser.Api.ScheduledTasks
}
/// <summary>
- /// Class StartScheduledTask
+ /// Class StartScheduledTask.
/// </summary>
[Route("/ScheduledTasks/Running/{Id}", "POST", Summary = "Starts a scheduled task")]
public class StartScheduledTask : IReturnVoid
@@ -52,7 +52,7 @@ namespace MediaBrowser.Api.ScheduledTasks
}
/// <summary>
- /// Class StopScheduledTask
+ /// Class StopScheduledTask.
/// </summary>
[Route("/ScheduledTasks/Running/{Id}", "DELETE", Summary = "Stops a scheduled task")]
public class StopScheduledTask : IReturnVoid
@@ -66,7 +66,7 @@ namespace MediaBrowser.Api.ScheduledTasks
}
/// <summary>
- /// Class UpdateScheduledTaskTriggers
+ /// Class UpdateScheduledTaskTriggers.
/// </summary>
[Route("/ScheduledTasks/{Id}/Triggers", "POST", Summary = "Updates the triggers for a scheduled task")]
public class UpdateScheduledTaskTriggers : List<TaskTriggerInfo>, IReturnVoid
@@ -80,7 +80,7 @@ namespace MediaBrowser.Api.ScheduledTasks
}
/// <summary>
- /// Class ScheduledTasksService
+ /// Class ScheduledTasksService.
/// </summary>
[Authenticated(Roles = "Admin")]
public class ScheduledTaskService : BaseApiService
diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
index 14b9b3618b..25dd39f2de 100644
--- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
+++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs
@@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.ScheduledTasks
{
/// <summary>
- /// Class ScheduledTasksWebSocketListener
+ /// Class ScheduledTasksWebSocketListener.
/// </summary>
public class ScheduledTasksWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<TaskInfo>, WebSocketListenerState>
{
diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs
index e9d339c6e3..64ee69300e 100644
--- a/MediaBrowser.Api/SearchService.cs
+++ b/MediaBrowser.Api/SearchService.cs
@@ -18,7 +18,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class GetSearchHints
+ /// Class GetSearchHints.
/// </summary>
[Route("/Search/Hints", "GET", Summary = "Gets search hints based on a search term")]
public class GetSearchHints : IReturn<SearchHintResult>
@@ -31,7 +31,7 @@ namespace MediaBrowser.Api
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
@@ -45,7 +45,7 @@ namespace MediaBrowser.Api
public Guid UserId { get; set; }
/// <summary>
- /// Search characters used to find items
+ /// Search characters used to find items.
/// </summary>
/// <value>The index by.</value>
[ApiMember(Name = "SearchTerm", Description = "The search term to filter on", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
@@ -104,13 +104,13 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class SearchService
+ /// Class SearchService.
/// </summary>
[Authenticated]
public class SearchService : BaseApiService
{
/// <summary>
- /// The _search engine
+ /// The _search engine.
/// </summary>
private readonly ISearchEngine _searchEngine;
private readonly ILibraryManager _libraryManager;
@@ -180,7 +180,6 @@ namespace MediaBrowser.Api
IsNews = request.IsNews,
IsSeries = request.IsSeries,
IsSports = request.IsSports
-
});
return new SearchHintResult
diff --git a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs
index d882aac887..2400d6defe 100644
--- a/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs
+++ b/MediaBrowser.Api/Sessions/SessionInfoWebSocketListener.cs
@@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.Sessions
{
/// <summary>
- /// Class SessionInfoWebSocketListener
+ /// Class SessionInfoWebSocketListener.
/// </summary>
public class SessionInfoWebSocketListener : BasePeriodicWebSocketListener<IEnumerable<SessionInfo>, WebSocketListenerState>
{
@@ -19,7 +19,7 @@ namespace MediaBrowser.Api.Sessions
protected override string Name => "Sessions";
/// <summary>
- /// The _kernel
+ /// The _kernel.
/// </summary>
private readonly ISessionManager _sessionManager;
@@ -31,48 +31,48 @@ namespace MediaBrowser.Api.Sessions
{
_sessionManager = sessionManager;
- _sessionManager.SessionStarted += _sessionManager_SessionStarted;
- _sessionManager.SessionEnded += _sessionManager_SessionEnded;
- _sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
- _sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped;
- _sessionManager.PlaybackProgress += _sessionManager_PlaybackProgress;
- _sessionManager.CapabilitiesChanged += _sessionManager_CapabilitiesChanged;
- _sessionManager.SessionActivity += _sessionManager_SessionActivity;
+ _sessionManager.SessionStarted += OnSessionManagerSessionStarted;
+ _sessionManager.SessionEnded += OnSessionManagerSessionEnded;
+ _sessionManager.PlaybackStart += OnSessionManagerPlaybackStart;
+ _sessionManager.PlaybackStopped += OnSessionManagerPlaybackStopped;
+ _sessionManager.PlaybackProgress += OnSessionManagerPlaybackProgress;
+ _sessionManager.CapabilitiesChanged += OnSessionManagerCapabilitiesChanged;
+ _sessionManager.SessionActivity += OnSessionManagerSessionActivity;
}
- void _sessionManager_SessionActivity(object sender, SessionEventArgs e)
+ private async void OnSessionManagerSessionActivity(object sender, SessionEventArgs e)
{
- SendData(false);
+ await SendData(false).ConfigureAwait(false);
}
- void _sessionManager_CapabilitiesChanged(object sender, SessionEventArgs e)
+ private async void OnSessionManagerCapabilitiesChanged(object sender, SessionEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
- void _sessionManager_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
+ private async void OnSessionManagerPlaybackProgress(object sender, PlaybackProgressEventArgs e)
{
- SendData(!e.IsAutomated);
+ await SendData(!e.IsAutomated).ConfigureAwait(false);
}
- void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
+ private async void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
- void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
+ private async void OnSessionManagerPlaybackStart(object sender, PlaybackProgressEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
- void _sessionManager_SessionEnded(object sender, SessionEventArgs e)
+ private async void OnSessionManagerSessionEnded(object sender, SessionEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
- void _sessionManager_SessionStarted(object sender, SessionEventArgs e)
+ private async void OnSessionManagerSessionStarted(object sender, SessionEventArgs e)
{
- SendData(true);
+ await SendData(true).ConfigureAwait(false);
}
/// <summary>
@@ -84,15 +84,16 @@ namespace MediaBrowser.Api.Sessions
return Task.FromResult(_sessionManager.Sessions);
}
+ /// <inheritdoc />
protected override void Dispose(bool dispose)
{
- _sessionManager.SessionStarted -= _sessionManager_SessionStarted;
- _sessionManager.SessionEnded -= _sessionManager_SessionEnded;
- _sessionManager.PlaybackStart -= _sessionManager_PlaybackStart;
- _sessionManager.PlaybackStopped -= _sessionManager_PlaybackStopped;
- _sessionManager.PlaybackProgress -= _sessionManager_PlaybackProgress;
- _sessionManager.CapabilitiesChanged -= _sessionManager_CapabilitiesChanged;
- _sessionManager.SessionActivity -= _sessionManager_SessionActivity;
+ _sessionManager.SessionStarted -= OnSessionManagerSessionStarted;
+ _sessionManager.SessionEnded -= OnSessionManagerSessionEnded;
+ _sessionManager.PlaybackStart -= OnSessionManagerPlaybackStart;
+ _sessionManager.PlaybackStopped -= OnSessionManagerPlaybackStopped;
+ _sessionManager.PlaybackProgress -= OnSessionManagerPlaybackProgress;
+ _sessionManager.CapabilitiesChanged -= OnSessionManagerCapabilitiesChanged;
+ _sessionManager.SessionActivity -= OnSessionManagerSessionActivity;
base.Dispose(dispose);
}
diff --git a/MediaBrowser.Api/Sessions/SessionService.cs b/MediaBrowser.Api/Sessions/SessionService.cs
index 020bb5042b..50adc56980 100644
--- a/MediaBrowser.Api/Sessions/SessionService.cs
+++ b/MediaBrowser.Api/Sessions/SessionService.cs
@@ -2,6 +2,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library;
@@ -45,14 +46,14 @@ namespace MediaBrowser.Api.Sessions
public string Id { get; set; }
/// <summary>
- /// Artist, Genre, Studio, Person, or any kind of BaseItem
+ /// Artist, Genre, Studio, Person, or any kind of BaseItem.
/// </summary>
/// <value>The type of the item.</value>
[ApiMember(Name = "ItemType", Description = "The type of item to browse to.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
public string ItemType { get; set; }
/// <summary>
- /// Artist name, genre name, item Id, etc
+ /// Artist name, genre name, item Id, etc.
/// </summary>
/// <value>The item identifier.</value>
[ApiMember(Name = "ItemId", Description = "The Id of the item.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
@@ -326,12 +327,12 @@ namespace MediaBrowser.Api.Sessions
var user = _userManager.GetUserById(request.ControllableByUserId);
- if (!user.Policy.EnableRemoteControlOfOtherUsers)
+ if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
{
result = result.Where(i => i.UserId.Equals(Guid.Empty) || i.ContainsUser(request.ControllableByUserId));
}
- if (!user.Policy.EnableSharedDeviceControl)
+ if (!user.HasPermission(PermissionKind.EnableSharedDeviceControl))
{
result = result.Where(i => !i.UserId.Equals(Guid.Empty));
}
diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs
index 44bb24ef2d..84abf7b8d5 100644
--- a/MediaBrowser.Api/SimilarItemsHelper.cs
+++ b/MediaBrowser.Api/SimilarItemsHelper.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class BaseGetSimilarItemsFromItem
+ /// Class BaseGetSimilarItemsFromItem.
/// </summary>
public class BaseGetSimilarItemsFromItem : BaseGetSimilarItems
{
@@ -50,14 +50,14 @@ namespace MediaBrowser.Api
public Guid UserId { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[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; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -65,11 +65,11 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class SimilarItemsHelper
+ /// Class SimilarItemsHelper.
/// </summary>
public static class SimilarItemsHelper
{
- internal static QueryResult<BaseItemDto> GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, ILogger logger, BaseGetSimilarItemsFromItem request, Type[] includeTypes, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
+ internal static QueryResult<BaseItemDto> GetSimilarItemsResult(DtoOptions dtoOptions, IUserManager userManager, IItemRepository itemRepository, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, BaseGetSimilarItemsFromItem request, Type[] includeTypes, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
{
var user = !request.UserId.Equals(Guid.Empty) ? userManager.GetUserById(request.UserId) : null;
@@ -179,18 +179,22 @@ namespace MediaBrowser.Api
{
return 5;
}
+
if (string.Equals(i.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Actor, StringComparison.OrdinalIgnoreCase))
{
return 3;
}
+
if (string.Equals(i.Type, PersonType.Composer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Composer, StringComparison.OrdinalIgnoreCase))
{
return 3;
}
+
if (string.Equals(i.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))
{
return 3;
}
+
if (string.Equals(i.Type, PersonType.Writer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase))
{
return 2;
@@ -218,6 +222,5 @@ namespace MediaBrowser.Api
return points;
}
-
}
}
diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs
index f2968c6b5c..a70da8e56c 100644
--- a/MediaBrowser.Api/Subtitles/SubtitleService.cs
+++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs
@@ -97,6 +97,7 @@ namespace MediaBrowser.Api.Subtitles
[ApiMember(Name = "CopyTimestamps", Description = "CopyTimestamps", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool CopyTimestamps { get; set; }
+
public bool AddVttTimeMap { get; set; }
}
@@ -214,6 +215,7 @@ namespace MediaBrowser.Api.Subtitles
{
request.Format = "json";
}
+
if (string.IsNullOrEmpty(request.Format))
{
var item = (Video)_libraryManager.GetItemById(request.Id);
diff --git a/MediaBrowser.Api/SuggestionsService.cs b/MediaBrowser.Api/SuggestionsService.cs
index 91f85db6ff..17afa8e79c 100644
--- a/MediaBrowser.Api/SuggestionsService.cs
+++ b/MediaBrowser.Api/SuggestionsService.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -17,10 +18,15 @@ namespace MediaBrowser.Api
public class GetSuggestedItems : IReturn<QueryResult<BaseItemDto>>
{
public string MediaType { get; set; }
+
public string Type { get; set; }
+
public Guid UserId { get; set; }
+
public bool EnableTotalRecordCount { get; set; }
+
public int? StartIndex { get; set; }
+
public int? Limit { get; set; }
public string[] GetMediaTypes()
diff --git a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs
new file mode 100644
index 0000000000..1e14ea552c
--- /dev/null
+++ b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs
@@ -0,0 +1,302 @@
+using System.Threading;
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Session;
+using MediaBrowser.Controller.SyncPlay;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.SyncPlay;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.Api.SyncPlay
+{
+ [Route("/SyncPlay/{SessionId}/NewGroup", "POST", Summary = "Create a new SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayNewGroup : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/JoinGroup", "POST", Summary = "Join an existing SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayJoinGroup : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Group id.
+ /// </summary>
+ /// <value>The Group id to join.</value>
+ [ApiMember(Name = "GroupId", Description = "Group Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string GroupId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the playing item id.
+ /// </summary>
+ /// <value>The client's currently playing item id.</value>
+ [ApiMember(Name = "PlayingItemId", Description = "Client's playing item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string PlayingItemId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/LeaveGroup", "POST", Summary = "Leave joined SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayLeaveGroup : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/ListGroups", "POST", Summary = "List SyncPlay groups")]
+ [Authenticated]
+ public class SyncPlayListGroups : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the filter item id.
+ /// </summary>
+ /// <value>The filter item id.</value>
+ [ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string FilterItemId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/PlayRequest", "POST", Summary = "Request play in SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayPlayRequest : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/PauseRequest", "POST", Summary = "Request pause in SyncPlay group")]
+ [Authenticated]
+ public class SyncPlayPauseRequest : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/SeekRequest", "POST", Summary = "Request seek in SyncPlay group")]
+ [Authenticated]
+ public class SyncPlaySeekRequest : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ [ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")]
+ public long PositionTicks { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/BufferingRequest", "POST", Summary = "Request group wait in SyncPlay group while buffering")]
+ [Authenticated]
+ public class SyncPlayBufferingRequest : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the date used to pin PositionTicks in time.
+ /// </summary>
+ /// <value>The date related to PositionTicks.</value>
+ [ApiMember(Name = "When", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
+ public string When { get; set; }
+
+ [ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")]
+ public long PositionTicks { get; set; }
+
+ /// <summary>
+ /// Gets or sets whether this is a buffering or a buffering-done request.
+ /// </summary>
+ /// <value><c>true</c> if buffering is complete; <c>false</c> otherwise.</value>
+ [ApiMember(Name = "BufferingDone", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")]
+ public bool BufferingDone { get; set; }
+ }
+
+ [Route("/SyncPlay/{SessionId}/UpdatePing", "POST", Summary = "Update session ping")]
+ [Authenticated]
+ public class SyncPlayUpdatePing : IReturnVoid
+ {
+ [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string SessionId { get; set; }
+
+ [ApiMember(Name = "Ping", IsRequired = true, DataType = "double", ParameterType = "query", Verb = "POST")]
+ public double Ping { get; set; }
+ }
+
+ /// <summary>
+ /// Class SyncPlayService.
+ /// </summary>
+ public class SyncPlayService : BaseApiService
+ {
+ /// <summary>
+ /// The session context.
+ /// </summary>
+ private readonly ISessionContext _sessionContext;
+
+ /// <summary>
+ /// The SyncPlay manager.
+ /// </summary>
+ private readonly ISyncPlayManager _syncPlayManager;
+
+ public SyncPlayService(
+ ILogger<SyncPlayService> logger,
+ IServerConfigurationManager serverConfigurationManager,
+ IHttpResultFactory httpResultFactory,
+ ISessionContext sessionContext,
+ ISyncPlayManager syncPlayManager)
+ : base(logger, serverConfigurationManager, httpResultFactory)
+ {
+ _sessionContext = sessionContext;
+ _syncPlayManager = syncPlayManager;
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayNewGroup request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ _syncPlayManager.NewGroup(currentSession, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayJoinGroup request)
+ {
+ var currentSession = GetSession(_sessionContext);
+
+ Guid groupId;
+ Guid playingItemId = Guid.Empty;
+
+ if (!Guid.TryParse(request.GroupId, out groupId))
+ {
+ Logger.LogError("JoinGroup: {0} is not a valid format for GroupId. Ignoring request.", request.GroupId);
+ return;
+ }
+
+ // Both null and empty strings mean that client isn't playing anything
+ if (!string.IsNullOrEmpty(request.PlayingItemId) && !Guid.TryParse(request.PlayingItemId, out playingItemId))
+ {
+ Logger.LogError("JoinGroup: {0} is not a valid format for PlayingItemId. Ignoring request.", request.PlayingItemId);
+ return;
+ }
+
+ var joinRequest = new JoinGroupRequest()
+ {
+ GroupId = groupId,
+ PlayingItemId = playingItemId
+ };
+
+ _syncPlayManager.JoinGroup(currentSession, groupId, joinRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayLeaveGroup request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ _syncPlayManager.LeaveGroup(currentSession, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <value>The requested list of groups.</value>
+ public List<GroupInfoView> Post(SyncPlayListGroups request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var filterItemId = Guid.Empty;
+
+ if (!string.IsNullOrEmpty(request.FilterItemId) && !Guid.TryParse(request.FilterItemId, out filterItemId))
+ {
+ Logger.LogWarning("ListGroups: {0} is not a valid format for FilterItemId. Ignoring filter.", request.FilterItemId);
+ }
+
+ return _syncPlayManager.ListGroups(currentSession, filterItemId);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayPlayRequest request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = PlaybackRequestType.Play
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayPauseRequest request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = PlaybackRequestType.Pause
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlaySeekRequest request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = PlaybackRequestType.Seek,
+ PositionTicks = request.PositionTicks
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayBufferingRequest request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = request.BufferingDone ? PlaybackRequestType.BufferingDone : PlaybackRequestType.Buffering,
+ When = DateTime.Parse(request.When),
+ PositionTicks = request.PositionTicks
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public void Post(SyncPlayUpdatePing request)
+ {
+ var currentSession = GetSession(_sessionContext);
+ var syncPlayRequest = new PlaybackRequest()
+ {
+ Type = PlaybackRequestType.UpdatePing,
+ Ping = Convert.ToInt64(request.Ping)
+ };
+ _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None);
+ }
+ }
+}
diff --git a/MediaBrowser.Api/SyncPlay/TimeSyncService.cs b/MediaBrowser.Api/SyncPlay/TimeSyncService.cs
new file mode 100644
index 0000000000..4a9307e62f
--- /dev/null
+++ b/MediaBrowser.Api/SyncPlay/TimeSyncService.cs
@@ -0,0 +1,52 @@
+using System;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Services;
+using MediaBrowser.Model.SyncPlay;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.Api.SyncPlay
+{
+ [Route("/GetUtcTime", "GET", Summary = "Get UtcTime")]
+ public class GetUtcTime : IReturnVoid
+ {
+ // Nothing
+ }
+
+ /// <summary>
+ /// Class TimeSyncService.
+ /// </summary>
+ public class TimeSyncService : BaseApiService
+ {
+ public TimeSyncService(
+ ILogger<TimeSyncService> logger,
+ IServerConfigurationManager serverConfigurationManager,
+ IHttpResultFactory httpResultFactory)
+ : base(logger, serverConfigurationManager, httpResultFactory)
+ {
+ // Do nothing
+ }
+
+ /// <summary>
+ /// Handles the specified request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <value>The current UTC time response.</value>
+ public UtcTimeResponse Get(GetUtcTime request)
+ {
+ // Important to keep the following line at the beginning
+ var requestReceptionTime = DateTime.UtcNow.ToUniversalTime().ToString("o");
+
+ var response = new UtcTimeResponse();
+ response.RequestReceptionTime = requestReceptionTime;
+
+ // Important to keep the following two lines at the end
+ var responseTransmissionTime = DateTime.UtcNow.ToUniversalTime().ToString("o");
+ response.ResponseTransmissionTime = responseTransmissionTime;
+
+ // Implementing NTP on such a high level results in this useless
+ // information being sent. On the other hand it enables future additions.
+ return response;
+ }
+ }
+}
diff --git a/MediaBrowser.Api/System/ActivityLogService.cs b/MediaBrowser.Api/System/ActivityLogService.cs
index f95fa7ca0b..7ca31c21ab 100644
--- a/MediaBrowser.Api/System/ActivityLogService.cs
+++ b/MediaBrowser.Api/System/ActivityLogService.cs
@@ -1,5 +1,7 @@
using System;
using System.Globalization;
+using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
@@ -20,7 +22,7 @@ namespace MediaBrowser.Api.System
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
@@ -53,7 +55,10 @@ namespace MediaBrowser.Api.System
(DateTime?)null :
DateTime.Parse(request.MinDate, null, DateTimeStyles.RoundtripKind).ToUniversalTime();
- var result = _activityManager.GetActivityLogEntries(minDate, request.HasUserId, request.StartIndex, request.Limit);
+ var filterFunc = new Func<IQueryable<ActivityLog>, IQueryable<ActivityLog>>(
+ entries => entries.Where(entry => entry.DateCreated >= minDate));
+
+ var result = _activityManager.GetPagedResult(filterFunc, request.StartIndex, request.Limit);
return ToOptimizedResult(result);
}
diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
index f8b6ee65d6..39976371a9 100644
--- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
+++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System;
using System.Threading.Tasks;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity;
@@ -8,9 +8,9 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.System
{
/// <summary>
- /// Class SessionInfoWebSocketListener
+ /// Class SessionInfoWebSocketListener.
/// </summary>
- public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<List<ActivityLogEntry>, WebSocketListenerState>
+ public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<ActivityLogEntry[], WebSocketListenerState>
{
/// <summary>
/// Gets the name.
@@ -19,17 +19,17 @@ namespace MediaBrowser.Api.System
protected override string Name => "ActivityLogEntry";
/// <summary>
- /// The _kernel
+ /// The _kernel.
/// </summary>
private readonly IActivityManager _activityManager;
public ActivityLogWebSocketListener(ILogger<ActivityLogWebSocketListener> logger, IActivityManager activityManager) : base(logger)
{
_activityManager = activityManager;
- _activityManager.EntryCreated += _activityManager_EntryCreated;
+ _activityManager.EntryCreated += OnEntryCreated;
}
- void _activityManager_EntryCreated(object sender, GenericEventArgs<ActivityLogEntry> e)
+ private void OnEntryCreated(object sender, GenericEventArgs<ActivityLogEntry> e)
{
SendData(true);
}
@@ -38,15 +38,15 @@ namespace MediaBrowser.Api.System
/// Gets the data to send.
/// </summary>
/// <returns>Task{SystemInfo}.</returns>
- protected override Task<List<ActivityLogEntry>> GetDataToSend()
+ protected override Task<ActivityLogEntry[]> GetDataToSend()
{
- return Task.FromResult(new List<ActivityLogEntry>());
+ return Task.FromResult(Array.Empty<ActivityLogEntry>());
}
-
+ /// <inheritdoc />
protected override void Dispose(bool dispose)
{
- _activityManager.EntryCreated -= _activityManager_EntryCreated;
+ _activityManager.EntryCreated -= OnEntryCreated;
base.Dispose(dispose);
}
diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs
index c57cc93d55..e0e20d828e 100644
--- a/MediaBrowser.Api/System/SystemService.cs
+++ b/MediaBrowser.Api/System/SystemService.cs
@@ -18,30 +18,27 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.System
{
/// <summary>
- /// Class GetSystemInfo
+ /// Class GetSystemInfo.
/// </summary>
[Route("/System/Info", "GET", Summary = "Gets information about the server")]
[Authenticated(EscapeParentalControl = true, AllowBeforeStartupWizard = true)]
public class GetSystemInfo : IReturn<SystemInfo>
{
-
}
[Route("/System/Info/Public", "GET", Summary = "Gets public information about the server")]
public class GetPublicSystemInfo : IReturn<PublicSystemInfo>
{
-
}
[Route("/System/Ping", "POST")]
[Route("/System/Ping", "GET")]
public class PingSystem : IReturnVoid
{
-
}
/// <summary>
- /// Class RestartApplication
+ /// Class RestartApplication.
/// </summary>
[Route("/System/Restart", "POST", Summary = "Restarts the application, if needed")]
[Authenticated(Roles = "Admin", AllowLocal = true)]
@@ -83,16 +80,15 @@ namespace MediaBrowser.Api.System
[Authenticated]
public class GetWakeOnLanInfo : IReturn<WakeOnLanInfo[]>
{
-
}
/// <summary>
- /// Class SystemInfoService
+ /// Class SystemInfoService.
/// </summary>
public class SystemService : BaseApiService
{
/// <summary>
- /// The _app host
+ /// The _app host.
/// </summary>
private readonly IServerApplicationHost _appHost;
private readonly IApplicationPaths _appPaths;
@@ -153,7 +149,6 @@ namespace MediaBrowser.Api.System
DateModified = _fileSystem.GetLastWriteTimeUtc(i),
Name = i.Name,
Size = i.Length
-
}).OrderByDescending(i => i.DateModified)
.ThenByDescending(i => i.DateCreated)
.ThenBy(i => i.Name)
diff --git a/MediaBrowser.Api/TranscodingJob.cs b/MediaBrowser.Api/TranscodingJob.cs
index 8c24e3ce18..bfc311a272 100644
--- a/MediaBrowser.Api/TranscodingJob.cs
+++ b/MediaBrowser.Api/TranscodingJob.cs
@@ -32,6 +32,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The path.</value>
public MediaSourceInfo MediaSource { get; set; }
+
public string Path { get; set; }
/// <summary>
/// Gets or sets the type.
@@ -43,6 +44,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The process.</value>
public Process Process { get; set; }
+
public ILogger Logger { get; private set; }
/// <summary>
/// Gets or sets the active request count.
@@ -62,18 +64,23 @@ namespace MediaBrowser.Api
public object ProcessLock = new object();
public bool HasExited { get; set; }
+
public bool IsUserPaused { get; set; }
public string Id { get; set; }
public float? Framerate { get; set; }
+
public double? CompletionPercentage { get; set; }
public long? BytesDownloaded { get; set; }
+
public long? BytesTranscoded { get; set; }
+
public int? BitRate { get; set; }
public long? TranscodingPositionTicks { get; set; }
+
public long? DownloadPositionTicks { get; set; }
public TranscodingThrottler TranscodingThrottler { get; set; }
@@ -81,6 +88,7 @@ namespace MediaBrowser.Api
private readonly object _timerLock = new object();
public DateTime LastPingDate { get; set; }
+
public int PingTimeout { get; set; }
public TranscodingJob(ILogger logger)
diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs
index cd8e8dfbe4..165abd613d 100644
--- a/MediaBrowser.Api/TvShowsService.cs
+++ b/MediaBrowser.Api/TvShowsService.cs
@@ -19,7 +19,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class GetNextUpEpisodes
+ /// Class GetNextUpEpisodes.
/// </summary>
[Route("/Shows/NextUp", "GET", Summary = "Gets a list of next up episodes")]
public class GetNextUpEpisodes : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
@@ -39,14 +39,14 @@ namespace MediaBrowser.Api
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[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; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -73,6 +73,7 @@ namespace MediaBrowser.Api
[ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableUserData { get; set; }
+
public bool EnableTotalRecordCount { get; set; }
public GetNextUpEpisodes()
@@ -99,14 +100,14 @@ namespace MediaBrowser.Api
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[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; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -143,7 +144,7 @@ namespace MediaBrowser.Api
public Guid UserId { get; set; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -175,7 +176,7 @@ namespace MediaBrowser.Api
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
@@ -211,7 +212,7 @@ namespace MediaBrowser.Api
public Guid UserId { get; set; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -243,18 +244,18 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class TvShowsService
+ /// Class TvShowsService.
/// </summary>
[Authenticated]
public class TvShowsService : BaseApiService
{
/// <summary>
- /// The _user manager
+ /// The _user manager.
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
- /// The _library manager
+ /// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
@@ -306,7 +307,6 @@ namespace MediaBrowser.Api
ParentId = parentIdGuid,
Recursive = true,
DtoOptions = options
-
});
var returnItems = _dtoService.GetBaseItemDtos(itemsResult, options, user);
@@ -378,7 +378,7 @@ namespace MediaBrowser.Api
{
var user = _userManager.GetUserById(request.UserId);
- var series = GetSeries(request.Id, user);
+ var series = GetSeries(request.Id);
if (series == null)
{
@@ -390,7 +390,6 @@ namespace MediaBrowser.Api
IsMissing = request.IsMissing,
IsSpecialSeason = request.IsSpecialSeason,
AdjacentTo = request.AdjacentTo
-
});
var dtoOptions = GetDtoOptions(_authContext, request);
@@ -404,7 +403,7 @@ namespace MediaBrowser.Api
};
}
- private Series GetSeries(string seriesId, User user)
+ private Series GetSeries(string seriesId)
{
if (!string.IsNullOrWhiteSpace(seriesId))
{
@@ -433,7 +432,7 @@ namespace MediaBrowser.Api
}
else if (request.Season.HasValue)
{
- var series = GetSeries(request.Id, user);
+ var series = GetSeries(request.Id);
if (series == null)
{
@@ -446,7 +445,7 @@ namespace MediaBrowser.Api
}
else
{
- var series = GetSeries(request.Id, user);
+ var series = GetSeries(request.Id);
if (series == null)
{
diff --git a/MediaBrowser.Api/UserLibrary/ArtistsService.cs b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
index 3d08d5437c..9875e02084 100644
--- a/MediaBrowser.Api/UserLibrary/ArtistsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ArtistsService.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.UserLibrary
{
/// <summary>
- /// Class GetArtists
+ /// Class GetArtists.
/// </summary>
[Route("/Artists", "GET", Summary = "Gets all artists from a given item, folder, or the entire library")]
public class GetArtists : GetItemsByName
@@ -45,13 +45,13 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class ArtistsService
+ /// Class ArtistsService.
/// </summary>
[Authenticated]
public class ArtistsService : BaseItemsByNameService<MusicArtist>
{
public ArtistsService(
- ILogger<GenresService> logger,
+ ILogger<ArtistsService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index c4a52d5f52..fd639caf11 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
@@ -14,7 +15,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.UserLibrary
{
/// <summary>
- /// Class BaseItemsByNameService
+ /// Class BaseItemsByNameService.
/// </summary>
/// <typeparam name="TItemType">The type of the T item type.</typeparam>
public abstract class BaseItemsByNameService<TItemType> : BaseApiService
@@ -28,7 +29,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="userDataRepository">The user data repository.</param>
/// <param name="dtoService">The dto service.</param>
protected BaseItemsByNameService(
- ILogger logger,
+ ILogger<BaseItemsByNameService<TItemType>> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
@@ -51,7 +52,7 @@ namespace MediaBrowser.Api.UserLibrary
protected IUserManager UserManager { get; }
/// <summary>
- /// Gets the library manager
+ /// Gets the library manager.
/// </summary>
protected ILibraryManager LibraryManager { get; }
@@ -209,6 +210,7 @@ namespace MediaBrowser.Api.UserLibrary
{
SetItemCounts(dto, i.Item2);
}
+
return dto;
});
@@ -321,7 +323,6 @@ namespace MediaBrowser.Api.UserLibrary
{
ibnItems = ibnItems.Take(request.Limit.Value);
}
-
}
var tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>()));
@@ -375,7 +376,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GetItemsByName
+ /// Class GetItemsByName.
/// </summary>
public class GetItemsByName : BaseItemsRequest, IReturn<QueryResult<BaseItemDto>>
{
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
index 7561b5c892..344861a496 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
@@ -111,14 +111,14 @@ namespace MediaBrowser.Api.UserLibrary
public int? StartIndex { get; set; }
/// <summary>
- /// The maximum number of items to return
+ /// The maximum number of items to return.
/// </summary>
/// <value>The limit.</value>
[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; }
/// <summary>
- /// Whether or not to perform the query recursively
+ /// Whether or not to perform the query recursively.
/// </summary>
/// <value><c>true</c> if recursive; otherwise, <c>false</c>.</value>
[ApiMember(Name = "Recursive", Description = "When searching within folders, this determines whether or not the search will be recursive. true/false", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
@@ -141,7 +141,7 @@ namespace MediaBrowser.Api.UserLibrary
public string ParentId { get; set; }
/// <summary>
- /// Fields to return within the items, in addition to basic information
+ /// Fields to return within the items, in addition to basic information.
/// </summary>
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -162,14 +162,14 @@ namespace MediaBrowser.Api.UserLibrary
public string IncludeItemTypes { get; set; }
/// <summary>
- /// Filters to apply to the results
+ /// Filters to apply to the results.
/// </summary>
/// <value>The filters.</value>
[ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Filters { get; set; }
/// <summary>
- /// Gets or sets the Isfavorite option
+ /// Gets or sets the Isfavorite option.
/// </summary>
/// <value>IsFavorite</value>
[ApiMember(Name = "IsFavorite", Description = "Optional filter by items that are marked as favorite, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
@@ -190,7 +190,7 @@ namespace MediaBrowser.Api.UserLibrary
public string ImageTypes { get; set; }
/// <summary>
- /// What to sort the results by
+ /// What to sort the results by.
/// </summary>
/// <value>The sort by.</value>
[ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -200,7 +200,7 @@ namespace MediaBrowser.Api.UserLibrary
public bool? IsPlayed { get; set; }
/// <summary>
- /// Limit results to items containing specific genres
+ /// Limit results to items containing specific genres.
/// </summary>
/// <value>The genres.</value>
[ApiMember(Name = "Genres", Description = "Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -215,7 +215,7 @@ namespace MediaBrowser.Api.UserLibrary
public string Tags { get; set; }
/// <summary>
- /// Limit results to items containing specific years
+ /// Limit results to items containing specific years.
/// </summary>
/// <value>The years.</value>
[ApiMember(Name = "Years", Description = "Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -234,7 +234,7 @@ namespace MediaBrowser.Api.UserLibrary
public string EnableImageTypes { get; set; }
/// <summary>
- /// Limit results to items containing a specific person
+ /// Limit results to items containing a specific person.
/// </summary>
/// <value>The person.</value>
[ApiMember(Name = "Person", Description = "Optional. If specified, results will be filtered to include only those containing the specified person.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
@@ -244,14 +244,14 @@ namespace MediaBrowser.Api.UserLibrary
public string PersonIds { get; set; }
/// <summary>
- /// If the Person filter is used, this can also be used to restrict to a specific person type
+ /// If the Person filter is used, this can also be used to restrict to a specific person type.
/// </summary>
/// <value>The type of the person.</value>
[ApiMember(Name = "PersonTypes", Description = "Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string PersonTypes { get; set; }
/// <summary>
- /// Limit results to items containing specific studios
+ /// Limit results to items containing specific studios.
/// </summary>
/// <value>The studios.</value>
[ApiMember(Name = "Studios", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
@@ -322,8 +322,11 @@ namespace MediaBrowser.Api.UserLibrary
public bool? CollapseBoxSetItems { get; set; }
public int? MinWidth { get; set; }
+
public int? MinHeight { get; set; }
+
public int? MaxWidth { get; set; }
+
public int? MaxHeight { get; set; }
/// <summary>
diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs
index 1fa272a5f7..7bdfbac981 100644
--- a/MediaBrowser.Api/UserLibrary/GenresService.cs
+++ b/MediaBrowser.Api/UserLibrary/GenresService.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.UserLibrary
{
/// <summary>
- /// Class GetGenres
+ /// Class GetGenres.
/// </summary>
[Route("/Genres", "GET", Summary = "Gets all genres from a given item, folder, or the entire library")]
public class GetGenres : GetItemsByName
@@ -22,7 +22,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GetGenre
+ /// Class GetGenre.
/// </summary>
[Route("/Genres/{Name}", "GET", Summary = "Gets a genre, by name")]
public class GetGenre : IReturn<BaseItemDto>
@@ -43,7 +43,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GenresService
+ /// Class GenresService.
/// </summary>
[Authenticated]
public class GenresService : BaseItemsByNameService<Genre>
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index c4d44042b1..7efe0552c6 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -2,10 +2,11 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dto;
@@ -14,11 +15,12 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
+using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
namespace MediaBrowser.Api.UserLibrary
{
/// <summary>
- /// Class GetItems
+ /// Class GetItems.
/// </summary>
[Route("/Items", "GET", Summary = "Gets items based on a query.")]
[Route("/Users/{UserId}/Items", "GET", Summary = "Gets items based on a query.")]
@@ -32,18 +34,18 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class ItemsService
+ /// Class ItemsService.
/// </summary>
[Authenticated]
public class ItemsService : BaseApiService
{
/// <summary>
- /// The _user manager
+ /// The _user manager.
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
- /// The _library manager
+ /// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
@@ -59,7 +61,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="localization">The localization.</param>
/// <param name="dtoService">The dto service.</param>
public ItemsService(
- ILogger logger,
+ ILogger<ItemsService> logger,
IServerConfigurationManager serverConfigurationManager,
IHttpResultFactory httpResultFactory,
IUserManager userManager,
@@ -86,7 +88,7 @@ namespace MediaBrowser.Api.UserLibrary
var ancestorIds = Array.Empty<Guid>();
- var excludeFolderIds = user.Configuration.LatestItemsExcludes;
+ var excludeFolderIds = user.GetPreference(PreferenceKind.LatestItemExcludes);
if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0)
{
ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true)
@@ -211,14 +213,14 @@ namespace MediaBrowser.Api.UserLibrary
request.IncludeItemTypes = "Playlist";
}
- bool isInEnabledFolder = user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id)
+ bool isInEnabledFolder = user.GetPreference(PreferenceKind.EnabledFolders).Any(i => new Guid(i) == item.Id)
// Assume all folders inside an EnabledChannel are enabled
- || user.Policy.EnabledChannels.Any(i => new Guid(i) == item.Id);
+ || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id);
var collectionFolders = _libraryManager.GetCollectionFolders(item);
foreach (var collectionFolder in collectionFolders)
{
- if (user.Policy.EnabledFolders.Contains(
+ if (user.GetPreference(PreferenceKind.EnabledFolders).Contains(
collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture),
StringComparer.OrdinalIgnoreCase))
{
@@ -226,9 +228,12 @@ namespace MediaBrowser.Api.UserLibrary
}
}
- if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !isInEnabledFolder && !user.Policy.EnableAllChannels)
+ if (!(item is UserRootFolder)
+ && !isInEnabledFolder
+ && !user.HasPermission(PermissionKind.EnableAllFolders)
+ && !user.HasPermission(PermissionKind.EnableAllChannels))
{
- Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name);
+ Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Username, item.Name);
return new QueryResult<BaseItem>
{
Items = Array.Empty<BaseItem>(),
@@ -491,7 +496,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class DateCreatedComparer
+ /// Class DateCreatedComparer.
/// </summary>
public class DateCreatedComparer : IComparer<BaseItem>
{
diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs
index 3204e5219f..7924339ed3 100644
--- a/MediaBrowser.Api/UserLibrary/PersonsService.cs
+++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.UserLibrary
{
/// <summary>
- /// Class GetPersons
+ /// Class GetPersons.
/// </summary>
[Route("/Persons", "GET", Summary = "Gets all persons from a given item, folder, or the entire library")]
public class GetPersons : GetItemsByName
@@ -22,7 +22,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GetPerson
+ /// Class GetPerson.
/// </summary>
[Route("/Persons/{Name}", "GET", Summary = "Gets a person, by name")]
public class GetPerson : IReturn<BaseItemDto>
@@ -43,7 +43,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class PersonsService
+ /// Class PersonsService.
/// </summary>
[Authenticated]
public class PersonsService : BaseItemsByNameService<Person>
diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
index d0faca163b..d809cc2e79 100644
--- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs
+++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs
@@ -1,8 +1,8 @@
using System;
using System.Globalization;
using System.Threading.Tasks;
+using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
@@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.UserLibrary
{
/// <summary>
- /// Class MarkPlayedItem
+ /// Class MarkPlayedItem.
/// </summary>
[Route("/Users/{UserId}/PlayedItems/{Id}", "POST", Summary = "Marks an item as played")]
public class MarkPlayedItem : IReturn<UserItemDataDto>
@@ -38,7 +38,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class MarkUnplayedItem
+ /// Class MarkUnplayedItem.
/// </summary>
[Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE", Summary = "Marks an item as unplayed")]
public class MarkUnplayedItem : IReturn<UserItemDataDto>
@@ -81,7 +81,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class OnPlaybackStart
+ /// Class OnPlaybackStart.
/// </summary>
[Route("/Users/{UserId}/PlayingItems/{Id}", "POST", Summary = "Reports that a user has begun playing an item")]
public class OnPlaybackStart : IReturnVoid
@@ -123,7 +123,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class OnPlaybackProgress
+ /// Class OnPlaybackProgress.
/// </summary>
[Route("/Users/{UserId}/PlayingItems/{Id}/Progress", "POST", Summary = "Reports a user's playback progress")]
public class OnPlaybackProgress : IReturnVoid
@@ -181,7 +181,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class OnPlaybackStopped
+ /// Class OnPlaybackStopped.
/// </summary>
[Route("/Users/{UserId}/PlayingItems/{Id}", "DELETE", Summary = "Reports that a user has stopped playing an item")]
public class OnPlaybackStopped : IReturnVoid
diff --git a/MediaBrowser.Api/UserLibrary/StudiosService.cs b/MediaBrowser.Api/UserLibrary/StudiosService.cs
index 683ce5d09d..66350955f5 100644
--- a/MediaBrowser.Api/UserLibrary/StudiosService.cs
+++ b/MediaBrowser.Api/UserLibrary/StudiosService.cs
@@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.UserLibrary
{
/// <summary>
- /// Class GetStudios
+ /// Class GetStudios.
/// </summary>
[Route("/Studios", "GET", Summary = "Gets all studios from a given item, folder, or the entire library")]
public class GetStudios : GetItemsByName
@@ -21,7 +21,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GetStudio
+ /// Class GetStudio.
/// </summary>
[Route("/Studios/{Name}", "GET", Summary = "Gets a studio, by name")]
public class GetStudio : IReturn<BaseItemDto>
@@ -42,7 +42,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class StudiosService
+ /// Class StudiosService.
/// </summary>
[Authenticated]
public class StudiosService : BaseItemsByNameService<Studio>
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 7fa750adba..f9cbba4104 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -20,7 +20,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.UserLibrary
{
/// <summary>
- /// Class GetItem
+ /// Class GetItem.
/// </summary>
[Route("/Users/{UserId}/Items/{Id}", "GET", Summary = "Gets an item from a user's library")]
public class GetItem : IReturn<BaseItemDto>
@@ -41,7 +41,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GetItem
+ /// Class GetItem.
/// </summary>
[Route("/Users/{UserId}/Items/Root", "GET", Summary = "Gets the root folder from a user's library")]
public class GetRootFolder : IReturn<BaseItemDto>
@@ -55,7 +55,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GetIntros
+ /// Class GetIntros.
/// </summary>
[Route("/Users/{UserId}/Items/{Id}/Intros", "GET", Summary = "Gets intros to play before the main media item plays")]
public class GetIntros : IReturn<QueryResult<BaseItemDto>>
@@ -76,7 +76,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class MarkFavoriteItem
+ /// Class MarkFavoriteItem.
/// </summary>
[Route("/Users/{UserId}/FavoriteItems/{Id}", "POST", Summary = "Marks an item as a favorite")]
public class MarkFavoriteItem : IReturn<UserItemDataDto>
@@ -97,7 +97,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class UnmarkFavoriteItem
+ /// Class UnmarkFavoriteItem.
/// </summary>
[Route("/Users/{UserId}/FavoriteItems/{Id}", "DELETE", Summary = "Unmarks an item as a favorite")]
public class UnmarkFavoriteItem : IReturn<UserItemDataDto>
@@ -118,7 +118,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class ClearUserItemRating
+ /// Class ClearUserItemRating.
/// </summary>
[Route("/Users/{UserId}/Items/{Id}/Rating", "DELETE", Summary = "Deletes a user's saved personal rating for an item")]
public class DeleteUserItemRating : IReturn<UserItemDataDto>
@@ -139,7 +139,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class UpdateUserItemRating
+ /// Class UpdateUserItemRating.
/// </summary>
[Route("/Users/{UserId}/Items/{Id}/Rating", "POST", Summary = "Updates a user's rating for an item")]
public class UpdateUserItemRating : IReturn<UserItemDataDto>
@@ -167,7 +167,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GetLocalTrailers
+ /// Class GetLocalTrailers.
/// </summary>
[Route("/Users/{UserId}/Items/{Id}/LocalTrailers", "GET", Summary = "Gets local trailers for an item")]
public class GetLocalTrailers : IReturn<BaseItemDto[]>
@@ -188,7 +188,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GetSpecialFeatures
+ /// Class GetSpecialFeatures.
/// </summary>
[Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET", Summary = "Gets special features for an item")]
public class GetSpecialFeatures : IReturn<BaseItemDto[]>
@@ -259,7 +259,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class UserLibraryService
+ /// Class UserLibraryService.
/// </summary>
[Authenticated]
public class UserLibraryService : BaseApiService
@@ -312,7 +312,7 @@ namespace MediaBrowser.Api.UserLibrary
if (!request.IsPlayed.HasValue)
{
- if (user.Configuration.HidePlayedInLatest)
+ if (user.HidePlayedInLatest)
{
request.IsPlayed = false;
}
diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
index 0fffb06223..6f1620dddf 100644
--- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs
@@ -27,6 +27,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? IncludeExternalContent { get; set; }
+
public bool IncludeHidden { get; set; }
public string PresetViews { get; set; }
@@ -80,6 +81,7 @@ namespace MediaBrowser.Api.UserLibrary
{
query.IncludeExternalContent = request.IncludeExternalContent.Value;
}
+
query.IncludeHidden = request.IncludeHidden;
if (!string.IsNullOrWhiteSpace(request.PresetViews))
@@ -129,7 +131,6 @@ namespace MediaBrowser.Api.UserLibrary
{
Name = i.Name,
Id = i.Id.ToString("N", CultureInfo.InvariantCulture)
-
})
.OrderBy(i => i.Name)
.ToArray();
@@ -141,6 +142,7 @@ namespace MediaBrowser.Api.UserLibrary
class SpecialViewOption
{
public string Name { get; set; }
+
public string Id { get; set; }
}
}
diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs
index d023ee90ab..0523f89fa7 100644
--- a/MediaBrowser.Api/UserLibrary/YearsService.cs
+++ b/MediaBrowser.Api/UserLibrary/YearsService.cs
@@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api.UserLibrary
{
/// <summary>
- /// Class GetYears
+ /// Class GetYears.
/// </summary>
[Route("/Years", "GET", Summary = "Gets all years from a given item, folder, or the entire library")]
public class GetYears : GetItemsByName
@@ -21,7 +21,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class GetYear
+ /// Class GetYear.
/// </summary>
[Route("/Years/{Year}", "GET", Summary = "Gets a year")]
public class GetYear : IReturn<BaseItemDto>
@@ -42,7 +42,7 @@ namespace MediaBrowser.Api.UserLibrary
}
/// <summary>
- /// Class YearsService
+ /// Class YearsService.
/// </summary>
[Authenticated]
public class YearsService : BaseItemsByNameService<Year>
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 78fc6c6941..131def5542 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
@@ -18,7 +19,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Api
{
/// <summary>
- /// Class GetUsers
+ /// Class GetUsers.
/// </summary>
[Route("/Users", "GET", Summary = "Gets a list of users")]
[Authenticated]
@@ -40,7 +41,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class GetUser
+ /// Class GetUser.
/// </summary>
[Route("/Users/{Id}", "GET", Summary = "Gets a user by Id")]
[Authenticated(EscapeParentalControl = true)]
@@ -55,7 +56,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class DeleteUser
+ /// Class DeleteUser.
/// </summary>
[Route("/Users/{Id}", "DELETE", Summary = "Deletes a user")]
[Authenticated(Roles = "Admin")]
@@ -70,7 +71,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class AuthenticateUser
+ /// Class AuthenticateUser.
/// </summary>
[Route("/Users/{Id}/Authenticate", "POST", Summary = "Authenticates a user")]
public class AuthenticateUser : IReturn<AuthenticationResult>
@@ -94,7 +95,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class AuthenticateUser
+ /// Class AuthenticateUser.
/// </summary>
[Route("/Users/AuthenticateByName", "POST", Summary = "Authenticates a user")]
public class AuthenticateUserByName : IReturn<AuthenticationResult>
@@ -118,7 +119,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UpdateUserPassword
+ /// Class UpdateUserPassword.
/// </summary>
[Route("/Users/{Id}/Password", "POST", Summary = "Updates a user's password")]
[Authenticated]
@@ -148,7 +149,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UpdateUserEasyPassword
+ /// Class UpdateUserEasyPassword.
/// </summary>
[Route("/Users/{Id}/EasyPassword", "POST", Summary = "Updates a user's easy password")]
[Authenticated]
@@ -176,7 +177,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UpdateUser
+ /// Class UpdateUser.
/// </summary>
[Route("/Users/{Id}", "POST", Summary = "Updates a user")]
[Authenticated]
@@ -185,7 +186,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UpdateUser
+ /// Class UpdateUser.
/// </summary>
[Route("/Users/{Id}/Policy", "POST", Summary = "Updates a user policy")]
[Authenticated(Roles = "admin")]
@@ -196,7 +197,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UpdateUser
+ /// Class UpdateUser.
/// </summary>
[Route("/Users/{Id}/Configuration", "POST", Summary = "Updates a user configuration")]
[Authenticated]
@@ -207,7 +208,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class CreateUser
+ /// Class CreateUser.
/// </summary>
[Route("/Users/New", "POST", Summary = "Creates a user")]
[Authenticated(Roles = "Admin")]
@@ -235,7 +236,7 @@ namespace MediaBrowser.Api
}
/// <summary>
- /// Class UsersService
+ /// Class UsersService.
/// </summary>
public class UserService : BaseApiService
{
@@ -300,12 +301,12 @@ namespace MediaBrowser.Api
if (request.IsDisabled.HasValue)
{
- users = users.Where(i => i.Policy.IsDisabled == request.IsDisabled.Value);
+ users = users.Where(i => i.HasPermission(PermissionKind.IsDisabled) == request.IsDisabled.Value);
}
if (request.IsHidden.HasValue)
{
- users = users.Where(i => i.Policy.IsHidden == request.IsHidden.Value);
+ users = users.Where(i => i.HasPermission(PermissionKind.IsHidden) == request.IsHidden.Value);
}
if (filterByDevice)
@@ -322,12 +323,12 @@ namespace MediaBrowser.Api
{
if (!_networkManager.IsInLocalNetwork(Request.RemoteIp))
{
- users = users.Where(i => i.Policy.EnableRemoteAccess);
+ users = users.Where(i => i.HasPermission(PermissionKind.EnableRemoteAccess));
}
}
var result = users
- .OrderBy(u => u.Name)
+ .OrderBy(u => u.Username)
.Select(i => _userManager.GetUserDto(i, Request.RemoteIp))
.ToArray();
@@ -397,7 +398,7 @@ namespace MediaBrowser.Api
// Password should always be null
return Post(new AuthenticateUserByName
{
- Username = user.Name,
+ Username = user.Username,
Password = null,
Pw = request.Pw
});
@@ -456,7 +457,12 @@ namespace MediaBrowser.Api
}
else
{
- var success = await _userManager.AuthenticateUser(user.Name, request.CurrentPw, request.CurrentPassword, Request.RemoteIp, false).ConfigureAwait(false);
+ var success = await _userManager.AuthenticateUser(
+ user.Username,
+ request.CurrentPw,
+ request.CurrentPassword,
+ Request.RemoteIp,
+ false).ConfigureAwait(false);
if (success == null)
{
@@ -506,10 +512,10 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(id);
- if (string.Equals(user.Name, dtoUser.Name, StringComparison.Ordinal))
+ if (string.Equals(user.Username, dtoUser.Name, StringComparison.Ordinal))
{
- _userManager.UpdateUser(user);
- _userManager.UpdateConfiguration(user, dtoUser.Configuration);
+ await _userManager.UpdateUserAsync(user);
+ _userManager.UpdateConfiguration(user.Id, dtoUser.Configuration);
}
else
{
@@ -560,7 +566,6 @@ namespace MediaBrowser.Api
AssertCanUpdateUser(_authContext, _userManager, request.Id, false);
_userManager.UpdateConfiguration(request.Id, request);
-
}
public void Post(UpdateUserPolicy request)
@@ -568,24 +573,24 @@ namespace MediaBrowser.Api
var user = _userManager.GetUserById(request.Id);
// If removing admin access
- if (!request.IsAdministrator && user.Policy.IsAdministrator)
+ if (!request.IsAdministrator && user.HasPermission(PermissionKind.IsAdministrator))
{
- if (_userManager.Users.Count(i => i.Policy.IsAdministrator) == 1)
+ if (_userManager.Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1)
{
throw new ArgumentException("There must be at least one user in the system with administrative access.");
}
}
// If disabling
- if (request.IsDisabled && user.Policy.IsAdministrator)
+ if (request.IsDisabled && user.HasPermission(PermissionKind.IsAdministrator))
{
throw new ArgumentException("Administrators cannot be disabled.");
}
// If disabling
- if (request.IsDisabled && !user.Policy.IsDisabled)
+ if (request.IsDisabled && !user.HasPermission(PermissionKind.IsDisabled))
{
- if (_userManager.Users.Count(i => !i.Policy.IsDisabled) == 1)
+ if (_userManager.Users.Count(i => !i.HasPermission(PermissionKind.IsDisabled)) == 1)
{
throw new ArgumentException("There must be at least one enabled user in the system.");
}
@@ -594,7 +599,7 @@ namespace MediaBrowser.Api
_sessionMananger.RevokeUserTokens(user.Id, currentToken);
}
- _userManager.UpdateUserPolicy(request.Id, request);
+ _userManager.UpdatePolicy(request.Id, request);
}
}
}
diff --git a/MediaBrowser.Api/VideosService.cs b/MediaBrowser.Api/VideosService.cs
index b11fd48d3c..957a279f85 100644
--- a/MediaBrowser.Api/VideosService.cs
+++ b/MediaBrowser.Api/VideosService.cs
@@ -128,6 +128,7 @@ namespace MediaBrowser.Api
var items = request.Ids.Split(',')
.Select(i => _libraryManager.GetItemById(i))
.OfType<Video>()
+ .OrderBy(i => i.Id)
.ToList();
if (items.Count < 2)