diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml index 2a1c0e6f22..7617f0f5a2 100644 --- a/.ci/azure-pipelines-main.yml +++ b/.ci/azure-pipelines-main.yml @@ -64,28 +64,28 @@ jobs: arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)' zipAfterPublish: false - - task: PublishPipelineArtifact@0 + - task: PublishPipelineArtifact@1 displayName: 'Publish Artifact Naming' condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release')) inputs: targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/Emby.Naming.dll' artifactName: 'Jellyfin.Naming' - - task: PublishPipelineArtifact@0 + - task: PublishPipelineArtifact@1 displayName: 'Publish Artifact Controller' condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release')) inputs: targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Controller.dll' artifactName: 'Jellyfin.Controller' - - task: PublishPipelineArtifact@0 + - task: PublishPipelineArtifact@1 displayName: 'Publish Artifact Model' condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release')) inputs: targetPath: '$(build.ArtifactStagingDirectory)/Jellyfin.Server/MediaBrowser.Model.dll' artifactName: 'Jellyfin.Model' - - task: PublishPipelineArtifact@0 + - task: PublishPipelineArtifact@1 displayName: 'Publish Artifact Common' condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release')) inputs: diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml index a3c7f8526c..eca8aa90f9 100644 --- a/.ci/azure-pipelines-test.yml +++ b/.ci/azure-pipelines-test.yml @@ -74,7 +74,6 @@ jobs: - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging displayName: 'Run ReportGenerator' - enabled: false inputs: reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml" targetdir: "$(Agent.TempDirectory)/merged/" @@ -84,10 +83,16 @@ jobs: - task: PublishCodeCoverageResults@1 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging displayName: 'Publish Code Coverage' - enabled: false inputs: codeCoverageTool: "cobertura" #summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2 summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml" pathToSources: $(Build.SourcesDirectory) failIfCoverageEmpty: true + + - task: PublishPipelineArtifact@1 + displayName: 'Publish OpenAPI Artifact' + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) + inputs: + targetPath: "tests/Jellyfin.Api.Tests/bin/Release/netcoreapp3.1/openapi.json" + artifactName: 'OpenAPI Spec' diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7ddc49d5ce..09c523eb28 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -17,7 +17,7 @@ "type": "process", "args": [ "test", - "${workspaceFolder}/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj" + "${workspaceFolder}/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj" ], "problemMatcher": "$msCompile" } diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index e383d02b74..f0724b4129 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -57,6 +57,7 @@ - [Larvitar](https://github.com/Larvitar) - [LeoVerto](https://github.com/LeoVerto) - [Liggy](https://github.com/Liggy) + - [lmaonator](https://github.com/lmaonator) - [LogicalPhallacy](https://github.com/LogicalPhallacy) - [loli10K](https://github.com/loli10K) - [lostmypillow](https://github.com/lostmypillow) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index d1e17f4169..fd4244f64d 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -136,8 +136,8 @@ namespace Emby.Naming.Common CleanDateTimes = new[] { - @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19[0-9]{2}|20[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*", - @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9]{2}|20[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*" + @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19[0-9]{2}|20[0-9]{2})(?![0-9]+|\W[0-9]{2}\W[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*", + @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9]{2}|20[0-9]{2})(?![0-9]+|\W[0-9]{2}\W[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*" }; CleanStrings = new[] diff --git a/Jellyfin.Api/Controllers/AlbumsController.cs b/Jellyfin.Api/Controllers/AlbumsController.cs index 190d4bd07c..357f646a2b 100644 --- a/Jellyfin.Api/Controllers/AlbumsController.cs +++ b/Jellyfin.Api/Controllers/AlbumsController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; @@ -52,7 +53,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Albums/{albumId}/Similar")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarAlbums( - [FromRoute] string albumId, + [FromRoute, Required] string albumId, [FromQuery] Guid? userId, [FromQuery] string? excludeArtistIds, [FromQuery] int? limit) @@ -84,7 +85,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Artists/{artistId}/Similar")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarArtists( - [FromRoute] string artistId, + [FromRoute, Required] string artistId, [FromQuery] Guid? userId, [FromQuery] string? excludeArtistIds, [FromQuery] int? limit) diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs index 3f72830cdd..d38214116c 100644 --- a/Jellyfin.Api/Controllers/ArtistsController.cs +++ b/Jellyfin.Api/Controllers/ArtistsController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; @@ -469,7 +470,7 @@ namespace Jellyfin.Api.Controllers /// An containing the artist. [HttpGet("{name}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetArtistByName([FromRoute] string name, [FromQuery] Guid? userId) + public ActionResult GetArtistByName([FromRoute, Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions().AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 48fd198b5d..4efd450316 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Helpers; @@ -91,8 +92,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesAudioFile] public async Task GetAudioStream( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs index bdd7dfd967..33a969f859 100644 --- a/Jellyfin.Api/Controllers/ChannelsController.cs +++ b/Jellyfin.Api/Controllers/ChannelsController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -90,7 +91,7 @@ namespace Jellyfin.Api.Controllers /// Channel features returned. /// An containing the channel features. [HttpGet("{channelId}/Features")] - public ActionResult GetChannelFeatures([FromRoute] string channelId) + public ActionResult GetChannelFeatures([FromRoute, Required] string channelId) { return _channelManager.GetChannelFeatures(channelId); } @@ -114,7 +115,7 @@ namespace Jellyfin.Api.Controllers /// [HttpGet("{channelId}/Items")] public async Task>> GetChannelItems( - [FromRoute] Guid channelId, + [FromRoute, Required] Guid channelId, [FromQuery] Guid? folderId, [FromQuery] Guid? userId, [FromQuery] int? startIndex, diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index c5910d6e81..f78690b063 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/Jellyfin.Api/Controllers/CollectionController.cs @@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task AddToCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string? itemIds) { await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true); return NoContent(); @@ -103,7 +103,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpDelete("{collectionId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) + public async Task RemoveFromCollection([FromRoute, Required] Guid collectionId, [FromQuery, Required] string? itemIds) { await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false); return NoContent(); diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index 606e27970a..8c8c29078c 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -76,7 +76,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Configuration/{key}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesFile(MediaTypeNames.Application.Json)] - public ActionResult GetNamedConfiguration([FromRoute] string? key) + public ActionResult GetNamedConfiguration([FromRoute, Required] string? key) { return _configurationManager.GetConfiguration(key); } @@ -90,7 +90,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Configuration/{key}")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task UpdateNamedConfiguration([FromRoute] string? key) + public async Task UpdateNamedConfiguration([FromRoute, Required] string? key) { var configurationType = _configurationManager.GetConfigurationType(key); var configuration = await JsonSerializer.DeserializeAsync(Request.Body, configurationType, _serializerOptions).ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs index c3b67eec3f..6bb7b19105 100644 --- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs +++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs @@ -43,7 +43,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "displayPreferencesId", Justification = "Imported from ServiceStack")] public ActionResult GetDisplayPreferences( - [FromRoute] string? displayPreferencesId, + [FromRoute, Required] string? displayPreferencesId, [FromQuery] [Required] Guid userId, [FromQuery] [Required] string? client) { @@ -97,7 +97,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "displayPreferencesId", Justification = "Imported from ServiceStack")] public ActionResult UpdateDisplayPreferences( - [FromRoute] string? displayPreferencesId, + [FromRoute, Required] string? displayPreferencesId, [FromQuery, Required] Guid userId, [FromQuery, Required] string? client, [FromBody, Required] DisplayPreferencesDto displayPreferences) diff --git a/Jellyfin.Api/Controllers/DlnaController.cs b/Jellyfin.Api/Controllers/DlnaController.cs index 397299a73f..052a6aff2e 100644 --- a/Jellyfin.Api/Controllers/DlnaController.cs +++ b/Jellyfin.Api/Controllers/DlnaController.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using Jellyfin.Api.Constants; using MediaBrowser.Controller.Dlna; using MediaBrowser.Model.Dlna; @@ -59,7 +60,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Profiles/{profileId}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetProfile([FromRoute] string profileId) + public ActionResult GetProfile([FromRoute, Required] string profileId) { var profile = _dlnaManager.GetProfile(profileId); if (profile == null) @@ -80,7 +81,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete("Profiles/{profileId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteProfile([FromRoute] string profileId) + public ActionResult DeleteProfile([FromRoute, Required] string profileId) { var existingDeviceProfile = _dlnaManager.GetProfile(profileId); if (existingDeviceProfile == null) @@ -117,7 +118,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Profiles/{profileId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateProfile([FromRoute] string profileId, [FromBody] DeviceProfile deviceProfile) + public ActionResult UpdateProfile([FromRoute, Required] string profileId, [FromBody] DeviceProfile deviceProfile) { var existingDeviceProfile = _dlnaManager.GetProfile(profileId); if (existingDeviceProfile == null) diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 1b139000d7..271ae293b4 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net.Mime; @@ -46,7 +47,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [Produces(MediaTypeNames.Text.Xml)] [ProducesFile(MediaTypeNames.Text.Xml)] - public ActionResult GetDescriptionXml([FromRoute] string serverId) + public ActionResult GetDescriptionXml([FromRoute, Required] string serverId) { var url = GetAbsoluteUri(); var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); @@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesFile(MediaTypeNames.Text.Xml)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetContentDirectory([FromRoute] string serverId) + public ActionResult GetContentDirectory([FromRoute, Required] string serverId) { return Ok(_contentDirectory.GetServiceXml()); } @@ -84,7 +85,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesFile(MediaTypeNames.Text.Xml)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetMediaReceiverRegistrar([FromRoute] string serverId) + public ActionResult GetMediaReceiverRegistrar([FromRoute, Required] string serverId) { return Ok(_mediaReceiverRegistrar.GetServiceXml()); } @@ -101,7 +102,7 @@ namespace Jellyfin.Api.Controllers [Produces(MediaTypeNames.Text.Xml)] [ProducesFile(MediaTypeNames.Text.Xml)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] - public ActionResult GetConnectionManager([FromRoute] string serverId) + public ActionResult GetConnectionManager([FromRoute, Required] string serverId) { return Ok(_connectionManager.GetServiceXml()); } @@ -112,7 +113,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Control response. [HttpPost("{serverId}/ContentDirectory/Control")] - public async Task> ProcessContentDirectoryControlRequest([FromRoute] string serverId) + public async Task> ProcessContentDirectoryControlRequest([FromRoute, Required] string serverId) { return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false); } @@ -123,7 +124,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Control response. [HttpPost("{serverId}/ConnectionManager/Control")] - public async Task> ProcessConnectionManagerControlRequest([FromRoute] string serverId) + public async Task> ProcessConnectionManagerControlRequest([FromRoute, Required] string serverId) { return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false); } @@ -134,7 +135,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Control response. [HttpPost("{serverId}/MediaReceiverRegistrar/Control")] - public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute] string serverId) + public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute, Required] string serverId) { return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false); } @@ -191,7 +192,7 @@ namespace Jellyfin.Api.Controllers [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesImageFile] - public ActionResult GetIconId([FromRoute] string serverId, [FromRoute] string fileName) + public ActionResult GetIconId([FromRoute, Required] string serverId, [FromRoute, Required] string fileName) { return GetIconInternal(fileName); } @@ -203,7 +204,7 @@ namespace Jellyfin.Api.Controllers /// Icon stream. [HttpGet("icons/{fileName}")] [ProducesImageFile] - public ActionResult GetIcon([FromRoute] string fileName) + public ActionResult GetIcon([FromRoute, Required] string fileName) { return GetIconInternal(fileName); } diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 759a574b87..a0bd68d8c9 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -169,8 +169,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesPlaylistFile] public async Task GetMasterHlsVideoPlaylist( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -337,8 +337,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesPlaylistFile] public async Task GetMasterHlsAudioPlaylist( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -503,8 +503,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesPlaylistFile] public async Task GetVariantHlsVideoPlaylist( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -669,8 +669,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesPlaylistFile] public async Task GetVariantHlsAudioPlaylist( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -838,10 +838,10 @@ namespace Jellyfin.Api.Controllers [ProducesVideoFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")] public async Task GetHlsVideoSegment( - [FromRoute] Guid itemId, - [FromRoute] string playlistId, - [FromRoute] int segmentId, - [FromRoute] string container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string playlistId, + [FromRoute, Required] int segmentId, + [FromRoute, Required] string container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, @@ -1008,10 +1008,10 @@ namespace Jellyfin.Api.Controllers [ProducesAudioFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "playlistId", Justification = "Imported from ServiceStack")] public async Task GetHlsAudioSegment( - [FromRoute] Guid itemId, - [FromRoute] string playlistId, - [FromRoute] int segmentId, - [FromRoute] string container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string playlistId, + [FromRoute, Required] int segmentId, + [FromRoute, Required] string container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs index 55ad712007..de6aa86c94 100644 --- a/Jellyfin.Api/Controllers/GenresController.cs +++ b/Jellyfin.Api/Controllers/GenresController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; using Jellyfin.Api.Constants; @@ -260,7 +261,7 @@ namespace Jellyfin.Api.Controllers /// An containing the genre. [HttpGet("{genreName}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetGenre([FromRoute] string genreName, [FromQuery] Guid? userId) + public ActionResult GetGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() .AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index e534be50af..054e586ce3 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -57,7 +58,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesAudioFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] - public ActionResult GetHlsAudioSegmentLegacy([FromRoute] string itemId, [FromRoute] string segmentId) + public ActionResult GetHlsAudioSegmentLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string segmentId) { // TODO: Deprecate with new iOS app var file = segmentId + Path.GetExtension(Request.Path); @@ -78,7 +79,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesPlaylistFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] - public ActionResult GetHlsPlaylistLegacy([FromRoute] string itemId, [FromRoute] string playlistId) + public ActionResult GetHlsPlaylistLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string playlistId) { var file = playlistId + Path.GetExtension(Request.Path); file = Path.Combine(_serverConfigurationManager.GetTranscodePath(), file); @@ -118,10 +119,10 @@ namespace Jellyfin.Api.Controllers [ProducesVideoFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] public ActionResult GetHlsVideoSegmentLegacy( - [FromRoute] string itemId, - [FromRoute] string playlistId, - [FromRoute] string segmentId, - [FromRoute] string segmentContainer) + [FromRoute, Required] string itemId, + [FromRoute, Required] string playlistId, + [FromRoute, Required] string segmentId, + [FromRoute, Required] string segmentContainer) { var file = segmentId + Path.GetExtension(Request.Path); var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath(); diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index c012acef37..3006918ca0 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; @@ -91,9 +92,9 @@ namespace Jellyfin.Api.Controllers [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task PostUserImage( - [FromRoute] Guid userId, - [FromRoute] ImageType imageType, - [FromRoute] int? index = null) + [FromRoute, Required] Guid userId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? index = null) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { @@ -138,9 +139,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult DeleteUserImage( - [FromRoute] Guid userId, - [FromRoute] ImageType imageType, - [FromRoute] int? index = null) + [FromRoute, Required] Guid userId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? index = null) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) { @@ -176,9 +177,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DeleteItemImage( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -206,9 +207,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task SetItemImage( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -239,9 +240,9 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateItemImageIndex( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int imageIndex, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int imageIndex, [FromQuery] int newIndex) { var item = _libraryManager.GetItemById(itemId); @@ -265,7 +266,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task>> GetItemImageInfos([FromRoute] Guid itemId) + public async Task>> GetItemImageInfos([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -354,10 +355,10 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesImageFile] public async Task GetItemImage( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -370,7 +371,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -433,23 +434,23 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesImageFile] public async Task GetItemImage2( - [FromRoute] Guid itemId, - [FromRoute] ImageType imageType, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, - [FromRoute] string tag, + [FromRoute, Required] string tag, [FromQuery] bool? cropWhitespace, - [FromRoute] string format, + [FromRoute, Required] string format, [FromQuery] bool? addPlayedIndicator, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -512,14 +513,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesImageFile] public async Task GetArtistImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -528,7 +529,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetArtist(name); if (item == null) @@ -591,14 +592,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesImageFile] public async Task GetGenreImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -607,7 +608,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetGenre(name); if (item == null) @@ -670,14 +671,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesImageFile] public async Task GetMusicGenreImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -686,7 +687,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetMusicGenre(name); if (item == null) @@ -749,14 +750,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesImageFile] public async Task GetPersonImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -765,7 +766,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetPerson(name); if (item == null) @@ -828,14 +829,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesImageFile] public async Task GetStudioImage( - [FromRoute] string name, - [FromRoute] ImageType imageType, - [FromRoute] string tag, - [FromRoute] string format, - [FromRoute] int? maxWidth, - [FromRoute] int? maxHeight, - [FromRoute] double? percentPlayed, - [FromRoute] int? unplayedCount, + [FromRoute, Required] string name, + [FromRoute, Required] ImageType imageType, + [FromRoute, Required] string tag, + [FromRoute, Required] string format, + [FromRoute, Required] int? maxWidth, + [FromRoute, Required] int? maxHeight, + [FromRoute, Required] double? percentPlayed, + [FromRoute, Required] int? unplayedCount, [FromQuery] int? width, [FromQuery] int? height, [FromQuery] int? quality, @@ -844,7 +845,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var item = _libraryManager.GetStudio(name); if (item == null) @@ -907,8 +908,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesImageFile] public async Task GetUserImage( - [FromRoute] Guid userId, - [FromRoute] ImageType imageType, + [FromRoute, Required] Guid userId, + [FromRoute, Required] ImageType imageType, [FromQuery] string? tag, [FromQuery] string? format, [FromQuery] int? maxWidth, @@ -923,7 +924,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? blur, [FromQuery] string? backgroundColor, [FromQuery] string? foregroundLayer, - [FromRoute] int? imageIndex = null) + [FromRoute, Required] int? imageIndex = null) { var user = _userManager.GetUserById(userId); if (user == null) diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index 73bd30c4d1..01bfbba4e7 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -64,7 +64,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Songs/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromSong( - [FromRoute] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -101,7 +101,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Albums/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromAlbum( - [FromRoute] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -138,7 +138,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Playlists/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromPlaylist( - [FromRoute] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -211,7 +211,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Artists/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromArtists( - [FromRoute] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -248,7 +248,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("MusicGenres/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromMusicGenres( - [FromRoute] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, @@ -285,7 +285,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Items/{id}/InstantMix")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetInstantMixFromItem( - [FromRoute] Guid id, + [FromRoute, Required] Guid id, [FromQuery] Guid? userId, [FromQuery] int? limit, [FromQuery] string? fields, diff --git a/Jellyfin.Api/Controllers/ItemLookupController.cs b/Jellyfin.Api/Controllers/ItemLookupController.cs index 6eaa61dfee..cf70386500 100644 --- a/Jellyfin.Api/Controllers/ItemLookupController.cs +++ b/Jellyfin.Api/Controllers/ItemLookupController.cs @@ -74,7 +74,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetExternalIdInfos([FromRoute] Guid itemId) + public ActionResult> GetExternalIdInfos([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -296,7 +296,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task ApplySearchCriteria( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromBody, Required] RemoteSearchResult searchResult, [FromQuery] bool replaceAllImages = true) { diff --git a/Jellyfin.Api/Controllers/ItemRefreshController.cs b/Jellyfin.Api/Controllers/ItemRefreshController.cs index 3f5d305c18..49865eb5ee 100644 --- a/Jellyfin.Api/Controllers/ItemRefreshController.cs +++ b/Jellyfin.Api/Controllers/ItemRefreshController.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using Jellyfin.Api.Constants; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -53,7 +54,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult Post( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] MetadataRefreshMode metadataRefreshMode = MetadataRefreshMode.None, [FromQuery] MetadataRefreshMode imageRefreshMode = MetadataRefreshMode.None, [FromQuery] bool replaceAllMetadata = false, diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index ec52f49964..4308a434df 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -68,7 +68,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request) + public async Task UpdateItem([FromRoute, Required] Guid itemId, [FromBody, Required] BaseItemDto request) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -141,7 +141,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Items/{itemId}/MetadataEditor")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetMetadataEditorInfo([FromRoute] Guid itemId) + public ActionResult GetMetadataEditorInfo([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); @@ -195,7 +195,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}/ContentType")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UpdateItemContentType([FromRoute] Guid itemId, [FromQuery, Required] string? contentType) + public ActionResult UpdateItemContentType([FromRoute, Required] Guid itemId, [FromQuery, Required] string? contentType) { var item = _libraryManager.GetItemById(itemId); if (item == null) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index f9273bad6d..06ab176b2f 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; using Jellyfin.Api.Constants; @@ -144,7 +145,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{uId}/Items", Name = "GetItems_2")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetItems( - [FromRoute] Guid? uId, + [FromRoute, Required] Guid? uId, [FromQuery] Guid? userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, @@ -529,7 +530,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Items/Resume")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetResumeItems( - [FromRoute] Guid userId, + [FromRoute, Required] Guid userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] string? searchTerm, diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 4f48e5dbd1..78d70b04e9 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -106,7 +106,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesFile("video/*", "audio/*")] - public ActionResult GetFile([FromRoute] Guid itemId) + public ActionResult GetFile([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -145,7 +145,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetThemeSongs( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] bool inheritFromParent = false) { @@ -211,7 +211,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult GetThemeVideos( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] bool inheritFromParent = false) { @@ -276,7 +276,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetThemeMedia( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] bool inheritFromParent = false) { @@ -439,7 +439,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetAncestors([FromRoute] Guid itemId, [FromQuery] Guid? userId) + public ActionResult> GetAncestors([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId) { var item = _libraryManager.GetItemById(itemId); @@ -556,7 +556,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Library/Movies/Updated")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult PostUpdatedMovies([FromRoute] string? tmdbId, [FromRoute] string? imdbId) + public ActionResult PostUpdatedMovies([FromRoute, Required] string? tmdbId, [FromRoute, Required] string? imdbId) { var movies = _libraryManager.GetItemList(new InternalItemsQuery { @@ -620,7 +620,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesFile("video/*", "audio/*")] - public async Task GetDownload([FromRoute] Guid itemId) + public async Task GetDownload([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -689,7 +689,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarItems( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] string? excludeArtistIds, [FromQuery] Guid? userId, [FromQuery] int? limit, diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 7d720a5c5e..bbae42157b 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -210,7 +211,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Channels/{channelId}")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult GetChannel([FromRoute] Guid channelId, [FromQuery] Guid? userId) + public ActionResult GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) @@ -407,7 +408,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Recordings/{recordingId}")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult GetRecording([FromRoute] Guid recordingId, [FromQuery] Guid? userId) + public ActionResult GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) @@ -429,7 +430,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Tuners/{tunerId}/Reset")] [ProducesResponseType(StatusCodes.Status204NoContent)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult ResetTuner([FromRoute] string tunerId) + public ActionResult ResetTuner([FromRoute, Required] string tunerId) { AssertUserCanManageLiveTv(); _liveTvManager.ResetTuner(tunerId, CancellationToken.None); @@ -745,7 +746,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetProgram( - [FromRoute] string programId, + [FromRoute, Required] string programId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) @@ -766,7 +767,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteRecording([FromRoute] Guid recordingId) + public ActionResult DeleteRecording([FromRoute, Required] Guid recordingId) { AssertUserCanManageLiveTv(); @@ -793,7 +794,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete("Timers/{timerId}")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task CancelTimer([FromRoute] string timerId) + public async Task CancelTimer([FromRoute, Required] string timerId) { AssertUserCanManageLiveTv(); await _liveTvManager.CancelTimer(timerId).ConfigureAwait(false); @@ -811,7 +812,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")] - public async Task UpdateTimer([FromRoute] string timerId, [FromBody] TimerInfoDto timerInfo) + public async Task UpdateTimer([FromRoute, Required] string timerId, [FromBody] TimerInfoDto timerInfo) { AssertUserCanManageLiveTv(); await _liveTvManager.UpdateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false); @@ -845,7 +846,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task> GetSeriesTimer([FromRoute] string timerId) + public async Task> GetSeriesTimer([FromRoute, Required] string timerId) { var timer = await _liveTvManager.GetSeriesTimer(timerId, CancellationToken.None).ConfigureAwait(false); if (timer == null) @@ -885,7 +886,7 @@ namespace Jellyfin.Api.Controllers [HttpDelete("SeriesTimers/{timerId}")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task CancelSeriesTimer([FromRoute] string timerId) + public async Task CancelSeriesTimer([FromRoute, Required] string timerId) { AssertUserCanManageLiveTv(); await _liveTvManager.CancelSeriesTimer(timerId).ConfigureAwait(false); @@ -903,7 +904,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")] - public async Task UpdateSeriesTimer([FromRoute] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo) + public async Task UpdateSeriesTimer([FromRoute, Required] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo) { AssertUserCanManageLiveTv(); await _liveTvManager.UpdateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false); @@ -935,7 +936,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status404NotFound)] [Obsolete("This endpoint is obsolete.")] - public ActionResult GetRecordingGroup([FromRoute] Guid? groupId) + public ActionResult GetRecordingGroup([FromRoute, Required] Guid? groupId) { return NotFound(); } @@ -1179,7 +1180,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesVideoFile] - public async Task GetLiveRecordingFile([FromRoute] string recordingId) + public async Task GetLiveRecordingFile([FromRoute, Required] string recordingId) { var path = _liveTvManager.GetEmbyTvActiveRecordingPath(recordingId); @@ -1210,7 +1211,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesVideoFile] - public async Task GetLiveStreamFile([FromRoute] string streamId, [FromRoute] string container) + public async Task GetLiveStreamFile([FromRoute, Required] string streamId, [FromRoute, Required] string container) { var liveStreamInfo = await _mediaSourceManager.GetDirectStreamProviderByUniqueId(streamId, CancellationToken.None).ConfigureAwait(false); if (liveStreamInfo == null) diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index 2a204e650e..ee171e8566 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -69,7 +69,7 @@ namespace Jellyfin.Api.Controllers /// A containing a with the playback information. [HttpGet("Items/{itemId}/PlaybackInfo")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetPlaybackInfo([FromRoute] Guid itemId, [FromQuery, Required] Guid? userId) + public async Task> GetPlaybackInfo([FromRoute, Required] Guid itemId, [FromQuery, Required] Guid? userId) { return await _mediaInfoHelper.GetPlaybackInfo( itemId, @@ -101,7 +101,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Items/{itemId}/PlaybackInfo")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetPostedPlaybackInfo( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] Guid? userId, [FromQuery] long? maxStreamingBitrate, [FromQuery] long? startTimeTicks, diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs index 0d319137a4..570ae8fdc7 100644 --- a/Jellyfin.Api/Controllers/MusicGenresController.cs +++ b/Jellyfin.Api/Controllers/MusicGenresController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; using Jellyfin.Api.Constants; @@ -258,7 +259,7 @@ namespace Jellyfin.Api.Controllers /// An containing a with the music genre. [HttpGet("{genreName}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetMusicGenre([FromRoute] string genreName, [FromQuery] Guid? userId) + public ActionResult GetMusicGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions().AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs index 3d6a879093..7e406b418b 100644 --- a/Jellyfin.Api/Controllers/PackageController.cs +++ b/Jellyfin.Api/Controllers/PackageController.cs @@ -44,7 +44,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Packages/{name}")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> GetPackageInfo( - [FromRoute] [Required] string? name, + [FromRoute, Required] string? name, [FromQuery] string? assemblyGuid) { var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false); @@ -84,7 +84,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status404NotFound)] [Authorize(Policy = Policies.RequiresElevation)] public async Task InstallPackage( - [FromRoute] [Required] string? name, + [FromRoute, Required] string? name, [FromQuery] string? assemblyGuid, [FromQuery] string? version) { @@ -115,7 +115,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult CancelPackageInstallation( - [FromRoute] [Required] Guid packageId) + [FromRoute, Required] Guid packageId) { _installationManager.CancelInstallation(packageId); return NoContent(); diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index b6ccec666a..8bd610dad9 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; using Jellyfin.Api.Constants; @@ -262,7 +263,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{name}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetPerson([FromRoute] string name, [FromQuery] Guid? userId) + public ActionResult GetPerson([FromRoute, Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() .AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index f4c6a9253d..69e0b8e07b 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -84,7 +84,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task AddToPlaylist( - [FromRoute] Guid playlistId, + [FromRoute, Required] Guid playlistId, [FromQuery] string? ids, [FromQuery] Guid? userId) { @@ -103,9 +103,9 @@ namespace Jellyfin.Api.Controllers [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task MoveItem( - [FromRoute] string? playlistId, - [FromRoute] string? itemId, - [FromRoute] int newIndex) + [FromRoute, Required] string? playlistId, + [FromRoute, Required] string? itemId, + [FromRoute, Required] int newIndex) { await _playlistManager.MoveItemAsync(playlistId, itemId, newIndex).ConfigureAwait(false); return NoContent(); @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers /// An on success. [HttpDelete("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds) + public async Task RemoveFromPlaylist([FromRoute, Required] string? playlistId, [FromQuery] string? entryIds) { await _playlistManager.RemoveFromPlaylistAsync(playlistId, RequestHelpers.Split(entryIds, ',', true)).ConfigureAwait(false); return NoContent(); @@ -143,15 +143,15 @@ namespace Jellyfin.Api.Controllers /// The original playlist items. [HttpGet("{playlistId}/Items")] public ActionResult> GetPlaylistItems( - [FromRoute] Guid playlistId, - [FromRoute] Guid userId, - [FromRoute] int? startIndex, - [FromRoute] int? limit, - [FromRoute] string? fields, - [FromRoute] bool? enableImages, - [FromRoute] bool? enableUserData, - [FromRoute] int? imageTypeLimit, - [FromRoute] string? enableImageTypes) + [FromRoute, Required] Guid playlistId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] int? startIndex, + [FromRoute, Required] int? limit, + [FromRoute, Required] string? fields, + [FromRoute, Required] bool? enableImages, + [FromRoute, Required] bool? enableUserData, + [FromRoute, Required] int? imageTypeLimit, + [FromRoute, Required] string? enableImageTypes) { var playlist = (Playlist)_libraryManager.GetItemById(playlistId); if (playlist == null) diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 22f2ca5c32..5c15e9a0d7 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Jellyfin.Api.Constants; @@ -71,8 +72,8 @@ namespace Jellyfin.Api.Controllers [HttpPost("Users/{userId}/PlayedItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult MarkPlayedItem( - [FromRoute] Guid userId, - [FromRoute] Guid itemId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, [FromQuery] DateTime? datePlayed) { var user = _userManager.GetUserById(userId); @@ -96,7 +97,7 @@ namespace Jellyfin.Api.Controllers /// A containing the . [HttpDelete("Users/{userId}/PlayedItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult MarkUnplayedItem([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult MarkUnplayedItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); var session = RequestHelpers.GetSession(_sessionManager, _authContext, Request); @@ -195,8 +196,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackStart( - [FromRoute] Guid userId, - [FromRoute] Guid itemId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] int? audioStreamIndex, [FromQuery] int? subtitleStreamIndex, @@ -245,8 +246,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackProgress( - [FromRoute] Guid userId, - [FromRoute] Guid itemId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] long? positionTicks, [FromQuery] int? audioStreamIndex, @@ -297,8 +298,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Required for ServiceStack")] public async Task OnPlaybackStopped( - [FromRoute] Guid userId, - [FromRoute] Guid itemId, + [FromRoute, Required] Guid userId, + [FromRoute, Required] Guid itemId, [FromQuery] string? mediaSourceId, [FromQuery] string? nextMediaType, [FromQuery] long? positionTicks, diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index a82f2621af..342b0328d2 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -64,7 +64,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult UninstallPlugin([FromRoute] Guid pluginId) + public ActionResult UninstallPlugin([FromRoute, Required] Guid pluginId) { var plugin = _appHost.Plugins.FirstOrDefault(p => p.Id == pluginId); if (plugin == null) @@ -86,7 +86,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{pluginId}/Configuration")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetPluginConfiguration([FromRoute] Guid pluginId) + public ActionResult GetPluginConfiguration([FromRoute, Required] Guid pluginId) { if (!(_appHost.Plugins.FirstOrDefault(p => p.Id == pluginId) is IHasPluginConfiguration plugin)) { @@ -113,7 +113,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("{pluginId}/Configuration")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task UpdatePluginConfiguration([FromRoute] Guid pluginId) + public async Task UpdatePluginConfiguration([FromRoute, Required] Guid pluginId) { if (!(_appHost.Plugins.FirstOrDefault(p => p.Id == pluginId) is IHasPluginConfiguration plugin)) { @@ -172,7 +172,7 @@ namespace Jellyfin.Api.Controllers [Obsolete("This endpoint should not be used.")] [HttpPost("RegistrationRecords/{name}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetRegistrationStatus([FromRoute] string? name) + public ActionResult GetRegistrationStatus([FromRoute, Required] string? name) { return new MBRegistrationRecord { @@ -194,7 +194,7 @@ namespace Jellyfin.Api.Controllers [Obsolete("Paid plugins are not supported")] [HttpGet("Registrations/{name}")] [ProducesResponseType(StatusCodes.Status501NotImplemented)] - public ActionResult GetRegistration([FromRoute] string? name) + public ActionResult GetRegistration([FromRoute, Required] string? name) { // TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins, // delete all these registration endpoints. They are only kept for compatibility. diff --git a/Jellyfin.Api/Controllers/RemoteImageController.cs b/Jellyfin.Api/Controllers/RemoteImageController.cs index 5975964220..5f095443b9 100644 --- a/Jellyfin.Api/Controllers/RemoteImageController.cs +++ b/Jellyfin.Api/Controllers/RemoteImageController.cs @@ -71,7 +71,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> GetRemoteImages( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] ImageType? type, [FromQuery] int? startIndex, [FromQuery] int? limit, @@ -134,7 +134,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetRemoteImageProviders([FromRoute] Guid itemId) + public ActionResult> GetRemoteImageProviders([FromRoute, Required] Guid itemId) { var item = _libraryManager.GetItemById(itemId); if (item == null) @@ -211,7 +211,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task DownloadRemoteImage( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery, Required] ImageType type, [FromQuery] string? imageUrl) { diff --git a/Jellyfin.Api/Controllers/ScheduledTasksController.cs b/Jellyfin.Api/Controllers/ScheduledTasksController.cs index e672070c06..3206f27347 100644 --- a/Jellyfin.Api/Controllers/ScheduledTasksController.cs +++ b/Jellyfin.Api/Controllers/ScheduledTasksController.cs @@ -94,7 +94,7 @@ namespace Jellyfin.Api.Controllers [HttpPost("Running/{taskId}")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult StartTask([FromRoute] string? taskId) + public ActionResult StartTask([FromRoute, Required] string? taskId) { var task = _taskManager.ScheduledTasks.FirstOrDefault(o => o.Id.Equals(taskId, StringComparison.OrdinalIgnoreCase)); diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index ba8d515988..cff7c15019 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -336,7 +336,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult AddUserToSession( [FromRoute, Required] string? sessionId, - [FromRoute] Guid userId) + [FromRoute, Required] Guid userId) { _sessionManager.AddAdditionalUser(sessionId, userId); return NoContent(); @@ -353,8 +353,8 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult RemoveUserFromSession( - [FromRoute] string? sessionId, - [FromRoute] Guid userId) + [FromRoute, Required] string? sessionId, + [FromRoute, Required] Guid userId) { _sessionManager.RemoveAdditionalUser(sessionId, userId); return NoContent(); diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs index 6f2787d933..cdd5f958e9 100644 --- a/Jellyfin.Api/Controllers/StudiosController.cs +++ b/Jellyfin.Api/Controllers/StudiosController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; @@ -259,7 +260,7 @@ namespace Jellyfin.Api.Controllers /// An containing the studio. [HttpGet("{name}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetStudio([FromRoute] string name, [FromQuery] Guid? userId) + public ActionResult GetStudio([FromRoute, Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions().AddClientFields(Request); diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index a19e49f11a..ff26ca921d 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -87,8 +87,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult DeleteSubtitle( - [FromRoute] Guid itemId, - [FromRoute] int index) + [FromRoute, Required] Guid itemId, + [FromRoute, Required] int index) { var item = _libraryManager.GetItemById(itemId); @@ -113,7 +113,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] public async Task>> SearchRemoteSubtitles( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromRoute, Required] string? language, [FromQuery] bool? isPerfectMatch) { @@ -133,7 +133,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task DownloadRemoteSubtitles( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromRoute, Required] string? subtitleId) { var video = (Video)_libraryManager.GetItemById(itemId); @@ -196,7 +196,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] long? endPositionTicks, [FromQuery] bool copyTimestamps = false, [FromQuery] bool addVttTimeMap = false, - [FromRoute] long startPositionTicks = 0) + [FromRoute, Required] long startPositionTicks = 0) { if (string.Equals(format, "js", StringComparison.OrdinalIgnoreCase)) { @@ -256,9 +256,9 @@ namespace Jellyfin.Api.Controllers [ProducesPlaylistFile] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] public async Task GetSubtitlePlaylist( - [FromRoute] Guid itemId, - [FromRoute] int index, - [FromRoute] string? mediaSourceId, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] int index, + [FromRoute, Required] string? mediaSourceId, [FromQuery, Required] int segmentLength) { var item = (Video)_libraryManager.GetItemById(itemId); diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index 42db6b6a11..d7c81a3ab6 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/Jellyfin.Api/Controllers/SuggestionsController.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; @@ -53,7 +54,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Suggestions")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSuggestions( - [FromRoute] Guid userId, + [FromRoute, Required] Guid userId, [FromQuery] string? mediaType, [FromQuery] string? type, [FromQuery] int? startIndex, diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index b00653d9bd..6bbdbc6137 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; using System.Threading.Tasks; @@ -94,8 +95,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status302Found)] [ProducesAudioFile] public async Task GetUniversalAudioStream( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] string? mediaSourceId, [FromQuery] string? deviceId, [FromQuery] Guid? userId, diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 746710475f..435588146c 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.IgnoreParentalControl)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetUserById([FromRoute] Guid userId) + public ActionResult GetUserById([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); @@ -132,7 +132,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteUser([FromRoute] Guid userId) + public ActionResult DeleteUser([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); _sessionManager.RevokeUserTokens(user.Id, null); @@ -265,7 +265,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task UpdateUserPassword( - [FromRoute] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UpdateUserPassword request) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) @@ -323,7 +323,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult UpdateUserEasyPassword( - [FromRoute] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UpdateUserEasyPassword request) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true)) @@ -365,7 +365,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task UpdateUser( - [FromRoute] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UserDto updateUser) { if (updateUser == null) @@ -409,7 +409,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult UpdateUserPolicy( - [FromRoute] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UserPolicy newPolicy) { if (newPolicy == null) @@ -464,7 +464,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] public ActionResult UpdateUserConfiguration( - [FromRoute] Guid userId, + [FromRoute, Required] Guid userId, [FromBody] UserConfiguration userConfig) { if (!RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false)) diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index f55ff6f3d5..48262f0620 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -70,7 +71,7 @@ namespace Jellyfin.Api.Controllers /// An containing the d item. [HttpGet("Users/{userId}/Items/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task> GetItem([FromRoute] Guid userId, [FromRoute] Guid itemId) + public async Task> GetItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -93,7 +94,7 @@ namespace Jellyfin.Api.Controllers /// An containing the user's root folder. [HttpGet("Users/{userId}/Items/Root")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetRootFolder([FromRoute] Guid userId) + public ActionResult GetRootFolder([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); var item = _libraryManager.GetUserRootFolder(); @@ -110,7 +111,7 @@ namespace Jellyfin.Api.Controllers /// An containing the intros to play. [HttpGet("Users/{userId}/Items/{itemId}/Intros")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task>> GetIntros([FromRoute] Guid userId, [FromRoute] Guid itemId) + public async Task>> GetIntros([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -138,7 +139,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpPost("Users/{userId}/FavoriteItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult MarkFavoriteItem([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult MarkFavoriteItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { return MarkFavorite(userId, itemId, true); } @@ -152,7 +153,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpDelete("Users/{userId}/FavoriteItems/{itemId}")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult UnmarkFavoriteItem([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult UnmarkFavoriteItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { return MarkFavorite(userId, itemId, false); } @@ -166,7 +167,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpDelete("Users/{userId}/Items/{itemId}/Rating")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult DeleteUserItemRating([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult DeleteUserItemRating([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { return UpdateUserItemRatingInternal(userId, itemId, null); } @@ -181,7 +182,7 @@ namespace Jellyfin.Api.Controllers /// An containing the . [HttpPost("Users/{userId}/Items/{itemId}/Rating")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult UpdateUserItemRating([FromRoute] Guid userId, [FromRoute] Guid itemId, [FromQuery] bool? likes) + public ActionResult UpdateUserItemRating([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId, [FromQuery] bool? likes) { return UpdateUserItemRatingInternal(userId, itemId, likes); } @@ -195,7 +196,7 @@ namespace Jellyfin.Api.Controllers /// The items local trailers. [HttpGet("Users/{userId}/Items/{itemId}/LocalTrailers")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetLocalTrailers([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult> GetLocalTrailers([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -230,7 +231,7 @@ namespace Jellyfin.Api.Controllers /// An containing the special features. [HttpGet("Users/{userId}/Items/{itemId}/SpecialFeatures")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetSpecialFeatures([FromRoute] Guid userId, [FromRoute] Guid itemId) + public ActionResult> GetSpecialFeatures([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -264,7 +265,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Items/Latest")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetLatestMedia( - [FromRoute] Guid userId, + [FromRoute, Required] Guid userId, [FromQuery] Guid? parentId, [FromQuery] string? fields, [FromQuery] string? includeItemTypes, diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 6df7cc779f..d575bfc3b8 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; using Jellyfin.Api.Extensions; @@ -64,7 +65,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/Views")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetUserViews( - [FromRoute] Guid userId, + [FromRoute, Required] Guid userId, [FromQuery] bool? includeExternalContent, [FromQuery] string? presetViews, [FromQuery] bool includeHidden = false) @@ -126,7 +127,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Users/{userId}/GroupingOptions")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult> GetGroupingOptions([FromRoute] Guid userId) + public ActionResult> GetGroupingOptions([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); if (user == null) diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 92e82727e6..2afa878f41 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Globalization; using System.IO; using System.Threading; @@ -164,7 +165,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesPlaylistFile] public async Task GetLiveHlsStream( - [FromRoute] Guid itemId, + [FromRoute, Required] Guid itemId, [FromQuery] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 2bf5b48569..c07ee5a9c1 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -116,7 +116,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{itemId}/AdditionalParts")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetAdditionalPart([FromRoute] Guid itemId, [FromQuery] Guid? userId) + public ActionResult> GetAdditionalPart([FromRoute, Required] Guid itemId, [FromQuery] Guid? userId) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) @@ -163,7 +163,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeleteAlternateSources([FromRoute] Guid itemId) + public async Task DeleteAlternateSources([FromRoute, Required] Guid itemId) { var video = (Video)_libraryManager.GetItemById(itemId); @@ -333,8 +333,8 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] [ProducesVideoFile] public async Task GetVideoStream( - [FromRoute] Guid itemId, - [FromRoute] string? container, + [FromRoute, Required] Guid itemId, + [FromRoute, Required] string? container, [FromQuery] bool? @static, [FromQuery] string? @params, [FromQuery] string? tag, diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs index eb91ac23e9..4ecf0407bf 100644 --- a/Jellyfin.Api/Controllers/YearsController.cs +++ b/Jellyfin.Api/Controllers/YearsController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; @@ -179,7 +180,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("{year}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult GetYear([FromRoute] int year, [FromQuery] Guid? userId) + public ActionResult GetYear([FromRoute, Required] int year, [FromQuery] Guid? userId) { var item = _libraryManager.GetYear(year); if (item == null) diff --git a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs index deb54dbe61..884bfbe447 100644 --- a/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs +++ b/Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs @@ -72,7 +72,7 @@ namespace Jellyfin.Api.Helpers return new NoContentResult(); } - return new PhysicalFileResult(path, contentType); + return new PhysicalFileResult(path, contentType) { EnableRangeProcessing = true }; } /// diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index b12590080c..c11799bc8c 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -92,6 +92,7 @@ namespace Jellyfin.Api.Helpers } var enableDlnaHeaders = !string.IsNullOrWhiteSpace(streamingRequest.Params) || + streamingRequest.StreamOptions.ContainsKey("dlnaheaders") || string.Equals(httpRequest.Headers["GetContentFeatures.DLNA.ORG"], "1", StringComparison.OrdinalIgnoreCase); var state = new StreamState(mediaSourceManager, transcodingJobType, transcodingJobHelper) diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs index 3858916d15..620e828306 100644 --- a/Jellyfin.Data/Entities/ActivityLog.cs +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -11,7 +9,7 @@ namespace Jellyfin.Data.Entities /// /// An entity referencing an activity log entry. /// - public partial class ActivityLog : IHasConcurrencyToken + public class ActivityLog : IHasConcurrencyToken { /// /// Initializes a new instance of the class. @@ -32,13 +30,11 @@ namespace Jellyfin.Data.Entities throw new ArgumentNullException(nameof(type)); } - this.Name = name; - this.Type = type; - this.UserId = userId; - this.DateCreated = DateTime.UtcNow; - this.LogSeverity = LogLevel.Trace; - - Init(); + Name = name; + Type = type; + UserId = userId; + DateCreated = DateTime.UtcNow; + LogSeverity = LogLevel.Trace; } /// @@ -47,38 +43,21 @@ namespace Jellyfin.Data.Entities /// protected ActivityLog() { - Init(); } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The name. - /// The type. - /// The user's id. - /// The new instance. - public static ActivityLog Create(string name, string type, Guid userId) - { - return new ActivityLog(name, type, userId); - } - - /************************************************************************* - * Properties - *************************************************************************/ - /// /// Gets or sets the identity of this instance. /// This is the key in the backing database. /// - [Key] - [Required] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } /// /// Gets or sets the name. - /// Required, Max length = 512. /// + /// + /// Required, Max length = 512. + /// [Required] [MaxLength(512)] [StringLength(512)] @@ -86,24 +65,30 @@ namespace Jellyfin.Data.Entities /// /// Gets or sets the overview. - /// Max length = 512. /// + /// + /// Max length = 512. + /// [MaxLength(512)] [StringLength(512)] public string Overview { get; set; } /// /// Gets or sets the short overview. - /// Max length = 512. /// + /// + /// Max length = 512. + /// [MaxLength(512)] [StringLength(512)] public string ShortOverview { get; set; } /// /// Gets or sets the type. - /// Required, Max length = 256. /// + /// + /// Required, Max length = 256. + /// [Required] [MaxLength(256)] [StringLength(256)] @@ -111,43 +96,42 @@ namespace Jellyfin.Data.Entities /// /// Gets or sets the user id. - /// Required. /// - [Required] + /// + /// Required. + /// public Guid UserId { get; set; } /// /// Gets or sets the item id. - /// Max length = 256. /// + /// + /// Max length = 256. + /// [MaxLength(256)] [StringLength(256)] public string ItemId { get; set; } /// /// Gets or sets the date created. This should be in UTC. - /// Required. /// - [Required] + /// + /// Required. + /// public DateTime DateCreated { get; set; } /// /// Gets or sets the log severity. Default is . - /// Required. /// - [Required] + /// + /// Required. + /// public LogLevel LogSeverity { get; set; } - /// - /// Gets or sets the row version. - /// Required, ConcurrencyToken. - /// + /// [ConcurrencyCheck] - [Required] public uint RowVersion { get; set; } - partial void Init(); - /// public void OnSavingChanges() { diff --git a/Jellyfin.Data/Entities/DisplayPreferences.cs b/Jellyfin.Data/Entities/DisplayPreferences.cs index cda83f6bb7..701e4df004 100644 --- a/Jellyfin.Data/Entities/DisplayPreferences.cs +++ b/Jellyfin.Data/Entities/DisplayPreferences.cs @@ -1,4 +1,6 @@ -using System; +#pragma warning disable CA2227 + +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs index ca12ba4213..878811e59c 100644 --- a/Jellyfin.Data/Entities/Group.cs +++ b/Jellyfin.Data/Entities/Group.cs @@ -1,9 +1,8 @@ -#pragma warning disable CS1591 +#pragma warning disable CA2227 using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Jellyfin.Data.Enums; using Jellyfin.Data.Interfaces; @@ -13,11 +12,10 @@ namespace Jellyfin.Data.Entities /// /// An entity representing a group. /// - public partial class Group : IHasPermissions, IHasConcurrencyToken + public class Group : IHasPermissions, IHasConcurrencyToken { /// /// Initializes a new instance of the class. - /// Public constructor with required data. /// /// The name of the group. public Group(string name) @@ -31,33 +29,25 @@ namespace Jellyfin.Data.Entities Id = Guid.NewGuid(); Permissions = new HashSet(); - ProviderMappings = new HashSet(); Preferences = new HashSet(); - - Init(); } /// /// Initializes a new instance of the class. - /// Default constructor. Protected due to required properties, but present because EF needs it. /// + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// protected Group() { - Init(); } - /************************************************************************* - * Properties - *************************************************************************/ - /// /// Gets or sets the id of this group. /// /// /// Identity, Indexed, Required. /// - [Key] - [Required] public Guid Id { get; protected set; } /// @@ -71,42 +61,19 @@ namespace Jellyfin.Data.Entities [StringLength(255)] public string Name { get; set; } - /// - /// Gets or sets the row version. - /// - /// - /// Required, Concurrency Token. - /// + /// [ConcurrencyCheck] - [Required] public uint RowVersion { get; set; } - public void OnSavingChanges() - { - RowVersion++; - } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - - [ForeignKey("Permission_GroupPermissions_Id")] + /// + /// Gets or sets a collection containing the group's permissions. + /// public virtual ICollection Permissions { get; protected set; } - [ForeignKey("ProviderMapping_ProviderMappings_Id")] - public virtual ICollection ProviderMappings { get; protected set; } - - [ForeignKey("Preference_Preferences_Id")] - public virtual ICollection Preferences { get; protected set; } - /// - /// Static create function (for use in LINQ queries, etc.) + /// Gets or sets a collection containing the group's preferences. /// - /// The name of this group. - public static Group Create(string name) - { - return new Group(name); - } + public virtual ICollection Preferences { get; protected set; } /// public bool HasPermission(PermissionKind kind) @@ -120,6 +87,10 @@ namespace Jellyfin.Data.Entities Permissions.First(p => p.Kind == kind).Value = value; } - partial void Init(); + /// + public void OnSavingChanges() + { + RowVersion++; + } } } diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs index cf0895ad43..ab8452e62c 100644 --- a/Jellyfin.Data/Entities/ImageInfo.cs +++ b/Jellyfin.Data/Entities/ImageInfo.cs @@ -1,32 +1,65 @@ -#pragma warning disable CS1591 - -using System; +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Jellyfin.Data.Entities { + /// + /// An entity representing an image. + /// public class ImageInfo { + /// + /// Initializes a new instance of the class. + /// + /// The path. public ImageInfo(string path) { Path = path; LastModified = DateTime.UtcNow; } - [Key] - [Required] + /// + /// Initializes a new instance of the class. + /// + /// + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected ImageInfo() + { + } + + /// + /// Gets or sets the id. + /// + /// + /// Identity, Indexed, Required. + /// [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } + /// + /// Gets or sets the user id. + /// public Guid? UserId { get; protected set; } + /// + /// Gets or sets the path of the image. + /// + /// + /// Required. + /// [Required] [MaxLength(512)] [StringLength(512)] public string Path { get; set; } - [Required] + /// + /// Gets or sets the date last modified. + /// + /// + /// Required. + /// public DateTime LastModified { get; set; } } } diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs index 023cdc7400..d81e4a31c4 100644 --- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs @@ -1,12 +1,13 @@ -#pragma warning disable CS1591 - -using System; +using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Enums; namespace Jellyfin.Data.Entities { + /// + /// An entity that represents a user's display preferences for a specific item. + /// public class ItemDisplayPreferences { /// diff --git a/Jellyfin.Data/Entities/Libraries/Artwork.cs b/Jellyfin.Data/Entities/Libraries/Artwork.cs index 7be22af254..06cd333301 100644 --- a/Jellyfin.Data/Entities/Libraries/Artwork.cs +++ b/Jellyfin.Data/Entities/Libraries/Artwork.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Libraries/Book.cs b/Jellyfin.Data/Entities/Libraries/Book.cs index 8337788ddd..2e63f75bd9 100644 --- a/Jellyfin.Data/Entities/Libraries/Book.cs +++ b/Jellyfin.Data/Entities/Libraries/Book.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System.Collections.Generic; using Jellyfin.Data.Interfaces; diff --git a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs index bd716712b1..4a3d290f01 100644 --- a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -8,7 +10,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity containing metadata for a book. /// - public class BookMetadata : Metadata, IHasCompanies + public class BookMetadata : ItemMetadata, IHasCompanies { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Libraries/Chapter.cs b/Jellyfin.Data/Entities/Libraries/Chapter.cs index d9293c3cc4..f503de3794 100644 --- a/Jellyfin.Data/Entities/Libraries/Chapter.cs +++ b/Jellyfin.Data/Entities/Libraries/Chapter.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Libraries/Collection.cs b/Jellyfin.Data/Entities/Libraries/Collection.cs index 2e1bbcfb60..39eded752d 100644 --- a/Jellyfin.Data/Entities/Libraries/Collection.cs +++ b/Jellyfin.Data/Entities/Libraries/Collection.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs index c9306f630a..4467c9bbdc 100644 --- a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs @@ -73,7 +73,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Gets or sets the next item in the collection. /// /// - /// TODO check if this properly updated dependant and has the proper principal relationship + /// TODO check if this properly updated dependant and has the proper principal relationship. /// public virtual CollectionItem Next { get; set; } @@ -81,7 +81,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Gets or sets the previous item in the collection. /// /// - /// TODO check if this properly updated dependant and has the proper principal relationship + /// TODO check if this properly updated dependant and has the proper principal relationship. /// public virtual CollectionItem Previous { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/Company.cs b/Jellyfin.Data/Entities/Libraries/Company.cs index 02da26bc22..3b6ed3309a 100644 --- a/Jellyfin.Data/Entities/Libraries/Company.cs +++ b/Jellyfin.Data/Entities/Libraries/Company.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; diff --git a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs index 60cc96a340..8aa0486afa 100644 --- a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs @@ -6,7 +6,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity holding metadata for a . /// - public class CompanyMetadata : Metadata + public class CompanyMetadata : ItemMetadata { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Libraries/CustomItem.cs b/Jellyfin.Data/Entities/Libraries/CustomItem.cs index 6a4f0a5378..115489c786 100644 --- a/Jellyfin.Data/Entities/Libraries/CustomItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CustomItem.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System.Collections.Generic; using Jellyfin.Data.Interfaces; diff --git a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs index bc18355281..f0daedfbe8 100644 --- a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs @@ -5,7 +5,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity containing metadata for a custom item. /// - public class CustomItemMetadata : Metadata + public class CustomItemMetadata : ItemMetadata { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Libraries/Episode.cs b/Jellyfin.Data/Entities/Libraries/Episode.cs index 430a11e3c8..0bdc2d7642 100644 --- a/Jellyfin.Data/Entities/Libraries/Episode.cs +++ b/Jellyfin.Data/Entities/Libraries/Episode.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; using Jellyfin.Data.Interfaces; diff --git a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs index 348100cb4c..7efb840f0b 100644 --- a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs @@ -6,7 +6,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity containing metadata for an . /// - public class EpisodeMetadata : Metadata + public class EpisodeMetadata : ItemMetadata { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Libraries/Genre.cs b/Jellyfin.Data/Entities/Libraries/Genre.cs index aeedd7bfde..2a2dbd1a5d 100644 --- a/Jellyfin.Data/Entities/Libraries/Genre.cs +++ b/Jellyfin.Data/Entities/Libraries/Genre.cs @@ -14,8 +14,8 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The name. - /// The metadata. - public Genre(string name, Metadata metadata) + /// The metadata. + public Genre(string name, ItemMetadata itemMetadata) { if (string.IsNullOrEmpty(name)) { @@ -24,12 +24,12 @@ namespace Jellyfin.Data.Entities.Libraries Name = name; - if (metadata == null) + if (itemMetadata == null) { - throw new ArgumentNullException(nameof(metadata)); + throw new ArgumentNullException(nameof(itemMetadata)); } - metadata.Genres.Add(this); + itemMetadata.Genres.Add(this); } /// diff --git a/Jellyfin.Data/Entities/Libraries/Metadata.cs b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs similarity index 92% rename from Jellyfin.Data/Entities/Libraries/Metadata.cs rename to Jellyfin.Data/Entities/Libraries/ItemMetadata.cs index 877bb5fbdc..1d2dc0f669 100644 --- a/Jellyfin.Data/Entities/Libraries/Metadata.cs +++ b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -9,14 +11,14 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An abstract class that holds metadata. /// - public abstract class Metadata : IHasArtwork, IHasConcurrencyToken + public abstract class ItemMetadata : IHasArtwork, IHasConcurrencyToken { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The title or name of the object. /// ISO-639-3 3-character language codes. - protected Metadata(string title, string language) + protected ItemMetadata(string title, string language) { if (string.IsNullOrEmpty(title)) { @@ -41,12 +43,12 @@ namespace Jellyfin.Data.Entities.Libraries } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// Default constructor. Protected due to being abstract. /// - protected Metadata() + protected ItemMetadata() { } diff --git a/Jellyfin.Data/Entities/Libraries/MediaFile.cs b/Jellyfin.Data/Entities/Libraries/MediaFile.cs index 8bc649c98f..9924d5728d 100644 --- a/Jellyfin.Data/Entities/Libraries/MediaFile.cs +++ b/Jellyfin.Data/Entities/Libraries/MediaFile.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs index 6e6de598e8..fcfb35bfac 100644 --- a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs +++ b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs @@ -14,8 +14,8 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The provider id. - /// The metadata entity. - public MetadataProviderId(string providerId, Metadata metadata) + /// The metadata entity. + public MetadataProviderId(string providerId, ItemMetadata itemMetadata) { if (string.IsNullOrEmpty(providerId)) { @@ -24,12 +24,12 @@ namespace Jellyfin.Data.Entities.Libraries ProviderId = providerId; - if (metadata == null) + if (itemMetadata == null) { - throw new ArgumentNullException(nameof(metadata)); + throw new ArgumentNullException(nameof(itemMetadata)); } - metadata.Sources.Add(this); + itemMetadata.Sources.Add(this); } /// diff --git a/Jellyfin.Data/Entities/Libraries/Movie.cs b/Jellyfin.Data/Entities/Libraries/Movie.cs index 0a8cc83dda..08db904fa8 100644 --- a/Jellyfin.Data/Entities/Libraries/Movie.cs +++ b/Jellyfin.Data/Entities/Libraries/Movie.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System.Collections.Generic; using Jellyfin.Data.Interfaces; diff --git a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs index 31102bf130..aa1501a5cc 100644 --- a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -8,7 +10,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity holding the metadata for a movie. /// - public class MovieMetadata : Metadata, IHasCompanies + public class MovieMetadata : ItemMetadata, IHasCompanies { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs index 2ed1f78c56..06aff6f457 100644 --- a/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs +++ b/Jellyfin.Data/Entities/Libraries/MusicAlbum.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System.Collections.Generic; namespace Jellyfin.Data.Entities.Libraries diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs index cc5919bfe3..05c0b0374b 100644 --- a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -6,7 +8,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity holding the metadata for a music album. /// - public class MusicAlbumMetadata : Metadata + public class MusicAlbumMetadata : ItemMetadata { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Libraries/Person.cs b/Jellyfin.Data/Entities/Libraries/Person.cs index 8beb3dd084..af4c87b73c 100644 --- a/Jellyfin.Data/Entities/Libraries/Person.cs +++ b/Jellyfin.Data/Entities/Libraries/Person.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Entities/Libraries/PersonRole.cs b/Jellyfin.Data/Entities/Libraries/PersonRole.cs index 5290228d6e..cd38ee83d0 100644 --- a/Jellyfin.Data/Entities/Libraries/PersonRole.cs +++ b/Jellyfin.Data/Entities/Libraries/PersonRole.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -16,17 +18,17 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The role type. - /// The metadata. - public PersonRole(PersonRoleType type, Metadata metadata) + /// The metadata. + public PersonRole(PersonRoleType type, ItemMetadata itemMetadata) { Type = type; - if (metadata == null) + if (itemMetadata == null) { - throw new ArgumentNullException(nameof(metadata)); + throw new ArgumentNullException(nameof(itemMetadata)); } - metadata.PersonRoles.Add(this); + itemMetadata.PersonRoles.Add(this); Sources = new HashSet(); } diff --git a/Jellyfin.Data/Entities/Libraries/Photo.cs b/Jellyfin.Data/Entities/Libraries/Photo.cs index 44338a4cea..25562ec96f 100644 --- a/Jellyfin.Data/Entities/Libraries/Photo.cs +++ b/Jellyfin.Data/Entities/Libraries/Photo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System.Collections.Generic; using Jellyfin.Data.Interfaces; diff --git a/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs b/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs index 1ef9dd5f9c..ffc790b574 100644 --- a/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/PhotoMetadata.cs @@ -5,7 +5,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity that holds metadata for a photo. /// - public class PhotoMetadata : Metadata + public class PhotoMetadata : ItemMetadata { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Libraries/Rating.cs b/Jellyfin.Data/Entities/Libraries/Rating.cs index ba054a39e0..98226cd802 100644 --- a/Jellyfin.Data/Entities/Libraries/Rating.cs +++ b/Jellyfin.Data/Entities/Libraries/Rating.cs @@ -14,17 +14,17 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The value. - /// The metadata. - public Rating(double value, Metadata metadata) + /// The metadata. + public Rating(double value, ItemMetadata itemMetadata) { Value = value; - if (metadata == null) + if (itemMetadata == null) { - throw new ArgumentNullException(nameof(metadata)); + throw new ArgumentNullException(nameof(itemMetadata)); } - metadata.Ratings.Add(this); + itemMetadata.Ratings.Add(this); } /// diff --git a/Jellyfin.Data/Entities/Libraries/Release.cs b/Jellyfin.Data/Entities/Libraries/Release.cs index 43c7080d79..b633e08fb3 100644 --- a/Jellyfin.Data/Entities/Libraries/Release.cs +++ b/Jellyfin.Data/Entities/Libraries/Release.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/Jellyfin.Data/Entities/Libraries/Season.cs b/Jellyfin.Data/Entities/Libraries/Season.cs index eef788bad2..eb6674dbc3 100644 --- a/Jellyfin.Data/Entities/Libraries/Season.cs +++ b/Jellyfin.Data/Entities/Libraries/Season.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; diff --git a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs index eedeb089e8..7ce79756b2 100644 --- a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs @@ -6,7 +6,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity that holds metadata for seasons. /// - public class SeasonMetadata : Metadata + public class SeasonMetadata : ItemMetadata { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Libraries/Series.cs b/Jellyfin.Data/Entities/Libraries/Series.cs index e959c1fe00..8c8317d14b 100644 --- a/Jellyfin.Data/Entities/Libraries/Series.cs +++ b/Jellyfin.Data/Entities/Libraries/Series.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; diff --git a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs index 898f3006dc..877dbfc69c 100644 --- a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -9,7 +11,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity representing series metadata. /// - public class SeriesMetadata : Metadata, IHasCompanies + public class SeriesMetadata : ItemMetadata, IHasCompanies { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Libraries/Track.cs b/Jellyfin.Data/Entities/Libraries/Track.cs index 09ce82a9b4..782bfb5ce7 100644 --- a/Jellyfin.Data/Entities/Libraries/Track.cs +++ b/Jellyfin.Data/Entities/Libraries/Track.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA2227 + using System; using System.Collections.Generic; using Jellyfin.Data.Interfaces; diff --git a/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs b/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs index 048068a1a3..321f93bf2e 100644 --- a/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/TrackMetadata.cs @@ -5,7 +5,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// An entity holding metadata for a track. /// - public class TrackMetadata : Metadata + public class TrackMetadata : ItemMetadata { /// /// Initializes a new instance of the class. diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index c0f67f8363..d92e5d9d25 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Enums; @@ -10,7 +8,7 @@ namespace Jellyfin.Data.Entities /// /// An entity representing whether the associated user has a specific permission. /// - public partial class Permission : IHasConcurrencyToken + public class Permission : IHasConcurrencyToken { /// /// Initializes a new instance of the class. @@ -22,8 +20,6 @@ namespace Jellyfin.Data.Entities { Kind = kind; Value = value; - - Init(); } /// @@ -32,21 +28,14 @@ namespace Jellyfin.Data.Entities /// protected Permission() { - Init(); } - /************************************************************************* - * Properties - *************************************************************************/ - /// /// Gets or sets the id of this permission. /// /// /// Identity, Indexed, Required. /// - [Key] - [Required] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } @@ -56,7 +45,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public PermissionKind Kind { get; protected set; } /// @@ -65,36 +53,16 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool Value { get; set; } - /// - /// Gets or sets the row version. - /// - /// - /// Required, ConcurrencyToken. - /// + /// [ConcurrencyCheck] - [Required] public uint RowVersion { get; set; } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The permission kind. - /// The value of this permission. - /// The newly created instance. - public static Permission Create(PermissionKind kind, bool value) - { - return new Permission(kind, value); - } - /// public void OnSavingChanges() { RowVersion++; } - - partial void Init(); } } diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index 1797f0a401..4efddf2a41 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -31,18 +31,12 @@ namespace Jellyfin.Data.Entities { } - /************************************************************************* - * Properties - *************************************************************************/ - /// /// Gets or sets the id of this preference. /// /// /// Identity, Indexed, Required. /// - [Key] - [Required] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } @@ -52,7 +46,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public PreferenceKind Kind { get; protected set; } /// @@ -66,27 +59,10 @@ namespace Jellyfin.Data.Entities [StringLength(65535)] public string Value { get; set; } - /// - /// Gets or sets the row version. - /// - /// - /// Required, ConcurrencyToken. - /// + /// [ConcurrencyCheck] - [Required] public uint RowVersion { get; set; } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The preference kind. - /// The value. - /// The new instance. - public static Preference Create(PreferenceKind kind, string value) - { - return new Preference(kind, value); - } - /// public void OnSavingChanges() { diff --git a/Jellyfin.Data/Entities/ProviderMapping.cs b/Jellyfin.Data/Entities/ProviderMapping.cs deleted file mode 100644 index 44ebfba76d..0000000000 --- a/Jellyfin.Data/Entities/ProviderMapping.cs +++ /dev/null @@ -1,129 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace Jellyfin.Data.Entities -{ - public partial class ProviderMapping - { - partial void Init(); - - /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected ProviderMapping() - { - Init(); - } - - /// - /// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving. - /// - public static ProviderMapping CreateProviderMappingUnsafe() - { - return new ProviderMapping(); - } - - /// - /// Public constructor with required data. - /// - /// - /// - /// - /// - /// - public ProviderMapping(string providername, string providersecrets, string providerdata, User _user0, Group _group1) - { - if (string.IsNullOrEmpty(providername)) - { - throw new ArgumentNullException(nameof(providername)); - } - - this.ProviderName = providername; - - if (string.IsNullOrEmpty(providersecrets)) - { - throw new ArgumentNullException(nameof(providersecrets)); - } - - this.ProviderSecrets = providersecrets; - - if (string.IsNullOrEmpty(providerdata)) - { - throw new ArgumentNullException(nameof(providerdata)); - } - - this.ProviderData = providerdata; - - Init(); - } - - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// - /// - /// - /// - /// - public static ProviderMapping Create(string providername, string providersecrets, string providerdata, User _user0, Group _group1) - { - return new ProviderMapping(providername, providersecrets, providerdata, _user0, _group1); - } - - /************************************************************************* - * Properties - *************************************************************************/ - - /// - /// Identity, Indexed, Required. - /// - [Key] - [Required] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public int Id { get; protected set; } - - /// - /// Required, Max length = 255 - /// - [Required] - [MaxLength(255)] - [StringLength(255)] - public string ProviderName { get; set; } - - /// - /// Required, Max length = 65535 - /// - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string ProviderSecrets { get; set; } - - /// - /// Required, Max length = 65535 - /// - [Required] - [MaxLength(65535)] - [StringLength(65535)] - public string ProviderData { get; set; } - - /// - /// Required, ConcurrenyToken. - /// - [ConcurrencyCheck] - [Required] - public uint RowVersion { get; set; } - - public void OnSavingChanges() - { - RowVersion++; - } - - /************************************************************************* - * Navigation properties - *************************************************************************/ - } -} - diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 7ea1f44986..f7ab57a1b1 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CA2227 using System; using System.Collections.Generic; @@ -15,7 +15,7 @@ namespace Jellyfin.Data.Entities /// /// An entity representing a user. /// - public partial class User : IHasPermissions, IHasConcurrencyToken + public class User : IHasPermissions, IHasConcurrencyToken { /// /// The values being delimited here are Guids, so commas work as they do not appear in Guids. @@ -75,7 +75,6 @@ namespace Jellyfin.Data.Entities AddDefaultPermissions(); AddDefaultPreferences(); - Init(); } /// @@ -84,21 +83,14 @@ namespace Jellyfin.Data.Entities /// protected User() { - Init(); } - /************************************************************************* - * Properties - *************************************************************************/ - /// /// Gets or sets the Id of the user. /// /// /// Identity, Indexed, Required. /// - [Key] - [Required] [JsonIgnore] public Guid Id { get; set; } @@ -139,7 +131,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool MustUpdatePassword { get; set; } /// @@ -180,7 +171,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public int InvalidLoginAttemptCount { get; set; } /// @@ -204,7 +194,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public SubtitlePlaybackMode SubtitleMode { get; set; } /// @@ -213,7 +202,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool PlayDefaultAudioTrack { get; set; } /// @@ -232,7 +220,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool DisplayMissingEpisodes { get; set; } /// @@ -241,7 +228,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool DisplayCollectionsView { get; set; } /// @@ -250,7 +236,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool EnableLocalPassword { get; set; } /// @@ -259,7 +244,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool HidePlayedInLatest { get; set; } /// @@ -268,7 +252,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool RememberAudioSelections { get; set; } /// @@ -277,7 +260,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool RememberSubtitleSelections { get; set; } /// @@ -286,7 +268,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool EnableNextEpisodeAutoPlay { get; set; } /// @@ -295,7 +276,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool EnableAutoLogin { get; set; } /// @@ -304,7 +284,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public bool EnableUserPreferenceAccess { get; set; } /// @@ -322,7 +301,6 @@ namespace Jellyfin.Data.Entities /// This is a temporary stopgap for until the library db is migrated. /// This corresponds to the value of the index of this user in the library db. /// - [Required] public long InternalId { get; set; } /// @@ -340,7 +318,9 @@ namespace Jellyfin.Data.Entities [Required] public virtual DisplayPreferences DisplayPreferences { get; set; } - [Required] + /// + /// Gets or sets the level of sync play permissions this user has. + /// public SyncPlayAccess SyncPlayAccess { get; set; } /// @@ -350,13 +330,8 @@ namespace Jellyfin.Data.Entities /// Required, Concurrency Token. /// [ConcurrencyCheck] - [Required] public uint RowVersion { get; set; } - /************************************************************************* - * Navigation properties - *************************************************************************/ - /// /// Gets or sets the list of access schedules this user has. /// @@ -395,18 +370,6 @@ namespace Jellyfin.Data.Entities [ForeignKey("Preference_Preferences_Guid")] public virtual ICollection Preferences { get; protected set; } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The username for the created user. - /// The Id of the user's authentication provider. - /// The Id of the user's password reset provider. - /// The created instance. - public static User Create(string username, string authenticationProviderId, string passwordResetProviderId) - { - return new User(username, authenticationProviderId, passwordResetProviderId); - } - /// public void OnSavingChanges() { @@ -519,7 +482,5 @@ namespace Jellyfin.Data.Entities Preferences.Add(new Preference(val, string.Empty)); } } - - partial void Init(); } } diff --git a/Jellyfin.Data/Enums/ArtKind.cs b/Jellyfin.Data/Enums/ArtKind.cs index 71b4db6f26..f7a73848c8 100644 --- a/Jellyfin.Data/Enums/ArtKind.cs +++ b/Jellyfin.Data/Enums/ArtKind.cs @@ -1,13 +1,33 @@ -#pragma warning disable CS1591 - namespace Jellyfin.Data.Enums { + /// + /// An enum representing types of art. + /// public enum ArtKind { - Other, - Poster, - Banner, - Thumbnail, - Logo + /// + /// Another type of art, not covered by the other members. + /// + Other = 0, + + /// + /// A poster. + /// + Poster = 1, + + /// + /// A banner. + /// + Banner = 2, + + /// + /// A thumbnail. + /// + Thumbnail = 3, + + /// + /// A logo. + /// + Logo = 4 } } diff --git a/Jellyfin.Data/Enums/DynamicDayOfWeek.cs b/Jellyfin.Data/Enums/DynamicDayOfWeek.cs index a33cd9d1cd..d3d8dd8227 100644 --- a/Jellyfin.Data/Enums/DynamicDayOfWeek.cs +++ b/Jellyfin.Data/Enums/DynamicDayOfWeek.cs @@ -1,18 +1,58 @@ -#pragma warning disable CS1591 - namespace Jellyfin.Data.Enums { + /// + /// An enum that represents a day of the week, weekdays, weekends, or all days. + /// public enum DynamicDayOfWeek { + /// + /// Sunday. + /// Sunday = 0, + + /// + /// Monday. + /// Monday = 1, + + /// + /// Tuesday. + /// Tuesday = 2, + + /// + /// Wednesday. + /// Wednesday = 3, + + /// + /// Thursday. + /// Thursday = 4, + + /// + /// Friday. + /// Friday = 5, + + /// + /// Saturday. + /// Saturday = 6, + + /// + /// All days of the week. + /// Everyday = 7, + + /// + /// A week day, or Monday-Friday. + /// Weekday = 8, + + /// + /// Saturday and Sunday. + /// Weekend = 9 } } diff --git a/Jellyfin.Data/Enums/IndexingKind.cs b/Jellyfin.Data/Enums/IndexingKind.cs index fafe47e0c1..c0df077143 100644 --- a/Jellyfin.Data/Enums/IndexingKind.cs +++ b/Jellyfin.Data/Enums/IndexingKind.cs @@ -1,7 +1,8 @@ -#pragma warning disable CS1591 - -namespace Jellyfin.Data.Enums +namespace Jellyfin.Data.Enums { + /// + /// An enum representing a type of indexing in a user's display preferences. + /// public enum IndexingKind { /// diff --git a/Jellyfin.Data/Enums/MediaFileKind.cs b/Jellyfin.Data/Enums/MediaFileKind.cs index b03591fb80..797c26ec27 100644 --- a/Jellyfin.Data/Enums/MediaFileKind.cs +++ b/Jellyfin.Data/Enums/MediaFileKind.cs @@ -1,13 +1,33 @@ -#pragma warning disable CS1591 - namespace Jellyfin.Data.Enums { + /// + /// An enum representing the type of media file. + /// public enum MediaFileKind { - Main, - Sidecar, - AdditionalPart, - AlternativeFormat, - AdditionalStream + /// + /// The main file. + /// + Main = 0, + + /// + /// A sidecar file. + /// + Sidecar = 1, + + /// + /// An additional part to the main file. + /// + AdditionalPart = 2, + + /// + /// An alternative format to the main file. + /// + AlternativeFormat = 3, + + /// + /// An additional stream for the main file. + /// + AdditionalStream = 4 } } diff --git a/Jellyfin.Data/Enums/PersonRoleType.cs b/Jellyfin.Data/Enums/PersonRoleType.cs index 2d80eaa4ca..1e619f5eef 100644 --- a/Jellyfin.Data/Enums/PersonRoleType.cs +++ b/Jellyfin.Data/Enums/PersonRoleType.cs @@ -1,20 +1,68 @@ -#pragma warning disable CS1591 - namespace Jellyfin.Data.Enums { + /// + /// An enum representing a person's role in a specific media item. + /// public enum PersonRoleType { - Other, - Director, - Artist, - OriginalArtist, - Actor, - VoiceActor, - Producer, - Remixer, - Conductor, - Composer, - Author, - Editor + /// + /// Another role, not covered by the other types. + /// + Other = 0, + + /// + /// The director of the media. + /// + Director = 1, + + /// + /// An artist. + /// + Artist = 2, + + /// + /// The original artist. + /// + OriginalArtist = 3, + + /// + /// An actor. + /// + Actor = 4, + + /// + /// A voice actor. + /// + VoiceActor = 5, + + /// + /// A producer. + /// + Producer = 6, + + /// + /// A remixer. + /// + Remixer = 7, + + /// + /// A conductor. + /// + Conductor = 8, + + /// + /// A composer. + /// + Composer = 9, + + /// + /// An author. + /// + Author = 10, + + /// + /// An editor. + /// + Editor = 11 } } diff --git a/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs b/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs index c8fc211593..ca41300edf 100644 --- a/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs +++ b/Jellyfin.Data/Enums/SubtitlePlaybackMode.cs @@ -1,13 +1,33 @@ -#pragma warning disable CS1591 - -namespace Jellyfin.Data.Enums +namespace Jellyfin.Data.Enums { + /// + /// An enum representing a subtitle playback mode. + /// public enum SubtitlePlaybackMode { + /// + /// The default subtitle playback mode. + /// Default = 0, + + /// + /// Always show subtitles. + /// Always = 1, + + /// + /// Only show forced subtitles. + /// OnlyForced = 2, + + /// + /// Don't show subtitles. + /// None = 3, + + /// + /// Only show subtitles when the current audio stream is in a different language. + /// Smart = 4 } } diff --git a/Jellyfin.Data/Enums/UnratedItem.cs b/Jellyfin.Data/Enums/UnratedItem.cs index 5259e77394..871794086d 100644 --- a/Jellyfin.Data/Enums/UnratedItem.cs +++ b/Jellyfin.Data/Enums/UnratedItem.cs @@ -1,17 +1,53 @@ -#pragma warning disable CS1591 - namespace Jellyfin.Data.Enums { + /// + /// An enum representing an unrated item. + /// public enum UnratedItem { - Movie, - Trailer, - Series, - Music, - Book, - LiveTvChannel, - LiveTvProgram, - ChannelContent, - Other + /// + /// A movie. + /// + Movie = 0, + + /// + /// A trailer. + /// + Trailer = 1, + + /// + /// A series. + /// + Series = 2, + + /// + /// Music. + /// + Music = 3, + + /// + /// A book. + /// + Book = 4, + + /// + /// A live TV channel + /// + LiveTvChannel = 5, + + /// + /// A live TV program. + /// + LiveTvProgram = 6, + + /// + /// Channel content. + /// + ChannelContent = 7, + + /// + /// Another type, not covered by the other fields. + /// + Other = 8 } } diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 203eeaf3bb..95343f91b5 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -4,7 +4,7 @@ netstandard2.0;netstandard2.1 false true - true + true true true true @@ -45,4 +45,8 @@ + + + + diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 85d5f2a3f5..c3bec1c71c 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -66,10 +66,13 @@ + + PreserveNewest + PreserveNewest - + PreserveNewest diff --git a/Jellyfin.Server/wwwroot/api-docs/banner-dark.svg b/Jellyfin.Server/wwwroot/api-docs/banner-dark.svg new file mode 100644 index 0000000000..b62b7545c7 --- /dev/null +++ b/Jellyfin.Server/wwwroot/api-docs/banner-dark.svg @@ -0,0 +1,34 @@ + + + \ No newline at end of file diff --git a/Jellyfin.Server/wwwroot/api-docs/swagger/custom.css b/Jellyfin.Server/wwwroot/api-docs/swagger/custom.css index e69de29bb2..acb59888e0 100644 --- a/Jellyfin.Server/wwwroot/api-docs/swagger/custom.css +++ b/Jellyfin.Server/wwwroot/api-docs/swagger/custom.css @@ -0,0 +1,15 @@ +/* logo */ +.topbar-wrapper img[alt="Swagger UI"], .topbar-wrapper span { + visibility: collapse; +} + +.topbar-wrapper .link:after { + content: url(../banner-dark.svg); + display: block; + -moz-box-sizing: border-box; + box-sizing: border-box; + max-width: 100%; + max-height: 100%; + width: 150px; +} +/* end logo */ diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs index 07167bda63..c088d8cec1 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs @@ -154,6 +154,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb item.IndexNumber = Convert.ToInt32(episode.DvdEpisodeNumber ?? episode.AiredEpisodeNumber); item.ParentIndexNumber = episode.DvdSeason ?? episode.AiredSeason; } + else if (string.Equals(id.SeriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase)) + { + if (episode.AbsoluteNumber.GetValueOrDefault() != 0) + { + item.IndexNumber = episode.AbsoluteNumber; + } + } else if (episode.AiredEpisodeNumber.HasValue) { item.IndexNumber = episode.AiredEpisodeNumber; diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 75587da1f8..fd45dca2af 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -66,8 +66,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Data", "Jellyfin.D EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Implementations", "Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj", "{DAE48069-6D86-4BA6-B148-D1D49B6DDA52}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.Api.Tests", "tests\MediaBrowser.Api.Tests\MediaBrowser.Api.Tests.csproj", "{7C93C84F-105C-48E5-A878-406FA0A5B296}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -178,10 +176,6 @@ Global {DAE48069-6D86-4BA6-B148-D1D49B6DDA52}.Debug|Any CPU.Build.0 = Debug|Any CPU {DAE48069-6D86-4BA6-B148-D1D49B6DDA52}.Release|Any CPU.ActiveCfg = Release|Any CPU {DAE48069-6D86-4BA6-B148-D1D49B6DDA52}.Release|Any CPU.Build.0 = Release|Any CPU - {7C93C84F-105C-48E5-A878-406FA0A5B296}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7C93C84F-105C-48E5-A878-406FA0A5B296}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7C93C84F-105C-48E5-A878-406FA0A5B296}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7C93C84F-105C-48E5-A878-406FA0A5B296}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs b/tests/Jellyfin.Api.Tests/BrandingServiceTests.cs similarity index 97% rename from tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs rename to tests/Jellyfin.Api.Tests/BrandingServiceTests.cs index 5d7f7765cd..6fc287420b 100644 --- a/tests/MediaBrowser.Api.Tests/BrandingServiceTests.cs +++ b/tests/Jellyfin.Api.Tests/BrandingServiceTests.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using MediaBrowser.Model.Branding; using Xunit; -namespace MediaBrowser.Api.Tests +namespace Jellyfin.Api.Tests { public sealed class BrandingServiceTests : IClassFixture { diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 368b6bf0bd..bcba3a2032 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -16,6 +16,7 @@ + diff --git a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs similarity index 99% rename from tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs rename to tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs index 2029f88e92..77f1640fa3 100644 --- a/tests/MediaBrowser.Api.Tests/JellyfinApplicationFactory.cs +++ b/tests/Jellyfin.Api.Tests/JellyfinApplicationFactory.cs @@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging; using Serilog; using Serilog.Extensions.Logging; -namespace MediaBrowser.Api.Tests +namespace Jellyfin.Api.Tests { /// /// Factory for bootstrapping the Jellyfin application in memory for functional end to end tests. diff --git a/tests/Jellyfin.Api.Tests/OpenApiSpecTests.cs b/tests/Jellyfin.Api.Tests/OpenApiSpecTests.cs new file mode 100644 index 0000000000..3a85b55141 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/OpenApiSpecTests.cs @@ -0,0 +1,42 @@ +using System.IO; +using System.Reflection; +using System.Text.Json; +using System.Threading.Tasks; +using MediaBrowser.Model.Branding; +using Xunit; +using Xunit.Abstractions; + +namespace Jellyfin.Api.Tests +{ + public sealed class OpenApiSpecTests : IClassFixture + { + private readonly JellyfinApplicationFactory _factory; + private readonly ITestOutputHelper _outputHelper; + + public OpenApiSpecTests(JellyfinApplicationFactory factory, ITestOutputHelper outputHelper) + { + _factory = factory; + _outputHelper = outputHelper; + } + + [Fact] + public async Task GetSpec_ReturnsCorrectResponse() + { + // Arrange + var client = _factory.CreateClient(); + + // Act + var response = await client.GetAsync("/api-docs/openapi.json"); + + // Assert + response.EnsureSuccessStatusCode(); + Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString()); + + // Write out for publishing + var responseBody = await response.Content.ReadAsStringAsync(); + string outputPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "openapi.json")); + _outputHelper.WriteLine("Writing OpenAPI Spec JSON to '{0}'.", outputPath); + File.WriteAllText(outputPath, responseBody); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs index 917d8fb3a9..ed971eed7b 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs @@ -47,6 +47,10 @@ namespace Jellyfin.Naming.Tests.Video // FIXME: [InlineData("Robin Hood [Multi-Subs] [2018].mkv", "Robin Hood", 2018)] [InlineData(@"3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv", "3.Days.to.Kill", 2014)] // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again [InlineData("3 days to kill (2005).mkv", "3 days to kill", 2005)] + [InlineData("My Movie 2013.12.09", "My Movie 2013.12.09", null)] + [InlineData("My Movie 2013-12-09", "My Movie 2013-12-09", null)] + [InlineData("My Movie 20131209", "My Movie 20131209", null)] + [InlineData("My Movie 2013-12-09 2013", "My Movie 2013-12-09", 2013)] public void CleanDateTimeTest(string input, string expectedName, int? expectedYear) { input = Path.GetFileName(input); diff --git a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj b/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj deleted file mode 100644 index b3fd853e2c..0000000000 --- a/tests/MediaBrowser.Api.Tests/MediaBrowser.Api.Tests.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - netcoreapp3.1 - false - true - enable - - - - - - - - - - - - - - - - - - - - - - - - ../jellyfin-tests.ruleset - - - diff --git a/tests/jellyfin-tests.ruleset b/tests/jellyfin-tests.ruleset index 5a113e955e..e2abaf5bb8 100644 --- a/tests/jellyfin-tests.ruleset +++ b/tests/jellyfin-tests.ruleset @@ -1,5 +1,5 @@ - + @@ -17,6 +17,6 @@ - +