diff options
24 files changed, 188 insertions, 92 deletions
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 800b3d51f..2a9779cd5 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -32,8 +32,10 @@ - [nevado](https://github.com/nevado) - [mark-monteiro](https://github.com/mark-monteiro) - [ullmie02](https://github.com/ullmie02) + - [geilername](https://github.com/geilername) - [pR0Ps](https://github.com/pR0Ps) + # Emby Contributors - [LukePulverenti](https://github.com/LukePulverenti) diff --git a/Dockerfile b/Dockerfile index 0bccd9d64..1029a4cee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG FFMPEG_VERSION=latest FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master -RUN apk add curl \ +RUN apk add curl git \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ && yarn install \ diff --git a/Dockerfile.arm b/Dockerfile.arm index e7cbb0909..5151d4cb6 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -3,7 +3,7 @@ ARG DOTNET_VERSION=3.1 FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master -RUN apk add curl \ +RUN apk add curl git \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ && yarn install \ diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index a0cd0dacc..bf9604bbf 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -3,7 +3,7 @@ ARG DOTNET_VERSION=3.1 FROM node:alpine as web-builder ARG JELLYFIN_WEB_VERSION=master -RUN apk add curl \ +RUN apk add curl git \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && cd jellyfin-web-* \ && yarn install \ diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index 1bd906b8e..7d6ca0d79 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -1,6 +1,6 @@ { - "HeaderLiveTV": "Netti-TV", - "NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa", + "HeaderLiveTV": "TV-lähetykset", + "NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa.", "NameSeasonUnknown": "Tuntematon Kausi", "NameSeasonNumber": "Kausi {0}", "NameInstallFailed": "{0} asennus epäonnistui", @@ -8,9 +8,9 @@ "Music": "Musiikki", "Movies": "Elokuvat", "MixedContent": "Sekoitettu sisältö", - "MessageServerConfigurationUpdated": "Palvelimen konfiguraatio on päivitetty", - "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen konfiguraatio-osa {0} on päivitetty", - "MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty {0}", + "MessageServerConfigurationUpdated": "Palvelimen asetukset on päivitetty", + "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusryhmä {0} on päivitetty", + "MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty versioon {0}", "MessageApplicationUpdated": "Jellyfin palvelin on päivitetty", "Latest": "Viimeisin", "LabelRunningTimeValue": "Kesto: {0}", @@ -24,12 +24,12 @@ "HeaderFavoriteSongs": "Lempikappaleet", "HeaderFavoriteShows": "Lempisarjat", "HeaderFavoriteEpisodes": "Lempijaksot", - "HeaderCameraUploads": "Kamera lataukset", + "HeaderCameraUploads": "Kamerasta lähetetyt", "HeaderFavoriteArtists": "Lempiartistit", "HeaderFavoriteAlbums": "Lempialbumit", - "HeaderContinueWatching": "Jatka Katsomista", - "HeaderAlbumArtists": "Albumiartistit", - "Genres": "Lajipiiri", + "HeaderContinueWatching": "Jatka katsomista", + "HeaderAlbumArtists": "Albumin artistit", + "Genres": "Tyylilaji", "Folders": "Kansiot", "Favorites": "Suosikit", "FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys kohteesta {0}", @@ -44,5 +44,53 @@ "Artists": "Artistit", "Application": "Sovellus", "AppDeviceValues": "Sovellus: {0}, Laite: {1}", - "Albums": "Albumit" + "Albums": "Albumit", + "User": "Käyttäjä", + "System": "Järjestelmä", + "ScheduledTaskFailedWithName": "{0} epäonnistui", + "PluginUpdatedWithName": "{0} päivitetty", + "PluginInstalledWithName": "{0} asennettu", + "Photos": "Kuvat", + "ScheduledTaskStartedWithName": "{0} aloitettu", + "PluginUninstalledWithName": "{0} poistettu", + "Playlists": "Soittolistat", + "VersionNumber": "Versio {0}", + "ValueSpecialEpisodeName": "Erikois - {0}", + "ValueHasBeenAddedToLibrary": "{0} lisättiin mediakirjastoon", + "UserStoppedPlayingItemWithValues": "{0} toistaminen valmistui {1} laitteella {2}", + "UserStartedPlayingItemWithValues": "{0} toistaa {1} laitteella {2}", + "UserPolicyUpdatedWithName": "Käyttöoikeudet päivitetty käyttäjälle {0}", + "UserPasswordChangedWithName": "Salasana vaihdettu käyttäjälle {0}", + "UserOnlineFromDevice": "{0} on paikalla osoitteesta {1}", + "UserOfflineFromDevice": "{0} yhteys katkaistu {1}", + "UserLockedOutWithName": "Käyttäjä {0} kirjautui ulos", + "UserDownloadingItemWithValues": "{0} latautumassa {1}", + "UserDeletedWithName": "Poistettiin käyttäjä {0}", + "UserCreatedWithName": "Luotiin käyttäjä {0}", + "TvShows": "TV-Ohjelmat", + "Sync": "Synkronoi", + "SubtitlesDownloadedForItem": "Tekstitys ladattu {0}", + "SubtitleDownloadFailureFromForItem": "Tekstityksen lataaminen epäonnistui {0} - {1}", + "StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Kokeile hetken kuluttua uudelleen.", + "Songs": "Kappaleet", + "Shows": "Ohjelmat", + "ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen", + "ProviderValue": "Palveluntarjoaja: {0}", + "Plugin": "Laajennus", + "NotificationOptionVideoPlaybackStopped": "Videon toistaminen pysäytetty", + "NotificationOptionVideoPlayback": "Videon toistaminen aloitettu", + "NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos", + "NotificationOptionTaskFailed": "Ajastetun tehtävän ongelma", + "NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan", + "NotificationOptionPluginUpdateInstalled": "Laajennuksen päivitys asennettu", + "NotificationOptionPluginUninstalled": "Laajennus poistettu", + "NotificationOptionPluginInstalled": "Laajennus asennettu", + "NotificationOptionPluginError": "Ongelma laajennuksessa", + "NotificationOptionNewLibraryContent": "Uusi sisältö lisätty", + "NotificationOptionInstallationFailed": "Asennusvirhe", + "NotificationOptionCameraImageUploaded": "Kameran kuva lisätty", + "NotificationOptionAudioPlaybackStopped": "Äänen toistaminen pysäytetty", + "NotificationOptionAudioPlayback": "Äänen toistaminen aloitettu", + "NotificationOptionApplicationUpdateInstalled": "Ohjelmistopäivitys asennettu", + "NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla" } diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index 33bdbfb98..3b0dc4ab2 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -50,10 +50,10 @@ "NotificationOptionCameraImageUploaded": "相機相片已上傳", "NotificationOptionInstallationFailed": "安裝失敗", "NotificationOptionNewLibraryContent": "已新增新內容", - "NotificationOptionPluginError": "外掛失敗", - "NotificationOptionPluginInstalled": "外掛已安裝", - "NotificationOptionPluginUninstalled": "外掛已移除", - "NotificationOptionPluginUpdateInstalled": "已更新外掛", + "NotificationOptionPluginError": "擴充元件安裝失敗", + "NotificationOptionPluginInstalled": "擴充元件已安裝", + "NotificationOptionPluginUninstalled": "擴充元件已移除", + "NotificationOptionPluginUpdateInstalled": "已更新擴充元件", "NotificationOptionServerRestartRequired": "伺服器需要重新啟動", "NotificationOptionTaskFailed": "排程任務失敗", "NotificationOptionUserLockedOut": "使用者已鎖定", diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 0bba19f2a..e2cee5b03 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -67,17 +67,17 @@ namespace Emby.Server.Implementations.Session public event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded; /// <summary> - /// Occurs when [playback start]. + /// Occurs when playback has started. /// </summary> public event EventHandler<PlaybackProgressEventArgs> PlaybackStart; /// <summary> - /// Occurs when [playback progress]. + /// Occurs when playback has progressed. /// </summary> public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress; /// <summary> - /// Occurs when [playback stopped]. + /// Occurs when playback has stopped. /// </summary> public event EventHandler<PlaybackStopEventArgs> PlaybackStopped; @@ -690,7 +690,7 @@ namespace Emby.Server.Implementations.Session } /// <summary> - /// Used to report playback progress for an item + /// Used to report playback progress for an item. /// </summary> /// <returns>Task.</returns> public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated) @@ -1383,20 +1383,16 @@ namespace Emby.Server.Implementations.Session user = _userManager.GetUserByName(request.Username); } - if (user != null) + if (user == null) { - // TODO: Move this to userManager? - if (!string.IsNullOrEmpty(request.DeviceId) - && !_deviceManager.CanAccessDevice(user, request.DeviceId)) - { - throw new SecurityException("User is not allowed access from this device."); - } + AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request)); + throw new SecurityException("Invalid username or password entered."); } - if (user == null) + if (!string.IsNullOrEmpty(request.DeviceId) + && !_deviceManager.CanAccessDevice(user, request.DeviceId)) { - AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request)); - throw new SecurityException("Invalid user or password entered."); + throw new SecurityException("User is not allowed access from this device."); } if (enforcePassword) @@ -1439,14 +1435,13 @@ namespace Emby.Server.Implementations.Session DeviceId = deviceId, UserId = user.Id, Limit = 1 - }).Items.FirstOrDefault(); - var allExistingForDevice = _authRepo.Get(new AuthenticationInfoQuery - { - DeviceId = deviceId - - }).Items; + var allExistingForDevice = _authRepo.Get( + new AuthenticationInfoQuery + { + DeviceId = deviceId + }).Items; foreach (var auth in allExistingForDevice) { @@ -1503,7 +1498,6 @@ namespace Emby.Server.Implementations.Session { Limit = 1, AccessToken = accessToken - }).Items.FirstOrDefault(); if (existing != null) @@ -1696,23 +1690,24 @@ namespace Emby.Server.Implementations.Session throw new ArgumentNullException(nameof(itemId)); } - //var item = _libraryManager.GetItemById(new Guid(itemId)); + var item = _libraryManager.GetItemById(new Guid(itemId)); - //var info = GetItemInfo(item, null, null); + var info = GetItemInfo(item, null); - //ReportNowViewingItem(sessionId, info); + ReportNowViewingItem(sessionId, info); } public void ReportNowViewingItem(string sessionId, BaseItemDto item) { - //var session = GetSession(sessionId); + var session = GetSession(sessionId); - //session.NowViewingItem = item; + session.NowViewingItem = item; } public void ReportTranscodingInfo(string deviceId, TranscodingInfo info) { - var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId)); + var session = Sessions.FirstOrDefault(i => + string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)); if (session != null) { @@ -1727,8 +1722,9 @@ namespace Emby.Server.Implementations.Session public SessionInfo GetSession(string deviceId, string client, string version) { - return Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) && - string.Equals(i.Client, client)); + return Sessions.FirstOrDefault(i => + string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) + && string.Equals(i.Client, client, StringComparison.OrdinalIgnoreCase)); } public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index ab74bab1c..7837aa65b 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -327,15 +327,19 @@ namespace MediaBrowser.Api.Playback private bool EnableThrottling(StreamState state) { + var encodingOptions = ServerConfigurationManager.GetEncodingOptions(); + + // enable throttling when NOT using hardware acceleration + if (encodingOptions.HardwareAccelerationType == string.Empty) + { + return state.InputProtocol == MediaProtocol.File && + state.RunTimeTicks.HasValue && + state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && + state.IsInputVideo && + state.VideoType == VideoType.VideoFile && + !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase); + } return false; - //// do not use throttling with hardware encoders - //return state.InputProtocol == MediaProtocol.File && - // state.RunTimeTicks.HasValue && - // state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && - // state.IsInputVideo && - // state.VideoType == VideoType.VideoFile && - // !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && - // string.Equals(GetVideoEncoder(state), "libx264", StringComparison.OrdinalIgnoreCase); } /// <summary> diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 9aa7b2c88..054371321 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -230,6 +230,17 @@ namespace MediaBrowser.Api.Session public string Id { get; set; } } + [Route("/Sessions/Viewing", "POST", Summary = "Reports that a session is viewing an item")] + [Authenticated] + public class ReportViewing : IReturnVoid + { + [ApiMember(Name = "SessionId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string SessionId { get; set; } + + [ApiMember(Name = "ItemId", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string ItemId { get; set; } + } + [Route("/Sessions/Logout", "POST", Summary = "Reports that a session has ended")] [Authenticated] public class ReportSessionEnded : IReturnVoid @@ -276,7 +287,7 @@ namespace MediaBrowser.Api.Session public class SessionsService : BaseApiService { /// <summary> - /// The _session manager. + /// The session manager. /// </summary> private readonly ISessionManager _sessionManager; @@ -438,14 +449,12 @@ namespace MediaBrowser.Api.Session public Task Post(SendSystemCommand request) { var name = request.Command; - if (Enum.TryParse(name, true, out GeneralCommandType commandType)) { name = commandType.ToString(); } var currentSession = GetSession(_sessionContext); - var command = new GeneralCommand { Name = name, @@ -518,16 +527,13 @@ namespace MediaBrowser.Api.Session { request.Id = GetSession(_sessionContext).Id; } + _sessionManager.ReportCapabilities(request.Id, new ClientCapabilities { PlayableMediaTypes = SplitValue(request.PlayableMediaTypes, ','), - SupportedCommands = SplitValue(request.SupportedCommands, ','), - SupportsMediaControl = request.SupportsMediaControl, - SupportsSync = request.SupportsSync, - SupportsPersistentIdentifier = request.SupportsPersistentIdentifier }); } @@ -538,7 +544,15 @@ namespace MediaBrowser.Api.Session { request.Id = GetSession(_sessionContext).Id; } + _sessionManager.ReportCapabilities(request.Id, request); } + + public void Post(ReportViewing request) + { + request.SessionId = GetSession(_sessionContext).Id; + + _sessionManager.ReportNowViewingItem(request.SessionId, request.ItemId); + } } } diff --git a/MediaBrowser.Api/UserLibrary/PlaystateService.cs b/MediaBrowser.Api/UserLibrary/PlaystateService.cs index 9d1cf5d9e..d0faca163 100644 --- a/MediaBrowser.Api/UserLibrary/PlaystateService.cs +++ b/MediaBrowser.Api/UserLibrary/PlaystateService.cs @@ -103,10 +103,6 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "MediaSourceId", Description = "The id of the MediaSource", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] public string MediaSourceId { get; set; } - /// <summary> - /// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes. - /// </summary> - /// <value><c>true</c> if likes; otherwise, <c>false</c>.</value> [ApiMember(Name = "CanSeek", Description = "Indicates if the client can seek", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")] public bool CanSeek { get; set; } diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index ac140150b..401514349 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -240,7 +240,7 @@ namespace MediaBrowser.Api public class UserService : BaseApiService { /// <summary> - /// The _user manager + /// The user manager. /// </summary> private readonly IUserManager _userManager; private readonly ISessionManager _sessionMananger; diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs index 9b4ed772d..07ca2b58b 100644 --- a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs +++ b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs @@ -1,25 +1,47 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - using System; using System.Collections.Generic; namespace MediaBrowser.Common.Configuration { + /// <summary> + /// Provides an interface to retrieve a configuration store. Classes with this interface are scanned for at + /// application start to dynamically register configuration for various modules/plugins. + /// </summary> public interface IConfigurationFactory { + /// <summary> + /// Get the configuration store for this module. + /// </summary> + /// <returns>The configuration store.</returns> IEnumerable<ConfigurationStore> GetConfigurations(); } + /// <summary> + /// Describes a single entry in the application configuration. + /// </summary> public class ConfigurationStore { + /// <summary> + /// Gets or sets the unique identifier for the configuration. + /// </summary> public string Key { get; set; } + /// <summary> + /// Gets or sets the type used to store the data for this configuration entry. + /// </summary> public Type ConfigurationType { get; set; } } + /// <summary> + /// A configuration store that can be validated. + /// </summary> public interface IValidatingConfiguration { + /// <summary> + /// Validation method to be invoked before saving the configuration. + /// </summary> + /// <param name="oldConfig">The old configuration.</param> + /// <param name="newConfig">The new configuration.</param> void Validate(object oldConfig, object newConfig); } } diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs index 4c9120e0c..9132404a0 100644 --- a/MediaBrowser.Controller/Net/IAuthService.cs +++ b/MediaBrowser.Controller/Net/IAuthService.cs @@ -1,3 +1,5 @@ +#nullable enable + using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; @@ -7,6 +9,6 @@ namespace MediaBrowser.Controller.Net public interface IAuthService { void Authenticate(IRequest request, IAuthenticationAttributes authAttribtues); - User Authenticate(HttpRequest request, IAuthenticationAttributes authAttribtues); + User? Authenticate(HttpRequest request, IAuthenticationAttributes authAttribtues); } } diff --git a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs index e57929989..1e8654c4d 100644 --- a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs +++ b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs @@ -4,16 +4,22 @@ using System.Threading.Tasks; namespace MediaBrowser.Controller.Plugins { /// <summary> - /// Interface IServerEntryPoint + /// Represents an entry point for a module in the application. This interface is scanned for automatically and + /// provides a hook to initialize the module at application start. + /// The entry point can additionally be flagged as a pre-startup task by implementing the + /// <see cref="IRunBeforeStartup"/> interface. /// </summary> public interface IServerEntryPoint : IDisposable { /// <summary> - /// Runs this instance. + /// Run the initialization for this module. This method is invoked at application start. /// </summary> Task RunAsync(); } + /// <summary> + /// Indicates that a <see cref="IServerEntryPoint"/> should be invoked as a pre-startup task. + /// </summary> public interface IRunBeforeStartup { diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index 6e8385cf8..f1f10a3a3 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -107,6 +107,8 @@ namespace MediaBrowser.Controller.Session public BaseItem FullNowPlayingItem { get; set; } + public BaseItemDto NowViewingItem { get; set; } + /// <summary> /// Gets or sets the device id. /// </summary> @@ -239,11 +241,6 @@ namespace MediaBrowser.Controller.Session SessionControllers = controllers.ToArray(); } - public bool ContainsUser(string userId) - { - return ContainsUser(new Guid(userId)); - } - public bool ContainsUser(Guid userId) { if (UserId.Equals(userId)) diff --git a/MediaBrowser.Model/Services/ApiMemberAttribute.cs b/MediaBrowser.Model/Services/ApiMemberAttribute.cs index 7267fce24..8e50836f4 100644 --- a/MediaBrowser.Model/Services/ApiMemberAttribute.cs +++ b/MediaBrowser.Model/Services/ApiMemberAttribute.cs @@ -1,10 +1,10 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - using System; namespace MediaBrowser.Model.Services { + /// <summary> + /// Identifies a single API endpoint. + /// </summary> [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)] public class ApiMemberAttribute : Attribute { diff --git a/deployment/fedora-package-x64/Dockerfile b/deployment/fedora-package-x64/Dockerfile index f5c3ab7a6..05a4ef21f 100644 --- a/deployment/fedora-package-x64/Dockerfile +++ b/deployment/fedora-package-x64/Dockerfile @@ -12,11 +12,11 @@ ENV ARTIFACT_DIR=/dist RUN dnf update -y # Install build dependencies -RUN dnf install -y @buildsys-build rpmdevtools dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs-yarn +RUN dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel nodejs-yarn # Install DotNET SDK -RUN dnf copr enable -y @dotnet-sig/dotnet \ - && rpmdev-setuptree \ +RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc \ + && curl -o /etc/yum.repos.d/microsoft-prod.repo https://packages.microsoft.com/config/fedora/$(rpm -E %fedora)/prod.repo \ && dnf install -y dotnet-sdk-${SDK_VERSION} dotnet-runtime-${SDK_VERSION} # Create symlinks and directories diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs index a2f5c2501..3b3d03c8b 100644 --- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs @@ -83,7 +83,7 @@ namespace Jellyfin.Api.Tests.Auth a => a.Authenticate( It.IsAny<HttpRequest>(), It.IsAny<AuthenticatedAttribute>())) - .Returns((User)null); + .Returns((User?)null); var authenticateResult = await _sut.AuthenticateAsync(); diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 0e8ef135e..1d7e4f7af 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -3,6 +3,8 @@ <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index da5e6576d..86bb11bd4 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -3,6 +3,8 @@ <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index a7848316e..e0f1f236c 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -9,15 +9,15 @@ namespace Jellyfin.MediaEncoding.Tests { public class EncoderValidatorTests { - private class GetFFmpegVersionTestData : IEnumerable<object[]> + private class GetFFmpegVersionTestData : IEnumerable<object?[]> { - public IEnumerator<object[]> GetEnumerator() + public IEnumerator<object?[]> GetEnumerator() { - yield return new object[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) }; - yield return new object[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; - yield return new object[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; - yield return new object[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; - yield return new object[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegV421Output, new Version(4, 2, 1) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegV42Output, new Version(4, 2) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegV414Output, new Version(4, 1, 4) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegV404Output, new Version(4, 0, 4) }; + yield return new object?[] { EncoderValidatorTestsData.FFmpegGitUnknownOutput, null }; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -25,7 +25,7 @@ namespace Jellyfin.MediaEncoding.Tests [Theory] [ClassData(typeof(GetFFmpegVersionTestData))] - public void GetFFmpegVersionTest(string versionOutput, Version version) + public void GetFFmpegVersionTest(string versionOutput, Version? version) { Assert.Equal(version, EncoderValidator.GetFFmpegVersion(versionOutput)); } diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index c01edd9fe..b5e4a1287 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -3,6 +3,8 @@ <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index f246d459b..9602d9e58 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -3,6 +3,7 @@ <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> + <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index c554bc937..29733a1c4 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -3,6 +3,8 @@ <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> + <TreatWarningsAsErrors>true</TreatWarningsAsErrors> + <Nullable>enable</Nullable> <RootNamespace>Jellyfin.Server.Implementations.Tests</RootNamespace> </PropertyGroup> |
