aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile23
-rw-r--r--Emby.Dlna/ContentDirectory/ControlHandler.cs1
-rw-r--r--Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs1
-rw-r--r--Emby.Dlna/PlayTo/Device.cs6
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs47
-rw-r--r--Emby.Dlna/PlayTo/PlayToManager.cs7
-rw-r--r--Emby.Dlna/PlayTo/PlaylistItemFactory.cs4
-rw-r--r--Emby.Dlna/PlayTo/SsdpHttpClient.cs3
-rw-r--r--Emby.Dlna/PlayTo/TransportCommands.cs16
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs10
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs8
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs21
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs9
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs4
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs14
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs3
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json2
-rw-r--r--Emby.Server.Implementations/Localization/countries.json6
-rw-r--r--Emby.Server.Implementations/MediaEncoder/EncodingManager.cs20
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs20
-rw-r--r--Jellyfin.Api/Auth/CustomAuthenticationHandler.cs5
-rw-r--r--Jellyfin.Api/Controllers/ArtistsController.cs31
-rw-r--r--Jellyfin.Api/Controllers/ChannelsController.cs2
-rw-r--r--Jellyfin.Api/Controllers/FilterController.cs43
-rw-r--r--Jellyfin.Api/Controllers/GenresController.cs8
-rw-r--r--Jellyfin.Api/Controllers/ItemsController.cs18
-rw-r--r--Jellyfin.Api/Controllers/LibraryController.cs2
-rw-r--r--Jellyfin.Api/Controllers/MoviesController.cs4
-rw-r--r--Jellyfin.Api/Controllers/MusicGenresController.cs8
-rw-r--r--Jellyfin.Api/Controllers/PackageController.cs8
-rw-r--r--Jellyfin.Api/Controllers/PersonsController.cs4
-rw-r--r--Jellyfin.Api/Controllers/SearchController.cs2
-rw-r--r--Jellyfin.Api/Controllers/StudiosController.cs8
-rw-r--r--Jellyfin.Api/Controllers/TrailersController.cs2
-rw-r--r--Jellyfin.Api/Controllers/TvShowsController.cs22
-rw-r--r--Jellyfin.Api/Controllers/YearsController.cs9
-rw-r--r--Jellyfin.Api/Helpers/TranscodingJobHelper.cs5
-rw-r--r--Jellyfin.Networking/Manager/NetworkManager.cs3
-rw-r--r--Jellyfin.Server.Implementations/JellyfinDb.cs2
-rw-r--r--Jellyfin.Server.Implementations/ModelBuilderExtensions.cs48
-rw-r--r--Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs21
-rw-r--r--Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs27
-rw-r--r--MediaBrowser.Common/Json/Converters/JsonDateTimeIso8601Converter.cs24
-rw-r--r--MediaBrowser.Common/Json/JsonDefaults.cs1
-rw-r--r--MediaBrowser.Controller/Channels/IChannelManager.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs45
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs56
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs5
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs15
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs33
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs11
-rw-r--r--MediaBrowser.Controller/Net/AuthorizationInfo.cs5
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs16
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs29
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs129
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs16
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs55
-rw-r--r--MediaBrowser.Model/Dlna/ContainerProfile.cs2
-rw-r--r--MediaBrowser.Model/Querying/NextUpQuery.cs2
-rw-r--r--MediaBrowser.Model/Search/SearchQuery.cs2
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs7
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs4
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs26
-rw-r--r--tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs4
67 files changed, 477 insertions, 527 deletions
diff --git a/Dockerfile b/Dockerfile
index 963027b49..41dd3d081 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -27,8 +27,15 @@ ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
+
+# https://github.com/intel/compute-runtime/releases
+ARG GMMLIB_VERSION=20.3.2
+ARG IGC_VERSION=1.0.5435
+ARG NEO_VERSION=20.46.18421
+ARG LEVEL_ZERO_VERSION=1.0.18421
+
# Install dependencies:
-# mesa-va-drivers: needed for AMD VAAPI
+# mesa-va-drivers: needed for AMD VAAPI. Mesa >= 20.1 is required for HEVC transcoding.
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget apt-transport-https \
&& wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | apt-key add - \
@@ -39,6 +46,20 @@ RUN apt-get update \
jellyfin-ffmpeg \
openssl \
locales \
+# Intel VAAPI Tone mapping dependencies:
+# Prefer NEO to Beignet since the latter one doesn't support Comet Lake or newer for now.
+# Do not use the intel-opencl-icd package from repo since they will not build with RELEASE_WITH_REGKEYS enabled.
+ && mkdir intel-compute-runtime \
+ && cd intel-compute-runtime \
+ && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-gmmlib_${GMMLIB_VERSION}_amd64.deb \
+ && wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-core_${IGC_VERSION}_amd64.deb \
+ && wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-opencl_${IGC_VERSION}_amd64.deb \
+ && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-opencl_${NEO_VERSION}_amd64.deb \
+ && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-ocloc_${NEO_VERSION}_amd64.deb \
+ && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-level-zero-gpu_${LEVEL_ZERO_VERSION}_amd64.deb \
+ && dpkg -i *.deb \
+ && cd .. \
+ && rm -rf intel-compute-runtime \
&& apt-get remove gnupg wget apt-transport-https -y \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y \
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
index b93651746..27f1fdaba 100644
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs
@@ -1681,7 +1681,6 @@ namespace Emby.Dlna.ContentDirectory
private ServerItem GetItemFromObjectId(string id)
{
return DidlBuilder.IsIdRoot(id)
-
? new ServerItem(_libraryManager.GetUserRootFolder())
: ParseItemId(id);
}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
index 1dc9c79c1..56788ae22 100644
--- a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
+++ b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using Emby.Dlna.Common;
-using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.MediaReceiverRegistrar
{
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
index f8ff03076..938ce5fbf 100644
--- a/Emby.Dlna/PlayTo/Device.cs
+++ b/Emby.Dlna/PlayTo/Device.cs
@@ -12,8 +12,6 @@ using System.Xml;
using System.Xml.Linq;
using Emby.Dlna.Common;
using Emby.Dlna.Ssdp;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
using Microsoft.Extensions.Logging;
namespace Emby.Dlna.PlayTo
@@ -345,7 +343,7 @@ namespace Emby.Dlna.PlayTo
RestartTimer(true);
}
- private string CreateDidlMeta(string value)
+ private static string CreateDidlMeta(string value)
{
if (string.IsNullOrEmpty(value))
{
@@ -962,7 +960,7 @@ namespace Emby.Dlna.PlayTo
url = "/dmr/" + url;
}
- if (!url.StartsWith("/", StringComparison.Ordinal))
+ if (!url.StartsWith('/'))
{
url = "/" + url;
}
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 3907b2a39..b7cd91a5c 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -9,7 +9,6 @@ using System.Threading.Tasks;
using Emby.Dlna.Didl;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Events;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
@@ -41,7 +40,6 @@ namespace Emby.Dlna.PlayTo
private readonly IUserDataManager _userDataManager;
private readonly ILocalizationManager _localization;
private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IConfigurationManager _config;
private readonly IMediaEncoder _mediaEncoder;
private readonly IDeviceDiscovery _deviceDiscovery;
@@ -68,7 +66,6 @@ namespace Emby.Dlna.PlayTo
IUserDataManager userDataManager,
ILocalizationManager localization,
IMediaSourceManager mediaSourceManager,
- IConfigurationManager config,
IMediaEncoder mediaEncoder)
{
_session = session;
@@ -84,7 +81,6 @@ namespace Emby.Dlna.PlayTo
_userDataManager = userDataManager;
_localization = localization;
_mediaSourceManager = mediaSourceManager;
- _config = config;
_mediaEncoder = mediaEncoder;
}
@@ -337,25 +333,17 @@ namespace Emby.Dlna.PlayTo
}
var startIndex = command.StartIndex ?? 0;
+ int len = items.Count - startIndex;
if (startIndex > 0)
{
- items = items.GetRange(startIndex, items.Count - startIndex);
+ items = items.GetRange(startIndex, len);
}
- var playlist = new List<PlaylistItem>();
- var isFirst = true;
-
- foreach (var item in items)
+ var playlist = new PlaylistItem[len];
+ playlist[0] = CreatePlaylistItem(items[0], user, command.StartPositionTicks.Value, command.MediaSourceId, command.AudioStreamIndex, command.SubtitleStreamIndex);
+ for (int i = 1; i < len; i++)
{
- if (isFirst && command.StartPositionTicks.HasValue)
- {
- playlist.Add(CreatePlaylistItem(item, user, command.StartPositionTicks.Value, command.MediaSourceId, command.AudioStreamIndex, command.SubtitleStreamIndex));
- isFirst = false;
- }
- else
- {
- playlist.Add(CreatePlaylistItem(item, user, 0, null, null, null));
- }
+ playlist[i] = CreatePlaylistItem(items[i], user, 0, null, null, null);
}
_logger.LogDebug("{0} - Playlist created", _session.DeviceName);
@@ -468,8 +456,8 @@ namespace Emby.Dlna.PlayTo
_dlnaManager.GetDefaultProfile();
var mediaSources = item is IHasMediaSources
- ? _mediaSourceManager.GetStaticMediaSources(item, true, user)
- : new List<MediaSourceInfo>();
+ ? _mediaSourceManager.GetStaticMediaSources(item, true, user).ToArray()
+ : Array.Empty<MediaSourceInfo>();
var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
playlistItem.StreamInfo.StartPositionTicks = startPostionTicks;
@@ -548,7 +536,7 @@ namespace Emby.Dlna.PlayTo
return null;
}
- private PlaylistItem GetPlaylistItem(BaseItem item, List<MediaSourceInfo> mediaSources, DeviceProfile profile, string deviceId, string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
+ private PlaylistItem GetPlaylistItem(BaseItem item, MediaSourceInfo[] mediaSources, DeviceProfile profile, string deviceId, string mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
{
if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
@@ -557,7 +545,7 @@ namespace Emby.Dlna.PlayTo
StreamInfo = new StreamBuilder(_mediaEncoder, _logger).BuildVideoItem(new VideoOptions
{
ItemId = item.Id,
- MediaSources = mediaSources.ToArray(),
+ MediaSources = mediaSources,
Profile = profile,
DeviceId = deviceId,
MaxBitrate = profile.MaxStreamingBitrate,
@@ -577,7 +565,7 @@ namespace Emby.Dlna.PlayTo
StreamInfo = new StreamBuilder(_mediaEncoder, _logger).BuildAudioItem(new AudioOptions
{
ItemId = item.Id,
- MediaSources = mediaSources.ToArray(),
+ MediaSources = mediaSources,
Profile = profile,
DeviceId = deviceId,
MaxBitrate = profile.MaxStreamingBitrate,
@@ -590,7 +578,7 @@ namespace Emby.Dlna.PlayTo
if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
{
- return new PlaylistItemFactory().Create((Photo)item, profile);
+ return PlaylistItemFactory.Create((Photo)item, profile);
}
throw new ArgumentException("Unrecognized item type.");
@@ -774,13 +762,14 @@ namespace Emby.Dlna.PlayTo
private async Task SeekAfterTransportChange(long positionTicks, CancellationToken cancellationToken)
{
- const int maxWait = 15000000;
- const int interval = 500;
+ const int MaxWait = 15000000;
+ const int Interval = 500;
+
var currentWait = 0;
- while (_device.TransportState != TransportState.Playing && currentWait < maxWait)
+ while (_device.TransportState != TransportState.Playing && currentWait < MaxWait)
{
- await Task.Delay(interval).ConfigureAwait(false);
- currentWait += interval;
+ await Task.Delay(Interval).ConfigureAwait(false);
+ currentWait += Interval;
}
await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false);
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
index cb183ce71..f34332d62 100644
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ b/Emby.Dlna/PlayTo/PlayToManager.cs
@@ -90,10 +90,10 @@ namespace Emby.Dlna.PlayTo
string location = info.Location.ToString();
// It has to report that it's a media renderer
- if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1 &&
- nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1)
+ if (!usn.Contains("MediaRenderer:", StringComparison.OrdinalIgnoreCase)
+ && !nt.Contains("MediaRenderer:", StringComparison.OrdinalIgnoreCase))
{
- // _logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
+ _logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
return;
}
@@ -203,7 +203,6 @@ namespace Emby.Dlna.PlayTo
_userDataManager,
_localization,
_mediaSourceManager,
- _config,
_mediaEncoder);
sessionInfo.AddController(controller);
diff --git a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
index bedc8b9ad..e28840a89 100644
--- a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
+++ b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -8,9 +8,9 @@ using MediaBrowser.Model.Session;
namespace Emby.Dlna.PlayTo
{
- public class PlaylistItemFactory
+ public static class PlaylistItemFactory
{
- public PlaylistItem Create(Photo item, DeviceProfile profile)
+ public static PlaylistItem Create(Photo item, DeviceProfile profile)
{
var playlistItem = new PlaylistItem
{
diff --git a/Emby.Dlna/PlayTo/SsdpHttpClient.cs b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
index f4d793790..557bc69a7 100644
--- a/Emby.Dlna/PlayTo/SsdpHttpClient.cs
+++ b/Emby.Dlna/PlayTo/SsdpHttpClient.cs
@@ -4,7 +4,6 @@ using System;
using System.Globalization;
using System.IO;
using System.Net.Http;
-using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
using System.Threading;
@@ -60,7 +59,7 @@ namespace Emby.Dlna.PlayTo
return serviceUrl;
}
- if (!serviceUrl.StartsWith("/", StringComparison.Ordinal))
+ if (!serviceUrl.StartsWith('/'))
{
serviceUrl = "/" + serviceUrl;
}
diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs
index fda17a8b4..0865968ad 100644
--- a/Emby.Dlna/PlayTo/TransportCommands.cs
+++ b/Emby.Dlna/PlayTo/TransportCommands.cs
@@ -78,7 +78,7 @@ namespace Emby.Dlna.PlayTo
private static StateVariable FromXml(XElement container)
{
- var allowedValues = new List<string>();
+ var allowedValues = Array.Empty<string>();
var element = container.Descendants(UPnpNamespaces.Svc + "allowedValueList")
.FirstOrDefault();
@@ -86,14 +86,14 @@ namespace Emby.Dlna.PlayTo
{
var values = element.Descendants(UPnpNamespaces.Svc + "allowedValue");
- allowedValues.AddRange(values.Select(child => child.Value));
+ allowedValues = values.Select(child => child.Value).ToArray();
}
return new StateVariable
{
Name = container.GetValue(UPnpNamespaces.Svc + "name"),
DataType = container.GetValue(UPnpNamespaces.Svc + "dataType"),
- AllowedValues = allowedValues.ToArray()
+ AllowedValues = allowedValues
};
}
@@ -103,12 +103,12 @@ namespace Emby.Dlna.PlayTo
foreach (var arg in action.ArgumentList)
{
- if (arg.Direction == "out")
+ if (string.Equals(arg.Direction, "out", StringComparison.Ordinal))
{
continue;
}
- if (arg.Name == "InstanceID")
+ if (string.Equals(arg.Name, "InstanceID", StringComparison.Ordinal))
{
stateString += BuildArgumentXml(arg, "0");
}
@@ -127,12 +127,12 @@ namespace Emby.Dlna.PlayTo
foreach (var arg in action.ArgumentList)
{
- if (arg.Direction == "out")
+ if (string.Equals(arg.Direction, "out", StringComparison.Ordinal))
{
continue;
}
- if (arg.Name == "InstanceID")
+ if (string.Equals(arg.Name, "InstanceID", StringComparison.Ordinal))
{
stateString += BuildArgumentXml(arg, "0");
}
@@ -151,7 +151,7 @@ namespace Emby.Dlna.PlayTo
foreach (var arg in action.ArgumentList)
{
- if (arg.Name == "InstanceID")
+ if (string.Equals(arg.Name, "InstanceID", StringComparison.Ordinal))
{
stateString += BuildArgumentXml(arg, "0");
}
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 30ccaf8de..5498f5a10 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1,21 +1,18 @@
#pragma warning disable CS1591
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
-using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using System.Xml.Serialization;
using Emby.Dlna;
using Emby.Dlna.Main;
using Emby.Dlna.Ssdp;
@@ -52,7 +49,6 @@ using Jellyfin.Networking.Manager;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Json;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
@@ -85,7 +81,6 @@ using MediaBrowser.Controller.SyncPlay;
using MediaBrowser.Controller.TV;
using MediaBrowser.LocalMetadata.Savers;
using MediaBrowser.MediaEncoding.BdInfo;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Globalization;
@@ -100,7 +95,6 @@ using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Plugins.Tmdb;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.XbmcMetadata.Providers;
-using Microsoft.AspNetCore.DataProtection.Repositories;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
@@ -128,7 +122,6 @@ namespace Emby.Server.Implementations
private IMediaEncoder _mediaEncoder;
private ISessionManager _sessionManager;
- private IHttpClientFactory _httpClientFactory;
private string[] _urlPrefixes;
/// <summary>
@@ -661,7 +654,6 @@ namespace Emby.Server.Implementations
_mediaEncoder = Resolve<IMediaEncoder>();
_sessionManager = Resolve<ISessionManager>();
- _httpClientFactory = Resolve<IHttpClientFactory>();
((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
@@ -1049,7 +1041,7 @@ namespace Emby.Server.Implementations
metafile = dir.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries)[^1];
int versionIndex = dir.LastIndexOf('_');
- if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version parsedVersion))
+ if (versionIndex != -1 && Version.TryParse(dir.AsSpan()[(versionIndex + 1)..], out Version parsedVersion))
{
// Versioned folder.
versions.Add(new LocalPlugin(Guid.Empty, metafile, parsedVersion, dir));
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 3d97a6ca8..57684a429 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -540,18 +540,18 @@ namespace Emby.Server.Implementations.Channels
{
IncludeItemTypes = new[] { nameof(Channel) },
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }
- }).Select(i => GetChannelFeatures(i.ToString("N", CultureInfo.InvariantCulture))).ToArray();
+ }).Select(i => GetChannelFeatures(i)).ToArray();
}
/// <inheritdoc />
- public ChannelFeatures GetChannelFeatures(string id)
+ public ChannelFeatures GetChannelFeatures(Guid? id)
{
- if (string.IsNullOrEmpty(id))
+ if (!id.HasValue)
{
throw new ArgumentNullException(nameof(id));
}
- var channel = GetChannel(id);
+ var channel = GetChannel(id.Value);
var channelProvider = GetChannelProvider(channel);
return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures());
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 7e01bd4b6..50c7a07d4 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -4519,17 +4519,17 @@ namespace Emby.Server.Implementations.Data
if (query.HasImdbId.HasValue)
{
- whereClauses.Add("ProviderIds like '%imdb=%'");
+ whereClauses.Add(GetProviderIdClause(query.HasImdbId.Value, "imdb"));
}
if (query.HasTmdbId.HasValue)
{
- whereClauses.Add("ProviderIds like '%tmdb=%'");
+ whereClauses.Add(GetProviderIdClause(query.HasTmdbId.Value, "tmdb"));
}
if (query.HasTvdbId.HasValue)
{
- whereClauses.Add("ProviderIds like '%tvdb=%'");
+ whereClauses.Add(GetProviderIdClause(query.HasTvdbId.Value, "tvdb"));
}
var includedItemByNameTypes = GetItemByNameTypesInQuery(query).SelectMany(MapIncludeItemTypes).ToList();
@@ -4769,6 +4769,21 @@ namespace Emby.Server.Implementations.Data
return whereClauses;
}
+ /// <summary>
+ /// Formats a where clause for the specified provider.
+ /// </summary>
+ /// <param name="includeResults">Whether or not to include items with this provider's ids.</param>
+ /// <param name="provider">Provider name.</param>
+ /// <returns>Formatted SQL clause.</returns>
+ private string GetProviderIdClause(bool includeResults, string provider)
+ {
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "ProviderIds {0} like '%{1}=%'",
+ includeResults ? string.Empty : "not",
+ provider);
+ }
+
private List<string> GetItemByNameTypesInQuery(InternalItemsQuery query)
{
var list = new List<string>();
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index df7a034e8..4a0fc8239 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -1,5 +1,6 @@
#pragma warning disable CS1591
+using System;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Net;
@@ -20,9 +21,15 @@ namespace Emby.Server.Implementations.HttpServer.Security
public AuthorizationInfo Authenticate(HttpRequest request)
{
var auth = _authorizationContext.GetAuthorizationInfo(request);
+
+ if (!auth.HasToken)
+ {
+ throw new AuthenticationException("Request does not contain a token.");
+ }
+
if (!auth.IsAuthenticated)
{
- throw new AuthenticationException("Invalid token.");
+ throw new SecurityException("Invalid token.");
}
if (auth.User?.HasPermission(PermissionKind.IsDisabled) ?? false)
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
index fdf2e3908..d62e2eefe 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
@@ -102,7 +102,8 @@ namespace Emby.Server.Implementations.HttpServer.Security
DeviceId = deviceId,
Version = version,
Token = token,
- IsAuthenticated = false
+ IsAuthenticated = false,
+ HasToken = false
};
if (string.IsNullOrWhiteSpace(token))
@@ -111,6 +112,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
return authInfo;
}
+ authInfo.HasToken = true;
var result = _authRepo.Get(new AuthenticationInfoQuery
{
AccessToken = token
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 013781258..5b926b0f4 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -2462,9 +2462,19 @@ namespace Emby.Server.Implementations.Library
public BaseItem GetParentItem(string parentId, Guid? userId)
{
- if (!string.IsNullOrEmpty(parentId))
+ if (string.IsNullOrEmpty(parentId))
{
- return GetItemById(new Guid(parentId));
+ return GetParentItem((Guid?)null, userId);
+ }
+
+ return GetParentItem(new Guid(parentId), userId);
+ }
+
+ public BaseItem GetParentItem(Guid? parentId, Guid? userId)
+ {
+ if (parentId.HasValue)
+ {
+ return GetItemById(parentId.Value);
}
if (userId.HasValue && userId != Guid.Empty)
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index c850e3a08..1d9529dff 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -156,8 +156,8 @@ namespace Emby.Server.Implementations.Library
ExcludeItemTypes = excludeItemTypes.ToArray(),
IncludeItemTypes = includeItemTypes.ToArray(),
Limit = query.Limit,
- IncludeItemsByName = string.IsNullOrEmpty(query.ParentId),
- ParentId = string.IsNullOrEmpty(query.ParentId) ? Guid.Empty : new Guid(query.ParentId),
+ IncludeItemsByName = !query.ParentId.HasValue,
+ ParentId = query.ParentId ?? Guid.Empty,
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
Recursive = true,
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index c0a4d1228..b6444b172 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -237,8 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (!inside)
{
- buffer[bufferIndex] = let;
- bufferIndex++;
+ buffer[bufferIndex++] = let;
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 1d6c26c13..c82b67b41 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -95,7 +95,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
extInf = line.Substring(ExtInfPrefix.Length).Trim();
_logger.LogInformation("Found m3u channel: {0}", extInf);
}
- else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase))
+ else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith('#'))
{
var channel = GetChannelnfo(extInf, tunerHostId, line);
if (string.IsNullOrWhiteSpace(channel.Id))
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index fe674cf36..16fde325f 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -70,7 +70,7 @@
"ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciada",
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
- "Shows": "Mostrar",
+ "Shows": "Series de Televisión",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",
diff --git a/Emby.Server.Implementations/Localization/countries.json b/Emby.Server.Implementations/Localization/countries.json
index 581e9f835..b08a3ae79 100644
--- a/Emby.Server.Implementations/Localization/countries.json
+++ b/Emby.Server.Implementations/Localization/countries.json
@@ -558,6 +558,12 @@
"TwoLetterISORegionName": "OM"
},
{
+ "DisplayName": "Palestine",
+ "Name": "PS",
+ "ThreeLetterISORegionName": "PSE",
+ "TwoLetterISORegionName": "PS"
+ },
+ {
"DisplayName": "Panama",
"Name": "PA",
"ThreeLetterISORegionName": "PAN",
diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index 438bbe24a..f27305cbe 100644
--- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -12,6 +12,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@@ -81,12 +82,7 @@ namespace Emby.Server.Implementations.MediaEncoder
return false;
}
- if (video.VideoType == VideoType.Iso)
- {
- return false;
- }
-
- if (video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
+ if (video.VideoType == VideoType.Dvd)
{
return false;
}
@@ -140,15 +136,19 @@ namespace Emby.Server.Implementations.MediaEncoder
// Add some time for the first chapter to make sure we don't end up with a black image
var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(_firstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
- var protocol = MediaProtocol.File;
-
- var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, null, Array.Empty<string>());
+ var inputPath = video.Path;
Directory.CreateDirectory(Path.GetDirectoryName(path));
var container = video.Container;
+ var mediaSource = new MediaSourceInfo
+ {
+ VideoType = video.VideoType,
+ IsoType = video.IsoType,
+ Protocol = video.PathProtocol.Value,
+ };
- var tempFile = await _encoder.ExtractVideoImage(inputPath, container, protocol, video.GetDefaultVideoStream(), video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
+ var tempFile = await _encoder.ExtractVideoImage(inputPath, container, mediaSource, video.GetDefaultVideoStream(), video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
File.Copy(tempFile, path, true);
try
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index a697c6476..447c587f9 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -56,13 +56,11 @@ namespace Emby.Server.Implementations.TV
return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request);
}
- var parentIdGuid = string.IsNullOrEmpty(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
-
BaseItem[] parents;
- if (parentIdGuid.HasValue)
+ if (request.ParentId.HasValue)
{
- var parent = _libraryManager.GetItemById(parentIdGuid.Value);
+ var parent = _libraryManager.GetItemById(request.ParentId.Value);
if (parent != null)
{
@@ -192,7 +190,7 @@ namespace Emby.Server.Implementations.TV
Func<Episode> getEpisode = () =>
{
- return _libraryManager.GetItemList(new InternalItemsQuery(user)
+ var nextEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
@@ -205,6 +203,18 @@ namespace Emby.Server.Implementations.TV
MinSortName = lastWatchedEpisode?.SortName,
DtoOptions = dtoOptions
}).Cast<Episode>().FirstOrDefault();
+
+ if (nextEpisode != null)
+ {
+ var userData = _userDataManager.GetUserData(user, nextEpisode);
+
+ if (userData.PlaybackPositionTicks > 0)
+ {
+ return null;
+ }
+ }
+
+ return nextEpisode;
};
if (lastWatchedEpisode != null)
diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
index 27a1f61be..c56233794 100644
--- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
+++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs
@@ -18,6 +18,7 @@ namespace Jellyfin.Api.Auth
public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
private readonly IAuthService _authService;
+ private readonly ILogger<CustomAuthenticationHandler> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="CustomAuthenticationHandler" /> class.
@@ -35,6 +36,7 @@ namespace Jellyfin.Api.Auth
ISystemClock clock) : base(options, logger, encoder, clock)
{
_authService = authService;
+ _logger = logger.CreateLogger<CustomAuthenticationHandler>();
}
/// <inheritdoc />
@@ -70,7 +72,8 @@ namespace Jellyfin.Api.Auth
}
catch (AuthenticationException ex)
{
- return Task.FromResult(AuthenticateResult.Fail(ex));
+ _logger.LogDebug(ex, "Error authenticating with {Handler}", nameof(CustomAuthenticationHandler));
+ return Task.FromResult(AuthenticateResult.NoResult());
}
catch (SecurityException ex)
{
diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs
index c65dc8620..fed7ed3e5 100644
--- a/Jellyfin.Api/Controllers/ArtistsController.cs
+++ b/Jellyfin.Api/Controllers/ArtistsController.cs
@@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations;
using System.Linq;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
-using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Data.Entities;
using MediaBrowser.Controller.Dto;
@@ -87,7 +86,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] string? searchTerm,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
@@ -119,16 +118,11 @@ namespace Jellyfin.Api.Controllers
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
User? user = null;
- BaseItem parentItem;
+ BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId);
if (userId.HasValue && !userId.Equals(Guid.Empty))
{
user = _userManager.GetUserById(userId.Value);
- parentItem = string.IsNullOrEmpty(parentId) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(parentId);
- }
- else
- {
- parentItem = string.IsNullOrEmpty(parentId) ? _libraryManager.RootFolder : _libraryManager.GetItemById(parentId);
}
var query = new InternalItemsQuery(user)
@@ -157,15 +151,15 @@ namespace Jellyfin.Api.Controllers
EnableTotalRecordCount = enableTotalRecordCount
};
- if (!string.IsNullOrWhiteSpace(parentId))
+ if (parentId.HasValue)
{
if (parentItem is Folder)
{
- query.AncestorIds = new[] { new Guid(parentId) };
+ query.AncestorIds = new[] { parentId.Value };
}
else
{
- query.ItemIds = new[] { new Guid(parentId) };
+ query.ItemIds = new[] { parentId.Value };
}
}
@@ -291,7 +285,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] string? searchTerm,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
@@ -323,16 +317,11 @@ namespace Jellyfin.Api.Controllers
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
User? user = null;
- BaseItem parentItem;
+ BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId);
if (userId.HasValue && !userId.Equals(Guid.Empty))
{
user = _userManager.GetUserById(userId.Value);
- parentItem = string.IsNullOrEmpty(parentId) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(parentId);
- }
- else
- {
- parentItem = string.IsNullOrEmpty(parentId) ? _libraryManager.RootFolder : _libraryManager.GetItemById(parentId);
}
var query = new InternalItemsQuery(user)
@@ -361,15 +350,15 @@ namespace Jellyfin.Api.Controllers
EnableTotalRecordCount = enableTotalRecordCount
};
- if (!string.IsNullOrWhiteSpace(parentId))
+ if (parentId.HasValue)
{
if (parentItem is Folder)
{
- query.AncestorIds = new[] { new Guid(parentId) };
+ query.AncestorIds = new[] { parentId.Value };
}
else
{
- query.ItemIds = new[] { new Guid(parentId) };
+ query.ItemIds = new[] { parentId.Value };
}
}
diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs
index c4dc44cc3..b70c76e80 100644
--- a/Jellyfin.Api/Controllers/ChannelsController.cs
+++ b/Jellyfin.Api/Controllers/ChannelsController.cs
@@ -92,7 +92,7 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Channel features returned.</response>
/// <returns>An <see cref="OkResult"/> containing the channel features.</returns>
[HttpGet("{channelId}/Features")]
- public ActionResult<ChannelFeatures> GetChannelFeatures([FromRoute, Required] string channelId)
+ public ActionResult<ChannelFeatures> GetChannelFeatures([FromRoute, Required] Guid channelId)
{
return _channelManager.GetChannelFeatures(channelId);
}
diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs
index 31cb9e273..9220b988f 100644
--- a/Jellyfin.Api/Controllers/FilterController.cs
+++ b/Jellyfin.Api/Controllers/FilterController.cs
@@ -50,33 +50,24 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryFiltersLegacy> GetQueryFiltersLegacy(
[FromQuery] Guid? userId,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes)
{
- var parentItem = string.IsNullOrEmpty(parentId)
- ? null
- : _libraryManager.GetItemById(parentId);
-
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
- if (includeItemTypes.Length == 1
- && (string.Equals(includeItemTypes[0], nameof(BoxSet), StringComparison.OrdinalIgnoreCase)
- || string.Equals(includeItemTypes[0], nameof(Playlist), StringComparison.OrdinalIgnoreCase)
- || string.Equals(includeItemTypes[0], nameof(Trailer), StringComparison.OrdinalIgnoreCase)
- || string.Equals(includeItemTypes[0], "Program", StringComparison.OrdinalIgnoreCase)))
+ BaseItem? item = null;
+ if (includeItemTypes.Length != 1
+ || !(string.Equals(includeItemTypes[0], nameof(BoxSet), StringComparison.OrdinalIgnoreCase)
+ || string.Equals(includeItemTypes[0], nameof(Playlist), StringComparison.OrdinalIgnoreCase)
+ || string.Equals(includeItemTypes[0], nameof(Trailer), StringComparison.OrdinalIgnoreCase)
+ || string.Equals(includeItemTypes[0], "Program", StringComparison.OrdinalIgnoreCase)))
{
- parentItem = null;
+ item = _libraryManager.GetParentItem(parentId, user?.Id);
}
- var item = string.IsNullOrEmpty(parentId)
- ? user == null
- ? _libraryManager.RootFolder
- : _libraryManager.GetUserRootFolder()
- : parentItem;
-
var query = new InternalItemsQuery
{
User = user,
@@ -92,7 +83,12 @@ namespace Jellyfin.Api.Controllers
}
};
- var itemList = ((Folder)item!).GetItemList(query);
+ if (item is not Folder folder)
+ {
+ return new QueryFiltersLegacy();
+ }
+
+ var itemList = folder.GetItemList(query);
return new QueryFiltersLegacy
{
Years = itemList.Select(i => i.ProductionYear ?? -1)
@@ -140,7 +136,7 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryFilters> GetQueryFilters(
[FromQuery] Guid? userId,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
[FromQuery] bool? isAiring,
[FromQuery] bool? isMovie,
@@ -150,14 +146,11 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? isSeries,
[FromQuery] bool? recursive)
{
- var parentItem = string.IsNullOrEmpty(parentId)
- ? null
- : _libraryManager.GetItemById(parentId);
-
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
+ BaseItem? parentItem = null;
if (includeItemTypes.Length == 1
&& (string.Equals(includeItemTypes[0], nameof(BoxSet), StringComparison.OrdinalIgnoreCase)
|| string.Equals(includeItemTypes[0], nameof(Playlist), StringComparison.OrdinalIgnoreCase)
@@ -166,6 +159,10 @@ namespace Jellyfin.Api.Controllers
{
parentItem = null;
}
+ else if (parentId.HasValue)
+ {
+ parentItem = _libraryManager.GetItemById(parentId.Value);
+ }
var filters = new QueryFilters();
var genreQuery = new InternalItemsQuery(user)
diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs
index d2b41e0a8..b6755ed5e 100644
--- a/Jellyfin.Api/Controllers/GenresController.cs
+++ b/Jellyfin.Api/Controllers/GenresController.cs
@@ -72,7 +72,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] string? searchTerm,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
@@ -109,15 +109,15 @@ namespace Jellyfin.Api.Controllers
EnableTotalRecordCount = enableTotalRecordCount
};
- if (!string.IsNullOrWhiteSpace(parentId))
+ if (parentId.HasValue)
{
if (parentItem is Folder)
{
- query.AncestorIds = new[] { new Guid(parentId) };
+ query.AncestorIds = new[] { parentId.Value };
}
else
{
- query.ItemIds = new[] { new Guid(parentId) };
+ query.ItemIds = new[] { parentId.Value };
}
}
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index b0979fbcf..7e9035f80 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -176,7 +176,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? recursive,
[FromQuery] string? searchTerm,
[FromQuery] string? sortOrder,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
@@ -239,14 +239,8 @@ namespace Jellyfin.Api.Controllers
parentId = null;
}
- BaseItem? item = null;
+ var item = _libraryManager.GetParentItem(parentId, userId);
QueryResult<BaseItem> result;
- if (!string.IsNullOrEmpty(parentId))
- {
- item = _libraryManager.GetItemById(parentId);
- }
-
- item ??= _libraryManager.GetUserRootFolder();
if (!(item is Folder folder))
{
@@ -343,7 +337,7 @@ namespace Jellyfin.Api.Controllers
ItemIds = ids,
MinCommunityRating = minCommunityRating,
MinCriticRating = minCriticRating,
- ParentId = string.IsNullOrWhiteSpace(parentId) ? Guid.Empty : new Guid(parentId),
+ ParentId = parentId ?? Guid.Empty,
ParentIndexNumber = parentIndexNumber,
EnableTotalRecordCount = enableTotalRecordCount,
ExcludeItemIds = excludeItemIds,
@@ -615,7 +609,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? recursive,
[FromQuery] string? searchTerm,
[FromQuery] string? sortOrder,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
@@ -773,7 +767,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] string? searchTerm,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
[FromQuery] bool? enableUserData,
@@ -785,7 +779,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? enableImages = true)
{
var user = _userManager.GetUserById(userId);
- var parentIdGuid = string.IsNullOrWhiteSpace(parentId) ? Guid.Empty : new Guid(parentId);
+ var parentIdGuid = parentId ?? Guid.Empty;
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index 3ff77e8e0..184843b39 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -362,7 +362,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
- public ActionResult DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] ids)
+ public ActionResult DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids)
{
if (ids.Length == 0)
{
diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs
index 75dfd4e68..4d788ad7d 100644
--- a/Jellyfin.Api/Controllers/MoviesController.cs
+++ b/Jellyfin.Api/Controllers/MoviesController.cs
@@ -65,7 +65,7 @@ namespace Jellyfin.Api.Controllers
[HttpGet("Recommendations")]
public ActionResult<IEnumerable<RecommendationDto>> GetMovieRecommendations(
[FromQuery] Guid? userId,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] int categoryLimit = 5,
[FromQuery] int itemLimit = 8)
@@ -78,7 +78,7 @@ namespace Jellyfin.Api.Controllers
var categories = new List<RecommendationDto>();
- var parentIdGuid = string.IsNullOrWhiteSpace(parentId) ? Guid.Empty : new Guid(parentId);
+ var parentIdGuid = parentId ?? Guid.Empty;
var query = new InternalItemsQuery(user)
{
diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs
index e7d0a61c5..2608a9cd0 100644
--- a/Jellyfin.Api/Controllers/MusicGenresController.cs
+++ b/Jellyfin.Api/Controllers/MusicGenresController.cs
@@ -72,7 +72,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] string? searchTerm,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
@@ -109,15 +109,15 @@ namespace Jellyfin.Api.Controllers
EnableTotalRecordCount = enableTotalRecordCount
};
- if (!string.IsNullOrWhiteSpace(parentId))
+ if (parentId.HasValue)
{
if (parentItem is Folder)
{
- query.AncestorIds = new[] { new Guid(parentId) };
+ query.AncestorIds = new[] { parentId.Value };
}
else
{
- query.ItemIds = new[] { new Guid(parentId) };
+ query.ItemIds = new[] { parentId.Value };
}
}
diff --git a/Jellyfin.Api/Controllers/PackageController.cs b/Jellyfin.Api/Controllers/PackageController.cs
index 83b359766..6295dfc05 100644
--- a/Jellyfin.Api/Controllers/PackageController.cs
+++ b/Jellyfin.Api/Controllers/PackageController.cs
@@ -45,13 +45,13 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<PackageInfo>> GetPackageInfo(
[FromRoute, Required] string name,
- [FromQuery] string? assemblyGuid)
+ [FromQuery] Guid? assemblyGuid)
{
var packages = await _installationManager.GetAvailablePackages().ConfigureAwait(false);
var result = _installationManager.FilterPackages(
packages,
name,
- string.IsNullOrEmpty(assemblyGuid) ? default : Guid.Parse(assemblyGuid))
+ assemblyGuid ?? default)
.FirstOrDefault();
if (result == null)
@@ -92,7 +92,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.RequiresElevation)]
public async Task<ActionResult> InstallPackage(
[FromRoute, Required] string name,
- [FromQuery] string? assemblyGuid,
+ [FromQuery] Guid? assemblyGuid,
[FromQuery] string? version,
[FromQuery] string? repositoryUrl)
{
@@ -106,7 +106,7 @@ namespace Jellyfin.Api.Controllers
var package = _installationManager.GetCompatibleVersions(
packages,
name,
- string.IsNullOrEmpty(assemblyGuid) ? Guid.Empty : Guid.Parse(assemblyGuid),
+ assemblyGuid ?? Guid.Empty,
specificVersion: string.IsNullOrEmpty(version) ? null : Version.Parse(version))
.FirstOrDefault();
diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs
index aaad36551..17e631197 100644
--- a/Jellyfin.Api/Controllers/PersonsController.cs
+++ b/Jellyfin.Api/Controllers/PersonsController.cs
@@ -79,7 +79,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludePersonTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] personTypes,
- [FromQuery] string? appearsInItemId,
+ [FromQuery] Guid? appearsInItemId,
[FromQuery] Guid? userId,
[FromQuery] bool? enableImages = true)
{
@@ -102,7 +102,7 @@ namespace Jellyfin.Api.Controllers
NameContains = searchTerm,
User = user,
IsFavorite = !isFavorite.HasValue && isFavoriteInFilters ? true : isFavorite,
- AppearsInItemId = string.IsNullOrEmpty(appearsInItemId) ? Guid.Empty : Guid.Parse(appearsInItemId),
+ AppearsInItemId = appearsInItemId ?? Guid.Empty,
Limit = limit ?? 0
});
diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs
index 076fe58f1..08255ff8f 100644
--- a/Jellyfin.Api/Controllers/SearchController.cs
+++ b/Jellyfin.Api/Controllers/SearchController.cs
@@ -86,7 +86,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery] bool? isMovie,
[FromQuery] bool? isSeries,
[FromQuery] bool? isNews,
diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs
index 5090bf1de..bb54c59f6 100644
--- a/Jellyfin.Api/Controllers/StudiosController.cs
+++ b/Jellyfin.Api/Controllers/StudiosController.cs
@@ -71,7 +71,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] string? searchTerm,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
@@ -109,15 +109,15 @@ namespace Jellyfin.Api.Controllers
EnableTotalRecordCount = enableTotalRecordCount
};
- if (!string.IsNullOrWhiteSpace(parentId))
+ if (parentId.HasValue)
{
if (parentItem is Folder)
{
- query.AncestorIds = new[] { new Guid(parentId) };
+ query.AncestorIds = new[] { parentId.Value };
}
else
{
- query.ItemIds = new[] { new Guid(parentId) };
+ query.ItemIds = new[] { parentId.Value };
}
}
diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs
index 5b71fed5a..8e9ece14f 100644
--- a/Jellyfin.Api/Controllers/TrailersController.cs
+++ b/Jellyfin.Api/Controllers/TrailersController.cs
@@ -145,7 +145,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] bool? recursive,
[FromQuery] string? searchTerm,
[FromQuery] string? sortOrder,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs
index 57b056f50..03fd1846d 100644
--- a/Jellyfin.Api/Controllers/TvShowsController.cs
+++ b/Jellyfin.Api/Controllers/TvShowsController.cs
@@ -76,7 +76,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? limit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] string? seriesId,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery] bool? enableImges,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
@@ -132,7 +132,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery] bool? enableImges,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
@@ -144,7 +144,7 @@ namespace Jellyfin.Api.Controllers
var minPremiereDate = DateTime.Now.Date.ToUniversalTime().AddDays(-1);
- var parentIdGuid = string.IsNullOrWhiteSpace(parentId) ? Guid.Empty : new Guid(parentId);
+ var parentIdGuid = parentId ?? Guid.Empty;
var options = new DtoOptions { Fields = fields }
.AddClientFields(Request)
@@ -194,14 +194,14 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<QueryResult<BaseItemDto>> GetEpisodes(
- [FromRoute, Required] string seriesId,
+ [FromRoute, Required] Guid seriesId,
[FromQuery] Guid? userId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] int? season,
- [FromQuery] string? seasonId,
+ [FromQuery] Guid? seasonId,
[FromQuery] bool? isMissing,
[FromQuery] string? adjacentTo,
- [FromQuery] string? startItemId,
+ [FromQuery] Guid? startItemId,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] bool? enableImages,
@@ -220,9 +220,9 @@ namespace Jellyfin.Api.Controllers
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes!);
- if (!string.IsNullOrWhiteSpace(seasonId)) // Season id was supplied. Get episodes by season id.
+ if (seasonId.HasValue) // Season id was supplied. Get episodes by season id.
{
- var item = _libraryManager.GetItemById(new Guid(seasonId));
+ var item = _libraryManager.GetItemById(seasonId.Value);
if (!(item is Season seasonItem))
{
return NotFound("No season exists with Id " + seasonId);
@@ -264,10 +264,10 @@ namespace Jellyfin.Api.Controllers
.ToList();
}
- if (!string.IsNullOrWhiteSpace(startItemId))
+ if (startItemId.HasValue)
{
episodes = episodes
- .SkipWhile(i => !string.Equals(i.Id.ToString("N", CultureInfo.InvariantCulture), startItemId, StringComparison.OrdinalIgnoreCase))
+ .SkipWhile(i => startItemId.Value.Equals(i.Id))
.ToList();
}
@@ -316,7 +316,7 @@ namespace Jellyfin.Api.Controllers
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<QueryResult<BaseItemDto>> GetSeasons(
- [FromRoute, Required] string seriesId,
+ [FromRoute, Required] Guid seriesId,
[FromQuery] Guid? userId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool? isSpecialSeason,
diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs
index ec7c3de97..48c639b08 100644
--- a/Jellyfin.Api/Controllers/YearsController.cs
+++ b/Jellyfin.Api/Controllers/YearsController.cs
@@ -71,7 +71,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] string? sortOrder,
- [FromQuery] string? parentId,
+ [FromQuery] Guid? parentId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
@@ -89,16 +89,11 @@ namespace Jellyfin.Api.Controllers
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
User? user = null;
- BaseItem parentItem;
+ BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId);
if (userId.HasValue && !userId.Equals(Guid.Empty))
{
user = _userManager.GetUserById(userId.Value);
- parentItem = string.IsNullOrEmpty(parentId) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(parentId);
- }
- else
- {
- parentItem = string.IsNullOrEmpty(parentId) ? _libraryManager.RootFolder : _libraryManager.GetItemById(parentId);
}
IList<BaseItem> items;
diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
index 99c90c315..bb2265dba 100644
--- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
+++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
@@ -762,11 +762,6 @@ namespace Jellyfin.Api.Helpers
private async Task AcquireResources(StreamState state, CancellationTokenSource cancellationTokenSource)
{
- if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && _isoManager.CanMount(state.MediaPath))
- {
- state.IsoMount = await _isoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
- }
-
if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Request.LiveStreamId))
{
var liveStreamResponse = await _mediaSourceManager.OpenLiveStream(
diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs
index 1a5614b7b..85da927fb 100644
--- a/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -256,7 +256,7 @@ namespace Jellyfin.Networking.Manager
}
catch (ArgumentException e)
{
- _logger.LogWarning(e, "Ignoring LAN value {value}.", v);
+ _logger.LogWarning(e, "Ignoring LAN value {Value}.", v);
}
}
@@ -668,7 +668,6 @@ namespace Jellyfin.Networking.Manager
if (address.AddressFamily == AddressFamily.InterNetworkV6)
{
int i = str.IndexOf("%", StringComparison.OrdinalIgnoreCase);
-
if (i != -1)
{
str = str.Substring(0, i);
diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs
index 45e71f16e..bf8818f8d 100644
--- a/Jellyfin.Server.Implementations/JellyfinDb.cs
+++ b/Jellyfin.Server.Implementations/JellyfinDb.cs
@@ -1,5 +1,6 @@
#pragma warning disable CS1591
+using System;
using System.Linq;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Interfaces;
@@ -140,6 +141,7 @@ namespace Jellyfin.Server.Implementations
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
+ modelBuilder.SetDefaultDateTimeKind(DateTimeKind.Utc);
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("jellyfin");
diff --git a/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs b/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs
new file mode 100644
index 000000000..80ad65a42
--- /dev/null
+++ b/Jellyfin.Server.Implementations/ModelBuilderExtensions.cs
@@ -0,0 +1,48 @@
+using System;
+using Jellyfin.Server.Implementations.ValueConverters;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Jellyfin.Server.Implementations
+{
+ /// <summary>
+ /// Model builder extensions.
+ /// </summary>
+ public static class ModelBuilderExtensions
+ {
+ /// <summary>
+ /// Specify value converter for the object type.
+ /// </summary>
+ /// <param name="modelBuilder">The model builder.</param>
+ /// <param name="converter">The <see cref="ValueConverter{TModel,TProvider}"/>.</param>
+ /// <typeparam name="T">The type to convert.</typeparam>
+ /// <returns>The modified <see cref="ModelBuilder"/>.</returns>
+ public static ModelBuilder UseValueConverterForType<T>(this ModelBuilder modelBuilder, ValueConverter converter)
+ {
+ var type = typeof(T);
+ foreach (var entityType in modelBuilder.Model.GetEntityTypes())
+ {
+ foreach (var property in entityType.GetProperties())
+ {
+ if (property.ClrType == type)
+ {
+ property.SetValueConverter(converter);
+ }
+ }
+ }
+
+ return modelBuilder;
+ }
+
+ /// <summary>
+ /// Specify the default <see cref="DateTimeKind"/>.
+ /// </summary>
+ /// <param name="modelBuilder">The model builder to extend.</param>
+ /// <param name="kind">The <see cref="DateTimeKind"/> to specify.</param>
+ public static void SetDefaultDateTimeKind(this ModelBuilder modelBuilder, DateTimeKind kind)
+ {
+ modelBuilder.UseValueConverterForType<DateTime>(new DateTimeKindValueConverter(kind));
+ modelBuilder.UseValueConverterForType<DateTime?>(new DateTimeKindValueConverter(kind));
+ }
+ }
+} \ No newline at end of file
diff --git a/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
new file mode 100644
index 000000000..8a510898b
--- /dev/null
+++ b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs
@@ -0,0 +1,21 @@
+using System;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace Jellyfin.Server.Implementations.ValueConverters
+{
+ /// <summary>
+ /// ValueConverter to specify kind.
+ /// </summary>
+ public class DateTimeKindValueConverter : ValueConverter<DateTime, DateTime>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DateTimeKindValueConverter"/> class.
+ /// </summary>
+ /// <param name="kind">The kind to specify.</param>
+ /// <param name="mappingHints">The mapping hints.</param>
+ public DateTimeKindValueConverter(DateTimeKind kind, ConverterMappingHints mappingHints = null)
+ : base(v => v.ToUniversalTime(), v => DateTime.SpecifyKind(v, kind), mappingHints)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 6cb88c9f7..e5b9620f7 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -285,20 +285,17 @@ namespace Jellyfin.Server.Extensions
private static void AddSwaggerTypeMappings(this SwaggerGenOptions options)
{
/*
- * TODO remove when System.Text.Json supports non-string keys.
- * Used in Jellyfin.Api.Controller.GetChannels.
+ * TODO remove when System.Text.Json properly supports non-string keys.
+ * Used in BaseItemDto.ImageBlurHashes
*/
options.MapType<Dictionary<ImageType, string>>(() =>
new OpenApiSchema
{
Type = "object",
- Properties = typeof(ImageType).GetEnumNames().ToDictionary(
- name => name,
- name => new OpenApiSchema
- {
- Type = "string",
- Format = "string"
- })
+ AdditionalProperties = new OpenApiSchema
+ {
+ Type = "string"
+ }
});
/*
@@ -312,16 +309,10 @@ namespace Jellyfin.Server.Extensions
name => name,
name => new OpenApiSchema
{
- Type = "object", Properties = new Dictionary<string, OpenApiSchema>
+ Type = "object",
+ AdditionalProperties = new OpenApiSchema
{
- {
- "string",
- new OpenApiSchema
- {
- Type = "string",
- Format = "string"
- }
- }
+ Type = "string"
}
})
});
diff --git a/MediaBrowser.Common/Json/Converters/JsonDateTimeIso8601Converter.cs b/MediaBrowser.Common/Json/Converters/JsonDateTimeIso8601Converter.cs
deleted file mode 100644
index 63b344a9d..000000000
--- a/MediaBrowser.Common/Json/Converters/JsonDateTimeIso8601Converter.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-using System.Globalization;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace MediaBrowser.Common.Json.Converters
-{
- /// <summary>
- /// Returns an ISO8601 formatted datetime.
- /// </summary>
- /// <remarks>
- /// Used for legacy compatibility.
- /// </remarks>
- public class JsonDateTimeIso8601Converter : JsonConverter<DateTime>
- {
- /// <inheritdoc />
- public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- => reader.GetDateTime();
-
- /// <inheritdoc />
- public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
- => writer.WriteStringValue(value.ToString("O", CultureInfo.InvariantCulture));
- }
-}
diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs
index 1aaf4a43b..c5050a21d 100644
--- a/MediaBrowser.Common/Json/JsonDefaults.cs
+++ b/MediaBrowser.Common/Json/JsonDefaults.cs
@@ -43,7 +43,6 @@ namespace MediaBrowser.Common.Json
options.Converters.Add(new JsonVersionConverter());
options.Converters.Add(new JsonStringEnumConverter());
options.Converters.Add(new JsonNullableStructConverterFactory());
- options.Converters.Add(new JsonDateTimeIso8601Converter());
return options;
}
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs
index 9a9d22d33..ddae7dbd3 100644
--- a/MediaBrowser.Controller/Channels/IChannelManager.cs
+++ b/MediaBrowser.Controller/Channels/IChannelManager.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Channels
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>ChannelFeatures.</returns>
- ChannelFeatures GetChannelFeatures(string id);
+ ChannelFeatures GetChannelFeatures(Guid? id);
/// <summary>
/// Gets all channel features.
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 07f381881..6320b01b8 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -143,26 +143,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The video3 D format.</value>
public Video3DFormat? Video3DFormat { get; set; }
- public string[] GetPlayableStreamFileNames()
- {
- var videoType = VideoType;
-
- if (videoType == VideoType.Iso && IsoType == Model.Entities.IsoType.BluRay)
- {
- videoType = VideoType.BluRay;
- }
- else if (videoType == VideoType.Iso && IsoType == Model.Entities.IsoType.Dvd)
- {
- videoType = VideoType.Dvd;
- }
- else
- {
- return Array.Empty<string>();
- }
-
- throw new NotImplementedException();
- }
-
/// <summary>
/// Gets or sets the aspect ratio.
/// </summary>
@@ -415,31 +395,6 @@ namespace MediaBrowser.Controller.Entities
return updateType;
}
- public static string[] QueryPlayableStreamFiles(string rootPath, VideoType videoType)
- {
- if (videoType == VideoType.Dvd)
- {
- return FileSystem.GetFiles(rootPath, new[] { ".vob" }, false, true)
- .OrderByDescending(i => i.Length)
- .ThenBy(i => i.FullName)
- .Take(1)
- .Select(i => i.FullName)
- .ToArray();
- }
-
- if (videoType == VideoType.BluRay)
- {
- return FileSystem.GetFiles(rootPath, new[] { ".m2ts" }, false, true)
- .OrderByDescending(i => i.Length)
- .ThenBy(i => i.FullName)
- .Take(1)
- .Select(i => i.FullName)
- .ToArray();
- }
-
- return Array.Empty<string>();
- }
-
/// <summary>
/// Gets a value indicating whether [is3 D].
/// </summary>
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 601ca3536..24b101694 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -574,5 +574,7 @@ namespace MediaBrowser.Controller.Library
void RunMetadataSavers(IReadOnlyList<BaseItem> items, ItemUpdateType updateReason);
BaseItem GetParentItem(string parentId, Guid? userId);
+
+ BaseItem GetParentItem(Guid? parentId, Guid? userId);
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 9c6bbb916..91a03e647 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -390,25 +390,9 @@ namespace MediaBrowser.Controller.MediaEncoding
public string GetInputPathArgument(EncodingJobInfo state)
{
- var protocol = state.InputProtocol;
var mediaPath = state.MediaPath ?? string.Empty;
- string[] inputPath;
- if (state.IsInputVideo
- && !(state.VideoType == VideoType.Iso && state.IsoMount == null))
- {
- inputPath = MediaEncoderHelpers.GetInputArgument(
- _fileSystem,
- mediaPath,
- state.IsoMount,
- state.PlayableStreamFileNames);
- }
- else
- {
- inputPath = new[] { mediaPath };
- }
-
- return _mediaEncoder.GetInputArgument(inputPath, protocol);
+ return _mediaEncoder.GetInputArgument(mediaPath, state.MediaSource);
}
/// <summary>
@@ -2673,18 +2657,10 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- public string GetProbeSizeArgument(int numInputFiles)
- => numInputFiles > 1 ? "-probesize " + _configuration.GetFFmpegProbeSize() : string.Empty;
-
- public string GetAnalyzeDurationArgument(int numInputFiles)
- => numInputFiles > 1 ? "-analyzeduration " + _configuration.GetFFmpegAnalyzeDuration() : string.Empty;
-
public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions)
{
var inputModifier = string.Empty;
-
- var numInputFiles = state.PlayableStreamFileNames.Length > 0 ? state.PlayableStreamFileNames.Length : 1;
- var probeSizeArgument = GetProbeSizeArgument(numInputFiles);
+ var probeSizeArgument = string.Empty;
string analyzeDurationArgument;
if (state.MediaSource.AnalyzeDurationMs.HasValue)
@@ -2693,7 +2669,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else
{
- analyzeDurationArgument = GetAnalyzeDurationArgument(numInputFiles);
+ analyzeDurationArgument = string.Empty;
}
if (!string.IsNullOrEmpty(probeSizeArgument))
@@ -2877,32 +2853,6 @@ namespace MediaBrowser.Controller.MediaEncoding
state.IsoType = mediaSource.IsoType;
- if (mediaSource.VideoType.HasValue)
- {
- state.VideoType = mediaSource.VideoType.Value;
-
- if (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd)
- {
- state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, mediaSource.VideoType.Value).Select(Path.GetFileName).ToArray();
- }
- else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.BluRay)
- {
- state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.BluRay).Select(Path.GetFileName).ToArray();
- }
- else if (mediaSource.VideoType.Value == VideoType.Iso && state.IsoType == IsoType.Dvd)
- {
- state.PlayableStreamFileNames = Video.QueryPlayableStreamFiles(state.MediaPath, VideoType.Dvd).Select(Path.GetFileName).ToArray();
- }
- else
- {
- state.PlayableStreamFileNames = Array.Empty<string>();
- }
- }
- else
- {
- state.PlayableStreamFileNames = Array.Empty<string>();
- }
-
if (mediaSource.Timestamp.HasValue)
{
state.InputTimestamp = mediaSource.Timestamp.Value;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 52794a69b..dacd6dea6 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -33,10 +33,6 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool IsInputVideo { get; set; }
- public IIsoMount IsoMount { get; set; }
-
- public string[] PlayableStreamFileNames { get; set; }
-
public string OutputAudioCodec { get; set; }
public int? OutputVideoBitrate { get; set; }
@@ -313,7 +309,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{
TranscodingType = jobType;
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- PlayableStreamFileNames = Array.Empty<string>();
SupportedAudioCodecs = Array.Empty<string>();
SupportedVideoCodecs = Array.Empty<string>();
SupportedSubtitleCodecs = Array.Empty<string>();
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index f6bc1f4de..e7f042d2f 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@@ -61,18 +62,18 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Extracts the video image.
/// </summary>
- Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
+ Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
- Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
+ Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
/// <summary>
/// Extracts the video images on interval.
/// </summary>
Task ExtractVideoImagesOnInterval(
- string[] inputFiles,
+ string inputFile,
string container,
MediaStream videoStream,
- MediaProtocol protocol,
+ MediaSourceInfo mediaSource,
Video3DFormat? threedFormat,
TimeSpan interval,
string targetDirectory,
@@ -91,10 +92,10 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets the input argument.
/// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
+ /// <param name="inputFile">The input file.</param>
+ /// <param name="mediaSource">The mediaSource.</param>
/// <returns>System.String.</returns>
- string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol);
+ string GetInputArgument(string inputFile, MediaSourceInfo mediaSource);
/// <summary>
/// Gets the time parameter.
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
index ce53c23ad..281d50372 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
@@ -13,38 +13,5 @@ namespace MediaBrowser.Controller.MediaEncoding
/// </summary>
public static class MediaEncoderHelpers
{
- /// <summary>
- /// Gets the input argument.
- /// </summary>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="videoPath">The video path.</param>
- /// <param name="isoMount">The iso mount.</param>
- /// <param name="playableStreamFileNames">The playable stream file names.</param>
- /// <returns>string[].</returns>
- public static string[] GetInputArgument(IFileSystem fileSystem, string videoPath, IIsoMount isoMount, IReadOnlyCollection<string> playableStreamFileNames)
- {
- if (playableStreamFileNames.Count > 0)
- {
- if (isoMount == null)
- {
- return GetPlayableStreamFiles(fileSystem, videoPath, playableStreamFileNames);
- }
-
- return GetPlayableStreamFiles(fileSystem, isoMount.MountedPath, playableStreamFileNames);
- }
-
- return new[] { videoPath };
- }
-
- private static string[] GetPlayableStreamFiles(IFileSystem fileSystem, string rootPath, IEnumerable<string> filenames)
- {
- var allFiles = fileSystem
- .GetFilePaths(rootPath, true)
- .ToArray();
-
- return filenames.Select(name => allFiles.FirstOrDefault(f => string.Equals(Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
- .Where(f => !string.IsNullOrEmpty(f))
- .ToArray();
- }
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
index 59729de49..2cb04bdc4 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
@@ -1,9 +1,7 @@
#pragma warning disable CS1591
-using System;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.IO;
namespace MediaBrowser.Controller.MediaEncoding
{
@@ -14,14 +12,5 @@ namespace MediaBrowser.Controller.MediaEncoding
public bool ExtractChapters { get; set; }
public DlnaProfileType MediaType { get; set; }
-
- public IIsoMount MountedIso { get; set; }
-
- public string[] PlayableStreamFileNames { get; set; }
-
- public MediaInfoRequest()
- {
- PlayableStreamFileNames = Array.Empty<string>();
- }
}
}
diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
index 0194c596f..93573e08e 100644
--- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs
+++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs
@@ -58,5 +58,10 @@ namespace MediaBrowser.Controller.Net
/// Gets or sets a value indicating whether the token is authenticated.
/// </summary>
public bool IsAuthenticated { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the request has a token.
+ /// </summary>
+ public bool HasToken { get; set; }
}
}
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index bc940d0b8..e8aeabf9d 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -87,19 +87,19 @@ namespace MediaBrowser.MediaEncoding.Attachments
MediaAttachment mediaAttachment,
CancellationToken cancellationToken)
{
- var attachmentPath = await GetReadableFile(mediaSource.Path, mediaSource.Path, mediaSource.Protocol, mediaAttachment, cancellationToken).ConfigureAwait(false);
+ var attachmentPath = await GetReadableFile(mediaSource.Path, mediaSource.Path, mediaSource, mediaAttachment, cancellationToken).ConfigureAwait(false);
return File.OpenRead(attachmentPath);
}
private async Task<string> GetReadableFile(
string mediaPath,
string inputFile,
- MediaProtocol protocol,
+ MediaSourceInfo mediaSource,
MediaAttachment mediaAttachment,
CancellationToken cancellationToken)
{
- var outputPath = GetAttachmentCachePath(mediaPath, protocol, mediaAttachment.Index);
- await ExtractAttachment(inputFile, protocol, mediaAttachment.Index, outputPath, cancellationToken)
+ var outputPath = GetAttachmentCachePath(mediaPath, mediaSource, mediaAttachment.Index);
+ await ExtractAttachment(inputFile, mediaSource, mediaAttachment.Index, outputPath, cancellationToken)
.ConfigureAwait(false);
return outputPath;
@@ -107,7 +107,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
private async Task ExtractAttachment(
string inputFile,
- MediaProtocol protocol,
+ MediaSourceInfo mediaSource,
int attachmentStreamIndex,
string outputPath,
CancellationToken cancellationToken)
@@ -121,7 +121,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
if (!File.Exists(outputPath))
{
await ExtractAttachmentInternal(
- _mediaEncoder.GetInputArgument(new[] { inputFile }, protocol),
+ _mediaEncoder.GetInputArgument(inputFile, mediaSource),
attachmentStreamIndex,
outputPath,
cancellationToken).ConfigureAwait(false);
@@ -234,10 +234,10 @@ namespace MediaBrowser.MediaEncoding.Attachments
}
}
- private string GetAttachmentCachePath(string mediaPath, MediaProtocol protocol, int attachmentStreamIndex)
+ private string GetAttachmentCachePath(string mediaPath, MediaSourceInfo mediaSource, int attachmentStreamIndex)
{
string filename;
- if (protocol == MediaProtocol.File)
+ if (mediaSource.Protocol == MediaProtocol.File)
{
var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture);
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
index 63310fdf6..d0ea0429b 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
@@ -1,53 +1,44 @@
#pragma warning disable CS1591
using System;
-using System.Collections.Generic;
using System.Globalization;
-using System.Linq;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Encoder
{
public static class EncodingUtils
{
- public static string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol)
+ public static string GetInputArgument(string inputPrefix, string inputFile, MediaProtocol protocol)
{
if (protocol != MediaProtocol.File)
{
- var url = inputFiles[0];
-
- return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", url);
+ return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", inputFile);
}
- return GetConcatInputArgument(inputFiles);
+ return GetConcatInputArgument(inputFile, inputPrefix);
}
/// <summary>
/// Gets the concat input argument.
/// </summary>
- /// <param name="inputFiles">The input files.</param>
+ /// <param name="inputFile">The input file.</param>
+ /// <param name="inputPrefix">The input prefix.</param>
/// <returns>System.String.</returns>
- private static string GetConcatInputArgument(IReadOnlyList<string> inputFiles)
+ private static string GetConcatInputArgument(string inputFile, string inputPrefix)
{
// Get all streams
// If there's more than one we'll need to use the concat command
- if (inputFiles.Count > 1)
- {
- var files = string.Join("|", inputFiles.Select(NormalizePath));
-
- return string.Format(CultureInfo.InvariantCulture, "concat:\"{0}\"", files);
- }
-
// Determine the input path for video files
- return GetFileInputArgument(inputFiles[0]);
+ return GetFileInputArgument(inputFile, inputPrefix);
}
/// <summary>
/// Gets the file input argument.
/// </summary>
/// <param name="path">The path.</param>
+ /// <param name="inputPrefix">The path prefix.</param>
/// <returns>System.String.</returns>
- private static string GetFileInputArgument(string path)
+ private static string GetFileInputArgument(string path, string inputPrefix)
{
if (path.IndexOf("://", StringComparison.Ordinal) != -1)
{
@@ -57,7 +48,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// Quotes are valid path characters in linux and they need to be escaped here with a leading \
path = NormalizePath(path);
- return string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", path);
+ return string.Format(CultureInfo.InvariantCulture, "{1}:\"{0}\"", path, inputPrefix);
}
/// <summary>
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 753da46a6..380894278 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -18,6 +18,7 @@ using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.MediaEncoding.Probing;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
@@ -34,9 +35,14 @@ namespace MediaBrowser.MediaEncoding.Encoder
public class MediaEncoder : IMediaEncoder, IDisposable
{
/// <summary>
- /// The default image extraction timeout in milliseconds.
+ /// The default SDR image extraction timeout in milliseconds.
/// </summary>
- internal const int DefaultImageExtractionTimeout = 10000;
+ internal const int DefaultSdrImageExtractionTimeout = 10000;
+
+ /// <summary>
+ /// The default HDR image extraction timeout in milliseconds.
+ /// </summary>
+ internal const int DefaultHdrImageExtractionTimeout = 20000;
/// <summary>
/// The us culture.
@@ -83,8 +89,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
_jsonSerializerOptions = JsonDefaults.GetOptions();
}
- private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
-
/// <inheritdoc />
public string EncoderPath => _ffmpegPath;
@@ -320,33 +324,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
{
var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
+ var inputFile = request.MediaSource.Path;
- var inputFiles = MediaEncoderHelpers.GetInputArgument(_fileSystem, request.MediaSource.Path, request.MountedIso, request.PlayableStreamFileNames);
-
- var probeSize = EncodingHelper.GetProbeSizeArgument(inputFiles.Length);
- string analyzeDuration;
+ string analyzeDuration = string.Empty;
if (request.MediaSource.AnalyzeDurationMs > 0)
{
analyzeDuration = "-analyzeduration " +
(request.MediaSource.AnalyzeDurationMs * 1000).ToString();
}
- else
- {
- analyzeDuration = EncodingHelper.GetAnalyzeDurationArgument(inputFiles.Length);
- }
-
- probeSize = probeSize + " " + analyzeDuration;
- probeSize = probeSize.Trim();
var forceEnableLogging = request.MediaSource.Protocol != MediaProtocol.File;
return GetMediaInfoInternal(
- GetInputArgument(inputFiles, request.MediaSource.Protocol),
+ GetInputArgument(inputFile, request.MediaSource),
request.MediaSource.Path,
request.MediaSource.Protocol,
extractChapters,
- probeSize,
+ analyzeDuration,
request.MediaType == DlnaProfileType.Audio,
request.MediaSource.VideoType,
forceEnableLogging,
@@ -356,12 +351,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Gets the input argument.
/// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
+ /// <param name="inputFile">The input file.</param>
+ /// <param name="mediaSource">The mediaSource.</param>
/// <returns>System.String.</returns>
/// <exception cref="ArgumentException">Unrecognized InputType.</exception>
- public string GetInputArgument(IReadOnlyList<string> inputFiles, MediaProtocol protocol)
- => EncodingUtils.GetInputArgument(inputFiles, protocol);
+ public string GetInputArgument(string inputFile, MediaSourceInfo mediaSource)
+ {
+ var prefix = "file";
+ if (mediaSource.VideoType == VideoType.BluRay || mediaSource.VideoType == VideoType.Iso)
+ {
+ prefix = "bluray";
+ }
+
+ return EncodingUtils.GetInputArgument(prefix, inputFile, mediaSource.Protocol);
+ }
/// <summary>
/// Gets the media info internal.
@@ -459,31 +462,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
public Task<string> ExtractAudioImage(string path, int? imageStreamIndex, CancellationToken cancellationToken)
{
- return ExtractImage(new[] { path }, null, null, imageStreamIndex, MediaProtocol.File, true, null, null, cancellationToken);
+ var mediaSource = new MediaSourceInfo
+ {
+ Protocol = MediaProtocol.File
+ };
+
+ return ExtractImage(path, null, null, imageStreamIndex, mediaSource, true, null, null, cancellationToken);
}
- public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
+ public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken)
{
- return ExtractImage(inputFiles, container, videoStream, null, protocol, false, threedFormat, offset, cancellationToken);
+ return ExtractImage(inputFile, container, videoStream, null, mediaSource, false, threedFormat, offset, cancellationToken);
}
- public Task<string> ExtractVideoImage(string[] inputFiles, string container, MediaProtocol protocol, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken)
+ public Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken)
{
- return ExtractImage(inputFiles, container, imageStream, imageStreamIndex, protocol, false, null, null, cancellationToken);
+ return ExtractImage(inputFile, container, imageStream, imageStreamIndex, mediaSource, false, null, null, cancellationToken);
}
private async Task<string> ExtractImage(
- string[] inputFiles,
+ string inputFile,
string container,
MediaStream videoStream,
int? imageStreamIndex,
- MediaProtocol protocol,
+ MediaSourceInfo mediaSource,
bool isAudio,
Video3DFormat? threedFormat,
TimeSpan? offset,
CancellationToken cancellationToken)
{
- var inputArgument = GetInputArgument(inputFiles, protocol);
+ var inputArgument = GetInputArgument(inputFile, mediaSource);
if (isAudio)
{
@@ -495,9 +503,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
else
{
+ // The failure of HDR extraction usually occurs when using custom ffmpeg that does not contain the zscale filter.
+ try
+ {
+ return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, true, cancellationToken).ConfigureAwait(false);
+ }
+ catch (ArgumentException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "I-frame or HDR image extraction failed, will attempt with I-frame extraction disabled. Input: {Arguments}", inputArgument);
+ }
+
try
{
- return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, cancellationToken).ConfigureAwait(false);
+ return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, true, cancellationToken).ConfigureAwait(false);
+ }
+ catch (ArgumentException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "HDR image extraction failed, will fallback to SDR image extraction. Input: {Arguments}", inputArgument);
+ }
+
+ try
+ {
+ return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, true, false, cancellationToken).ConfigureAwait(false);
}
catch (ArgumentException)
{
@@ -509,10 +544,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
- return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, cancellationToken).ConfigureAwait(false);
+ return await ExtractImageInternal(inputArgument, container, videoStream, imageStreamIndex, threedFormat, offset, false, false, cancellationToken).ConfigureAwait(false);
}
- private async Task<string> ExtractImageInternal(string inputPath, string container, MediaStream videoStream, int? imageStreamIndex, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, CancellationToken cancellationToken)
+ private async Task<string> ExtractImageInternal(string inputPath, string container, MediaStream videoStream, int? imageStreamIndex, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, bool allowTonemap, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(inputPath))
{
@@ -553,6 +588,20 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
+ var enableHdrExtraction = allowTonemap && string.Equals(videoStream?.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase);
+ if (enableHdrExtraction)
+ {
+ string tonemapFilters = "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p";
+ if (string.IsNullOrEmpty(vf))
+ {
+ vf = "-vf " + tonemapFilters;
+ }
+ else
+ {
+ vf += "," + tonemapFilters;
+ }
+ }
+
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
var enableThumbnail = useIFrame && !string.Equals("wtv", container, StringComparison.OrdinalIgnoreCase);
if (enableThumbnail)
@@ -569,8 +618,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads);
- var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
- var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
+ var probeSizeArgument = string.Empty;
+ var analyzeDurationArgument = string.Empty;
if (!string.IsNullOrWhiteSpace(probeSizeArgument))
{
@@ -635,7 +684,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var timeoutMs = _configurationManager.Configuration.ImageExtractionTimeoutMs;
if (timeoutMs <= 0)
{
- timeoutMs = DefaultImageExtractionTimeout;
+ timeoutMs = enableHdrExtraction ? DefaultHdrImageExtractionTimeout : DefaultSdrImageExtractionTimeout;
}
ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMilliseconds(timeoutMs)).ConfigureAwait(false);
@@ -679,10 +728,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
public async Task ExtractVideoImagesOnInterval(
- string[] inputFiles,
+ string inputFile,
string container,
MediaStream videoStream,
- MediaProtocol protocol,
+ MediaSourceInfo mediaSource,
Video3DFormat? threedFormat,
TimeSpan interval,
string targetDirectory,
@@ -690,7 +739,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
int? maxWidth,
CancellationToken cancellationToken)
{
- var inputArgument = GetInputArgument(inputFiles, protocol);
+ var inputArgument = GetInputArgument(inputFile, mediaSource);
var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(_usCulture);
@@ -706,8 +755,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads);
- var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
- var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
+ var probeSizeArgument = string.Empty;
+ var analyzeDurationArgument = string.Empty;
if (!string.IsNullOrWhiteSpace(probeSizeArgument))
{
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
index a5d641747..db6b47583 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
@@ -51,7 +51,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
eventsStarted = true;
}
- else if (!string.IsNullOrEmpty(line) && line.Trim().StartsWith(";", StringComparison.Ordinal))
+ else if (!string.IsNullOrEmpty(line) && line.Trim().StartsWith(';'))
{
// skip comment lines
}
@@ -151,13 +151,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
try
{
- var p = new SubtitleTrackEvent();
-
- p.StartPositionTicks = GetTimeCodeFromString(start);
- p.EndPositionTicks = GetTimeCodeFromString(end);
- p.Text = GetFormattedText(text);
-
- trackEvents.Add(p);
+ trackEvents.Add(
+ new SubtitleTrackEvent
+ {
+ StartPositionTicks = GetTimeCodeFromString(start),
+ EndPositionTicks = GetTimeCodeFromString(end),
+ Text = GetFormattedText(text)
+ });
}
catch
{
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index b61b8a0e0..b92c4ee06 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -168,18 +168,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
MediaStream subtitleStream,
CancellationToken cancellationToken)
{
- string[] inputFiles;
-
- if (mediaSource.VideoType.HasValue
- && (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd))
- {
- var mediaSourceItem = (Video)_libraryManager.GetItemById(new Guid(mediaSource.Id));
- inputFiles = mediaSourceItem.GetPlayableStreamFileNames();
- }
- else
- {
- inputFiles = new[] { mediaSource.Path };
- }
+ var inputFile = mediaSource.Path;
var protocol = mediaSource.Protocol;
if (subtitleStream.IsExternal)
@@ -187,7 +176,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
protocol = _mediaSourceManager.GetPathProtocol(subtitleStream.Path);
}
- var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, protocol, subtitleStream, cancellationToken).ConfigureAwait(false);
+ var fileInfo = await GetReadableFile(mediaSource.Path, inputFile, mediaSource, subtitleStream, cancellationToken).ConfigureAwait(false);
var stream = await GetSubtitleStream(fileInfo.Path, fileInfo.Protocol, fileInfo.IsExternal, cancellationToken).ConfigureAwait(false);
@@ -220,8 +209,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private async Task<SubtitleInfo> GetReadableFile(
string mediaPath,
- string[] inputFiles,
- MediaProtocol protocol,
+ string inputFile,
+ MediaSourceInfo mediaSource,
MediaStream subtitleStream,
CancellationToken cancellationToken)
{
@@ -252,9 +241,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
// Extract
- var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, "." + outputFormat);
+ var outputPath = GetSubtitleCachePath(mediaPath, mediaSource, subtitleStream.Index, "." + outputFormat);
- await ExtractTextSubtitle(inputFiles, protocol, subtitleStream.Index, outputCodec, outputPath, cancellationToken)
+ await ExtractTextSubtitle(inputFile, mediaSource, subtitleStream.Index, outputCodec, outputPath, cancellationToken)
.ConfigureAwait(false);
return new SubtitleInfo(outputPath, MediaProtocol.File, outputFormat, false);
@@ -266,14 +255,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (GetReader(currentFormat, false) == null)
{
// Convert
- var outputPath = GetSubtitleCachePath(mediaPath, protocol, subtitleStream.Index, ".srt");
+ var outputPath = GetSubtitleCachePath(mediaPath, mediaSource, subtitleStream.Index, ".srt");
- await ConvertTextSubtitleToSrt(subtitleStream.Path, subtitleStream.Language, protocol, outputPath, cancellationToken).ConfigureAwait(false);
+ await ConvertTextSubtitleToSrt(subtitleStream.Path, subtitleStream.Language, mediaSource, outputPath, cancellationToken).ConfigureAwait(false);
return new SubtitleInfo(outputPath, MediaProtocol.File, "srt", true);
}
- return new SubtitleInfo(subtitleStream.Path, protocol, currentFormat, true);
+ return new SubtitleInfo(subtitleStream.Path, mediaSource.Protocol, currentFormat, true);
}
private ISubtitleParser GetReader(string format, bool throwIfMissing)
@@ -363,11 +352,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// </summary>
/// <param name="inputPath">The input path.</param>
/// <param name="language">The language.</param>
- /// <param name="inputProtocol">The input protocol.</param>
+ /// <param name="mediaSource">The input mediaSource.</param>
/// <param name="outputPath">The output path.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- private async Task ConvertTextSubtitleToSrt(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken)
+ private async Task ConvertTextSubtitleToSrt(string inputPath, string language, MediaSourceInfo mediaSource, string outputPath, CancellationToken cancellationToken)
{
var semaphore = GetLock(outputPath);
@@ -377,7 +366,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
if (!File.Exists(outputPath))
{
- await ConvertTextSubtitleToSrtInternal(inputPath, language, inputProtocol, outputPath, cancellationToken).ConfigureAwait(false);
+ await ConvertTextSubtitleToSrtInternal(inputPath, language, mediaSource, outputPath, cancellationToken).ConfigureAwait(false);
}
}
finally
@@ -391,14 +380,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// </summary>
/// <param name="inputPath">The input path.</param>
/// <param name="language">The language.</param>
- /// <param name="inputProtocol">The input protocol.</param>
+ /// <param name="mediaSource">The input mediaSource.</param>
/// <param name="outputPath">The output path.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">
/// The <c>inputPath</c> or <c>outputPath</c> is <c>null</c>.
/// </exception>
- private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaProtocol inputProtocol, string outputPath, CancellationToken cancellationToken)
+ private async Task ConvertTextSubtitleToSrtInternal(string inputPath, string language, MediaSourceInfo mediaSource, string outputPath, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(inputPath))
{
@@ -412,7 +401,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
- var encodingParam = await GetSubtitleFileCharacterSet(inputPath, language, inputProtocol, cancellationToken).ConfigureAwait(false);
+ var encodingParam = await GetSubtitleFileCharacterSet(inputPath, language, mediaSource.Protocol, cancellationToken).ConfigureAwait(false);
// FFmpeg automatically convert character encoding when it is UTF-16
// If we specify character encoding, it rejects with "do not specify a character encoding" and "Unable to recode subtitle event"
@@ -515,8 +504,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// Extracts the text subtitle.
/// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
+ /// <param name="inputFile">The input file.</param>
+ /// <param name="mediaSource">The mediaSource.</param>
/// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
/// <param name="outputCodec">The output codec.</param>
/// <param name="outputPath">The output path.</param>
@@ -524,8 +513,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <returns>Task.</returns>
/// <exception cref="ArgumentException">Must use inputPath list overload.</exception>
private async Task ExtractTextSubtitle(
- string[] inputFiles,
- MediaProtocol protocol,
+ string inputFile,
+ MediaSourceInfo mediaSource,
int subtitleStreamIndex,
string outputCodec,
string outputPath,
@@ -540,7 +529,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (!File.Exists(outputPath))
{
await ExtractTextSubtitleInternal(
- _mediaEncoder.GetInputArgument(inputFiles, protocol),
+ _mediaEncoder.GetInputArgument(inputFile, mediaSource),
subtitleStreamIndex,
outputCodec,
outputPath,
@@ -706,9 +695,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- private string GetSubtitleCachePath(string mediaPath, MediaProtocol protocol, int subtitleStreamIndex, string outputSubtitleExtension)
+ private string GetSubtitleCachePath(string mediaPath, MediaSourceInfo mediaSource, int subtitleStreamIndex, string outputSubtitleExtension)
{
- if (protocol == MediaProtocol.File)
+ if (mediaSource.Protocol == MediaProtocol.File)
{
var ticksParam = string.Empty;
diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs
index 09afa64bb..56c89d854 100644
--- a/MediaBrowser.Model/Dlna/ContainerProfile.cs
+++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs
@@ -47,7 +47,7 @@ namespace MediaBrowser.Model.Dlna
public static bool ContainsContainer(string profileContainers, string inputContainer)
{
var isNegativeList = false;
- if (profileContainers != null && profileContainers.StartsWith("-", StringComparison.Ordinal))
+ if (profileContainers != null && profileContainers.StartsWith('-'))
{
isNegativeList = true;
profileContainers = profileContainers.Substring(1);
diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs
index ee13ffc16..4ad336d33 100644
--- a/MediaBrowser.Model/Querying/NextUpQuery.cs
+++ b/MediaBrowser.Model/Querying/NextUpQuery.cs
@@ -18,7 +18,7 @@ namespace MediaBrowser.Model.Querying
/// Gets or sets the parent identifier.
/// </summary>
/// <value>The parent identifier.</value>
- public string ParentId { get; set; }
+ public Guid? ParentId { get; set; }
/// <summary>
/// Gets or sets the series id.
diff --git a/MediaBrowser.Model/Search/SearchQuery.cs b/MediaBrowser.Model/Search/SearchQuery.cs
index 01ad319a4..ce60062cd 100644
--- a/MediaBrowser.Model/Search/SearchQuery.cs
+++ b/MediaBrowser.Model/Search/SearchQuery.cs
@@ -47,7 +47,7 @@ namespace MediaBrowser.Model.Search
public string[] ExcludeItemTypes { get; set; }
- public string ParentId { get; set; }
+ public Guid? ParentId { get; set; }
public bool? IsMovie { get; set; }
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index c61187fdf..4fff57273 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -150,11 +150,6 @@ namespace MediaBrowser.Providers.MediaInfo
public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
where T : Video
{
- if (item.VideoType == VideoType.Iso)
- {
- return _cachedTask;
- }
-
if (item.IsPlaceHolder)
{
return _cachedTask;
@@ -208,7 +203,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
item.ShortcutPath = File.ReadAllLines(item.Path)
.Select(NormalizeStrmLine)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i) && !i.StartsWith("#", StringComparison.OrdinalIgnoreCase));
+ .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i) && !i.StartsWith('#'));
}
public Task<ItemUpdateType> FetchAudioInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 776dee780..6d39c091e 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -116,7 +116,7 @@ namespace MediaBrowser.Providers.MediaInfo
streamFileNames = Array.Empty<string>();
}
- mediaInfoResult = await GetMediaInfo(item, streamFileNames, cancellationToken).ConfigureAwait(false);
+ mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
}
@@ -128,7 +128,6 @@ namespace MediaBrowser.Providers.MediaInfo
private Task<Model.MediaInfo.MediaInfo> GetMediaInfo(
Video item,
- string[] streamFileNames,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -145,7 +144,6 @@ namespace MediaBrowser.Providers.MediaInfo
return _mediaEncoder.GetMediaInfo(
new MediaInfoRequest
{
- PlayableStreamFileNames = streamFileNames,
ExtractChapters = true,
MediaType = DlnaProfileType.Video,
MediaSource = new MediaSourceInfo
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index fc38d3832..c36c3af6a 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -9,6 +9,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@@ -50,7 +51,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
// No support for this
- if (video.VideoType == VideoType.Iso || video.VideoType == VideoType.Dvd || video.VideoType == VideoType.BluRay)
+ if (video.VideoType == VideoType.Dvd)
{
return Task.FromResult(new DynamicImageResponse { HasImage = false });
}
@@ -69,11 +70,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var protocol = item.PathProtocol ?? MediaProtocol.File;
- var inputPath = MediaEncoderHelpers.GetInputArgument(
- _fileSystem,
- item.Path,
- null,
- item.GetPlayableStreamFileNames());
+ var inputPath = item.Path;
var mediaStreams =
item.GetMediaStreams();
@@ -107,7 +104,14 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, imageStream, videoIndex, cancellationToken).ConfigureAwait(false);
+ MediaSourceInfo mediaSource = new MediaSourceInfo
+ {
+ VideoType = item.VideoType,
+ IsoType = item.IsoType,
+ Protocol = item.PathProtocol.Value,
+ };
+
+ extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, mediaSource, imageStream, videoIndex, cancellationToken).ConfigureAwait(false);
}
else
{
@@ -119,8 +123,14 @@ namespace MediaBrowser.Providers.MediaInfo
: TimeSpan.FromSeconds(10);
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
+ var mediaSource = new MediaSourceInfo
+ {
+ VideoType = item.VideoType,
+ IsoType = item.IsoType,
+ Protocol = item.PathProtocol.Value,
+ };
- extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, protocol, videoStream, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false);
+ extractedImagePath = await _mediaEncoder.ExtractVideoImage(inputPath, item.Container, mediaSource, videoStream, item.Video3DFormat, imageOffset, cancellationToken).ConfigureAwait(false);
}
return new DynamicImageResponse
diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
index 90c491666..ee20cc573 100644
--- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
+++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs
@@ -69,7 +69,7 @@ namespace Jellyfin.Api.Tests.Auth
}
[Fact]
- public async Task HandleAuthenticateAsyncShouldFailOnAuthenticationException()
+ public async Task HandleAuthenticateAsyncShouldProvideNoResultOnAuthenticationException()
{
var errorMessage = _fixture.Create<string>();
@@ -81,7 +81,7 @@ namespace Jellyfin.Api.Tests.Auth
var authenticateResult = await _sut.AuthenticateAsync();
Assert.False(authenticateResult.Succeeded);
- Assert.Equal(errorMessage, authenticateResult.Failure?.Message);
+ Assert.True(authenticateResult.None);
}
[Fact]