mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-07-20 05:04:18 +01:00
Merge remote-tracking branch 'upstream/api-migration' into api-filter
This commit is contained in:
@@ -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;
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -18,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
|
||||
{
|
||||
@@ -408,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)
|
||||
@@ -448,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>
|
||||
@@ -477,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>
|
||||
@@ -567,14 +577,14 @@ namespace MediaBrowser.Api.Images
|
||||
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);
|
||||
@@ -597,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,
|
||||
@@ -741,13 +828,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>
|
||||
@@ -758,15 +867,7 @@ namespace MediaBrowser.Api.Images
|
||||
/// <returns>Task.</returns>
|
||||
public async Task PostImage(BaseItem entity, Stream inputStream, ImageType imageType, string mimeType)
|
||||
{
|
||||
using var reader = new StreamReader(inputStream);
|
||||
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
|
||||
var bytes = Convert.FromBase64String(text);
|
||||
|
||||
var memoryStream = new MemoryStream(bytes)
|
||||
{
|
||||
Position = 0
|
||||
};
|
||||
var memoryStream = await GetMemoryStream(inputStream);
|
||||
|
||||
// Handle image/png; charset=utf-8
|
||||
mimeType = mimeType.Split(';').FirstOrDefault();
|
||||
@@ -775,5 +876,32 @@ namespace MediaBrowser.Api.Images
|
||||
|
||||
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);
|
||||
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(user, memoryStream, mimeType, user.ProfileImage.Path)
|
||||
.ConfigureAwait(false);
|
||||
await _userManager.UpdateUserAsync(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -350,6 +356,7 @@ namespace MediaBrowser.Api.Library
|
||||
_moviesServiceLogger = moviesServiceLogger;
|
||||
}
|
||||
|
||||
// Content Types available for each Library
|
||||
private string[] GetRepresentativeItemTypes(string contentType)
|
||||
{
|
||||
return contentType switch
|
||||
@@ -359,7 +366,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"},
|
||||
@@ -425,7 +432,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);
|
||||
}
|
||||
|
||||
@@ -763,8 +769,8 @@ namespace MediaBrowser.Api.Library
|
||||
{
|
||||
try
|
||||
{
|
||||
_activityManager.Create(new Jellyfin.Data.Entities.ActivityLog(
|
||||
string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name),
|
||||
_activityManager.Create(new ActivityLog(
|
||||
string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name),
|
||||
"UserDownloadingContent",
|
||||
auth.UserId)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -859,7 +860,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.");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -251,7 +253,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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -196,7 +197,7 @@ namespace MediaBrowser.Api.Playback
|
||||
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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
{
|
||||
@@ -576,10 +584,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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -1,300 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Subtitles;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
|
||||
|
||||
namespace MediaBrowser.Api.Subtitles
|
||||
{
|
||||
[Route("/Videos/{Id}/Subtitles/{Index}", "DELETE", Summary = "Deletes an external subtitle file")]
|
||||
[Authenticated(Roles = "Admin")]
|
||||
public class DeleteSubtitle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "DELETE")]
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Items/{Id}/RemoteSearch/Subtitles/{Language}", "GET")]
|
||||
[Authenticated]
|
||||
public class SearchRemoteSubtitles : IReturn<RemoteSubtitleInfo[]>
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[ApiMember(Name = "Language", Description = "Language", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Language { get; set; }
|
||||
|
||||
public bool? IsPerfectMatch { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Items/{Id}/RemoteSearch/Subtitles/{SubtitleId}", "POST")]
|
||||
[Authenticated]
|
||||
public class DownloadRemoteSubtitles : IReturnVoid
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[ApiMember(Name = "SubtitleId", Description = "SubtitleId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
|
||||
public string SubtitleId { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Providers/Subtitles/Subtitles/{Id}", "GET")]
|
||||
[Authenticated]
|
||||
public class GetRemoteSubtitles : IReturnVoid
|
||||
{
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format.")]
|
||||
[Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/{StartPositionTicks}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format.")]
|
||||
public class GetSubtitle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
|
||||
public int Index { get; set; }
|
||||
|
||||
[ApiMember(Name = "Format", Description = "Format", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Format { get; set; }
|
||||
|
||||
[ApiMember(Name = "StartPositionTicks", Description = "StartPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public long StartPositionTicks { get; set; }
|
||||
|
||||
[ApiMember(Name = "EndPositionTicks", Description = "EndPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
|
||||
public long? EndPositionTicks { get; set; }
|
||||
|
||||
[ApiMember(Name = "CopyTimestamps", Description = "CopyTimestamps", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
public bool CopyTimestamps { get; set; }
|
||||
public bool AddVttTimeMap { get; set; }
|
||||
}
|
||||
|
||||
[Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/subtitles.m3u8", "GET", Summary = "Gets an HLS subtitle playlist.")]
|
||||
[Authenticated]
|
||||
public class GetSubtitlePlaylist
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
/// <value>The id.</value>
|
||||
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
[ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
|
||||
public int Index { get; set; }
|
||||
|
||||
[ApiMember(Name = "SegmentLength", Description = "The subtitle srgment length", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||
public int SegmentLength { get; set; }
|
||||
}
|
||||
|
||||
public class SubtitleService : BaseApiService
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ISubtitleManager _subtitleManager;
|
||||
private readonly ISubtitleEncoder _subtitleEncoder;
|
||||
private readonly IMediaSourceManager _mediaSourceManager;
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
private readonly IAuthorizationContext _authContext;
|
||||
|
||||
public SubtitleService(
|
||||
ILogger<SubtitleService> logger,
|
||||
IServerConfigurationManager serverConfigurationManager,
|
||||
IHttpResultFactory httpResultFactory,
|
||||
ILibraryManager libraryManager,
|
||||
ISubtitleManager subtitleManager,
|
||||
ISubtitleEncoder subtitleEncoder,
|
||||
IMediaSourceManager mediaSourceManager,
|
||||
IProviderManager providerManager,
|
||||
IFileSystem fileSystem,
|
||||
IAuthorizationContext authContext)
|
||||
: base(logger, serverConfigurationManager, httpResultFactory)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_subtitleManager = subtitleManager;
|
||||
_subtitleEncoder = subtitleEncoder;
|
||||
_mediaSourceManager = mediaSourceManager;
|
||||
_providerManager = providerManager;
|
||||
_fileSystem = fileSystem;
|
||||
_authContext = authContext;
|
||||
}
|
||||
|
||||
public async Task<object> Get(GetSubtitlePlaylist request)
|
||||
{
|
||||
var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));
|
||||
|
||||
var mediaSource = await _mediaSourceManager.GetMediaSource(item, request.MediaSourceId, null, false, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
var runtime = mediaSource.RunTimeTicks ?? -1;
|
||||
|
||||
if (runtime <= 0)
|
||||
{
|
||||
throw new ArgumentException("HLS Subtitles are not supported for this media.");
|
||||
}
|
||||
|
||||
var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks;
|
||||
if (segmentLengthTicks <= 0)
|
||||
{
|
||||
throw new ArgumentException("segmentLength was not given, or it was given incorrectly. (It should be bigger than 0)");
|
||||
}
|
||||
|
||||
builder.AppendLine("#EXTM3U");
|
||||
builder.AppendLine("#EXT-X-TARGETDURATION:" + request.SegmentLength.ToString(CultureInfo.InvariantCulture));
|
||||
builder.AppendLine("#EXT-X-VERSION:3");
|
||||
builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
|
||||
builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
|
||||
|
||||
long positionTicks = 0;
|
||||
|
||||
var accessToken = _authContext.GetAuthorizationInfo(Request).Token;
|
||||
|
||||
while (positionTicks < runtime)
|
||||
{
|
||||
var remaining = runtime - positionTicks;
|
||||
var lengthTicks = Math.Min(remaining, segmentLengthTicks);
|
||||
|
||||
builder.AppendLine("#EXTINF:" + TimeSpan.FromTicks(lengthTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture) + ",");
|
||||
|
||||
var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks);
|
||||
|
||||
var url = string.Format("stream.vtt?CopyTimestamps=true&AddVttTimeMap=true&StartPositionTicks={0}&EndPositionTicks={1}&api_key={2}",
|
||||
positionTicks.ToString(CultureInfo.InvariantCulture),
|
||||
endPositionTicks.ToString(CultureInfo.InvariantCulture),
|
||||
accessToken);
|
||||
|
||||
builder.AppendLine(url);
|
||||
|
||||
positionTicks += segmentLengthTicks;
|
||||
}
|
||||
|
||||
builder.AppendLine("#EXT-X-ENDLIST");
|
||||
|
||||
return ResultFactory.GetResult(Request, builder.ToString(), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
public async Task<object> Get(GetSubtitle request)
|
||||
{
|
||||
if (string.Equals(request.Format, "js", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
request.Format = "json";
|
||||
}
|
||||
if (string.IsNullOrEmpty(request.Format))
|
||||
{
|
||||
var item = (Video)_libraryManager.GetItemById(request.Id);
|
||||
|
||||
var idString = request.Id.ToString("N", CultureInfo.InvariantCulture);
|
||||
var mediaSource = _mediaSourceManager.GetStaticMediaSources(item, false, null)
|
||||
.First(i => string.Equals(i.Id, request.MediaSourceId ?? idString));
|
||||
|
||||
var subtitleStream = mediaSource.MediaStreams
|
||||
.First(i => i.Type == MediaStreamType.Subtitle && i.Index == request.Index);
|
||||
|
||||
return await ResultFactory.GetStaticFileResult(Request, subtitleStream.Path).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (string.Equals(request.Format, "vtt", StringComparison.OrdinalIgnoreCase) && request.AddVttTimeMap)
|
||||
{
|
||||
using var stream = await GetSubtitles(request).ConfigureAwait(false);
|
||||
using var reader = new StreamReader(stream);
|
||||
|
||||
var text = reader.ReadToEnd();
|
||||
|
||||
text = text.Replace("WEBVTT", "WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000");
|
||||
|
||||
return ResultFactory.GetResult(Request, text, MimeTypes.GetMimeType("file." + request.Format));
|
||||
}
|
||||
|
||||
return ResultFactory.GetResult(Request, await GetSubtitles(request).ConfigureAwait(false), MimeTypes.GetMimeType("file." + request.Format));
|
||||
}
|
||||
|
||||
private Task<Stream> GetSubtitles(GetSubtitle request)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(request.Id);
|
||||
|
||||
return _subtitleEncoder.GetSubtitles(item,
|
||||
request.MediaSourceId,
|
||||
request.Index,
|
||||
request.Format,
|
||||
request.StartPositionTicks,
|
||||
request.EndPositionTicks ?? 0,
|
||||
request.CopyTimestamps,
|
||||
CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task<object> Get(SearchRemoteSubtitles request)
|
||||
{
|
||||
var video = (Video)_libraryManager.GetItemById(request.Id);
|
||||
|
||||
return await _subtitleManager.SearchSubtitles(video, request.Language, request.IsPerfectMatch, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public Task Delete(DeleteSubtitle request)
|
||||
{
|
||||
var item = _libraryManager.GetItemById(request.Id);
|
||||
return _subtitleManager.DeleteSubtitles(item, request.Index);
|
||||
}
|
||||
|
||||
public async Task<object> Get(GetRemoteSubtitles request)
|
||||
{
|
||||
var result = await _subtitleManager.GetRemoteSubtitles(request.Id, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
return ResultFactory.GetResult(Request, result.Stream, MimeTypes.GetMimeType("file." + result.Format));
|
||||
}
|
||||
|
||||
public void Post(DownloadRemoteSubtitles request)
|
||||
{
|
||||
var video = (Video)_libraryManager.GetItemById(request.Id);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.High);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error downloading subtitles");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
@@ -404,7 +404,7 @@ namespace MediaBrowser.Api
|
||||
};
|
||||
}
|
||||
|
||||
private Series GetSeries(string seriesId, User user)
|
||||
private Series GetSeries(string seriesId)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(seriesId))
|
||||
{
|
||||
@@ -433,7 +433,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 +446,7 @@ namespace MediaBrowser.Api
|
||||
}
|
||||
else
|
||||
{
|
||||
var series = GetSeries(request.Id, user);
|
||||
var series = GetSeries(request.Id);
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,6 +15,7 @@ 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
|
||||
{
|
||||
@@ -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>(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -312,7 +312,7 @@ namespace MediaBrowser.Api.UserLibrary
|
||||
|
||||
if (!request.IsPlayed.HasValue)
|
||||
{
|
||||
if (user.Configuration.HidePlayedInLatest)
|
||||
if (user.HidePlayedInLatest)
|
||||
{
|
||||
request.IsPlayed = false;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user