mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-01 13:28:27 +01:00
Merge remote-tracking branch 'upstream/api-migration' into api-sessionservice
This commit is contained in:
@@ -31,7 +31,7 @@ namespace MediaBrowser.Api
|
||||
/// <summary>
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private ILogger _logger;
|
||||
private ILogger<ApiEntryPoint> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// The configuration manager.
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace MediaBrowser.Api
|
||||
public abstract class BaseApiService : IService, IRequiresRequest
|
||||
{
|
||||
public BaseApiService(
|
||||
ILogger logger,
|
||||
ILogger<BaseApiService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory)
|
||||
{
|
||||
@@ -34,7 +34,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.
|
||||
|
||||
104
MediaBrowser.Api/Devices/DeviceService.cs
Normal file
104
MediaBrowser.Api/Devices/DeviceService.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Devices;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Security;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Devices;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Api.Devices
|
||||
{
|
||||
[Route("/Devices", "GET", Summary = "Gets all devices")]
|
||||
[Authenticated(Roles = "Admin")]
|
||||
public class GetDevices : DeviceQuery, IReturn<QueryResult<DeviceInfo>>
|
||||
{
|
||||
}
|
||||
|
||||
[Route("/Devices/Info", "GET", Summary = "Gets info for a device")]
|
||||
[Authenticated(Roles = "Admin")]
|
||||
public class GetDeviceInfo : IReturn<DeviceInfo>
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Devices/Options", "GET", Summary = "Gets options for a device")]
|
||||
[Authenticated(Roles = "Admin")]
|
||||
public class GetDeviceOptions : IReturn<DeviceOptions>
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Devices", "DELETE", Summary = "Deletes a device")]
|
||||
public class DeleteDevice
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Devices/Options", "POST", Summary = "Updates device options")]
|
||||
[Authenticated(Roles = "Admin")]
|
||||
public class PostDeviceOptions : DeviceOptions, IReturnVoid
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "Device Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
public class DeviceService : BaseApiService
|
||||
{
|
||||
private readonly IDeviceManager _deviceManager;
|
||||
private readonly IAuthenticationRepository _authRepo;
|
||||
private readonly ISessionManager _sessionManager;
|
||||
|
||||
public DeviceService(
|
||||
ILogger<DeviceService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IDeviceManager deviceManager,
|
||||
IAuthenticationRepository authRepo,
|
||||
ISessionManager sessionManager)
|
||||
: base(logger, serverConfigurationManager, httpResultFactory)
|
||||
{
|
||||
_deviceManager = deviceManager;
|
||||
_authRepo = authRepo;
|
||||
_sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
public void Post(PostDeviceOptions request)
|
||||
{
|
||||
_deviceManager.UpdateDeviceOptions(request.Id, request);
|
||||
}
|
||||
|
||||
public object Get(GetDevices request)
|
||||
{
|
||||
return ToOptimizedResult(_deviceManager.GetDevices(request));
|
||||
}
|
||||
|
||||
public object Get(GetDeviceInfo request)
|
||||
{
|
||||
return _deviceManager.GetDevice(request.Id);
|
||||
}
|
||||
|
||||
public object Get(GetDeviceOptions request)
|
||||
{
|
||||
return _deviceManager.GetDeviceOptions(request.Id);
|
||||
}
|
||||
|
||||
public void Delete(DeleteDevice request)
|
||||
{
|
||||
var sessions = _authRepo.Get(new AuthenticationInfoQuery
|
||||
{
|
||||
DeviceId = request.Id
|
||||
|
||||
}).Items;
|
||||
|
||||
foreach (var session in sessions)
|
||||
{
|
||||
_sessionManager.Logout(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,12 +226,7 @@ namespace MediaBrowser.Api
|
||||
/// <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)
|
||||
|
||||
@@ -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;
|
||||
@@ -280,9 +281,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 +331,7 @@ namespace MediaBrowser.Api.Images
|
||||
{
|
||||
int? width = null;
|
||||
int? height = null;
|
||||
string blurhash = null;
|
||||
long length = 0;
|
||||
|
||||
try
|
||||
@@ -332,9 +341,9 @@ namespace MediaBrowser.Api.Images
|
||||
var fileInfo = _fileSystem.GetFileInfo(info.Path);
|
||||
length = fileInfo.Length;
|
||||
|
||||
ImageDimensions size = _imageProcessor.GetImageDimensions(item, info, true);
|
||||
width = size.Width;
|
||||
height = size.Height;
|
||||
blurhash = info.BlurHash;
|
||||
width = info.Width;
|
||||
height = info.Height;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
@@ -357,6 +366,7 @@ namespace MediaBrowser.Api.Images
|
||||
ImageType = info.Type,
|
||||
ImageTag = _imageProcessor.GetImageCacheTag(item, info),
|
||||
Size = length,
|
||||
BlurHash = blurhash,
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
@@ -554,8 +564,7 @@ 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;
|
||||
@@ -606,6 +615,12 @@ namespace MediaBrowser.Api.Images
|
||||
IDictionary<string, string> headers,
|
||||
bool isHeadRequest)
|
||||
{
|
||||
if (!image.IsLocalFile)
|
||||
{
|
||||
item ??= _libraryManager.GetItemById(itemId);
|
||||
image = await _libraryManager.ConvertImageToLocal(item, image, request.Index ?? 0).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var options = new ImageProcessingOptions
|
||||
{
|
||||
CropWhiteSpace = cropwhitespace,
|
||||
|
||||
@@ -147,9 +147,8 @@ 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
|
||||
@@ -265,17 +264,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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -319,11 +319,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,6 +347,7 @@ namespace MediaBrowser.Api.Library
|
||||
_activityManager = activityManager;
|
||||
_localization = localization;
|
||||
_libraryMonitor = libraryMonitor;
|
||||
_moviesServiceLogger = moviesServiceLogger;
|
||||
}
|
||||
|
||||
private string[] GetRepresentativeItemTypes(string contentType)
|
||||
@@ -543,7 +547,7 @@ namespace MediaBrowser.Api.Library
|
||||
if (item is Movie || (program != null && program.IsMovie) || item is Trailer)
|
||||
{
|
||||
return new MoviesService(
|
||||
Logger,
|
||||
_moviesServiceLogger,
|
||||
ServerConfigurationManager,
|
||||
ResultFactory,
|
||||
_userManager,
|
||||
@@ -651,7 +655,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)
|
||||
{
|
||||
@@ -684,11 +688,11 @@ namespace MediaBrowser.Api.Library
|
||||
|
||||
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 +763,12 @@ namespace MediaBrowser.Api.Library
|
||||
{
|
||||
try
|
||||
{
|
||||
_activityManager.Create(new ActivityLogEntry
|
||||
_activityManager.Create(new Jellyfin.Data.Entities.ActivityLog(
|
||||
string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, 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
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
|
||||
@@ -9,10 +14,6 @@
|
||||
<Compile Include="..\SharedVersion.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Attachments" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -82,7 +82,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,
|
||||
@@ -273,7 +273,7 @@ namespace MediaBrowser.Api.Movies
|
||||
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();
|
||||
@@ -314,7 +314,7 @@ namespace MediaBrowser.Api.Movies
|
||||
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();
|
||||
|
||||
@@ -33,13 +33,18 @@ namespace MediaBrowser.Api.Movies
|
||||
/// </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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -81,7 +81,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,7 +193,7 @@ 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)
|
||||
@@ -243,9 +243,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";
|
||||
}
|
||||
|
||||
@@ -321,15 +321,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;
|
||||
}
|
||||
|
||||
@@ -790,7 +791,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,
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
public abstract class BaseHlsService : BaseStreamingService
|
||||
{
|
||||
public BaseHlsService(
|
||||
ILogger logger,
|
||||
ILogger<BaseHlsService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
@@ -209,24 +209,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)
|
||||
|
||||
@@ -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,
|
||||
@@ -700,12 +700,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;
|
||||
}
|
||||
@@ -720,22 +720,201 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||
//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);
|
||||
}
|
||||
|
||||
@@ -827,7 +1006,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 +1034,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 +1089,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))
|
||||
{
|
||||
|
||||
126
MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs
Normal file
126
MediaBrowser.Api/Playback/Hls/HlsCodecStringFactory.cs
Normal file
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace MediaBrowser.Api.Playback
|
||||
private readonly IAuthorizationContext _authContext;
|
||||
|
||||
public MediaInfoService(
|
||||
ILogger logger,
|
||||
ILogger<MediaInfoService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||
public class AudioService : BaseProgressiveStreamingService
|
||||
{
|
||||
public AudioService(
|
||||
ILogger logger,
|
||||
ILogger<AudioService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IHttpClient httpClient,
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||
protected IHttpClient HttpClient { get; private set; }
|
||||
|
||||
public BaseProgressiveStreamingService(
|
||||
ILogger logger,
|
||||
ILogger<BaseProgressiveStreamingService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IHttpClient httpClient,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -75,9 +75,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,6 +110,7 @@ namespace MediaBrowser.Api.Playback
|
||||
AuthorizationContext = authorizationContext;
|
||||
NetworkManager = networkManager;
|
||||
_encodingHelper = encodingHelper;
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
protected IHttpClient HttpClient { get; private set; }
|
||||
@@ -233,7 +236,7 @@ namespace MediaBrowser.Api.Playback
|
||||
AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId;
|
||||
|
||||
var mediaInfoService = new MediaInfoService(
|
||||
Logger,
|
||||
_loggerFactory.CreateLogger<MediaInfoService>(),
|
||||
ServerConfigurationManager,
|
||||
ResultFactory,
|
||||
MediaSourceManager,
|
||||
@@ -277,7 +280,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,
|
||||
@@ -331,7 +334,7 @@ namespace MediaBrowser.Api.Playback
|
||||
else
|
||||
{
|
||||
var service = new AudioService(
|
||||
Logger,
|
||||
_loggerFactory.CreateLogger<AudioService>(),
|
||||
ServerConfigurationManager,
|
||||
ResultFactory,
|
||||
HttpClient,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace MediaBrowser.Api
|
||||
/// </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;
|
||||
|
||||
|
||||
302
MediaBrowser.Api/SyncPlay/SyncPlayService.cs
Normal file
302
MediaBrowser.Api/SyncPlay/SyncPlayService.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
MediaBrowser.Api/SyncPlay/TimeSyncService.cs
Normal file
52
MediaBrowser.Api/SyncPlay/TimeSyncService.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Activity;
|
||||
@@ -10,7 +10,7 @@ namespace MediaBrowser.Api.System
|
||||
/// <summary>
|
||||
/// Class SessionInfoWebSocketListener
|
||||
/// </summary>
|
||||
public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<List<ActivityLogEntry>, WebSocketListenerState>
|
||||
public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<ActivityLogEntry[], WebSocketListenerState>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
@@ -26,10 +26,10 @@ namespace MediaBrowser.Api.System
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||
public class ArtistsService : BaseItemsByNameService<MusicArtist>
|
||||
{
|
||||
public ArtistsService(
|
||||
ILogger<GenresService> logger,
|
||||
ILogger<ArtistsService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
IUserManager userManager,
|
||||
|
||||
@@ -28,7 +28,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,
|
||||
|
||||
@@ -59,7 +59,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,
|
||||
|
||||
@@ -426,7 +426,7 @@ namespace MediaBrowser.Api
|
||||
catch (SecurityException e)
|
||||
{
|
||||
// rethrow adding IP address to message
|
||||
throw new SecurityException($"[{Request.RemoteIp}] {e.Message}");
|
||||
throw new SecurityException($"[{Request.RemoteIp}] {e.Message}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user