aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs18
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs13
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs122
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs48
-rw-r--r--Emby.Dlna/Server/DescriptionXmlBuilder.cs16
-rw-r--r--Emby.Dlna/Service/BaseControlHandler.cs44
-rw-r--r--Emby.Drawing/ImageProcessor.cs6
-rw-r--r--Emby.Drawing/NullImageEncoder.cs2
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs32
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs3
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs35
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs9
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs13
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs51
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs67
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs14
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/af.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ar.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/bg-BG.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/bn.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-AR.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_419.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_DO.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/fa.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/fi.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/fil.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr-CA.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/gsw.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/he.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/hr.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/is.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ja.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/lt-LT.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/lv.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/mk.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/mr.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ne.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/nn.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/pl.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-BR.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-PT.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ro.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/sl-SI.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/sq.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/sr.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ta.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/th.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/uk.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/ur_PK.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json1
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs2
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs18
-rw-r--r--Jellyfin.Api/Controllers/ArtistsController.cs12
-rw-r--r--Jellyfin.Api/Controllers/ChannelsController.cs12
-rw-r--r--Jellyfin.Api/Controllers/GenresController.cs6
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs13
-rw-r--r--Jellyfin.Api/Controllers/MusicGenresController.cs6
-rw-r--r--Jellyfin.Api/Controllers/PersonsController.cs4
-rw-r--r--Jellyfin.Api/Controllers/StudiosController.cs6
-rw-r--r--Jellyfin.Api/Controllers/TrailersController.cs5
-rw-r--r--Jellyfin.Api/Helpers/RequestHelpers.cs12
-rw-r--r--Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs5
-rw-r--r--Jellyfin.Drawing.Skia/SkiaEncoder.cs4
-rw-r--r--Jellyfin.Drawing.Skia/StripCollageBuilder.cs64
-rw-r--r--MediaBrowser.Common/Plugins/BasePlugin.cs25
-rw-r--r--MediaBrowser.Controller/Drawing/IImageEncoder.cs4
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs6
-rw-r--r--MediaBrowser.Controller/Entities/BaseItemExtensions.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs9
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs6
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs3
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs21
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs6
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs3
-rw-r--r--MediaBrowser.Controller/Resolvers/IItemResolver.cs3
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs12
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs3
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs15
-rw-r--r--MediaBrowser.Model/Session/GeneralCommandType.cs7
-rw-r--r--MediaBrowser.Model/System/PublicSystemInfo.cs7
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs30
-rw-r--r--tests/Jellyfin.Api.Tests/ModelBinders/CommaDelimitedArrayModelBinderTests.cs38
115 files changed, 500 insertions, 444 deletions
diff --git a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
index 8bf0cd961..464f71a6f 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Xml;
@@ -10,8 +8,16 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.MediaReceiverRegistrar
{
+ /// <summary>
+ /// Defines the <see cref="ControlHandler" />.
+ /// </summary>
public class ControlHandler : BaseControlHandler
{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ControlHandler"/> class.
+ /// </summary>
+ /// <param name="config">The <see cref="IServerConfigurationManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
+ /// <param name="logger">The <see cref="ILogger"/> for use with the <see cref="ControlHandler"/> instance.</param>
public ControlHandler(IServerConfigurationManager config, ILogger logger)
: base(config, logger)
{
@@ -35,9 +41,17 @@ namespace Emby.Dlna.MediaReceiverRegistrar
throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
}
+ /// <summary>
+ /// Records that the handle is authorized in the xml stream.
+ /// </summary>
+ /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
private static void HandleIsAuthorized(XmlWriter xmlWriter)
=> xmlWriter.WriteElementString("Result", "1");
+ /// <summary>
+ /// Records that the handle is validated in the xml stream.
+ /// </summary>
+ /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
private static void HandleIsValidated(XmlWriter xmlWriter)
=> xmlWriter.WriteElementString("Result", "1");
}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
index e6d845e1e..a5aae515c 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System.Net.Http;
using System.Threading.Tasks;
using Emby.Dlna.Service;
@@ -8,10 +6,19 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.MediaReceiverRegistrar
{
+ /// <summary>
+ /// Defines the <see cref="MediaReceiverRegistrarService" />.
+ /// </summary>
public class MediaReceiverRegistrarService : BaseService, IMediaReceiverRegistrar
{
private readonly IServerConfigurationManager _config;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaReceiverRegistrarService"/> class.
+ /// </summary>
+ /// <param name="logger">The <see cref="ILogger{MediaReceiverRegistrarService}"/> for use with the <see cref="MediaReceiverRegistrarService"/> instance.</param>
+ /// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/> for use with the <see cref="MediaReceiverRegistrarService"/> instance.</param>
+ /// <param name="config">The <see cref="IServerConfigurationManager"/> for use with the <see cref="MediaReceiverRegistrarService"/> instance.</param>
public MediaReceiverRegistrarService(
ILogger<MediaReceiverRegistrarService> logger,
IHttpClientFactory httpClientFactory,
@@ -24,7 +31,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
/// <inheritdoc />
public string GetServiceXml()
{
- return new MediaReceiverRegistrarXmlBuilder().GetXml();
+ return MediaReceiverRegistrarXmlBuilder.GetXml();
}
/// <inheritdoc />
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
index 26994925d..37840cd09 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
@@ -1,79 +1,89 @@
-#pragma warning disable CS1591
-
using System.Collections.Generic;
using Emby.Dlna.Common;
using Emby.Dlna.Service;
+using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.MediaReceiverRegistrar
{
- public class MediaReceiverRegistrarXmlBuilder
+ /// <summary>
+ /// Defines the <see cref="MediaReceiverRegistrarXmlBuilder" />.
+ /// See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drmnd/5d37515e-7a63-4709-8258-8fd4e0ed4482.
+ /// </summary>
+ public static class MediaReceiverRegistrarXmlBuilder
{
- public string GetXml()
+ /// <summary>
+ /// Retrieves an XML description of the X_MS_MediaReceiverRegistrar.
+ /// </summary>
+ /// <returns>An XML representation of this service.</returns>
+ public static string GetXml()
{
- return new ServiceXmlBuilder().GetXml(
- new ServiceActionListBuilder().GetActions(),
- GetStateVariables());
+ return new ServiceXmlBuilder().GetXml(ServiceActionListBuilder.GetActions(), GetStateVariables());
}
+ /// <summary>
+ /// The a list of all the state variables for this invocation.
+ /// </summary>
+ /// <returns>The <see cref="IEnumerable{StateVariable}"/>.</returns>
private static IEnumerable<StateVariable> GetStateVariables()
{
- var list = new List<StateVariable>();
-
- list.Add(new StateVariable
+ var list = new List<StateVariable>
{
- Name = "AuthorizationGrantedUpdateID",
- DataType = "ui4",
- SendsEvents = true
- });
+ new StateVariable
+ {
+ Name = "AuthorizationGrantedUpdateID",
+ DataType = "ui4",
+ SendsEvents = true
+ },
- list.Add(new StateVariable
- {
- Name = "A_ARG_TYPE_DeviceID",
- DataType = "string",
- SendsEvents = false
- });
+ new StateVariable
+ {
+ Name = "A_ARG_TYPE_DeviceID",
+ DataType = "string",
+ SendsEvents = false
+ },
- list.Add(new StateVariable
- {
- Name = "AuthorizationDeniedUpdateID",
- DataType = "ui4",
- SendsEvents = true
- });
+ new StateVariable
+ {
+ Name = "AuthorizationDeniedUpdateID",
+ DataType = "ui4",
+ SendsEvents = true
+ },
- list.Add(new StateVariable
- {
- Name = "ValidationSucceededUpdateID",
- DataType = "ui4",
- SendsEvents = true
- });
+ new StateVariable
+ {
+ Name = "ValidationSucceededUpdateID",
+ DataType = "ui4",
+ SendsEvents = true
+ },
- list.Add(new StateVariable
- {
- Name = "A_ARG_TYPE_RegistrationRespMsg",
- DataType = "bin.base64",
- SendsEvents = false
- });
+ new StateVariable
+ {
+ Name = "A_ARG_TYPE_RegistrationRespMsg",
+ DataType = "bin.base64",
+ SendsEvents = false
+ },
- list.Add(new StateVariable
- {
- Name = "A_ARG_TYPE_RegistrationReqMsg",
- DataType = "bin.base64",
- SendsEvents = false
- });
+ new StateVariable
+ {
+ Name = "A_ARG_TYPE_RegistrationReqMsg",
+ DataType = "bin.base64",
+ SendsEvents = false
+ },
- list.Add(new StateVariable
- {
- Name = "ValidationRevokedUpdateID",
- DataType = "ui4",
- SendsEvents = true
- });
+ new StateVariable
+ {
+ Name = "ValidationRevokedUpdateID",
+ DataType = "ui4",
+ SendsEvents = true
+ },
- list.Add(new StateVariable
- {
- Name = "A_ARG_TYPE_Result",
- DataType = "int",
- SendsEvents = false
- });
+ new StateVariable
+ {
+ Name = "A_ARG_TYPE_Result",
+ DataType = "int",
+ SendsEvents = false
+ }
+ };
return list;
}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
index 13545c689..1dc9c79c1 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
@@ -1,13 +1,19 @@
-#pragma warning disable CS1591
-
using System.Collections.Generic;
using Emby.Dlna.Common;
+using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.MediaReceiverRegistrar
{
- public class ServiceActionListBuilder
+ /// <summary>
+ /// Defines the <see cref="ServiceActionListBuilder" />.
+ /// </summary>
+ public static class ServiceActionListBuilder
{
- public IEnumerable<ServiceAction> GetActions()
+ /// <summary>
+ /// Returns a list of services that this instance provides.
+ /// </summary>
+ /// <returns>An <see cref="IEnumerable{ServiceAction}"/>.</returns>
+ public static IEnumerable<ServiceAction> GetActions()
{
return new[]
{
@@ -21,6 +27,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar
};
}
+ /// <summary>
+ /// Returns the action details for "IsValidated".
+ /// </summary>
+ /// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetIsValidated()
{
var action = new ServiceAction
@@ -43,6 +53,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action;
}
+ /// <summary>
+ /// Returns the action details for "IsAuthorized".
+ /// </summary>
+ /// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetIsAuthorized()
{
var action = new ServiceAction
@@ -65,6 +79,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action;
}
+ /// <summary>
+ /// Returns the action details for "RegisterDevice".
+ /// </summary>
+ /// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetRegisterDevice()
{
var action = new ServiceAction
@@ -87,6 +105,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action;
}
+ /// <summary>
+ /// Returns the action details for "GetValidationSucceededUpdateID".
+ /// </summary>
+ /// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetGetValidationSucceededUpdateID()
{
var action = new ServiceAction
@@ -103,7 +125,11 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action;
}
- private ServiceAction GetGetAuthorizationDeniedUpdateID()
+ /// <summary>
+ /// Returns the action details for "GetGetAuthorizationDeniedUpdateID".
+ /// </summary>
+ /// <returns>The <see cref="ServiceAction"/>.</returns>
+ private static ServiceAction GetGetAuthorizationDeniedUpdateID()
{
var action = new ServiceAction
{
@@ -119,7 +145,11 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action;
}
- private ServiceAction GetGetValidationRevokedUpdateID()
+ /// <summary>
+ /// Returns the action details for "GetValidationRevokedUpdateID".
+ /// </summary>
+ /// <returns>The <see cref="ServiceAction"/>.</returns>
+ private static ServiceAction GetGetValidationRevokedUpdateID()
{
var action = new ServiceAction
{
@@ -135,7 +165,11 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action;
}
- private ServiceAction GetGetAuthorizationGrantedUpdateID()
+ /// <summary>
+ /// Returns the action details for "GetAuthorizationGrantedUpdateID".
+ /// </summary>
+ /// <returns>The <see cref="ServiceAction"/>.</returns>
+ private static ServiceAction GetGetAuthorizationGrantedUpdateID()
{
var action = new ServiceAction
{
diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
index 1f429d0de..bca9e81cd 100644
--- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs
+++ b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
@@ -235,13 +235,13 @@ namespace Emby.Dlna.Server
.Append(SecurityElement.Escape(service.ServiceId ?? string.Empty))
.Append("</serviceId>");
builder.Append("<SCPDURL>")
- .Append(BuildUrl(service.ScpdUrl, true))
+ .Append(BuildUrl(service.ScpdUrl))
.Append("</SCPDURL>");
builder.Append("<controlURL>")
- .Append(BuildUrl(service.ControlUrl, true))
+ .Append(BuildUrl(service.ControlUrl))
.Append("</controlURL>");
builder.Append("<eventSubURL>")
- .Append(BuildUrl(service.EventSubUrl, true))
+ .Append(BuildUrl(service.EventSubUrl))
.Append("</eventSubURL>");
builder.Append("</service>");
@@ -250,13 +250,7 @@ namespace Emby.Dlna.Server
builder.Append("</serviceList>");
}
- /// <summary>
- /// Builds a valid url for inclusion in the xml.
- /// </summary>
- /// <param name="url">Url to include.</param>
- /// <param name="absoluteUrl">Optional. When set to true, the absolute url is always used.</param>
- /// <returns>The url to use for the element.</returns>
- private string BuildUrl(string url, bool absoluteUrl = false)
+ private string BuildUrl(string url)
{
if (string.IsNullOrEmpty(url))
{
@@ -267,7 +261,7 @@ namespace Emby.Dlna.Server
url = "/dlna/" + _serverUdn + "/" + url;
- if (EnableAbsoluteUrls || absoluteUrl)
+ if (EnableAbsoluteUrls)
{
url = _serverAddress.TrimEnd('/') + url;
}
diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs
index d160e3339..4b0bbba96 100644
--- a/Emby.Dlna/Service/BaseControlHandler.cs
+++ b/Emby.Dlna/Service/BaseControlHandler.cs
@@ -60,10 +60,8 @@ namespace Emby.Dlna.Service
Async = true
};
- using (var reader = XmlReader.Create(streamReader, readerSettings))
- {
- requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
- }
+ using var reader = XmlReader.Create(streamReader, readerSettings);
+ requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
}
Logger.LogDebug("Received control request {0}", requestInfo.LocalName);
@@ -124,10 +122,8 @@ namespace Emby.Dlna.Service
{
if (!reader.IsEmptyElement)
{
- using (var subReader = reader.ReadSubtree())
- {
- return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
- }
+ using var subReader = reader.ReadSubtree();
+ return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
}
else
{
@@ -150,12 +146,12 @@ namespace Emby.Dlna.Service
}
}
- return new ControlRequestInfo();
+ throw new EndOfStreamException("Stream ended but no body tag found.");
}
private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
{
- var result = new ControlRequestInfo();
+ string namespaceURI = null, localName = null;
await reader.MoveToContentAsync().ConfigureAwait(false);
await reader.ReadAsync().ConfigureAwait(false);
@@ -165,16 +161,14 @@ namespace Emby.Dlna.Service
{
if (reader.NodeType == XmlNodeType.Element)
{
- result.LocalName = reader.LocalName;
- result.NamespaceURI = reader.NamespaceURI;
+ localName = reader.LocalName;
+ namespaceURI = reader.NamespaceURI;
if (!reader.IsEmptyElement)
{
- using (var subReader = reader.ReadSubtree())
- {
- await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
- return result;
- }
+ var result = new ControlRequestInfo(localName, namespaceURI);
+ using var subReader = reader.ReadSubtree();
+ await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
}
else
{
@@ -187,7 +181,12 @@ namespace Emby.Dlna.Service
}
}
- return result;
+ if (localName != null && namespaceURI != null)
+ {
+ return new ControlRequestInfo(localName, namespaceURI);
+ }
+
+ throw new EndOfStreamException("Stream ended but no control found.");
}
private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
@@ -234,11 +233,18 @@ namespace Emby.Dlna.Service
private class ControlRequestInfo
{
+ public ControlRequestInfo(string localName, string namespaceUri)
+ {
+ LocalName = localName;
+ NamespaceURI = namespaceUri;
+ Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ }
+
public string LocalName { get; set; }
public string NamespaceURI { get; set; }
- public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ public Dictionary<string, string> Headers { get; }
}
}
}
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index ed20292f6..8a2301d2d 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -36,7 +36,7 @@ namespace Emby.Drawing
private readonly IImageEncoder _imageEncoder;
private readonly IMediaEncoder _mediaEncoder;
- private bool _disposed = false;
+ private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="ImageProcessor"/> class.
@@ -466,11 +466,11 @@ namespace Emby.Drawing
}
/// <inheritdoc />
- public void CreateImageCollage(ImageCollageOptions options)
+ public void CreateImageCollage(ImageCollageOptions options, string? libraryName)
{
_logger.LogInformation("Creating image collage and saving to {Path}", options.OutputPath);
- _imageEncoder.CreateImageCollage(options);
+ _imageEncoder.CreateImageCollage(options, libraryName);
_logger.LogInformation("Completed creation of image collage and saved to {Path}", options.OutputPath);
}
diff --git a/Emby.Drawing/NullImageEncoder.cs b/Emby.Drawing/NullImageEncoder.cs
index bbb5c1716..2a1cfd3da 100644
--- a/Emby.Drawing/NullImageEncoder.cs
+++ b/Emby.Drawing/NullImageEncoder.cs
@@ -38,7 +38,7 @@ namespace Emby.Drawing
}
/// <inheritdoc />
- public void CreateImageCollage(ImageCollageOptions options)
+ public void CreateImageCollage(ImageCollageOptions options, string? libraryName)
{
throw new NotImplementedException();
}
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 0c8b0339b..b69eccd81 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -821,38 +821,6 @@ namespace Emby.Server.Implementations
{
try
{
- if (plugin is IPluginAssembly assemblyPlugin)
- {
- var assembly = plugin.GetType().Assembly;
- var assemblyName = assembly.GetName();
- var assemblyFilePath = assembly.Location;
-
- var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
-
- assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);
-
- try
- {
- var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true);
- if (idAttributes.Length > 0)
- {
- var attribute = (GuidAttribute)idAttributes[0];
- var assemblyId = new Guid(attribute.Value);
-
- assemblyPlugin.SetId(assemblyId);
- }
- }
- catch (Exception ex)
- {
- Logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName);
- }
- }
-
- if (plugin is IHasPluginConfiguration hasPluginConfiguration)
- {
- hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
- }
-
plugin.RegisterServices(ServiceCollection);
}
catch (Exception ex)
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 0fb050a7a..8c756a7f4 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -157,7 +157,8 @@ namespace Emby.Server.Implementations.Data
protected bool TableExists(ManagedConnection connection, string name)
{
- return connection.RunInTransaction(db =>
+ return connection.RunInTransaction(
+ db =>
{
using (var statement = PrepareStatement(db, "select DISTINCT tbl_name from sqlite_master"))
{
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index d09f84e17..1c2aeda70 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -219,7 +219,8 @@ namespace Emby.Server.Implementations.Data
{
connection.RunQueries(queries);
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
var existingColumnNames = GetColumnNames(db, "AncestorIds");
AddColumn(db, "AncestorIds", "AncestorIdText", "Text", existingColumnNames);
@@ -495,7 +496,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id"))
{
@@ -546,7 +548,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
SaveItemsInTranscation(db, tuples);
}, TransactionMode);
@@ -2032,7 +2035,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
// First delete chapters
db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
@@ -2921,7 +2925,8 @@ namespace Emby.Server.Implementations.Data
var result = new QueryResult<BaseItem>();
using (var connection = GetConnection(true))
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
var statements = PrepareAll(db, statementTexts);
@@ -3324,7 +3329,8 @@ namespace Emby.Server.Implementations.Data
var result = new QueryResult<Guid>();
using (var connection = GetConnection(true))
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
var statements = PrepareAll(db, statementTexts);
@@ -4899,7 +4905,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
connection.ExecuteAll(sql);
}, TransactionMode);
@@ -4950,7 +4957,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
var idBlob = id.ToByteArray();
@@ -5357,7 +5365,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
itemCountColumns = new Dictionary<string, string>()
{
- { "itemTypes", "(" + itemCountColumnQuery + ") as itemTypes"}
+ { "itemTypes", "(" + itemCountColumnQuery + ") as itemTypes" }
};
}
@@ -5744,7 +5752,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
var itemIdBlob = itemId.ToByteArray();
@@ -5898,7 +5907,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
var itemIdBlob = id.ToByteArray();
@@ -6232,7 +6242,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
var itemIdBlob = id.ToByteArray();
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index 4a78aac8e..2c4e8e0fc 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -44,7 +44,8 @@ namespace Emby.Server.Implementations.Data
var users = userDatasTableExists ? null : userManager.Users;
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
db.ExecuteAll(string.Join(";", new[] {
@@ -178,7 +179,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
SaveUserData(db, internalUserId, key, userData);
}, TransactionMode);
@@ -246,7 +248,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
foreach (var userItemData in userDataList)
{
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index 57302b506..5f7e51858 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -133,9 +133,20 @@ namespace Emby.Server.Implementations.Images
protected virtual IEnumerable<string> GetStripCollageImagePaths(BaseItem primaryItem, IEnumerable<BaseItem> items)
{
+ var useBackdrop = primaryItem is CollectionFolder;
return items
.Select(i =>
{
+ // Use Backdrop instead of Primary image for Library images.
+ if (useBackdrop)
+ {
+ var backdrop = i.GetImageInfo(ImageType.Backdrop, 0);
+ if (backdrop != null && backdrop.IsLocalFile)
+ {
+ return backdrop.Path;
+ }
+ }
+
var image = i.GetImageInfo(ImageType.Primary, 0);
if (image != null && image.IsLocalFile)
{
@@ -190,7 +201,7 @@ namespace Emby.Server.Implementations.Images
return null;
}
- ImageProcessor.CreateImageCollage(options);
+ ImageProcessor.CreateImageCollage(options, primaryItem.Name);
return outputPath;
}
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 67cf8bf5b..376a15570 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.Library
private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths;
- private readonly Dictionary<string, ILiveStream> _openStreams = new Dictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
private IMediaSourceProvider[] _providers;
@@ -582,29 +583,20 @@ namespace Emby.Server.Implementations.Library
mediaSource.InferTotalBitrate();
}
- public async Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken)
+ public Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken)
{
- await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
+ var info = _openStreams.Values.FirstOrDefault(i =>
{
- var info = _openStreams.Values.FirstOrDefault(i =>
+ var liveStream = i as ILiveStream;
+ if (liveStream != null)
{
- var liveStream = i as ILiveStream;
- if (liveStream != null)
- {
- return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase);
- }
+ return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase);
+ }
- return false;
- });
+ return false;
+ });
- return info as IDirectStreamProvider;
- }
- finally
- {
- _liveStreamSemaphore.Release();
- }
+ return Task.FromResult(info as IDirectStreamProvider);
}
public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
@@ -793,29 +785,20 @@ namespace Emby.Server.Implementations.Library
return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info as IDirectStreamProvider);
}
- private async Task<ILiveStream> GetLiveStreamInfo(string id, CancellationToken cancellationToken)
+ private Task<ILiveStream> GetLiveStreamInfo(string id, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException(nameof(id));
}
- await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
+ if (_openStreams.TryGetValue(id, out ILiveStream info))
{
- if (_openStreams.TryGetValue(id, out ILiveStream info))
- {
- return info;
- }
- else
- {
- throw new ResourceNotFoundException();
- }
+ return Task.FromResult(info);
}
- finally
+ else
{
- _liveStreamSemaphore.Release();
+ return Task.FromException<ILiveStream>(new ResourceNotFoundException());
}
}
@@ -844,7 +827,7 @@ namespace Emby.Server.Implementations.Library
if (liveStream.ConsumerCount <= 0)
{
- _openStreams.Remove(id);
+ _openStreams.TryRemove(id, out _);
_logger.LogInformation("Closing live stream {0}", id);
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index 03059e6d3..70be52411 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -32,7 +32,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// <value>The priority.</value>
public override ResolverPriority Priority => ResolverPriority.Fourth;
- public MultiItemResolverResult ResolveMultiple(Folder parent,
+ public MultiItemResolverResult ResolveMultiple(
+ Folder parent,
List<FileSystemMetadata> files,
string collectionType,
IDirectoryService directoryService)
@@ -50,7 +51,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return result;
}
- private MultiItemResolverResult ResolveMultipleInternal(Folder parent,
+ private MultiItemResolverResult ResolveMultipleInternal(
+ Folder parent,
List<FileSystemMetadata> files,
string collectionType,
IDirectoryService directoryService)
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 79b6dded3..18ceb5e76 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using Emby.Naming.Audio;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -113,52 +116,48 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
IFileSystem fileSystem,
ILibraryManager libraryManager)
{
+ // check for audio files before digging down into directories
+ var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && libraryManager.IsAudioFile(fileSystemInfo.FullName));
+ if (foundAudioFile)
+ {
+ // at least one audio file exists
+ return true;
+ }
+
+ if (!allowSubfolders)
+ {
+ // not music since no audio file exists and we're not looking into subfolders
+ return false;
+ }
+
var discSubfolderCount = 0;
- var notMultiDisc = false;
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
var parser = new AlbumParser(namingOptions);
- foreach (var fileSystemInfo in list)
+
+ var directories = list.Where(fileSystemInfo => fileSystemInfo.IsDirectory);
+
+ var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
{
- if (fileSystemInfo.IsDirectory)
+ var path = fileSystemInfo.FullName;
+ var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
+
+ if (hasMusic)
{
- if (allowSubfolders)
+ if (parser.IsMultiPart(path))
{
- if (notMultiDisc)
- {
- continue;
- }
-
- var path = fileSystemInfo.FullName;
- var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
-
- if (hasMusic)
- {
- if (parser.IsMultiPart(path))
- {
- logger.LogDebug("Found multi-disc folder: " + path);
- discSubfolderCount++;
- }
- else
- {
- // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album
- notMultiDisc = true;
- }
- }
+ logger.LogDebug("Found multi-disc folder: " + path);
+ Interlocked.Increment(ref discSubfolderCount);
}
- }
- else
- {
- var fullName = fileSystemInfo.FullName;
-
- if (libraryManager.IsAudioFile(fullName))
+ else
{
- return true;
+ // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album
+ state.Stop();
}
}
- }
+ });
- if (notMultiDisc)
+ if (!result.IsCompleted)
{
return false;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 5f5cd0e92..e9e688fa6 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq;
+using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -94,7 +95,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
// If we contain an album assume we are an artist folder
- return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null;
+ var directories = args.FileSystemChildren.Where(i => i.IsDirectory);
+
+ var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
+ {
+ if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService))
+ {
+ // stop once we see a music album
+ state.Stop();
+ }
+ });
+
+ return !result.IsCompleted ? new MusicArtist() : null;
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
index 86a5d8b7d..59af7ce8a 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -50,7 +50,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
var fileExtension = Path.GetExtension(f.FullName) ??
string.Empty;
- return _validExtensions.Contains(fileExtension,
+ return _validExtensions.Contains(
+ fileExtension,
StringComparer
.OrdinalIgnoreCase);
}).ToList();
diff --git a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
index f1b61f7c7..582b64923 100644
--- a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
@@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv
return new[]
{
// Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
};
}
diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json
index d33e11893..977a1c2d7 100644
--- a/Emby.Server.Implementations/Localization/Core/af.json
+++ b/Emby.Server.Implementations/Localization/Core/af.json
@@ -85,7 +85,6 @@
"ItemAddedWithName": "{0} is in die versameling",
"HomeVideos": "Tuis opnames",
"HeaderRecordingGroups": "Groep Opnames",
- "HeaderCameraUploads": "Kamera Oplaai",
"Genres": "Genres",
"FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}",
"ChapterNameValue": "Hoofstuk",
diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json
index 4eac8e75d..4b898e6fe 100644
--- a/Emby.Server.Implementations/Localization/Core/ar.json
+++ b/Emby.Server.Implementations/Localization/Core/ar.json
@@ -16,7 +16,6 @@
"Folders": "المجلدات",
"Genres": "التضنيفات",
"HeaderAlbumArtists": "فناني الألبومات",
- "HeaderCameraUploads": "تحميلات الكاميرا",
"HeaderContinueWatching": "استئناف",
"HeaderFavoriteAlbums": "الألبومات المفضلة",
"HeaderFavoriteArtists": "الفنانون المفضلون",
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 3fc7c7dc0..1fed83276 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -16,7 +16,6 @@
"Folders": "Папки",
"Genres": "Жанрове",
"HeaderAlbumArtists": "Изпълнители на албуми",
- "HeaderCameraUploads": "Качени от камера",
"HeaderContinueWatching": "Продължаване на гледането",
"HeaderFavoriteAlbums": "Любими албуми",
"HeaderFavoriteArtists": "Любими изпълнители",
diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json
index 1bd190982..5667bf337 100644
--- a/Emby.Server.Implementations/Localization/Core/bn.json
+++ b/Emby.Server.Implementations/Localization/Core/bn.json
@@ -14,7 +14,6 @@
"HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
"HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
"HeaderContinueWatching": "দেখতে থাকুন",
- "HeaderCameraUploads": "ক্যামেরার আপলোড সমূহ",
"HeaderAlbumArtists": "এলবাম শিল্পী",
"Genres": "জেনার",
"Folders": "ফোল্ডারগুলো",
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 2c802a39e..b7852eccb 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -16,7 +16,6 @@
"Folders": "Carpetes",
"Genres": "Gèneres",
"HeaderAlbumArtists": "Artistes del Àlbum",
- "HeaderCameraUploads": "Pujades de Càmera",
"HeaderContinueWatching": "Continua Veient",
"HeaderFavoriteAlbums": "Àlbums Preferits",
"HeaderFavoriteArtists": "Artistes Preferits",
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index 464ca28ca..b34fad066 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -16,7 +16,6 @@
"Folders": "Složky",
"Genres": "Žánry",
"HeaderAlbumArtists": "Umělci alba",
- "HeaderCameraUploads": "Nahrané fotografie",
"HeaderContinueWatching": "Pokračovat ve sledování",
"HeaderFavoriteAlbums": "Oblíbená alba",
"HeaderFavoriteArtists": "Oblíbení interpreti",
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index f5397b62c..b29ad94ef 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -16,7 +16,6 @@
"Folders": "Mapper",
"Genres": "Genrer",
"HeaderAlbumArtists": "Albumkunstnere",
- "HeaderCameraUploads": "Kamera Uploads",
"HeaderContinueWatching": "Fortsæt Afspilning",
"HeaderFavoriteAlbums": "Favoritalbummer",
"HeaderFavoriteArtists": "Favoritkunstnere",
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index fcbe9566e..97ad1694a 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -16,7 +16,6 @@
"Folders": "Verzeichnisse",
"Genres": "Genres",
"HeaderAlbumArtists": "Album-Interpreten",
- "HeaderCameraUploads": "Kamera-Uploads",
"HeaderContinueWatching": "Fortsetzen",
"HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblings-Interpreten",
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 0753ea39d..c45cc11cb 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -16,7 +16,6 @@
"Folders": "Φάκελοι",
"Genres": "Είδη",
"HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
- "HeaderCameraUploads": "Μεταφορτώσεις Κάμερας",
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 544c38cfa..57ff13219 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -16,7 +16,6 @@
"Folders": "Folders",
"Genres": "Genres",
"HeaderAlbumArtists": "Album Artists",
- "HeaderCameraUploads": "Camera Uploads",
"HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favourite Albums",
"HeaderFavoriteArtists": "Favourite Artists",
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index 97a843160..92c54fb0e 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -16,7 +16,6 @@
"Folders": "Folders",
"Genres": "Genres",
"HeaderAlbumArtists": "Album Artists",
- "HeaderCameraUploads": "Camera Uploads",
"HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",
diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json
index ac96c788c..390074cdd 100644
--- a/Emby.Server.Implementations/Localization/Core/es-AR.json
+++ b/Emby.Server.Implementations/Localization/Core/es-AR.json
@@ -16,7 +16,6 @@
"Folders": "Carpetas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas de álbum",
- "HeaderCameraUploads": "Subidas de cámara",
"HeaderContinueWatching": "Seguir viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 4ba324aa1..ab54c0ea6 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
@@ -16,7 +16,6 @@
"Folders": "Carpetas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del álbum",
- "HeaderCameraUploads": "Subidas desde la cámara",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index e7bd3959b..d4e0b299d 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -16,7 +16,6 @@
"Folders": "Carpetas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del álbum",
- "HeaderCameraUploads": "Subidas desde la cámara",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json
index 0959ef2ca..dcd30694f 100644
--- a/Emby.Server.Implementations/Localization/Core/es_419.json
+++ b/Emby.Server.Implementations/Localization/Core/es_419.json
@@ -105,7 +105,6 @@
"Inherit": "Heredar",
"HomeVideos": "Videos caseros",
"HeaderRecordingGroups": "Grupos de grabación",
- "HeaderCameraUploads": "Subidas desde la cámara",
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}",
"DeviceOnlineWithName": "{0} está conectado",
"DeviceOfflineWithName": "{0} se ha desconectado",
diff --git a/Emby.Server.Implementations/Localization/Core/es_DO.json b/Emby.Server.Implementations/Localization/Core/es_DO.json
index 26732eb3f..b64ffbfbb 100644
--- a/Emby.Server.Implementations/Localization/Core/es_DO.json
+++ b/Emby.Server.Implementations/Localization/Core/es_DO.json
@@ -12,7 +12,6 @@
"Application": "Aplicación",
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
"HeaderContinueWatching": "Continuar Viendo",
- "HeaderCameraUploads": "Subidas de Cámara",
"HeaderAlbumArtists": "Artistas del Álbum",
"Genres": "Géneros",
"Folders": "Carpetas",
diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json
index 500c29217..1986decf0 100644
--- a/Emby.Server.Implementations/Localization/Core/fa.json
+++ b/Emby.Server.Implementations/Localization/Core/fa.json
@@ -16,7 +16,6 @@
"Folders": "پوشه‌ها",
"Genres": "ژانرها",
"HeaderAlbumArtists": "هنرمندان آلبوم",
- "HeaderCameraUploads": "آپلودهای دوربین",
"HeaderContinueWatching": "ادامه تماشا",
"HeaderFavoriteAlbums": "آلبوم‌های مورد علاقه",
"HeaderFavoriteArtists": "هنرمندان مورد علاقه",
diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
index f8d6e0e09..aae22583a 100644
--- a/Emby.Server.Implementations/Localization/Core/fi.json
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
@@ -24,7 +24,6 @@
"HeaderFavoriteSongs": "Lempikappaleet",
"HeaderFavoriteShows": "Lempisarjat",
"HeaderFavoriteEpisodes": "Lempijaksot",
- "HeaderCameraUploads": "Kamerasta Lähetetyt",
"HeaderFavoriteArtists": "Lempiartistit",
"HeaderFavoriteAlbums": "Lempialbumit",
"HeaderContinueWatching": "Jatka katsomista",
diff --git a/Emby.Server.Implementations/Localization/Core/fil.json b/Emby.Server.Implementations/Localization/Core/fil.json
index 47daf2044..1a3e18832 100644
--- a/Emby.Server.Implementations/Localization/Core/fil.json
+++ b/Emby.Server.Implementations/Localization/Core/fil.json
@@ -73,7 +73,6 @@
"HeaderFavoriteArtists": "Paboritong Artista",
"HeaderFavoriteAlbums": "Paboritong Albums",
"HeaderContinueWatching": "Ituloy Manood",
- "HeaderCameraUploads": "Camera Uploads",
"HeaderAlbumArtists": "Artista ng Album",
"Genres": "Kategorya",
"Folders": "Folders",
diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json
index cd1c8144f..3d7592e3c 100644
--- a/Emby.Server.Implementations/Localization/Core/fr-CA.json
+++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json
@@ -16,7 +16,6 @@
"Folders": "Dossiers",
"Genres": "Genres",
"HeaderAlbumArtists": "Artistes de l'album",
- "HeaderCameraUploads": "Photos transférées",
"HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes favoris",
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index 7fc996821..f4ca8575a 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -16,7 +16,6 @@
"Folders": "Dossiers",
"Genres": "Genres",
"HeaderAlbumArtists": "Artistes",
- "HeaderCameraUploads": "Photos transférées",
"HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes préférés",
diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json
index 8780a884b..ee1f8775e 100644
--- a/Emby.Server.Implementations/Localization/Core/gsw.json
+++ b/Emby.Server.Implementations/Localization/Core/gsw.json
@@ -16,7 +16,6 @@
"Folders": "Ordner",
"Genres": "Genres",
"HeaderAlbumArtists": "Album-Künstler",
- "HeaderCameraUploads": "Kamera-Uploads",
"HeaderContinueWatching": "weiter schauen",
"HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblings-Künstler",
diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json
index dc3a98154..f906d6e11 100644
--- a/Emby.Server.Implementations/Localization/Core/he.json
+++ b/Emby.Server.Implementations/Localization/Core/he.json
@@ -16,7 +16,6 @@
"Folders": "תיקיות",
"Genres": "ז'אנרים",
"HeaderAlbumArtists": "אמני האלבום",
- "HeaderCameraUploads": "העלאות ממצלמה",
"HeaderContinueWatching": "המשך לצפות",
"HeaderFavoriteAlbums": "אלבומים מועדפים",
"HeaderFavoriteArtists": "אמנים מועדפים",
diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json
index 97c77017b..a3c936240 100644
--- a/Emby.Server.Implementations/Localization/Core/hr.json
+++ b/Emby.Server.Implementations/Localization/Core/hr.json
@@ -16,7 +16,6 @@
"Folders": "Mape",
"Genres": "Žanrovi",
"HeaderAlbumArtists": "Izvođači na albumu",
- "HeaderCameraUploads": "Uvoz sa kamere",
"HeaderContinueWatching": "Nastavi gledati",
"HeaderFavoriteAlbums": "Omiljeni albumi",
"HeaderFavoriteArtists": "Omiljeni izvođači",
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index c5c3844e3..343d213d4 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -16,7 +16,6 @@
"Folders": "Könyvtárak",
"Genres": "Műfajok",
"HeaderAlbumArtists": "Album előadók",
- "HeaderCameraUploads": "Kamera feltöltések",
"HeaderContinueWatching": "Megtekintés folytatása",
"HeaderFavoriteAlbums": "Kedvenc albumok",
"HeaderFavoriteArtists": "Kedvenc előadók",
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index 585fc6f02..ef3ed2580 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -20,7 +20,6 @@
"HeaderFavoriteArtists": "Artis Favorit",
"HeaderFavoriteAlbums": "Album Favorit",
"HeaderContinueWatching": "Lanjut Menonton",
- "HeaderCameraUploads": "Unggahan Kamera",
"HeaderAlbumArtists": "Album Artis",
"Genres": "Aliran",
"Folders": "Folder",
diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json
index 0f0f9130b..0f769eaad 100644
--- a/Emby.Server.Implementations/Localization/Core/is.json
+++ b/Emby.Server.Implementations/Localization/Core/is.json
@@ -13,7 +13,6 @@
"HeaderFavoriteArtists": "Uppáhalds Listamenn",
"HeaderFavoriteAlbums": "Uppáhalds Plötur",
"HeaderContinueWatching": "Halda áfram að horfa",
- "HeaderCameraUploads": "Myndavéla upphal",
"HeaderAlbumArtists": "Höfundur plötu",
"Genres": "Tegundir",
"Folders": "Möppur",
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index bf1a0ef13..0a6238578 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -16,7 +16,6 @@
"Folders": "Cartelle",
"Genres": "Generi",
"HeaderAlbumArtists": "Artisti degli Album",
- "HeaderCameraUploads": "Caricamenti Fotocamera",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album Preferiti",
"HeaderFavoriteArtists": "Artisti Preferiti",
diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json
index a4d9f9ef6..35004f0eb 100644
--- a/Emby.Server.Implementations/Localization/Core/ja.json
+++ b/Emby.Server.Implementations/Localization/Core/ja.json
@@ -16,7 +16,6 @@
"Folders": "フォルダー",
"Genres": "ジャンル",
"HeaderAlbumArtists": "アルバムアーティスト",
- "HeaderCameraUploads": "カメラアップロード",
"HeaderContinueWatching": "視聴を続ける",
"HeaderFavoriteAlbums": "お気に入りのアルバム",
"HeaderFavoriteArtists": "お気に入りのアーティスト",
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index 5618ff4a8..91c1fb15b 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -16,7 +16,6 @@
"Folders": "Qaltalar",
"Genres": "Janrlar",
"HeaderAlbumArtists": "Álbom oryndaýshylary",
- "HeaderCameraUploads": "Kameradan júktelgender",
"HeaderContinueWatching": "Qaraýdy jalǵastyrý",
"HeaderFavoriteAlbums": "Tańdaýly álbomdar",
"HeaderFavoriteArtists": "Tańdaýly oryndaýshylar",
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index a33953c27..fb01e4645 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -16,7 +16,6 @@
"Folders": "폴더",
"Genres": "장르",
"HeaderAlbumArtists": "앨범 아티스트",
- "HeaderCameraUploads": "카메라 업로드",
"HeaderContinueWatching": "계속 시청하기",
"HeaderFavoriteAlbums": "즐겨찾는 앨범",
"HeaderFavoriteArtists": "즐겨찾는 아티스트",
diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json
index 35053766b..d4cb592ef 100644
--- a/Emby.Server.Implementations/Localization/Core/lt-LT.json
+++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json
@@ -16,7 +16,6 @@
"Folders": "Katalogai",
"Genres": "Žanrai",
"HeaderAlbumArtists": "Albumo atlikėjai",
- "HeaderCameraUploads": "Kameros",
"HeaderContinueWatching": "Žiūrėti toliau",
"HeaderFavoriteAlbums": "Mėgstami Albumai",
"HeaderFavoriteArtists": "Mėgstami Atlikėjai",
diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json
index dbcf17287..5e3acfbe9 100644
--- a/Emby.Server.Implementations/Localization/Core/lv.json
+++ b/Emby.Server.Implementations/Localization/Core/lv.json
@@ -72,7 +72,6 @@
"ItemAddedWithName": "{0} tika pievienots bibliotēkai",
"HeaderLiveTV": "Tiešraides TV",
"HeaderContinueWatching": "Turpināt Skatīšanos",
- "HeaderCameraUploads": "Kameras augšupielādes",
"HeaderAlbumArtists": "Albumu Izpildītāji",
"Genres": "Žanri",
"Folders": "Mapes",
diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json
index bbdf99aba..b780ef498 100644
--- a/Emby.Server.Implementations/Localization/Core/mk.json
+++ b/Emby.Server.Implementations/Localization/Core/mk.json
@@ -51,7 +51,6 @@
"HeaderFavoriteArtists": "Омилени Изведувачи",
"HeaderFavoriteAlbums": "Омилени Албуми",
"HeaderContinueWatching": "Продолжи со гледање",
- "HeaderCameraUploads": "Поставувања од камера",
"HeaderAlbumArtists": "Изведувачи од Албуми",
"Genres": "Жанрови",
"Folders": "Папки",
diff --git a/Emby.Server.Implementations/Localization/Core/mr.json b/Emby.Server.Implementations/Localization/Core/mr.json
index b6db2b0f2..fdb4171b5 100644
--- a/Emby.Server.Implementations/Localization/Core/mr.json
+++ b/Emby.Server.Implementations/Localization/Core/mr.json
@@ -54,7 +54,6 @@
"ItemAddedWithName": "{0} हे संग्रहालयात जोडले गेले",
"HomeVideos": "घरचे व्हिडीयो",
"HeaderRecordingGroups": "रेकॉर्डिंग गट",
- "HeaderCameraUploads": "कॅमेरा अपलोड",
"CameraImageUploadedFrom": "एक नवीन कॅमेरा चित्र {0} येथून अपलोड केले आहे",
"Application": "अ‍ॅप्लिकेशन",
"AppDeviceValues": "अ‍ॅप: {0}, यंत्र: {1}",
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index 7f8df1289..5e3d095ff 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -16,7 +16,6 @@
"Folders": "Fail-fail",
"Genres": "Genre-genre",
"HeaderAlbumArtists": "Album Artis-artis",
- "HeaderCameraUploads": "Muatnaik Kamera",
"HeaderContinueWatching": "Terus Menonton",
"HeaderFavoriteAlbums": "Album-album Kegemaran",
"HeaderFavoriteArtists": "Artis-artis Kegemaran",
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index 07a599121..245c3cd63 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -16,7 +16,6 @@
"Folders": "Mapper",
"Genres": "Sjangre",
"HeaderAlbumArtists": "Albumartister",
- "HeaderCameraUploads": "Kameraopplastinger",
"HeaderContinueWatching": "Fortsett å se",
"HeaderFavoriteAlbums": "Favorittalbum",
"HeaderFavoriteArtists": "Favorittartister",
diff --git a/Emby.Server.Implementations/Localization/Core/ne.json b/Emby.Server.Implementations/Localization/Core/ne.json
index 38c073709..8e820d40c 100644
--- a/Emby.Server.Implementations/Localization/Core/ne.json
+++ b/Emby.Server.Implementations/Localization/Core/ne.json
@@ -41,7 +41,6 @@
"HeaderFavoriteArtists": "मनपर्ने कलाकारहरू",
"HeaderFavoriteAlbums": "मनपर्ने एल्बमहरू",
"HeaderContinueWatching": "हेर्न जारी राख्नुहोस्",
- "HeaderCameraUploads": "क्यामेरा अपलोडहरू",
"HeaderAlbumArtists": "एल्बमका कलाकारहरू",
"Genres": "विधाहरू",
"Folders": "फोल्डरहरू",
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 41c74d54d..e102b92b9 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -16,7 +16,6 @@
"Folders": "Mappen",
"Genres": "Genres",
"HeaderAlbumArtists": "Albumartiesten",
- "HeaderCameraUploads": "Camera-uploads",
"HeaderContinueWatching": "Kijken hervatten",
"HeaderFavoriteAlbums": "Favoriete albums",
"HeaderFavoriteArtists": "Favoriete artiesten",
diff --git a/Emby.Server.Implementations/Localization/Core/nn.json b/Emby.Server.Implementations/Localization/Core/nn.json
index fb6e81beb..6236515b2 100644
--- a/Emby.Server.Implementations/Localization/Core/nn.json
+++ b/Emby.Server.Implementations/Localization/Core/nn.json
@@ -19,7 +19,6 @@
"HeaderFavoriteArtists": "Favoritt Artistar",
"HeaderFavoriteAlbums": "Favoritt Album",
"HeaderContinueWatching": "Fortsett å sjå",
- "HeaderCameraUploads": "Kamera Opplastingar",
"HeaderAlbumArtists": "Album Artist",
"Genres": "Sjangrar",
"Folders": "Mapper",
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index bdc0d0169..003e591b3 100644
--- a/Emby.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
@@ -16,7 +16,6 @@
"Folders": "Foldery",
"Genres": "Gatunki",
"HeaderAlbumArtists": "Wykonawcy albumów",
- "HeaderCameraUploads": "Przekazane obrazy",
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
"HeaderFavoriteAlbums": "Ulubione albumy",
"HeaderFavoriteArtists": "Ulubieni wykonawcy",
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index 275195640..5e49ca702 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -16,7 +16,6 @@
"Folders": "Pastas",
"Genres": "Gêneros",
"HeaderAlbumArtists": "Artistas do Álbum",
- "HeaderCameraUploads": "Envios da Câmera",
"HeaderContinueWatching": "Continuar Assistindo",
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index a7c17ecb6..90a4941c5 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-PT.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json
@@ -16,7 +16,6 @@
"Folders": "Pastas",
"Genres": "Géneros",
"HeaderAlbumArtists": "Artistas do Álbum",
- "HeaderCameraUploads": "Envios a partir da câmara",
"HeaderContinueWatching": "Continuar a Ver",
"HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos",
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index b534d0bbe..2079940cd 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -83,7 +83,6 @@
"Playlists": "Listas de Reprodução",
"Photos": "Fotografias",
"Movies": "Filmes",
- "HeaderCameraUploads": "Carregamentos a partir da câmara",
"FailedLoginAttemptWithUserName": "Tentativa de ligação falhada a partir de {0}",
"DeviceOnlineWithName": "{0} está connectado",
"DeviceOfflineWithName": "{0} desconectou-se",
diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json
index 699dd26da..bc008df3b 100644
--- a/Emby.Server.Implementations/Localization/Core/ro.json
+++ b/Emby.Server.Implementations/Localization/Core/ro.json
@@ -74,7 +74,6 @@
"HeaderFavoriteArtists": "Artiști Favoriți",
"HeaderFavoriteAlbums": "Albume Favorite",
"HeaderContinueWatching": "Vizionează în continuare",
- "HeaderCameraUploads": "Incărcări Cameră Foto",
"HeaderAlbumArtists": "Album Artiști",
"Genres": "Genuri",
"Folders": "Dosare",
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 648aa384b..95b93afb8 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -16,7 +16,6 @@
"Folders": "Папки",
"Genres": "Жанры",
"HeaderAlbumArtists": "Исполнители альбома",
- "HeaderCameraUploads": "Камеры",
"HeaderContinueWatching": "Продолжение просмотра",
"HeaderFavoriteAlbums": "Избранные альбомы",
"HeaderFavoriteArtists": "Избранные исполнители",
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 0ee652637..8e5026944 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -16,7 +16,6 @@
"Folders": "Priečinky",
"Genres": "Žánre",
"HeaderAlbumArtists": "Umelci albumu",
- "HeaderCameraUploads": "Nahrané fotografie",
"HeaderContinueWatching": "Pokračovať v pozeraní",
"HeaderFavoriteAlbums": "Obľúbené albumy",
"HeaderFavoriteArtists": "Obľúbení umelci",
diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json
index 329c562e7..ff4b9e84f 100644
--- a/Emby.Server.Implementations/Localization/Core/sl-SI.json
+++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json
@@ -16,7 +16,6 @@
"Folders": "Mape",
"Genres": "Zvrsti",
"HeaderAlbumArtists": "Izvajalci albuma",
- "HeaderCameraUploads": "Posnetki kamere",
"HeaderContinueWatching": "Nadaljuj gledanje",
"HeaderFavoriteAlbums": "Priljubljeni albumi",
"HeaderFavoriteArtists": "Priljubljeni izvajalci",
diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json
index 347ba5f97..ecca5af4c 100644
--- a/Emby.Server.Implementations/Localization/Core/sq.json
+++ b/Emby.Server.Implementations/Localization/Core/sq.json
@@ -96,7 +96,6 @@
"HeaderFavoriteArtists": "Artistët e preferuar",
"HeaderFavoriteAlbums": "Albumet e preferuar",
"HeaderContinueWatching": "Vazhdo të shikosh",
- "HeaderCameraUploads": "Ngarkimet nga Kamera",
"HeaderAlbumArtists": "Artistët e albumeve",
"Genres": "Zhanre",
"Folders": "Dosje",
diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json
index 5f3cbb1c8..2b1eccfaf 100644
--- a/Emby.Server.Implementations/Localization/Core/sr.json
+++ b/Emby.Server.Implementations/Localization/Core/sr.json
@@ -74,7 +74,6 @@
"HeaderFavoriteArtists": "Омиљени извођачи",
"HeaderFavoriteAlbums": "Омиљени албуми",
"HeaderContinueWatching": "Настави гледање",
- "HeaderCameraUploads": "Слања са камере",
"HeaderAlbumArtists": "Извођачи албума",
"Genres": "Жанрови",
"Folders": "Фасцикле",
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index c8662b2ca..12fda8a98 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -16,7 +16,6 @@
"Folders": "Mappar",
"Genres": "Genrer",
"HeaderAlbumArtists": "Albumartister",
- "HeaderCameraUploads": "Kamerauppladdningar",
"HeaderContinueWatching": "Fortsätt kolla",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritartister",
diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json
index 810b1b9ab..ae38f45e1 100644
--- a/Emby.Server.Implementations/Localization/Core/ta.json
+++ b/Emby.Server.Implementations/Localization/Core/ta.json
@@ -20,7 +20,6 @@
"MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது",
"Inherit": "மரபுரிமையாகப் பெறு",
"HeaderRecordingGroups": "பதிவு குழுக்கள்",
- "HeaderCameraUploads": "புகைப்பட பதிவேற்றங்கள்",
"Folders": "கோப்புறைகள்",
"FailedLoginAttemptWithUserName": "{0} இலிருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது",
"DeviceOnlineWithName": "{0} இணைக்கப்பட்டது",
diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json
index 3b77215a3..71dd2c7a3 100644
--- a/Emby.Server.Implementations/Localization/Core/th.json
+++ b/Emby.Server.Implementations/Localization/Core/th.json
@@ -50,7 +50,6 @@
"HeaderFavoriteArtists": "ศิลปินที่ชื่นชอบ",
"HeaderFavoriteAlbums": "อัมบั้มที่ชื่นชอบ",
"HeaderContinueWatching": "ดูต่อ",
- "HeaderCameraUploads": "อัปโหลดรูปถ่าย",
"HeaderAlbumArtists": "อัลบั้มศิลปิน",
"Genres": "ประเภท",
"Folders": "โฟลเดอร์",
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index 3cf3482eb..1e5f2cf19 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -16,7 +16,6 @@
"Folders": "Klasörler",
"Genres": "Türler",
"HeaderAlbumArtists": "Albüm Sanatçıları",
- "HeaderCameraUploads": "Kamera Yüklemeleri",
"HeaderContinueWatching": "İzlemeye Devam Et",
"HeaderFavoriteAlbums": "Favori Albümler",
"HeaderFavoriteArtists": "Favori Sanatçılar",
diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json
index e673465a4..06cc5f633 100644
--- a/Emby.Server.Implementations/Localization/Core/uk.json
+++ b/Emby.Server.Implementations/Localization/Core/uk.json
@@ -16,7 +16,6 @@
"HeaderFavoriteArtists": "Улюблені виконавці",
"HeaderFavoriteAlbums": "Улюблені альбоми",
"HeaderContinueWatching": "Продовжити перегляд",
- "HeaderCameraUploads": "Завантажено з камери",
"HeaderAlbumArtists": "Виконавці альбому",
"Genres": "Жанри",
"Folders": "Каталоги",
diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json
index 9a5874e29..fa7b2d4d0 100644
--- a/Emby.Server.Implementations/Localization/Core/ur_PK.json
+++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json
@@ -105,7 +105,6 @@
"Inherit": "وراثت میں",
"HomeVideos": "ہوم ویڈیو",
"HeaderRecordingGroups": "ریکارڈنگ گروپس",
- "HeaderCameraUploads": "کیمرہ اپلوڈز",
"FailedLoginAttemptWithUserName": "لاگن کئ کوشش ناکام {0}",
"DeviceOnlineWithName": "{0} متصل ھو چکا ھے",
"DeviceOfflineWithName": "{0} منقطع ھو چکا ھے",
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index 2392c8347..ac74deff8 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -103,7 +103,6 @@
"HeaderFavoriteEpisodes": "Tập Phim Yêu Thích",
"HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích",
"HeaderFavoriteAlbums": "Album Ưa Thích",
- "HeaderCameraUploads": "Máy Ảnh Tải Lên",
"FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}",
"DeviceOnlineWithName": "{0} đã kết nối",
"DeviceOfflineWithName": "{0} đã ngắt kết nối",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 6b563a9b1..53a902de2 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -16,7 +16,6 @@
"Folders": "文件夹",
"Genres": "风格",
"HeaderAlbumArtists": "专辑作家",
- "HeaderCameraUploads": "相机上传",
"HeaderContinueWatching": "继续观影",
"HeaderFavoriteAlbums": "收藏的专辑",
"HeaderFavoriteArtists": "最爱的艺术家",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index 1ac62baca..435e294ef 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -16,7 +16,6 @@
"Folders": "檔案夾",
"Genres": "風格",
"HeaderAlbumArtists": "專輯藝人",
- "HeaderCameraUploads": "相機上載",
"HeaderContinueWatching": "繼續觀看",
"HeaderFavoriteAlbums": "最愛專輯",
"HeaderFavoriteArtists": "最愛的藝人",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index 7b6540c3e..30f726630 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -16,7 +16,6 @@
"Folders": "資料夾",
"Genres": "風格",
"HeaderAlbumArtists": "專輯演出者",
- "HeaderCameraUploads": "相機上傳",
"HeaderContinueWatching": "繼續觀賞",
"HeaderFavoriteAlbums": "最愛專輯",
"HeaderFavoriteArtists": "最愛演出者",
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index 5adcefc1f..692d1667d 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
return new[]
{
// Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
};
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index 54e18eaea..184d155d4 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
return new[]
{
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
};
}
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
index 29393ae07..4bc12f44a 100644
--- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -54,7 +54,8 @@ namespace Emby.Server.Implementations.Security
{
if (tableNewlyCreated && TableExists(connection, "AccessTokens"))
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
var existingColumnNames = GetColumnNames(db, "AccessTokens");
@@ -88,7 +89,8 @@ namespace Emby.Server.Implementations.Security
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
using (var statement = db.PrepareStatement("insert into Tokens (AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity) values (@AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @UserName, @IsActive, @DateCreated, @DateLastActivity)"))
{
@@ -119,7 +121,8 @@ namespace Emby.Server.Implementations.Security
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
using (var statement = db.PrepareStatement("Update Tokens set AccessToken=@AccessToken, DeviceId=@DeviceId, AppName=@AppName, AppVersion=@AppVersion, DeviceName=@DeviceName, UserId=@UserId, UserName=@UserName, DateCreated=@DateCreated, DateLastActivity=@DateLastActivity where Id=@Id"))
{
@@ -151,7 +154,8 @@ namespace Emby.Server.Implementations.Security
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
using (var statement = db.PrepareStatement("Delete from Tokens where Id=@Id"))
{
@@ -346,7 +350,8 @@ namespace Emby.Server.Implementations.Security
{
using (var connection = GetConnection(true))
{
- return connection.RunInTransaction(db =>
+ return connection.RunInTransaction(
+ db =>
{
using (var statement = base.PrepareStatement(db, "select CustomName from Devices where Id=@DeviceId"))
{
@@ -377,7 +382,8 @@ namespace Emby.Server.Implementations.Security
using (var connection = GetConnection())
{
- connection.RunInTransaction(db =>
+ connection.RunInTransaction(
+ db =>
{
using (var statement = db.PrepareStatement("replace into devices (Id, CustomName, Capabilities) VALUES (@Id, @CustomName, (Select Capabilities from Devices where Id=@Id))"))
{
diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs
index d38214116..784ecaeb3 100644
--- a/Jellyfin.Api/Controllers/ArtistsController.cs
+++ b/Jellyfin.Api/Controllers/ArtistsController.cs
@@ -54,7 +54,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.</param>
/// <param name="excludeItemTypes">Optional. If specified, results will be filtered out based on item type. This allows multiple, comma delimited.</param>
/// <param name="includeItemTypes">Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited.</param>
- /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.</param>
+ /// <param name="filters">Optional. Specify additional filters to apply.</param>
/// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
/// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
/// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimited.</param>
@@ -89,7 +89,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
@@ -188,7 +188,7 @@ namespace Jellyfin.Api.Controllers
}).Where(i => i != null).Select(i => i!.Id).ToArray();
}
- foreach (var filter in RequestHelpers.GetFilters(filters))
+ foreach (var filter in filters)
{
switch (filter)
{
@@ -263,7 +263,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.</param>
/// <param name="excludeItemTypes">Optional. If specified, results will be filtered out based on item type. This allows multiple, comma delimited.</param>
/// <param name="includeItemTypes">Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited.</param>
- /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.</param>
+ /// <param name="filters">Optional. Specify additional filters to apply.</param>
/// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
/// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
/// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimited.</param>
@@ -298,7 +298,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
@@ -397,7 +397,7 @@ namespace Jellyfin.Api.Controllers
}).Where(i => i != null).Select(i => i!.Id).ToArray();
}
- foreach (var filter in RequestHelpers.GetFilters(filters))
+ foreach (var filter in filters)
{
switch (filter)
{
diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs
index 33a969f85..fda00b8d4 100644
--- a/Jellyfin.Api/Controllers/ChannelsController.cs
+++ b/Jellyfin.Api/Controllers/ChannelsController.cs
@@ -105,7 +105,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="sortOrder">Optional. Sort Order - Ascending,Descending.</param>
- /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.</param>
+ /// <param name="filters">Optional. Specify additional filters to apply.</param>
/// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime.</param>
/// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.</param>
/// <response code="200">Channel items returned.</response>
@@ -121,7 +121,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] string? sortOrder,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] string? sortBy,
[FromQuery] string? fields)
{
@@ -140,7 +140,7 @@ namespace Jellyfin.Api.Controllers
.AddItemFields(fields)
};
- foreach (var filter in RequestHelpers.GetFilters(filters))
+ foreach (var filter in filters)
{
switch (filter)
{
@@ -183,7 +183,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="userId">Optional. User Id.</param>
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
- /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.</param>
+ /// <param name="filters">Optional. Specify additional filters to apply.</param>
/// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.</param>
/// <param name="channelIds">Optional. Specify one or more channel id's, comma delimited.</param>
/// <response code="200">Latest channel items returned.</response>
@@ -196,7 +196,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] Guid? userId,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] string? fields,
[FromQuery] string? channelIds)
{
@@ -217,7 +217,7 @@ namespace Jellyfin.Api.Controllers
.AddItemFields(fields)
};
- foreach (var filter in RequestHelpers.GetFilters(filters))
+ foreach (var filter in filters)
{
switch (filter)
{
diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs
index de6aa86c9..f4b69b312 100644
--- a/Jellyfin.Api/Controllers/GenresController.cs
+++ b/Jellyfin.Api/Controllers/GenresController.cs
@@ -55,7 +55,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.</param>
/// <param name="excludeItemTypes">Optional. If specified, results will be filtered out based on item type. This allows multiple, comma delimited.</param>
/// <param name="includeItemTypes">Optional. If specified, results will be filtered in based on item type. This allows multiple, comma delimited.</param>
- /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.</param>
+ /// <param name="filters">Optional. Specify additional filters to apply.</param>
/// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
/// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
/// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimited.</param>
@@ -90,7 +90,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
@@ -188,7 +188,7 @@ namespace Jellyfin.Api.Controllers
.ToArray();
}
- foreach (var filter in RequestHelpers.GetFilters(filters))
+ foreach (var filter in filters)
{
switch (filter)
{
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index 652c4689d..b50b1e836 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -159,7 +159,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isHd,
[FromQuery] bool? is4K,
[FromQuery] string? locationTypes,
- [FromQuery] string? excludeLocationTypes,
+ [FromQuery] LocationType[] excludeLocationTypes,
[FromQuery] bool? isMissing,
[FromQuery] bool? isUnaired,
[FromQuery] double? minCommunityRating,
@@ -182,7 +182,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? imageTypes,
@@ -365,7 +365,7 @@ namespace Jellyfin.Api.Controllers
query.CollapseBoxSetItems = false;
}
- foreach (var filter in RequestHelpers.GetFilters(filters!))
+ foreach (var filter in filters)
{
switch (filter)
{
@@ -406,12 +406,9 @@ namespace Jellyfin.Api.Controllers
}
// ExcludeLocationTypes
- if (!string.IsNullOrEmpty(excludeLocationTypes))
+ if (excludeLocationTypes.Any(t => t == LocationType.Virtual))
{
- if (excludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray().Contains(LocationType.Virtual))
- {
- query.IsVirtualItem = false;
- }
+ query.IsVirtualItem = false;
}
if (!string.IsNullOrEmpty(locationTypes))
diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs
index 570ae8fdc..d216b7f72 100644
--- a/Jellyfin.Api/Controllers/MusicGenresController.cs
+++ b/Jellyfin.Api/Controllers/MusicGenresController.cs
@@ -55,7 +55,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.</param>
/// <param name="excludeItemTypes">Optional. If specified, results will be filtered out based on item type. This allows multiple, comma delimited.</param>
/// <param name="includeItemTypes">Optional. If specified, results will be filtered in based on item type. This allows multiple, comma delimited.</param>
- /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.</param>
+ /// <param name="filters">Optional. Specify additional filters to apply.</param>
/// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
/// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
/// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimited.</param>
@@ -89,7 +89,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
@@ -187,7 +187,7 @@ namespace Jellyfin.Api.Controllers
.ToArray();
}
- foreach (var filter in RequestHelpers.GetFilters(filters))
+ foreach (var filter in filters)
{
switch (filter)
{
diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs
index 8bd610dad..bbef1ff9a 100644
--- a/Jellyfin.Api/Controllers/PersonsController.cs
+++ b/Jellyfin.Api/Controllers/PersonsController.cs
@@ -89,7 +89,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
@@ -187,7 +187,7 @@ namespace Jellyfin.Api.Controllers
.ToArray();
}
- foreach (var filter in RequestHelpers.GetFilters(filters))
+ foreach (var filter in filters)
{
switch (filter)
{
diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs
index cdd5f958e..3a8ac1b24 100644
--- a/Jellyfin.Api/Controllers/StudiosController.cs
+++ b/Jellyfin.Api/Controllers/StudiosController.cs
@@ -53,7 +53,7 @@ namespace Jellyfin.Api.Controllers
/// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.</param>
/// <param name="excludeItemTypes">Optional. If specified, results will be filtered out based on item type. This allows multiple, comma delimited.</param>
/// <param name="includeItemTypes">Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited.</param>
- /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes.</param>
+ /// <param name="filters">Optional. Specify additional filters to apply.</param>
/// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
/// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
/// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimited.</param>
@@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
[FromQuery] string? includeItemTypes,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? genres,
@@ -188,7 +188,7 @@ namespace Jellyfin.Api.Controllers
.ToArray();
}
- foreach (var filter in RequestHelpers.GetFilters(filters))
+ foreach (var filter in filters)
{
switch (filter)
{
diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs
index 5157b08ae..dec449857 100644
--- a/Jellyfin.Api/Controllers/TrailersController.cs
+++ b/Jellyfin.Api/Controllers/TrailersController.cs
@@ -1,6 +1,7 @@
using System;
using Jellyfin.Api.Constants;
using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@@ -124,7 +125,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isHd,
[FromQuery] bool? is4K,
[FromQuery] string? locationTypes,
- [FromQuery] string? excludeLocationTypes,
+ [FromQuery] LocationType[] excludeLocationTypes,
[FromQuery] bool? isMissing,
[FromQuery] bool? isUnaired,
[FromQuery] double? minCommunityRating,
@@ -146,7 +147,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? parentId,
[FromQuery] string? fields,
[FromQuery] string? excludeItemTypes,
- [FromQuery] string? filters,
+ [FromQuery] ItemFilter[] filters,
[FromQuery] bool? isFavorite,
[FromQuery] string? mediaTypes,
[FromQuery] string? imageTypes,
diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs
index 8dcf08af5..fb5c283f5 100644
--- a/Jellyfin.Api/Helpers/RequestHelpers.cs
+++ b/Jellyfin.Api/Helpers/RequestHelpers.cs
@@ -57,18 +57,6 @@ namespace Jellyfin.Api.Helpers
}
/// <summary>
- /// Get parsed filters.
- /// </summary>
- /// <param name="filters">The filters.</param>
- /// <returns>Item filters.</returns>
- public static IEnumerable<ItemFilter> GetFilters(string? filters)
- {
- return string.IsNullOrEmpty(filters)
- ? Array.Empty<ItemFilter>()
- : filters.Split(',').Select(v => Enum.Parse<ItemFilter>(v, true));
- }
-
- /// <summary>
/// Splits a string at a separating character into an array of substrings.
/// </summary>
/// <param name="value">The string to split.</param>
diff --git a/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs b/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs
index 13469194a..208566dc8 100644
--- a/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs
+++ b/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs
@@ -18,11 +18,6 @@ namespace Jellyfin.Api.ModelBinders
var elementType = bindingContext.ModelType.GetElementType();
var converter = TypeDescriptor.GetConverter(elementType);
- if (valueProviderResult == ValueProviderResult.None)
- {
- return Task.CompletedTask;
- }
-
if (valueProviderResult.Length > 1)
{
var result = Array.CreateInstance(elementType, valueProviderResult.Length);
diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index a1caa751b..6a9dbdae4 100644
--- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -553,13 +553,13 @@ namespace Jellyfin.Drawing.Skia
}
/// <inheritdoc/>
- public void CreateImageCollage(ImageCollageOptions options)
+ public void CreateImageCollage(ImageCollageOptions options, string? libraryName)
{
double ratio = (double)options.Width / options.Height;
if (ratio >= 1.4)
{
- new StripCollageBuilder(this).BuildThumbCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
+ new StripCollageBuilder(this).BuildThumbCollage(options.InputPaths, options.OutputPath, options.Width, options.Height, libraryName);
}
else if (ratio >= .9)
{
diff --git a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
index 10bb59648..0e94f87f6 100644
--- a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
+++ b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
@@ -82,48 +82,62 @@ namespace Jellyfin.Drawing.Skia
/// <param name="outputPath">The path at which to place the resulting image.</param>
/// <param name="width">The desired width of the collage.</param>
/// <param name="height">The desired height of the collage.</param>
- public void BuildThumbCollage(string[] paths, string outputPath, int width, int height)
+ /// <param name="libraryName">The name of the library to draw on the collage.</param>
+ public void BuildThumbCollage(string[] paths, string outputPath, int width, int height, string? libraryName)
{
- using var bitmap = BuildThumbCollageBitmap(paths, width, height);
+ using var bitmap = BuildThumbCollageBitmap(paths, width, height, libraryName);
using var outputStream = new SKFileWStream(outputPath);
using var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels());
pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
}
- private SKBitmap BuildThumbCollageBitmap(string[] paths, int width, int height)
+ private SKBitmap BuildThumbCollageBitmap(string[] paths, int width, int height, string? libraryName)
{
var bitmap = new SKBitmap(width, height);
using var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColors.Black);
- // number of images used in the thumbnail
- var iCount = 3;
-
- // determine sizes for each image that will composited into the final image
- var iSlice = Convert.ToInt32(width / iCount);
- int iHeight = Convert.ToInt32(height * 1.00);
- int imageIndex = 0;
- for (int i = 0; i < iCount; i++)
+ using var backdrop = GetNextValidImage(paths, 0, out _);
+ if (backdrop == null)
{
- using var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex);
- imageIndex = newIndex;
- if (currentBitmap == null)
- {
- continue;
- }
+ return bitmap;
+ }
- // resize to the same aspect as the original
- int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
- using var resizedImage = SkiaEncoder.ResizeImage(currentBitmap, new SKImageInfo(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType, currentBitmap.ColorSpace));
+ // resize to the same aspect as the original
+ var backdropHeight = Math.Abs(width * backdrop.Height / backdrop.Width);
+ using var residedBackdrop = SkiaEncoder.ResizeImage(backdrop, new SKImageInfo(width, backdropHeight, backdrop.ColorType, backdrop.AlphaType, backdrop.ColorSpace));
+ // draw the backdrop
+ canvas.DrawImage(residedBackdrop, 0, 0);
- // crop image
- int ix = Math.Abs((iWidth - iSlice) / 2);
- using var subset = resizedImage.Subset(SKRectI.Create(ix, 0, iSlice, iHeight));
- // draw image onto canvas
- canvas.DrawImage(subset ?? resizedImage, iSlice * i, 0);
+ // draw shadow rectangle
+ var paintColor = new SKPaint
+ {
+ Color = SKColors.Black.WithAlpha(0x78),
+ Style = SKPaintStyle.Fill
+ };
+ canvas.DrawRect(0, 0, width, height, paintColor);
+
+ // draw library name
+ var textPaint = new SKPaint
+ {
+ Color = SKColors.White,
+ Style = SKPaintStyle.Fill,
+ TextSize = 112,
+ TextAlign = SKTextAlign.Center,
+ Typeface = SKTypeface.FromFamilyName("sans-serif", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright),
+ IsAntialias = true
+ };
+
+ // scale down text to 90% of the width if text is larger than 95% of the width
+ var textWidth = textPaint.MeasureText(libraryName);
+ if (textWidth > width * 0.95)
+ {
+ textPaint.TextSize = 0.9f * width * textPaint.TextSize / textWidth;
}
+ canvas.DrawText(libraryName, width / 2f, (height / 2f) + (textPaint.FontMetrics.XHeight / 2), textPaint);
+
return bitmap;
}
diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs
index 4b2918d08..8545fd5dc 100644
--- a/MediaBrowser.Common/Plugins/BasePlugin.cs
+++ b/MediaBrowser.Common/Plugins/BasePlugin.cs
@@ -3,6 +3,7 @@
using System;
using System.IO;
using System.Reflection;
+using System.Runtime.InteropServices;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
@@ -140,6 +141,30 @@ namespace MediaBrowser.Common.Plugins
{
ApplicationPaths = applicationPaths;
XmlSerializer = xmlSerializer;
+ if (this is IPluginAssembly assemblyPlugin)
+ {
+ var assembly = GetType().Assembly;
+ var assemblyName = assembly.GetName();
+ var assemblyFilePath = assembly.Location;
+
+ var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
+
+ assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);
+
+ var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true);
+ if (idAttributes.Length > 0)
+ {
+ var attribute = (GuidAttribute)idAttributes[0];
+ var assemblyId = new Guid(attribute.Value);
+
+ assemblyPlugin.SetId(assemblyId);
+ }
+ }
+
+ if (this is IHasPluginConfiguration hasPluginConfiguration)
+ {
+ hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
+ }
}
/// <summary>
diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
index f9b2e6fef..770c6dc2d 100644
--- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs
+++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#nullable enable
using System;
using System.Collections.Generic;
@@ -63,6 +64,7 @@ namespace MediaBrowser.Controller.Drawing
/// Create an image collage.
/// </summary>
/// <param name="options">The options to use when creating the collage.</param>
- void CreateImageCollage(ImageCollageOptions options);
+ /// <param name="libraryName">Optional. </param>
+ void CreateImageCollage(ImageCollageOptions options, string? libraryName);
}
}
diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
index b7edb1052..935a79031 100644
--- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs
+++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#nullable enable
using System;
using System.Collections.Generic;
@@ -75,7 +76,7 @@ namespace MediaBrowser.Controller.Drawing
/// </summary>
/// <param name="options">The options.</param>
/// <returns>Task.</returns>
- Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options);
+ Task<(string path, string? mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options);
/// <summary>
/// Gets the supported image output formats.
@@ -87,7 +88,8 @@ namespace MediaBrowser.Controller.Drawing
/// Creates the image collage.
/// </summary>
/// <param name="options">The options.</param>
- void CreateImageCollage(ImageCollageOptions options);
+ /// <param name="libraryName">The library name to draw onto the collage.</param>
+ void CreateImageCollage(ImageCollageOptions options, string? libraryName);
bool SupportsTransparency(string path);
}
diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
index 8a69971d0..c65477d39 100644
--- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
+++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
@@ -45,7 +45,8 @@ namespace MediaBrowser.Controller.Entities
{
if (file.StartsWith("http", System.StringComparison.OrdinalIgnoreCase))
{
- item.SetImage(new ItemImageInfo
+ item.SetImage(
+ new ItemImageInfo
{
Path = file,
Type = imageType
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 901ea875b..35ddaffad 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -255,7 +255,8 @@ namespace MediaBrowser.Controller.Entities
var id = child.Id;
if (dictionary.ContainsKey(id))
{
- Logger.LogError("Found folder containing items with duplicate id. Path: {path}, Child Name: {ChildName}",
+ Logger.LogError(
+ "Found folder containing items with duplicate id. Path: {path}, Child Name: {ChildName}",
Path ?? Name,
child.Path ?? child.Name);
}
@@ -984,7 +985,8 @@ namespace MediaBrowser.Controller.Entities
return items;
}
- private static bool CollapseBoxSetItems(InternalItemsQuery query,
+ private static bool CollapseBoxSetItems(
+ InternalItemsQuery query,
BaseItem queryParent,
User user,
IServerConfigurationManager configurationManager)
@@ -1593,7 +1595,8 @@ namespace MediaBrowser.Controller.Entities
/// <param name="datePlayed">The date played.</param>
/// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
/// <returns>Task.</returns>
- public override void MarkPlayed(User user,
+ public override void MarkPlayed(
+ User user,
DateTime? datePlayed,
bool resetPosition)
{
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 068a76769..7bb311900 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -343,7 +343,8 @@ namespace MediaBrowser.Controller.Entities
{
var parentFolders = GetMediaFolders(parent, query.User, new[] { CollectionType.TvShows, string.Empty });
- var result = _tvSeriesManager.GetNextUp(new NextUpQuery
+ var result = _tvSeriesManager.GetNextUp(
+ new NextUpQuery
{
Limit = query.Limit,
StartIndex = query.StartIndex,
@@ -443,7 +444,8 @@ namespace MediaBrowser.Controller.Entities
return Filter(item, query.User, query, BaseItem.UserDataManager, BaseItem.LibraryManager);
}
- public static QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items,
+ public static QueryResult<BaseItem> PostFilterAndSort(
+ IEnumerable<BaseItem> items,
BaseItem queryParent,
int? totalRecordLimit,
InternalItemsQuery query,
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 332730bcc..32703c2fd 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -566,7 +566,8 @@ namespace MediaBrowser.Controller.Library
int GetCount(InternalItemsQuery query);
- void AddExternalSubtitleStreams(List<MediaStream> streams,
+ void AddExternalSubtitleStreams(
+ List<MediaStream> streams,
string videoPath,
string[] files);
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index c5529ad5b..f913c32e4 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -70,15 +70,15 @@ namespace MediaBrowser.Controller.MediaEncoding
var codecMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
- {"qsv", hwEncoder + "_qsv"},
- {hwEncoder + "_qsv", hwEncoder + "_qsv"},
- {"nvenc", hwEncoder + "_nvenc"},
- {"amf", hwEncoder + "_amf"},
- {"omx", hwEncoder + "_omx"},
- {hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"},
- {"mediacodec", hwEncoder + "_mediacodec"},
- {"vaapi", hwEncoder + "_vaapi"},
- {"videotoolbox", hwEncoder + "_videotoolbox"}
+ { "qsv", hwEncoder + "_qsv" },
+ { hwEncoder + "_qsv", hwEncoder + "_qsv" },
+ { "nvenc", hwEncoder + "_nvenc" },
+ { "amf", hwEncoder + "_amf" },
+ { "omx", hwEncoder + "_omx" },
+ { hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m" },
+ { "mediacodec", hwEncoder + "_mediacodec" },
+ { "vaapi", hwEncoder + "_vaapi" },
+ { "videotoolbox", hwEncoder + "_videotoolbox" }
};
if (!string.IsNullOrEmpty(hwType)
@@ -1822,7 +1822,8 @@ namespace MediaBrowser.Controller.MediaEncoding
return (Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight));
}
- public List<string> GetScalingFilters(EncodingJobInfo state,
+ public List<string> GetScalingFilters(
+ EncodingJobInfo state,
int? videoWidth,
int? videoHeight,
Video3DFormat? threedFormat,
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index c7ec878d2..6cd0c70d2 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -342,7 +342,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value);
- var newSize = DrawingUtils.Resize(size,
+ var newSize = DrawingUtils.Resize(
+ size,
BaseRequest.Width ?? 0,
BaseRequest.Height ?? 0,
BaseRequest.MaxWidth ?? 0,
@@ -368,7 +369,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value);
- var newSize = DrawingUtils.Resize(size,
+ var newSize = DrawingUtils.Resize(
+ size,
BaseRequest.Width ?? 0,
BaseRequest.Height ?? 0,
BaseRequest.MaxWidth ?? 0,
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 17d6dc5d2..f6bc1f4de 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -68,7 +68,8 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Extracts the video images on interval.
/// </summary>
- Task ExtractVideoImagesOnInterval(string[] inputFiles,
+ Task ExtractVideoImagesOnInterval(
+ string[] inputFiles,
string container,
MediaStream videoStream,
MediaProtocol protocol,
diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs
index eb7fb793a..75286eadc 100644
--- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs
@@ -29,7 +29,8 @@ namespace MediaBrowser.Controller.Resolvers
public interface IMultiItemResolver
{
- MultiItemResolverResult ResolveMultiple(Folder parent,
+ MultiItemResolverResult ResolveMultiple(
+ Folder parent,
List<FileSystemMetadata> files,
string collectionType,
IDirectoryService directoryService);
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index 93e60753a..8b73ecbd4 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -38,7 +38,8 @@ namespace MediaBrowser.Model.Dlna
";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
- ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container,
+ ResponseProfile mediaProfile = _profile.GetImageMediaProfile(
+ container,
width,
height);
@@ -160,7 +161,8 @@ namespace MediaBrowser.Model.Dlna
string dlnaflags = string.Format(CultureInfo.InvariantCulture, ";DLNA.ORG_FLAGS={0}",
DlnaMaps.FlagsToString(flagValue));
- ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container,
+ ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(
+ container,
audioCodec,
videoCodec,
width,
@@ -221,7 +223,8 @@ namespace MediaBrowser.Model.Dlna
private static string GetImageOrgPnValue(string container, int? width, int? height)
{
MediaFormatProfile? format = new MediaFormatProfileResolver()
- .ResolveImageFormat(container,
+ .ResolveImageFormat(
+ container,
width,
height);
@@ -231,7 +234,8 @@ namespace MediaBrowser.Model.Dlna
private static string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels)
{
MediaFormatProfile? format = new MediaFormatProfileResolver()
- .ResolveAudioFormat(container,
+ .ResolveAudioFormat(
+ container,
audioBitrate,
audioSampleRate,
audioChannels);
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index 7e921b1fd..44412f3e4 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -277,7 +277,8 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- public ResponseProfile GetVideoMediaProfile(string container,
+ public ResponseProfile GetVideoMediaProfile(
+ string container,
string audioCodec,
string videoCodec,
int? width,
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index fc0aad072..4959a9b92 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -455,7 +455,8 @@ namespace MediaBrowser.Model.Dlna
if (directPlayProfile == null)
{
- _logger.LogInformation("Profile: {0}, No audio direct play profiles found for {1} with codec {2}",
+ _logger.LogInformation(
+ "Profile: {0}, No audio direct play profiles found for {1} with codec {2}",
options.Profile.Name ?? "Unknown Profile",
item.Path ?? "Unknown path",
audioStream.Codec ?? "Unknown codec");
@@ -679,7 +680,8 @@ namespace MediaBrowser.Model.Dlna
bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult.Item1);
bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamEligibilityResult.Item1);
- _logger.LogInformation("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
+ _logger.LogInformation(
+ "Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
options.Profile.Name ?? "Unknown Profile",
item.Path ?? "Unknown path",
isEligibleForDirectPlay,
@@ -973,7 +975,8 @@ namespace MediaBrowser.Model.Dlna
if (directPlay == null)
{
- _logger.LogInformation("Profile: {0}, No video direct play profiles found for {1} with codec {2}",
+ _logger.LogInformation(
+ "Profile: {0}, No video direct play profiles found for {1} with codec {2}",
profile.Name ?? "Unknown Profile",
mediaSource.Path ?? "Unknown path",
videoStream.Codec ?? "Unknown codec");
@@ -1137,7 +1140,8 @@ namespace MediaBrowser.Model.Dlna
private void LogConditionFailure(DeviceProfile profile, string type, ProfileCondition condition, MediaSourceInfo mediaSource)
{
- _logger.LogInformation("Profile: {0}, DirectPlay=false. Reason={1}.{2} Condition: {3}. ConditionValue: {4}. IsRequired: {5}. Path: {6}",
+ _logger.LogInformation(
+ "Profile: {0}, DirectPlay=false. Reason={1}.{2} Condition: {3}. ConditionValue: {4}. IsRequired: {5}. Path: {6}",
type,
profile.Name ?? "Unknown Profile",
condition.Property,
@@ -1342,7 +1346,8 @@ namespace MediaBrowser.Model.Dlna
if (itemBitrate > requestedMaxBitrate)
{
- _logger.LogInformation("Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}",
+ _logger.LogInformation(
+ "Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}",
playMethod, itemBitrate, requestedMaxBitrate);
return false;
}
diff --git a/MediaBrowser.Model/Session/GeneralCommandType.cs b/MediaBrowser.Model/Session/GeneralCommandType.cs
index 5a9042d5f..c58fa9a6b 100644
--- a/MediaBrowser.Model/Session/GeneralCommandType.cs
+++ b/MediaBrowser.Model/Session/GeneralCommandType.cs
@@ -43,6 +43,11 @@ namespace MediaBrowser.Model.Session
Guide = 32,
ToggleStats = 33,
PlayMediaSource = 34,
- PlayTrailers = 35
+ PlayTrailers = 35,
+ SetShuffleQueue = 36,
+ PlayState = 37,
+ PlayNext = 38,
+ ToggleOsdMenu = 39,
+ Play = 40
}
}
diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs
index d2f7556a5..53030843a 100644
--- a/MediaBrowser.Model/System/PublicSystemInfo.cs
+++ b/MediaBrowser.Model/System/PublicSystemInfo.cs
@@ -43,7 +43,10 @@ namespace MediaBrowser.Model.System
/// <summary>
/// Gets or sets a value indicating whether the startup wizard is completed.
/// </summary>
- /// <value>The startup completion status.</value>
- public bool StartupWizardCompleted { get; set; }
+ /// <remarks>
+ /// Nullable for OpenAPI specification only to retain backwards compatibility in apiclients.
+ /// </remarks>
+ /// <value>The startup completion status.</value>]
+ public bool? StartupWizardCompleted { get; set; }
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
index d231bfa2f..9804ec3bb 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs
@@ -214,7 +214,7 @@ namespace MediaBrowser.Providers.MediaInfo
return new[]
{
// Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
+ new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks }
};
}
}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
index 5e9a4a225..ce0dab701 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs
@@ -150,7 +150,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
string language,
CancellationToken cancellationToken)
{
- searchInfo.SeriesProviderIds.TryGetValue(nameof(MetadataProvider.Tvdb),
+ searchInfo.SeriesProviderIds.TryGetValue(
+ nameof(MetadataProvider.Tvdb),
out var seriesTvdbId);
var episodeQuery = new EpisodeQuery();
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
index 5fa8a3e1c..fd72ea4a8 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs
@@ -106,7 +106,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
.ConfigureAwait(false);
if (string.IsNullOrEmpty(episodeTvdbId))
{
- _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}",
+ _logger.LogError(
+ "Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}",
searchInfo.ParentIndexNumber, searchInfo.IndexNumber, seriesTvdbId);
return result;
}
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
index ca9b1d738..b34e52235 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Text;
@@ -123,7 +124,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
await _tvdbClientManager
.GetSeriesByIdAsync(Convert.ToInt32(tvdbId), metadataLanguage, cancellationToken)
.ConfigureAwait(false);
- MapSeriesToResult(result, seriesResult.Data, metadataLanguage);
+ await MapSeriesToResult(result, seriesResult.Data, metadataLanguage).ConfigureAwait(false);
}
catch (TvDbServerException e)
{
@@ -297,7 +298,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
return name.Trim();
}
- private void MapSeriesToResult(MetadataResult<Series> result, TvDbSharper.Dto.Series tvdbSeries, string metadataLanguage)
+ private async Task MapSeriesToResult(MetadataResult<Series> result, TvDbSharper.Dto.Series tvdbSeries, string metadataLanguage)
{
Series series = result.Item;
series.SetProviderId(MetadataProvider.Tvdb, tvdbSeries.Id.ToString());
@@ -340,20 +341,21 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
try
{
- var episodeSummary = _tvdbClientManager
- .GetSeriesEpisodeSummaryAsync(tvdbSeries.Id, metadataLanguage, CancellationToken.None).Result.Data;
- var maxSeasonNumber = episodeSummary.AiredSeasons.Select(s => Convert.ToInt32(s)).Max();
- var episodeQuery = new EpisodeQuery
+ var episodeSummary = await _tvdbClientManager.GetSeriesEpisodeSummaryAsync(tvdbSeries.Id, metadataLanguage, CancellationToken.None).ConfigureAwait(false);
+
+ if (episodeSummary.Data.AiredSeasons.Length != 0)
{
- AiredSeason = maxSeasonNumber
- };
- var episodesPage =
- _tvdbClientManager.GetEpisodesPageAsync(tvdbSeries.Id, episodeQuery, metadataLanguage, CancellationToken.None).Result.Data;
- result.Item.EndDate = episodesPage.Select(e =>
+ var maxSeasonNumber = episodeSummary.Data.AiredSeasons.Max(s => Convert.ToInt32(s, CultureInfo.InvariantCulture));
+ var episodeQuery = new EpisodeQuery
{
- DateTime.TryParse(e.FirstAired, out var firstAired);
- return firstAired;
- }).Max();
+ AiredSeason = maxSeasonNumber
+ };
+ var episodesPage = await _tvdbClientManager.GetEpisodesPageAsync(tvdbSeries.Id, episodeQuery, metadataLanguage, CancellationToken.None).ConfigureAwait(false);
+
+ result.Item.EndDate = episodesPage.Data
+ .Select(e => DateTime.TryParse(e.FirstAired, out var firstAired) ? firstAired : (DateTime?)null)
+ .Max();
+ }
}
catch (TvDbServerException e)
{
diff --git a/tests/Jellyfin.Api.Tests/ModelBinders/CommaDelimitedArrayModelBinderTests.cs b/tests/Jellyfin.Api.Tests/ModelBinders/CommaDelimitedArrayModelBinderTests.cs
index c801b4a52..89c7d62f7 100644
--- a/tests/Jellyfin.Api.Tests/ModelBinders/CommaDelimitedArrayModelBinderTests.cs
+++ b/tests/Jellyfin.Api.Tests/ModelBinders/CommaDelimitedArrayModelBinderTests.cs
@@ -1,17 +1,13 @@
using System;
using System.Collections.Generic;
-using System.Collections.Specialized;
using System.Globalization;
-using System.Text;
using System.Threading.Tasks;
using Jellyfin.Api.ModelBinders;
-using MediaBrowser.Controller.Entities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Primitives;
using Moq;
using Xunit;
-using Xunit.Sdk;
namespace Jellyfin.Api.Tests.ModelBinders
{
@@ -21,14 +17,14 @@ namespace Jellyfin.Api.Tests.ModelBinders
public async Task BindModelAsync_CorrectlyBindsValidCommaDelimitedStringArrayQuery()
{
var queryParamName = "test";
- var queryParamValues = new string[] { "lol", "xd" };
+ var queryParamValues = new[] { "lol", "xd" };
var queryParamString = "lol,xd";
var queryParamType = typeof(string[]);
var modelBinder = new CommaDelimitedArrayModelBinder();
var valueProvider = new QueryStringValueProvider(
new BindingSource(string.Empty, string.Empty, false, false),
- new QueryCollection(new Dictionary<string, StringValues>() { { queryParamName, new StringValues(queryParamString) } }),
+ new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
CultureInfo.InvariantCulture);
var bindingContextMock = new Mock<ModelBindingContext>();
bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
@@ -46,14 +42,14 @@ namespace Jellyfin.Api.Tests.ModelBinders
public async Task BindModelAsync_CorrectlyBindsValidCommaDelimitedIntArrayQuery()
{
var queryParamName = "test";
- var queryParamValues = new int[] { 42, 0 };
+ var queryParamValues = new[] { 42, 0 };
var queryParamString = "42,0";
var queryParamType = typeof(int[]);
var modelBinder = new CommaDelimitedArrayModelBinder();
var valueProvider = new QueryStringValueProvider(
new BindingSource(string.Empty, string.Empty, false, false),
- new QueryCollection(new Dictionary<string, StringValues>() { { queryParamName, new StringValues(queryParamString) } }),
+ new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
CultureInfo.InvariantCulture);
var bindingContextMock = new Mock<ModelBindingContext>();
bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
@@ -71,14 +67,14 @@ namespace Jellyfin.Api.Tests.ModelBinders
public async Task BindModelAsync_CorrectlyBindsValidCommaDelimitedEnumArrayQuery()
{
var queryParamName = "test";
- var queryParamValues = new TestType[] { TestType.How, TestType.Much };
+ var queryParamValues = new[] { TestType.How, TestType.Much };
var queryParamString = "How,Much";
var queryParamType = typeof(TestType[]);
var modelBinder = new CommaDelimitedArrayModelBinder();
var valueProvider = new QueryStringValueProvider(
new BindingSource(string.Empty, string.Empty, false, false),
- new QueryCollection(new Dictionary<string, StringValues>() { { queryParamName, new StringValues(queryParamString) } }),
+ new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
CultureInfo.InvariantCulture);
var bindingContextMock = new Mock<ModelBindingContext>();
bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
@@ -96,14 +92,14 @@ namespace Jellyfin.Api.Tests.ModelBinders
public async Task BindModelAsync_CorrectlyBindsValidCommaDelimitedEnumArrayQueryWithDoubleCommas()
{
var queryParamName = "test";
- var queryParamValues = new TestType[] { TestType.How, TestType.Much };
+ var queryParamValues = new[] { TestType.How, TestType.Much };
var queryParamString = "How,,Much";
var queryParamType = typeof(TestType[]);
var modelBinder = new CommaDelimitedArrayModelBinder();
var valueProvider = new QueryStringValueProvider(
new BindingSource(string.Empty, string.Empty, false, false),
- new QueryCollection(new Dictionary<string, StringValues>() { { queryParamName, new StringValues(queryParamString) } }),
+ new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
CultureInfo.InvariantCulture);
var bindingContextMock = new Mock<ModelBindingContext>();
bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
@@ -121,7 +117,7 @@ namespace Jellyfin.Api.Tests.ModelBinders
public async Task BindModelAsync_CorrectlyBindsValidEnumArrayQuery()
{
var queryParamName = "test";
- var queryParamValues = new TestType[] { TestType.How, TestType.Much };
+ var queryParamValues = new[] { TestType.How, TestType.Much };
var queryParamString1 = "How";
var queryParamString2 = "Much";
var queryParamType = typeof(TestType[]);
@@ -130,9 +126,9 @@ namespace Jellyfin.Api.Tests.ModelBinders
var valueProvider = new QueryStringValueProvider(
new BindingSource(string.Empty, string.Empty, false, false),
- new QueryCollection(new Dictionary<string, StringValues>()
+ new QueryCollection(new Dictionary<string, StringValues>
{
- { queryParamName, new StringValues(new string[] { queryParamString1, queryParamString2 }) },
+ { queryParamName, new StringValues(new[] { queryParamString1, queryParamString2 }) },
}),
CultureInfo.InvariantCulture);
var bindingContextMock = new Mock<ModelBindingContext>();
@@ -158,7 +154,7 @@ namespace Jellyfin.Api.Tests.ModelBinders
var valueProvider = new QueryStringValueProvider(
new BindingSource(string.Empty, string.Empty, false, false),
- new QueryCollection(new Dictionary<string, StringValues>()
+ new QueryCollection(new Dictionary<string, StringValues>
{
{ queryParamName, new StringValues(value: null) },
}),
@@ -171,7 +167,8 @@ namespace Jellyfin.Api.Tests.ModelBinders
await modelBinder.BindModelAsync(bindingContextMock.Object);
- Assert.False(bindingContextMock.Object.Result.IsModelSet);
+ Assert.True(bindingContextMock.Object.Result.IsModelSet);
+ Assert.Equal((TestType[])bindingContextMock.Object.Result.Model, queryParamValues);
}
[Fact]
@@ -184,7 +181,7 @@ namespace Jellyfin.Api.Tests.ModelBinders
var modelBinder = new CommaDelimitedArrayModelBinder();
var valueProvider = new QueryStringValueProvider(
new BindingSource(string.Empty, string.Empty, false, false),
- new QueryCollection(new Dictionary<string, StringValues>() { { queryParamName, new StringValues(queryParamString) } }),
+ new QueryCollection(new Dictionary<string, StringValues> { { queryParamName, new StringValues(queryParamString) } }),
CultureInfo.InvariantCulture);
var bindingContextMock = new Mock<ModelBindingContext>();
bindingContextMock.Setup(b => b.ValueProvider).Returns(valueProvider);
@@ -201,7 +198,6 @@ namespace Jellyfin.Api.Tests.ModelBinders
public async Task BindModelAsync_ThrowsIfCommaDelimitedEnumArrayQueryIsInvalid2()
{
var queryParamName = "test";
- var queryParamValues = new TestType[] { TestType.How, TestType.Much };
var queryParamString1 = "How";
var queryParamString2 = "😱";
var queryParamType = typeof(TestType[]);
@@ -210,9 +206,9 @@ namespace Jellyfin.Api.Tests.ModelBinders
var valueProvider = new QueryStringValueProvider(
new BindingSource(string.Empty, string.Empty, false, false),
- new QueryCollection(new Dictionary<string, StringValues>()
+ new QueryCollection(new Dictionary<string, StringValues>
{
- { queryParamName, new StringValues(new string[] { queryParamString1, queryParamString2 }) },
+ { queryParamName, new StringValues(new[] { queryParamString1, queryParamString2 }) },
}),
CultureInfo.InvariantCulture);
var bindingContextMock = new Mock<ModelBindingContext>();