Merge branch 'master' into simplify_authz
# Conflicts: # Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs
This commit is contained in:
commit
52e2776d8e
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
|
@ -27,11 +27,11 @@ jobs:
|
|||
dotnet-version: '7.0.x'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@8775e868027fa230df8586bdf502bbd9b618a477 # v2
|
||||
uses: github/codeql-action/init@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-extended
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@8775e868027fa230df8586bdf502bbd9b618a477 # v2
|
||||
uses: github/codeql-action/autobuild@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@8775e868027fa230df8586bdf502bbd9b618a477 # v2
|
||||
uses: github/codeql-action/analyze@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2
|
||||
|
|
|
@ -232,3 +232,4 @@
|
|||
- [Matthew Jones](https://github.com/matthew-jones-uk)
|
||||
- [Jakob Kukla](https://github.com/jakobkukla)
|
||||
- [Utku Özdemir](https://github.com/utkuozdemir)
|
||||
- [JPUC1143](https://github.com/Jpuc1143/)
|
||||
|
|
|
@ -4477,6 +4477,24 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
}
|
||||
|
||||
if (query.IncludeInheritedTags.Length > 0)
|
||||
{
|
||||
var paramName = "@IncludeInheritedTags";
|
||||
if (statement is null)
|
||||
{
|
||||
int index = 0;
|
||||
string includedTags = string.Join(',', query.IncludeInheritedTags.Select(_ => paramName + index++));
|
||||
whereClauses.Add("((select CleanValue from ItemValues where ItemId=Guid and Type=6 and cleanvalue in (" + includedTags + ")) is not null)");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < query.IncludeInheritedTags.Length; index++)
|
||||
{
|
||||
statement.TryBind(paramName + index, GetCleanValue(query.IncludeInheritedTags[index]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (query.SeriesStatuses.Length > 0)
|
||||
{
|
||||
var statuses = new List<string>();
|
||||
|
@ -5440,6 +5458,9 @@ AND Type = @InternalPersonType)");
|
|||
|
||||
list.AddRange(inheritedTags.Select(i => (6, i)));
|
||||
|
||||
// Remove all invalid values.
|
||||
list.RemoveAll(i => string.IsNullOrEmpty(i.Item2));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
|
@ -137,32 +137,33 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
|
||||
private static ProgramInfo GetProgramInfo(XmlTvProgram program, ListingsProviderInfo info)
|
||||
{
|
||||
string episodeTitle = program.Episode?.Title;
|
||||
string episodeTitle = program.Episode.Title;
|
||||
var programCategories = program.Categories.Where(c => !string.IsNullOrWhiteSpace(c)).ToList();
|
||||
|
||||
var programInfo = new ProgramInfo
|
||||
{
|
||||
ChannelId = program.ChannelId,
|
||||
EndDate = program.EndDate.UtcDateTime,
|
||||
EpisodeNumber = program.Episode?.Episode,
|
||||
EpisodeNumber = program.Episode.Episode,
|
||||
EpisodeTitle = episodeTitle,
|
||||
Genres = program.Categories,
|
||||
Genres = programCategories,
|
||||
StartDate = program.StartDate.UtcDateTime,
|
||||
Name = program.Title,
|
||||
Overview = program.Description,
|
||||
ProductionYear = program.CopyrightDate?.Year,
|
||||
SeasonNumber = program.Episode?.Series,
|
||||
IsSeries = program.Episode is not null,
|
||||
SeasonNumber = program.Episode.Series,
|
||||
IsSeries = program.Episode.Series is not null,
|
||||
IsRepeat = program.IsPreviouslyShown && !program.IsNew,
|
||||
IsPremiere = program.Premiere is not null,
|
||||
IsKids = program.Categories.Any(c => info.KidsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
|
||||
IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
|
||||
IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
|
||||
IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
|
||||
IsKids = programCategories.Any(c => info.KidsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
|
||||
IsMovie = programCategories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
|
||||
IsNews = programCategories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
|
||||
IsSports = programCategories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
|
||||
ImageUrl = string.IsNullOrEmpty(program.Icon?.Source) ? null : program.Icon.Source,
|
||||
HasImage = !string.IsNullOrEmpty(program.Icon?.Source),
|
||||
OfficialRating = string.IsNullOrEmpty(program.Rating?.Value) ? null : program.Rating.Value,
|
||||
CommunityRating = program.StarRating,
|
||||
SeriesId = program.Episode is null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture)
|
||||
SeriesId = program.Episode.Episode is null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture)
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(program.ProgramId))
|
||||
|
@ -243,7 +244,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
{
|
||||
Id = c.Id,
|
||||
Name = c.DisplayName,
|
||||
ImageUrl = string.IsNullOrEmpty(c.Icon.Source) ? null : c.Icon.Source,
|
||||
ImageUrl = string.IsNullOrEmpty(c.Icon?.Source) ? null : c.Icon.Source,
|
||||
Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number
|
||||
}).ToList();
|
||||
}
|
||||
|
|
|
@ -123,41 +123,64 @@ namespace Emby.Server.Implementations.Plugins
|
|||
continue;
|
||||
}
|
||||
|
||||
var assemblyLoadContext = new PluginLoadContext(plugin.Path);
|
||||
_assemblyLoadContexts.Add(assemblyLoadContext);
|
||||
|
||||
var assemblies = new List<Assembly>(plugin.DllFiles.Count);
|
||||
var loadedAll = true;
|
||||
|
||||
foreach (var file in plugin.DllFiles)
|
||||
{
|
||||
Assembly assembly;
|
||||
try
|
||||
{
|
||||
var assemblyLoadContext = new PluginLoadContext(file);
|
||||
_assemblyLoadContexts.Add(assemblyLoadContext);
|
||||
|
||||
assembly = assemblyLoadContext.LoadFromAssemblyPath(file);
|
||||
|
||||
// Load all required types to verify that the plugin will load
|
||||
assembly.GetTypes();
|
||||
assemblies.Add(assemblyLoadContext.LoadFromAssemblyPath(file));
|
||||
}
|
||||
catch (FileLoadException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load assembly {Path}. Disabling plugin.", file);
|
||||
_logger.LogError(ex, "Failed to load assembly {Path}. Disabling plugin", file);
|
||||
ChangePluginState(plugin, PluginStatus.Malfunctioned);
|
||||
continue;
|
||||
}
|
||||
catch (SystemException ex) when (ex is TypeLoadException or ReflectionTypeLoadException) // Undocumented exception
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load assembly {Path}. This error occurs when a plugin references an incompatible version of one of the shared libraries. Disabling plugin.", file);
|
||||
ChangePluginState(plugin, PluginStatus.NotSupported);
|
||||
continue;
|
||||
loadedAll = false;
|
||||
break;
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch (Exception ex)
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin.", file);
|
||||
_logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin", file);
|
||||
ChangePluginState(plugin, PluginStatus.Malfunctioned);
|
||||
continue;
|
||||
loadedAll = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadedAll)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Load all required types to verify that the plugin will load
|
||||
assembly.GetTypes();
|
||||
}
|
||||
catch (SystemException ex) when (ex is TypeLoadException or ReflectionTypeLoadException) // Undocumented exception
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load assembly {Path}. This error occurs when a plugin references an incompatible version of one of the shared libraries. Disabling plugin", assembly.Location);
|
||||
ChangePluginState(plugin, PluginStatus.NotSupported);
|
||||
break;
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch (Exception ex)
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin", assembly.Location);
|
||||
ChangePluginState(plugin, PluginStatus.Malfunctioned);
|
||||
break;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Loaded assembly {Assembly} from {Path}", assembly.FullName, file);
|
||||
_logger.LogInformation("Loaded assembly {Assembly} from {Path}", assembly.FullName, assembly.Location);
|
||||
yield return assembly;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,12 +99,17 @@ public class ImageController : BaseJellyfinApiController
|
|||
[FromRoute, Required] ImageType imageType,
|
||||
[FromQuery] int? index = null)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true))
|
||||
{
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
|
||||
}
|
||||
|
||||
var user = _userManager.GetUserById(userId);
|
||||
var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
||||
await using (memoryStream.ConfigureAwait(false))
|
||||
{
|
||||
|
@ -148,12 +153,17 @@ public class ImageController : BaseJellyfinApiController
|
|||
[FromRoute, Required] ImageType imageType,
|
||||
[FromRoute] int index)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true))
|
||||
{
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image.");
|
||||
}
|
||||
|
||||
var user = _userManager.GetUserById(userId);
|
||||
var memoryStream = await GetMemoryStream(Request.Body).ConfigureAwait(false);
|
||||
await using (memoryStream.ConfigureAwait(false))
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ using Jellyfin.Api.Extensions;
|
|||
using Jellyfin.Api.Helpers;
|
||||
using Jellyfin.Api.ModelBinders;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Dto;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
@ -240,7 +241,7 @@ public class ItemsController : BaseJellyfinApiController
|
|||
var isApiKey = User.GetIsApiKey();
|
||||
// if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method
|
||||
var user = !isApiKey && userId.HasValue && !userId.Value.Equals(default)
|
||||
? _userManager.GetUserById(userId.Value)
|
||||
? _userManager.GetUserById(userId.Value) ?? throw new ResourceNotFoundException()
|
||||
: null;
|
||||
|
||||
// beyond this point, we're either using an api key or we have a valid user
|
||||
|
@ -814,6 +815,11 @@ public class ItemsController : BaseJellyfinApiController
|
|||
[FromQuery] bool excludeActiveSessions = false)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var parentIdGuid = parentId ?? Guid.Empty;
|
||||
var dtoOptions = new DtoOptions { Fields = fields }
|
||||
.AddClientFields(User)
|
||||
|
|
|
@ -283,6 +283,11 @@ public class LibraryController : BaseJellyfinApiController
|
|||
userId,
|
||||
inheritFromParent);
|
||||
|
||||
if (themeSongs.Result is NotFoundObjectResult || themeVideos.Result is NotFoundObjectResult)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return new AllThemeMediaResult
|
||||
{
|
||||
ThemeSongsResult = themeSongs?.Value,
|
||||
|
@ -452,6 +457,10 @@ public class LibraryController : BaseJellyfinApiController
|
|||
if (user is not null)
|
||||
{
|
||||
parent = TranslateParentItem(parent, user);
|
||||
if (parent is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
|
||||
|
@ -672,6 +681,11 @@ public class LibraryController : BaseJellyfinApiController
|
|||
: _libraryManager.GetUserRootFolder())
|
||||
: _libraryManager.GetItemById(itemId);
|
||||
|
||||
if (item is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (item is Episode || (item is IItemByName && item is not MusicArtist))
|
||||
{
|
||||
return new QueryResult<BaseItemDto>();
|
||||
|
|
|
@ -1210,7 +1210,7 @@ public class LiveTvController : BaseJellyfinApiController
|
|||
|
||||
private async Task AssertUserCanManageLiveTv()
|
||||
{
|
||||
var user = _userManager.GetUserById(User.GetUserId());
|
||||
var user = _userManager.GetUserById(User.GetUserId()) ?? throw new ResourceNotFoundException();
|
||||
var session = await _sessionManager.LogSessionActivity(
|
||||
User.GetClient(),
|
||||
User.GetVersion(),
|
||||
|
|
|
@ -157,6 +157,11 @@ public class MusicGenresController : BaseJellyfinApiController
|
|||
item = _libraryManager.GetMusicGenre(genreName);
|
||||
}
|
||||
|
||||
if (item is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (userId.HasValue && !userId.Value.Equals(default))
|
||||
{
|
||||
var user = _userManager.GetUserById(userId.Value);
|
||||
|
|
|
@ -76,6 +76,11 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
[FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? datePlayed)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
|
||||
|
||||
var item = _libraryManager.GetItemById(itemId);
|
||||
|
@ -88,6 +93,11 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
foreach (var additionalUserInfo in session.AdditionalUsers)
|
||||
{
|
||||
var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
|
||||
if (additionalUser is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
UpdatePlayedStatus(additionalUser, item, true, datePlayed);
|
||||
}
|
||||
|
||||
|
@ -108,6 +118,11 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
public async Task<ActionResult<UserItemDataDto>> MarkUnplayedItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
|
||||
var item = _libraryManager.GetItemById(itemId);
|
||||
|
||||
|
@ -120,6 +135,11 @@ public class PlaystateController : BaseJellyfinApiController
|
|||
foreach (var additionalUserInfo in session.AdditionalUsers)
|
||||
{
|
||||
var additionalUser = _userManager.GetUserById(additionalUserInfo.UserId);
|
||||
if (additionalUser is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
UpdatePlayedStatus(additionalUser, item, false, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,10 @@ public class SessionController : BaseJellyfinApiController
|
|||
result = result.Where(i => i.SupportsRemoteControl);
|
||||
|
||||
var user = _userManager.GetUserById(controllableByUserId.Value);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers))
|
||||
{
|
||||
|
|
|
@ -147,6 +147,11 @@ public class UserController : BaseJellyfinApiController
|
|||
public async Task<ActionResult> DeleteUser([FromRoute, Required] Guid userId)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
await _sessionManager.RevokeUserTokens(user.Id, null).ConfigureAwait(false);
|
||||
await _userManager.DeleteUserAsync(userId).ConfigureAwait(false);
|
||||
return NoContent();
|
||||
|
@ -281,8 +286,8 @@ public class UserController : BaseJellyfinApiController
|
|||
{
|
||||
var success = await _userManager.AuthenticateUser(
|
||||
user.Username,
|
||||
request.CurrentPw,
|
||||
request.CurrentPw,
|
||||
request.CurrentPw ?? string.Empty,
|
||||
request.CurrentPw ?? string.Empty,
|
||||
HttpContext.GetNormalizedRemoteIp().ToString(),
|
||||
false).ConfigureAwait(false);
|
||||
|
||||
|
@ -292,7 +297,7 @@ public class UserController : BaseJellyfinApiController
|
|||
}
|
||||
}
|
||||
|
||||
await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false);
|
||||
await _userManager.ChangePassword(user, request.NewPw ?? string.Empty).ConfigureAwait(false);
|
||||
|
||||
var currentToken = User.GetToken();
|
||||
|
||||
|
@ -338,7 +343,7 @@ public class UserController : BaseJellyfinApiController
|
|||
}
|
||||
else
|
||||
{
|
||||
await _userManager.ChangeEasyPassword(user, request.NewPw, request.NewPassword).ConfigureAwait(false);
|
||||
await _userManager.ChangeEasyPassword(user, request.NewPw ?? string.Empty, request.NewPassword ?? string.Empty).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
|
@ -362,13 +367,17 @@ public class UserController : BaseJellyfinApiController
|
|||
[FromRoute, Required] Guid userId,
|
||||
[FromBody, Required] UserDto updateUser)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true))
|
||||
{
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "User update not allowed.");
|
||||
}
|
||||
|
||||
var user = _userManager.GetUserById(userId);
|
||||
|
||||
if (!string.Equals(user.Username, updateUser.Name, StringComparison.Ordinal))
|
||||
{
|
||||
await _userManager.RenameUser(user, updateUser.Name).ConfigureAwait(false);
|
||||
|
@ -398,6 +407,10 @@ public class UserController : BaseJellyfinApiController
|
|||
[FromBody, Required] UserPolicy newPolicy)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// If removing admin access
|
||||
if (!newPolicy.IsAdministrator && user.HasPermission(PermissionKind.IsAdministrator))
|
||||
|
|
|
@ -78,10 +78,18 @@ public class UserLibraryController : BaseJellyfinApiController
|
|||
public async Task<ActionResult<BaseItemDto>> GetItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var item = itemId.Equals(default)
|
||||
? _libraryManager.GetUserRootFolder()
|
||||
: _libraryManager.GetItemById(itemId);
|
||||
if (item is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false);
|
||||
|
||||
|
@ -101,6 +109,11 @@ public class UserLibraryController : BaseJellyfinApiController
|
|||
public ActionResult<BaseItemDto> GetRootFolder([FromRoute, Required] Guid userId)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var item = _libraryManager.GetUserRootFolder();
|
||||
var dtoOptions = new DtoOptions().AddClientFields(User);
|
||||
return _dtoService.GetBaseItemDto(item, dtoOptions, user);
|
||||
|
@ -118,10 +131,18 @@ public class UserLibraryController : BaseJellyfinApiController
|
|||
public async Task<ActionResult<QueryResult<BaseItemDto>>> GetIntros([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var item = itemId.Equals(default)
|
||||
? _libraryManager.GetUserRootFolder()
|
||||
: _libraryManager.GetItemById(itemId);
|
||||
if (item is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false);
|
||||
var dtoOptions = new DtoOptions().AddClientFields(User);
|
||||
|
@ -199,10 +220,18 @@ public class UserLibraryController : BaseJellyfinApiController
|
|||
public ActionResult<IEnumerable<BaseItemDto>> GetLocalTrailers([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var item = itemId.Equals(default)
|
||||
? _libraryManager.GetUserRootFolder()
|
||||
: _libraryManager.GetItemById(itemId);
|
||||
if (item is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var dtoOptions = new DtoOptions().AddClientFields(User);
|
||||
|
||||
|
@ -229,10 +258,18 @@ public class UserLibraryController : BaseJellyfinApiController
|
|||
public ActionResult<IEnumerable<BaseItemDto>> GetSpecialFeatures([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var item = itemId.Equals(default)
|
||||
? _libraryManager.GetUserRootFolder()
|
||||
: _libraryManager.GetItemById(itemId);
|
||||
if (item is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var dtoOptions = new DtoOptions().AddClientFields(User);
|
||||
|
||||
|
@ -274,6 +311,10 @@ public class UserLibraryController : BaseJellyfinApiController
|
|||
[FromQuery] bool groupItems = true)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!isPlayed.HasValue)
|
||||
{
|
||||
|
|
|
@ -155,7 +155,12 @@ public class VideosController : BaseJellyfinApiController
|
|||
|
||||
if (video.LinkedAlternateVersions.Length == 0)
|
||||
{
|
||||
video = (Video)_libraryManager.GetItemById(video.PrimaryVersionId);
|
||||
video = (Video?)_libraryManager.GetItemById(video.PrimaryVersionId);
|
||||
}
|
||||
|
||||
if (video is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
foreach (var link in video.GetLinkedAlternateVersions())
|
||||
|
|
|
@ -200,7 +200,7 @@ public class MediaInfoHelper
|
|||
options.SubtitleStreamIndex = subtitleStreamIndex;
|
||||
}
|
||||
|
||||
var user = _userManager.GetUserById(userId);
|
||||
var user = _userManager.GetUserById(userId) ?? throw new ResourceNotFoundException();
|
||||
|
||||
if (!enableDirectPlay)
|
||||
{
|
||||
|
|
|
@ -81,6 +81,11 @@ public static class RequestHelpers
|
|||
}
|
||||
|
||||
var user = userManager.GetUserById(userId);
|
||||
if (user is null)
|
||||
{
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
return user.EnableUserPreferenceAccess;
|
||||
}
|
||||
|
||||
|
@ -98,7 +103,7 @@ public static class RequestHelpers
|
|||
|
||||
if (session is null)
|
||||
{
|
||||
throw new ArgumentException("Session not found.");
|
||||
throw new ResourceNotFoundException("Session not found.");
|
||||
}
|
||||
|
||||
return session;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace Jellyfin.Api.Models.UserDtos;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Jellyfin.Api.Models.UserDtos;
|
||||
|
||||
/// <summary>
|
||||
/// The create user by name request body.
|
||||
|
@ -8,7 +10,8 @@ public class CreateUserByName
|
|||
/// <summary>
|
||||
/// Gets or sets the username.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
[Required]
|
||||
required public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password.
|
||||
|
|
|
@ -11,5 +11,5 @@ public class ForgotPasswordDto
|
|||
/// Gets or sets the entered username to have its password reset.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string? EnteredUsername { get; set; }
|
||||
required public string EnteredUsername { get; set; }
|
||||
}
|
||||
|
|
|
@ -11,5 +11,5 @@ public class ForgotPasswordPinDto
|
|||
/// Gets or sets the entered pin to have the password reset.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public string? Pin { get; set; }
|
||||
required public string Pin { get; set; }
|
||||
}
|
||||
|
|
|
@ -63,6 +63,11 @@ namespace Jellyfin.Data.Enums
|
|||
/// <summary>
|
||||
/// A list of ordered views.
|
||||
/// </summary>
|
||||
OrderedViews = 11
|
||||
OrderedViews = 11,
|
||||
|
||||
/// <summary>
|
||||
/// A list of allowed tags.
|
||||
/// </summary>
|
||||
AllowedTags = 12
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ using Jellyfin.Data.Enums;
|
|||
using Jellyfin.Data.Events;
|
||||
using Jellyfin.Data.Queries;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Devices;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Model.Devices;
|
||||
|
@ -185,6 +186,10 @@ namespace Jellyfin.Server.Implementations.Devices
|
|||
if (userId.HasValue)
|
||||
{
|
||||
var user = _userManager.GetUserById(userId.Value);
|
||||
if (user is null)
|
||||
{
|
||||
throw new ResourceNotFoundException();
|
||||
}
|
||||
|
||||
sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId));
|
||||
}
|
||||
|
|
|
@ -371,6 +371,7 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing),
|
||||
AccessSchedules = user.AccessSchedules.ToArray(),
|
||||
BlockedTags = user.GetPreference(PreferenceKind.BlockedTags),
|
||||
AllowedTags = user.GetPreference(PreferenceKind.AllowedTags),
|
||||
EnabledChannels = user.GetPreferenceValues<Guid>(PreferenceKind.EnabledChannels),
|
||||
EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices),
|
||||
EnabledFolders = user.GetPreferenceValues<Guid>(PreferenceKind.EnabledFolders),
|
||||
|
@ -696,6 +697,7 @@ namespace Jellyfin.Server.Implementations.Users
|
|||
// TODO: fix this at some point
|
||||
user.SetPreference(PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems ?? Array.Empty<UnratedItem>());
|
||||
user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
|
||||
user.SetPreference(PreferenceKind.AllowedTags, policy.AllowedTags);
|
||||
user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
|
||||
user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
|
||||
user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#nullable disable
|
||||
#pragma warning disable CA1002
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
@ -28,7 +27,7 @@ namespace MediaBrowser.Controller.Dto
|
|||
/// <param name="user">The user.</param>
|
||||
/// <param name="owner">The owner.</param>
|
||||
/// <returns>BaseItemDto.</returns>
|
||||
BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null);
|
||||
BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User? user = null, BaseItem? owner = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base item dtos.
|
||||
|
@ -38,7 +37,7 @@ namespace MediaBrowser.Controller.Dto
|
|||
/// <param name="user">The user.</param>
|
||||
/// <param name="owner">The owner.</param>
|
||||
/// <returns>The <see cref="IReadOnlyList{T}"/> of <see cref="BaseItemDto"/>.</returns>
|
||||
IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null);
|
||||
IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User? user = null, BaseItem? owner = null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item by name dto.
|
||||
|
@ -48,6 +47,6 @@ namespace MediaBrowser.Controller.Dto
|
|||
/// <param name="taggedItems">The list of tagged items.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>The item dto.</returns>
|
||||
BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, User user = null);
|
||||
BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem>? taggedItems, User? user = null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1607,6 +1607,12 @@ namespace MediaBrowser.Controller.Entities
|
|||
return false;
|
||||
}
|
||||
|
||||
var allowedTagsPreference = user.GetPreference(PreferenceKind.AllowedTags);
|
||||
if (allowedTagsPreference.Any() && !allowedTagsPreference.Any(i => Tags.Contains(i, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
EnableTotalRecordCount = true;
|
||||
ExcludeArtistIds = Array.Empty<Guid>();
|
||||
ExcludeInheritedTags = Array.Empty<string>();
|
||||
IncludeInheritedTags = Array.Empty<string>();
|
||||
ExcludeItemIds = Array.Empty<Guid>();
|
||||
ExcludeItemTypes = Array.Empty<BaseItemKind>();
|
||||
ExcludeTags = Array.Empty<string>();
|
||||
|
@ -95,6 +96,8 @@ namespace MediaBrowser.Controller.Entities
|
|||
|
||||
public string[] ExcludeInheritedTags { get; set; }
|
||||
|
||||
public string[] IncludeInheritedTags { get; set; }
|
||||
|
||||
public IReadOnlyList<string> Genres { get; set; }
|
||||
|
||||
public bool? IsSpecialSeason { get; set; }
|
||||
|
@ -368,6 +371,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
}
|
||||
|
||||
ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags);
|
||||
IncludeInheritedTags = user.GetPreference(PreferenceKind.AllowedTags);
|
||||
|
||||
User = user;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
@ -47,14 +45,14 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <param name="id">The id.</param>
|
||||
/// <returns>The user with the specified Id, or <c>null</c> if the user doesn't exist.</returns>
|
||||
/// <exception cref="ArgumentException"><c>id</c> is an empty Guid.</exception>
|
||||
User GetUserById(Guid id);
|
||||
User? GetUserById(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the user by.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>User.</returns>
|
||||
User GetUserByName(string name);
|
||||
User? GetUserByName(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Renames the user.
|
||||
|
@ -128,7 +126,7 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <param name="user">The user.</param>
|
||||
/// <param name="remoteEndPoint">The remote end point.</param>
|
||||
/// <returns>UserDto.</returns>
|
||||
UserDto GetUserDto(User user, string remoteEndPoint = null);
|
||||
UserDto GetUserDto(User user, string? remoteEndPoint = null);
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates the user.
|
||||
|
@ -139,7 +137,7 @@ namespace MediaBrowser.Controller.Library
|
|||
/// <param name="remoteEndPoint">Remove endpoint to use.</param>
|
||||
/// <param name="isUserSession">Specifies if a user session.</param>
|
||||
/// <returns>User wrapped in awaitable task.</returns>
|
||||
Task<User> AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession);
|
||||
Task<User?> AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession);
|
||||
|
||||
/// <summary>
|
||||
/// Starts the forgot password process.
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
@ -9,7 +7,7 @@ namespace MediaBrowser.Controller.Library
|
|||
{
|
||||
public static class LibraryManagerExtensions
|
||||
{
|
||||
public static BaseItem GetItemById(this ILibraryManager manager, string id)
|
||||
public static BaseItem? GetItemById(this ILibraryManager manager, string id)
|
||||
{
|
||||
return manager.GetItemById(new Guid(id));
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace MediaBrowser.Model.Users
|
|||
EnableSharedDeviceControl = true;
|
||||
|
||||
BlockedTags = Array.Empty<string>();
|
||||
AllowedTags = Array.Empty<string>();
|
||||
BlockUnratedItems = Array.Empty<UnratedItem>();
|
||||
|
||||
EnableUserPreferenceAccess = true;
|
||||
|
@ -86,6 +87,8 @@ namespace MediaBrowser.Model.Users
|
|||
|
||||
public string[] BlockedTags { get; set; }
|
||||
|
||||
public string[] AllowedTags { get; set; }
|
||||
|
||||
public bool EnableUserPreferenceAccess { get; set; }
|
||||
|
||||
public AccessSchedule[] AccessSchedules { get; set; }
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup>
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
|
|
23
tests/Directory.Build.props
Normal file
23
tests/Directory.Build.props
Normal file
|
@ -0,0 +1,23 @@
|
|||
<Project>
|
||||
<!-- Sets defaults for all test projects -->
|
||||
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)/jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -5,12 +5,6 @@
|
|||
<ProjectGuid>{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" />
|
||||
|
@ -27,17 +21,6 @@
|
|||
<PackageReference Include="Moq" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../Jellyfin.Api/Jellyfin.Api.csproj" />
|
||||
<ProjectReference Include="../../Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj" />
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
<ProjectGuid>{DF194677-DFD3-42AF-9F75-D44D5A416478}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
@ -22,17 +16,6 @@
|
|||
<PackageReference Include="FsCheck.Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../MediaBrowser.Common/MediaBrowser.Common.csproj" />
|
||||
<ProjectReference Include="../../MediaBrowser.Providers/MediaBrowser.Providers.csproj" />
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
<ProjectGuid>{462584F7-5023-4019-9EAC-B98CA458C0A0}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
|
@ -22,17 +16,6 @@
|
|||
<PackageReference Include="coverlet.collector" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../MediaBrowser.Controller/MediaBrowser.Controller.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
|
@ -17,17 +11,6 @@
|
|||
<PackageReference Include="coverlet.collector" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../Emby.Dlna/Emby.Dlna.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
@ -20,17 +14,6 @@
|
|||
<PackageReference Include="FsCheck.Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../MediaBrowser.Model/MediaBrowser.Model.csproj" />
|
||||
<ProjectReference Include="../../src/Jellyfin.Extensions/Jellyfin.Extensions.csproj" />
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
@ -19,16 +13,6 @@
|
|||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Jellyfin.MediaEncoding.Hls\Jellyfin.MediaEncoding.Hls.csproj" />
|
||||
<ProjectReference Include="..\..\src\Jellyfin.MediaEncoding.Keyframes\Jellyfin.MediaEncoding.Keyframes.csproj" />
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
<RootNamespace>Jellyfin.MediaEncoding.Keyframes</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
@ -20,17 +13,6 @@
|
|||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
<ProjectGuid>{28464062-0939-4AA7-9F7B-24DDDA61A7C0}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Test Data\**\*.*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
@ -31,17 +25,6 @@
|
|||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
|
@ -24,17 +18,6 @@
|
|||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../MediaBrowser.Model/MediaBrowser.Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
<ProjectGuid>{3998657B-1CCC-49DD-A19F-275DC8495F57}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
|
@ -26,15 +20,4 @@
|
|||
<ProjectReference Include="..\..\Emby.Naming\Emby.Naming.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -5,12 +5,6 @@
|
|||
<ProjectGuid>{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
@ -23,17 +17,6 @@
|
|||
<PackageReference Include="Moq" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../Emby.Server.Implementations/Emby.Server.Implementations.csproj" />
|
||||
<ProjectReference Include="../../MediaBrowser.Common/MediaBrowser.Common.csproj" />
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Test Data\**\*.*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
@ -26,17 +20,6 @@
|
|||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../MediaBrowser.Providers/MediaBrowser.Providers.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -5,13 +5,6 @@
|
|||
<ProjectGuid>{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
<RootNamespace>Jellyfin.Server.Implementations.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Test Data\**\*.*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
@ -32,17 +25,6 @@
|
|||
<PackageReference Include="coverlet.collector" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Emby.Server.Implementations\Emby.Server.Implementations.csproj" />
|
||||
<ProjectReference Include="..\..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" />
|
||||
|
|
|
@ -67,4 +67,23 @@ public class XmlTvListingsProviderTests
|
|||
Assert.Equal("https://domain.tld/image.png", program.ImageUrl);
|
||||
Assert.Equal("3297", program.ChannelId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Test Data/LiveTv/Listings/XmlTv/emptycategory.xml")]
|
||||
[InlineData("https://example.com/emptycategory.xml")]
|
||||
public async Task GetProgramsAsync_EmptyCategories_Success(string path)
|
||||
{
|
||||
var info = new ListingsProviderInfo()
|
||||
{
|
||||
Path = path
|
||||
};
|
||||
|
||||
var startDate = new DateTime(2022, 11, 4);
|
||||
var programs = await _xmlTvListingsProvider.GetProgramsAsync(info, "3297", startDate, startDate.AddDays(1), CancellationToken.None);
|
||||
var programsList = programs.ToList();
|
||||
Assert.Single(programsList);
|
||||
var program = programsList[0];
|
||||
Assert.DoesNotContain(program.Genres, g => string.IsNullOrEmpty(g));
|
||||
Assert.Equal("3297", program.ChannelId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<tv date="20221104">
|
||||
<programme channel="3297" start="20221104130000 -0400" stop="20221105235959 -0400">
|
||||
<category lang="en" />
|
||||
<category lang="en">sports</category>
|
||||
</programme>
|
||||
</tv>
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
|||
using Jellyfin.Api.Models.StartupDtos;
|
||||
using Jellyfin.Api.Models.UserDtos;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests
|
||||
|
@ -43,6 +44,33 @@ namespace Jellyfin.Server.Integration.Tests
|
|||
return auth!.AccessToken;
|
||||
}
|
||||
|
||||
public static async Task<UserDto> GetUserDtoAsync(HttpClient client)
|
||||
{
|
||||
using var response = await client.GetAsync("Users/Me").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var userDto = await JsonSerializer.DeserializeAsync<UserDto>(
|
||||
await response.Content.ReadAsStreamAsync().ConfigureAwait(false), JsonDefaults.Options).ConfigureAwait(false);
|
||||
Assert.NotNull(userDto);
|
||||
return userDto;
|
||||
}
|
||||
|
||||
public static async Task<BaseItemDto> GetRootFolderDtoAsync(HttpClient client, Guid userId = default)
|
||||
{
|
||||
if (userId.Equals(default))
|
||||
{
|
||||
var userDto = await GetUserDtoAsync(client).ConfigureAwait(false);
|
||||
userId = userDto.Id;
|
||||
}
|
||||
|
||||
var response = await client.GetAsync($"Users/{userId}/Items/Root").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var rootDto = await JsonSerializer.DeserializeAsync<BaseItemDto>(
|
||||
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||
JsonDefaults.Options).ConfigureAwait(false);
|
||||
Assert.NotNull(rootDto);
|
||||
return rootDto;
|
||||
}
|
||||
|
||||
public static void AddAuthHeader(this HttpHeaders headers, string accessToken)
|
||||
{
|
||||
headers.Add(AuthHeaderName, DummyAuthHeader + $", Token={accessToken}");
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||
|
||||
public sealed class ItemsControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||
private static string? _accessToken;
|
||||
|
||||
public ItemsControllerTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetItems_NoApiKeyOrUserId_BadRequest()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.GetAsync("Items").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Users/{0}/Items")]
|
||||
[InlineData("Users/{0}/Items/Resume")]
|
||||
public async Task GetUserItems_NonExistentUserId_NotFound(string format)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, Guid.NewGuid())).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Items?userId={0}")]
|
||||
[InlineData("Users/{0}/Items")]
|
||||
[InlineData("Users/{0}/Items/Resume")]
|
||||
public async Task GetItems_UserId_Ok(string format)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||
|
||||
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, userDto.Id)).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var items = await JsonSerializer.DeserializeAsync<QueryResult<BaseItemDto>>(
|
||||
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||
_jsonOptions).ConfigureAwait(false);
|
||||
Assert.NotNull(items);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||
|
||||
public sealed class LibraryControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private static string? _accessToken;
|
||||
|
||||
public LibraryControllerTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Items/{0}/File")]
|
||||
[InlineData("Items/{0}/ThemeSongs")]
|
||||
[InlineData("Items/{0}/ThemeVideos")]
|
||||
[InlineData("Items/{0}/ThemeMedia")]
|
||||
[InlineData("Items/{0}/Ancestors")]
|
||||
[InlineData("Items/{0}/Download")]
|
||||
[InlineData("Artists/{0}/Similar")]
|
||||
[InlineData("Items/{0}/Similar")]
|
||||
[InlineData("Albums/{0}/Similar")]
|
||||
[InlineData("Shows/{0}/Similar")]
|
||||
[InlineData("Movies/{0}/Similar")]
|
||||
[InlineData("Trailers/{0}/Similar")]
|
||||
public async Task Get_NonExistentItemId_NotFound(string format)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, Guid.NewGuid())).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||
|
||||
public sealed class MusicGenreControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private static string? _accessToken;
|
||||
|
||||
public MusicGenreControllerTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MusicGenres_FakeMusicGenre_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.GetAsync("MusicGenres/Fake-MusicGenre").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
}
|
|
@ -1,18 +1,13 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
using Xunit.Priority;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||
|
||||
[TestCaseOrderer(PriorityOrderer.Name, PriorityOrderer.Assembly)]
|
||||
public class PlaystateControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private static readonly Guid _testUserId = Guid.NewGuid();
|
||||
private static readonly Guid _testItemId = Guid.NewGuid();
|
||||
private static string? _accessToken;
|
||||
|
||||
public PlaystateControllerTests(JellyfinApplicationFactory factory)
|
||||
|
@ -20,31 +15,47 @@ public class PlaystateControllerTests : IClassFixture<JellyfinApplicationFactory
|
|||
_factory = factory;
|
||||
}
|
||||
|
||||
private Task<HttpResponseMessage> DeleteUserPlayedItems(HttpClient httpClient, Guid userId, Guid itemId)
|
||||
=> httpClient.DeleteAsync($"Users/{userId}/PlayedItems/{itemId}");
|
||||
|
||||
private Task<HttpResponseMessage> PostUserPlayedItems(HttpClient httpClient, Guid userId, Guid itemId)
|
||||
=> httpClient.PostAsync($"Users/{userId}/PlayedItems/{itemId}", null);
|
||||
|
||||
[Fact]
|
||||
[Priority(0)]
|
||||
public async Task DeleteMarkUnplayedItem_DoesNotExist_NotFound()
|
||||
public async Task DeleteMarkUnplayedItem_NonExistentUserId_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var response = await DeleteUserPlayedItems(client, _testUserId, _testItemId).ConfigureAwait(false);
|
||||
using var response = await client.DeleteAsync($"Users/{Guid.NewGuid()}/PlayedItems/{Guid.NewGuid()}").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(0)]
|
||||
public async Task PostMarkPlayedItem_DoesNotExist_NotFound()
|
||||
public async Task PostMarkPlayedItem_NonExistentUserId_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var response = await PostUserPlayedItems(client, _testUserId, _testItemId).ConfigureAwait(false);
|
||||
using var response = await client.PostAsync($"Users/{Guid.NewGuid()}/PlayedItems/{Guid.NewGuid()}", null).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteMarkUnplayedItem_NonExistentItemId_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||
|
||||
using var response = await client.DeleteAsync($"Users/{userDto.Id}/PlayedItems/{Guid.NewGuid()}").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PostMarkPlayedItem_NonExistentItemId_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||
|
||||
using var response = await client.PostAsync($"Users/{userDto.Id}/PlayedItems/{Guid.NewGuid()}", null).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||
|
||||
public class SessionControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private static string? _accessToken;
|
||||
|
||||
public SessionControllerTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetSessions_NonExistentUserId_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
using var response = await client.GetAsync($"Session/Sessions?userId={Guid.NewGuid()}").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
}
|
|
@ -66,6 +66,16 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
|||
Assert.False(users![0].HasConfiguredPassword);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(-1)]
|
||||
public async Task Me_Valid_Success()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
_ = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(0)]
|
||||
public async Task New_Valid_Success()
|
||||
|
@ -108,13 +118,26 @@ namespace Jellyfin.Server.Integration.Tests.Controllers
|
|||
|
||||
var createRequest = new CreateUserByName()
|
||||
{
|
||||
Name = username
|
||||
Name = username!
|
||||
};
|
||||
|
||||
using var response = await CreateUserByName(client, createRequest).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(0)]
|
||||
public async Task Delete_DoesntExist_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
// access token can't be null here as the previous test populated it
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken!);
|
||||
|
||||
using var response = await client.DeleteAsync($"User/{Guid.NewGuid()}").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Priority(1)]
|
||||
public async Task UpdateUserPassword_Valid_Success()
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions.Json;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Querying;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||
|
||||
public sealed class UserLibraryControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||
private static string? _accessToken;
|
||||
|
||||
public UserLibraryControllerTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetRootFolder_NonExistenUserId_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.GetAsync($"Users/{Guid.NewGuid()}/Items/Root").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetRootFolder_UserId_Valid()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
_ = await AuthHelper.GetRootFolderDtoAsync(client).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Users/{0}/Items/{1}")]
|
||||
[InlineData("Users/{0}/Items/{1}/Intros")]
|
||||
[InlineData("Users/{0}/Items/{1}/LocalTrailers")]
|
||||
[InlineData("Users/{0}/Items/{1}/SpecialFeatures")]
|
||||
[InlineData("Users/{0}/Items/{1}/Lyrics")]
|
||||
public async Task GetItem_NonExistenUserId_NotFound(string format)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client).ConfigureAwait(false);
|
||||
|
||||
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, Guid.NewGuid(), rootFolderDto.Id)).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Users/{0}/Items/{1}")]
|
||||
[InlineData("Users/{0}/Items/{1}/Intros")]
|
||||
[InlineData("Users/{0}/Items/{1}/LocalTrailers")]
|
||||
[InlineData("Users/{0}/Items/{1}/SpecialFeatures")]
|
||||
[InlineData("Users/{0}/Items/{1}/Lyrics")]
|
||||
public async Task GetItem_NonExistentItemId_NotFound(string format)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||
|
||||
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, userDto.Id, Guid.NewGuid())).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetItem_UserIdAndItemId_Valid()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client, userDto.Id).ConfigureAwait(false);
|
||||
|
||||
var response = await client.GetAsync($"Users/{userDto.Id}/Items/{rootFolderDto.Id}").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var rootDto = await JsonSerializer.DeserializeAsync<BaseItemDto>(
|
||||
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||
_jsonOptions).ConfigureAwait(false);
|
||||
Assert.NotNull(rootDto);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetIntros_UserIdAndItemId_Valid()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client, userDto.Id).ConfigureAwait(false);
|
||||
|
||||
var response = await client.GetAsync($"Users/{userDto.Id}/Items/{rootFolderDto.Id}/Intros").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var rootDto = await JsonSerializer.DeserializeAsync<QueryResult<BaseItemDto>>(
|
||||
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||
_jsonOptions).ConfigureAwait(false);
|
||||
Assert.NotNull(rootDto);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Users/{0}/Items/{1}/LocalTrailers")]
|
||||
[InlineData("Users/{0}/Items/{1}/SpecialFeatures")]
|
||||
public async Task LocalTrailersAndSpecialFeatures_UserIdAndItemId_Valid(string format)
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var userDto = await AuthHelper.GetUserDtoAsync(client).ConfigureAwait(false);
|
||||
var rootFolderDto = await AuthHelper.GetRootFolderDtoAsync(client, userDto.Id).ConfigureAwait(false);
|
||||
|
||||
var response = await client.GetAsync(string.Format(CultureInfo.InvariantCulture, format, userDto.Id, rootFolderDto.Id)).ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var rootDto = await JsonSerializer.DeserializeAsync<BaseItemDto[]>(
|
||||
await response.Content.ReadAsStreamAsync().ConfigureAwait(false),
|
||||
_jsonOptions).ConfigureAwait(false);
|
||||
Assert.NotNull(rootDto);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
namespace Jellyfin.Server.Integration.Tests.Controllers;
|
||||
|
||||
public sealed class VideosControllerTests : IClassFixture<JellyfinApplicationFactory>
|
||||
{
|
||||
private readonly JellyfinApplicationFactory _factory;
|
||||
private static string? _accessToken;
|
||||
|
||||
public VideosControllerTests(JellyfinApplicationFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAlternateSources_NonExistentItemId_NotFound()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
client.DefaultRequestHeaders.AddAuthHeader(_accessToken ??= await AuthHelper.CompleteStartupAsync(client).ConfigureAwait(false));
|
||||
|
||||
var response = await client.DeleteAsync($"Videos/{Guid.NewGuid()}").ConfigureAwait(false);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" />
|
||||
|
@ -29,17 +24,6 @@
|
|||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../Jellyfin.Server/Jellyfin.Server.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoFixture" />
|
||||
<PackageReference Include="AutoFixture.AutoMoq" />
|
||||
|
@ -22,17 +16,6 @@
|
|||
<PackageReference Include="Moq" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../Jellyfin.Server/Jellyfin.Server.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CodeAnalysisRuleSet>../jellyfin-tests.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="Test Data\**\*.*">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
@ -23,17 +17,6 @@
|
|||
<PackageReference Include="coverlet.collector" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code Analyzers -->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj" />
|
||||
<ProjectReference Include="../../MediaBrowser.Providers/MediaBrowser.Providers.csproj" />
|
||||
|
|
Loading…
Reference in New Issue
Block a user