aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/media_playback.md32
-rw-r--r--CONTRIBUTORS.md1
-rw-r--r--Dockerfile2
-rw-r--r--Dockerfile.arm10
-rw-r--r--Dockerfile.arm6411
-rw-r--r--Emby.Naming/Emby.Naming.csproj2
-rw-r--r--Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs33
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs23
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs9
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs28
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj7
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs18
-rw-r--r--Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs448
-rw-r--r--Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs28
-rw-r--r--Emby.Server.Implementations/Library/InvalidAuthProvider.cs47
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs38
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs10
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs6
-rw-r--r--Emby.Server.Implementations/ResourceFileManager.cs30
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs4
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs6
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs155
-rw-r--r--Jellyfin.Server/CoreAppHost.cs7
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj14
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs2
-rw-r--r--MediaBrowser.Common/Net/HttpRequestOptions.cs22
-rw-r--r--MediaBrowser.Common/Net/IHttpClient.cs11
-rw-r--r--MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs3
-rw-r--r--MediaBrowser.Controller/Entities/User.cs10
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs5
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs12
-rw-r--r--MediaBrowser.Controller/IResourceFileManager.cs11
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs50
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs1
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj2
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs3
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj2
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs2
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs13
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs148
m---------MediaBrowser.WebDashboard/jellyfin-web0
-rw-r--r--SharedVersion.cs4
-rw-r--r--build.yaml2
-rw-r--r--deployment/debian-package-x64/pkg-src/changelog12
-rw-r--r--deployment/fedora-package-x64/pkg-src/jellyfin.spec6
-rw-r--r--jellyfin.ruleset4
47 files changed, 533 insertions, 763 deletions
diff --git a/.github/ISSUE_TEMPLATE/media_playback.md b/.github/ISSUE_TEMPLATE/media_playback.md
new file mode 100644
index 000000000..93af33fbf
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/media_playback.md
@@ -0,0 +1,32 @@
+---
+name: Media playback issue
+about: Create a media playback issue report
+title: ''
+labels: mediaplayback
+assignees: ''
+
+---
+
+**Media Info of the file**
+<!-- Use the Media Info tool (set to text format, download here: https://mediaarea.net/en/MediaInfo) or copy the info from the web ui for the file with the playback issue. -->
+
+**Logs**
+<!-- Please paste any log message from during the playback issue, for example the ffmpeg command line can be very useful. -->
+
+**Stats for Nerds Screenshots**
+<!-- If available, add screenshots of the stats for nerds screen to help show the issue problem. -->
+
+**Server System (please complete the following information):**
+ - OS: [e.g. Docker on Linux, Docker on Windows, Debian, Windows]
+ - Jellyfin Version: [e.g. 10.0.1]
+ - Hardware settings & device: [e.g. NVENC on GTX1060, VAAPI on Intel i7 8700K]
+ - Reverse proxy: [e.g. no, nginx, apache, etc.]
+ - Other hardware notes: [e.g. Media mounted in CIFS/SMB share, Media mounted from Google Drive]
+
+**Client System (please complete the following information):**
+ - Device: [e.g. Apple iPhone XS, Xbox One S, LG OLED55C8, Samsung Galaxy Note9, Custom HTPC]
+ - OS: [e.g. iOS, Android, Windows, macOS]
+ - Client: [e.g. Web/Browser, webOS, Android, Android TV, Electron]
+ - Browser (if Web client): [e.g. Firefox, Chrome, Safari]
+ - Client and Browser Version: [e.g. 10.3.4 and 68.0]
+
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 81857e57c..c3fcea1e2 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -24,6 +24,7 @@
- [Lynxy](https://github.com/Lynxy)
- [fasheng](https://github.com/fasheng)
- [ploughpuff](https://github.com/ploughpuff)
+ - [pjeanjean](https://github.com/pjeanjean)
# Emby Contributors
diff --git a/Dockerfile b/Dockerfile
index fb3bb633f..c971f1cc1 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@ RUN apt-get update \
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.3
+ARG JELLYFIN_WEB_VERSION=10.3.5
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 2f43898fa..4847c726b 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -3,11 +3,6 @@
ARG DOTNET_VERSION=3.0
-FROM multiarch/qemu-user-static:x86_64-arm as qemu
-FROM alpine as qemu_extract
-COPY --from=qemu /usr/bin qemu-arm-static.tar.gz
-RUN tar -xzvf qemu-arm-static.tar.gz
-
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
@@ -21,8 +16,9 @@ RUN bash -c "source deployment/common.build.sh && \
build_jellyfin Jellyfin.Server Release linux-arm /jellyfin"
+FROM multiarch/qemu-user-static:x86_64-arm as qemu
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm32v7
-COPY --from=qemu_extract qemu-arm-static /usr/bin
+COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
&& rm -rf /var/lib/apt/lists/* \
@@ -30,7 +26,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.3
+ARG JELLYFIN_WEB_VERSION=10.3.5
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 5aa29edd5..a26cfc7b3 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -3,12 +3,6 @@
ARG DOTNET_VERSION=3.0
-FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
-FROM alpine as qemu_extract
-COPY --from=qemu /usr/bin qemu-aarch64-static.tar.gz
-RUN tar -xzvf qemu-aarch64-static.tar.gz
-
-
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
@@ -22,8 +16,9 @@ RUN bash -c "source deployment/common.build.sh && \
build_jellyfin Jellyfin.Server Release linux-arm64 /jellyfin"
+FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm64v8
-COPY --from=qemu_extract qemu-aarch64-static /usr/bin
+COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
&& rm -rf /var/lib/apt/lists/* \
@@ -31,7 +26,7 @@ RUN apt-get update \
&& chmod 777 /cache /config /media
COPY --from=builder /jellyfin /jellyfin
-ARG JELLYFIN_WEB_VERSION=10.3.3
+ARG JELLYFIN_WEB_VERSION=10.3.5
RUN curl -L https://github.com/jellyfin/jellyfin-web/archive/v${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& rm -rf /jellyfin/jellyfin-web \
&& mv jellyfin-web-${JELLYFIN_WEB_VERSION} /jellyfin/jellyfin-web
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index 6e05eb795..9e2a4950f 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -23,7 +23,7 @@
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.2" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.3" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>
diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
index 190e4d55c..0530a251c 100644
--- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
+++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
@@ -3,12 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -29,31 +27,39 @@ namespace Emby.Server.Implementations.Activity
{
public class ActivityLogEntryPoint : IServerEntryPoint
{
+ private readonly ILogger _logger;
private readonly IInstallationManager _installationManager;
private readonly ISessionManager _sessionManager;
private readonly ITaskManager _taskManager;
private readonly IActivityManager _activityManager;
private readonly ILocalizationManager _localization;
- private readonly ILibraryManager _libraryManager;
private readonly ISubtitleManager _subManager;
private readonly IUserManager _userManager;
- private readonly IServerConfigurationManager _config;
private readonly IServerApplicationHost _appHost;
private readonly IDeviceManager _deviceManager;
- public ActivityLogEntryPoint(ISessionManager sessionManager, IDeviceManager deviceManager, ITaskManager taskManager, IActivityManager activityManager, ILocalizationManager localization, IInstallationManager installationManager, ILibraryManager libraryManager, ISubtitleManager subManager, IUserManager userManager, IServerConfigurationManager config, IServerApplicationHost appHost)
+ public ActivityLogEntryPoint(
+ ILogger<ActivityLogEntryPoint> logger,
+ ISessionManager sessionManager,
+ IDeviceManager deviceManager,
+ ITaskManager taskManager,
+ IActivityManager activityManager,
+ ILocalizationManager localization,
+ IInstallationManager installationManager,
+ ISubtitleManager subManager,
+ IUserManager userManager,
+ IServerApplicationHost appHost)
{
+ _logger = logger;
_sessionManager = sessionManager;
+ _deviceManager = deviceManager;
_taskManager = taskManager;
_activityManager = activityManager;
_localization = localization;
_installationManager = installationManager;
- _libraryManager = libraryManager;
_subManager = subManager;
_userManager = userManager;
- _config = config;
_appHost = appHost;
- _deviceManager = deviceManager;
}
public Task RunAsync()
@@ -122,7 +128,7 @@ namespace Emby.Server.Implementations.Activity
if (item == null)
{
- //_logger.LogWarning("PlaybackStopped reported with null media info.");
+ _logger.LogWarning("PlaybackStopped reported with null media info.");
return;
}
@@ -153,7 +159,7 @@ namespace Emby.Server.Implementations.Activity
if (item == null)
{
- //_logger.LogWarning("PlaybackStart reported with null media info.");
+ _logger.LogWarning("PlaybackStart reported with null media info.");
return;
}
@@ -201,6 +207,7 @@ namespace Emby.Server.Implementations.Activity
{
return NotificationType.AudioPlayback.ToString();
}
+
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.VideoPlayback.ToString();
@@ -215,6 +222,7 @@ namespace Emby.Server.Implementations.Activity
{
return NotificationType.AudioPlaybackStopped.ToString();
}
+
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.VideoPlaybackStopped.ToString();
@@ -403,6 +411,7 @@ namespace Emby.Server.Implementations.Activity
{
vals.Add(e.Result.ErrorMessage);
}
+
if (!string.IsNullOrEmpty(e.Result.LongErrorMessage))
{
vals.Add(e.Result.LongErrorMessage);
@@ -412,7 +421,7 @@ namespace Emby.Server.Implementations.Activity
{
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
Type = NotificationType.TaskFailed.ToString(),
- Overview = string.Join(Environment.NewLine, vals.ToArray()),
+ Overview = string.Join(Environment.NewLine, vals),
ShortOverview = runningTime,
Severity = LogLevel.Error
});
@@ -489,6 +498,7 @@ namespace Emby.Server.Implementations.Activity
{
values.Add(CreateValueString(span.Hours, "hour"));
}
+
// Number of minutes
if (span.Minutes >= 1)
{
@@ -512,6 +522,7 @@ namespace Emby.Server.Implementations.Activity
builder.Append(values[i]);
}
+
// Return result
return builder.ToString();
}
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 53bc85b28..62cc6ec47 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -6,6 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
+using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
@@ -231,11 +232,6 @@ namespace Emby.Server.Implementations
/// <value>The server configuration manager.</value>
public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager;
- protected virtual IResourceFileManager CreateResourceFileManager()
- {
- return new ResourceFileManager(HttpResultFactory, LoggerFactory, FileSystemManager);
- }
-
/// <summary>
/// Gets or sets the user manager.
/// </summary>
@@ -826,10 +822,10 @@ namespace Emby.Server.Implementations
DtoService = new DtoService(LoggerFactory, LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ProviderManager, this, () => MediaSourceManager, () => LiveTvManager);
serviceCollection.AddSingleton(DtoService);
- ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager);
+ ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager);
serviceCollection.AddSingleton(ChannelManager);
- SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager);
+ SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, this, AuthenticationRepository, DeviceManager, MediaSourceManager);
serviceCollection.AddSingleton(SessionManager);
serviceCollection.AddSingleton<IDlnaManager>(
@@ -886,7 +882,7 @@ namespace Emby.Server.Implementations
SubtitleEncoder = new MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory);
serviceCollection.AddSingleton(SubtitleEncoder);
- serviceCollection.AddSingleton(CreateResourceFileManager());
+ serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager));
displayPreferencesRepo.Initialize();
@@ -1014,7 +1010,6 @@ namespace Emby.Server.Implementations
Video.LiveTvManager = LiveTvManager;
Folder.UserViewManager = UserViewManager;
UserView.TVSeriesManager = TVSeriesManager;
- UserView.PlaylistManager = PlaylistManager;
UserView.CollectionManager = CollectionManager;
BaseItem.MediaSourceManager = MediaSourceManager;
CollectionFolder.XmlSerializer = XmlSerializer;
@@ -1539,12 +1534,12 @@ namespace Emby.Server.Implementations
LogErrorResponseBody = false,
LogErrors = false,
LogRequest = false,
- TimeoutMs = 10000,
BufferContent = false,
CancellationToken = cancellationToken
}).ConfigureAwait(false))
{
- return GetWanApiUrl(response.ReadToEnd().Trim());
+ string res = await response.ReadToEndAsync().ConfigureAwait(false);
+ return GetWanApiUrl(res.Trim());
}
}
catch (Exception ex)
@@ -1697,15 +1692,15 @@ namespace Emby.Server.Implementations
LogErrorResponseBody = false,
LogErrors = LogPing,
LogRequest = LogPing,
- TimeoutMs = 5000,
BufferContent = false,
CancellationToken = cancellationToken
- }, "POST").ConfigureAwait(false))
+
+ }, HttpMethod.Post).ConfigureAwait(false))
{
using (var reader = new StreamReader(response.Content))
{
- var result = reader.ReadToEnd();
+ var result = await reader.ReadToEndAsync().ConfigureAwait(false);
var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase);
_validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid);
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 7e50650d7..e9961e8bd 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -6,7 +6,6 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@@ -20,7 +19,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Channels;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
@@ -40,11 +38,8 @@ namespace Emby.Server.Implementations.Channels
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
- private readonly IHttpClient _httpClient;
private readonly IProviderManager _providerManager;
- private readonly ILocalizationManager _localization;
-
public ChannelManager(
IUserManager userManager,
IDtoService dtoService,
@@ -54,8 +49,6 @@ namespace Emby.Server.Implementations.Channels
IFileSystem fileSystem,
IUserDataManager userDataManager,
IJsonSerializer jsonSerializer,
- ILocalizationManager localization,
- IHttpClient httpClient,
IProviderManager providerManager)
{
_userManager = userManager;
@@ -66,8 +59,6 @@ namespace Emby.Server.Implementations.Channels
_fileSystem = fileSystem;
_userDataManager = userDataManager;
_jsonSerializer = jsonSerializer;
- _localization = localization;
- _httpClient = httpClient;
_providerManager = providerManager;
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 7b28a22a8..2f1b60be9 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -89,14 +89,11 @@ namespace Emby.Server.Implementations.Dto
var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>();
var index = 0;
- var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType<Folder>().ToList();
-
foreach (var item in items)
{
- var dto = GetBaseItemDtoInternal(item, options, allCollectionFolders, user, owner);
+ var dto = GetBaseItemDtoInternal(item, options, user, owner);
- var tvChannel = item as LiveTvChannel;
- if (tvChannel != null)
+ if (item is LiveTvChannel tvChannel)
{
channelTuples.Add(new Tuple<BaseItemDto, LiveTvChannel>(dto, tvChannel));
}
@@ -105,9 +102,7 @@ namespace Emby.Server.Implementations.Dto
programTuples.Add(new Tuple<BaseItem, BaseItemDto>(item, dto));
}
- var byName = item as IItemByName;
-
- if (byName != null)
+ if (item is IItemByName byName)
{
if (options.ContainsField(ItemFields.ItemCounts))
{
@@ -130,8 +125,7 @@ namespace Emby.Server.Implementations.Dto
if (programTuples.Count > 0)
{
- var task = _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user);
- Task.WaitAll(task);
+ _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult();
}
if (channelTuples.Count > 0)
@@ -144,8 +138,7 @@ namespace Emby.Server.Implementations.Dto
public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
{
- var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType<Folder>().ToList();
- var dto = GetBaseItemDtoInternal(item, options, allCollectionFolders, user, owner);
+ var dto = GetBaseItemDtoInternal(item, options, user, owner);
var tvChannel = item as LiveTvChannel;
if (tvChannel != null)
{
@@ -188,7 +181,7 @@ namespace Emby.Server.Implementations.Dto
});
}
- private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, List<Folder> allCollectionFolders, User user = null, BaseItem owner = null)
+ private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
{
var dto = new BaseItemDto
{
@@ -312,6 +305,7 @@ namespace Emby.Server.Implementations.Dto
{
path = path.TrimStart('.');
}
+
if (!string.IsNullOrEmpty(path) && containers.Contains(path, StringComparer.OrdinalIgnoreCase))
{
fileExtensionContainer = path;
@@ -325,8 +319,7 @@ namespace Emby.Server.Implementations.Dto
public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, User user = null)
{
- var allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType<Folder>().ToList();
- var dto = GetBaseItemDtoInternal(item, options, allCollectionFolders, user);
+ var dto = GetBaseItemDtoInternal(item, options, user);
if (taggedItems != null && options.ContainsField(ItemFields.ItemCounts))
{
@@ -1051,14 +1044,15 @@ namespace Emby.Server.Implementations.Dto
}
else
{
- mediaStreams = dto.MediaSources.Where(i => string.Equals(i.Id, item.Id.ToString("N"), StringComparison.OrdinalIgnoreCase))
+ string id = item.Id.ToString("N");
+ mediaStreams = dto.MediaSources.Where(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => i.MediaStreams)
.ToArray();
}
}
else
{
- mediaStreams = _mediaSourceManager().GetStaticMediaSources(item, true).First().MediaStreams.ToArray();
+ mediaStreams = _mediaSourceManager().GetStaticMediaSources(item, true)[0].MediaStreams.ToArray();
}
dto.MediaStreams = mediaStreams;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index d4e17c42a..49015a07e 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -31,10 +31,9 @@
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
- <PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
- <PackageReference Include="sharpcompress" Version="0.22.0" />
+ <PackageReference Include="ServiceStack.Text.Core" Version="5.5.0" />
+ <PackageReference Include="sharpcompress" Version="0.23.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="1.0.0" />
- <PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
</ItemGroup>
<ItemGroup>
@@ -52,7 +51,7 @@
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.2" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.3" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
deleted file mode 100644
index f747b01b9..000000000
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientInfo.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-using System.Net.Http;
-
-namespace Emby.Server.Implementations.HttpClientManager
-{
- /// <summary>
- /// Class HttpClientInfo
- /// </summary>
- public class HttpClientInfo
- {
- /// <summary>
- /// Gets or sets the last timeout.
- /// </summary>
- /// <value>The last timeout.</value>
- public DateTime LastTimeout { get; set; }
- public HttpClient HttpClient { get; set; }
- }
-}
diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
index 1bebdd163..b82d55d0e 100644
--- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
+++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
@@ -1,11 +1,10 @@
using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
-using System.Net.Cache;
+using System.Net.Http;
+using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -55,12 +54,13 @@ namespace Emby.Server.Implementations.HttpClientManager
{
throw new ArgumentNullException(nameof(appPaths));
}
+
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
- _logger = loggerFactory.CreateLogger("HttpClient");
+ _logger = loggerFactory.CreateLogger(nameof(HttpClientManager));
_fileSystem = fileSystem;
_appPaths = appPaths;
_defaultUserAgentFn = defaultUserAgentFn;
@@ -74,27 +74,26 @@ namespace Emby.Server.Implementations.HttpClientManager
/// DON'T dispose it after use.
/// </summary>
/// <value>The HTTP clients.</value>
- private readonly ConcurrentDictionary<string, HttpClientInfo> _httpClients = new ConcurrentDictionary<string, HttpClientInfo>();
+ private readonly ConcurrentDictionary<string, HttpClient> _httpClients = new ConcurrentDictionary<string, HttpClient>();
/// <summary>
/// Gets
/// </summary>
- /// <param name="host">The host.</param>
+ /// <param name="url">The host.</param>
/// <param name="enableHttpCompression">if set to <c>true</c> [enable HTTP compression].</param>
/// <returns>HttpClient.</returns>
/// <exception cref="ArgumentNullException">host</exception>
- private HttpClientInfo GetHttpClient(string host, bool enableHttpCompression)
+ private HttpClient GetHttpClient(string url, bool enableHttpCompression)
{
- if (string.IsNullOrEmpty(host))
- {
- throw new ArgumentNullException(nameof(host));
- }
-
- var key = host + enableHttpCompression;
+ var key = GetHostFromUrl(url) + enableHttpCompression;
if (!_httpClients.TryGetValue(key, out var client))
{
- client = new HttpClientInfo();
+
+ client = new HttpClient()
+ {
+ BaseAddress = new Uri(url)
+ };
_httpClients.TryAdd(key, client);
}
@@ -102,110 +101,87 @@ namespace Emby.Server.Implementations.HttpClientManager
return client;
}
- private WebRequest GetRequest(HttpRequestOptions options, string method)
+ private HttpRequestMessage GetRequestMessage(HttpRequestOptions options, HttpMethod method)
{
string url = options.Url;
-
var uriAddress = new Uri(url);
string userInfo = uriAddress.UserInfo;
if (!string.IsNullOrWhiteSpace(userInfo))
{
- _logger.LogInformation("Found userInfo in url: {0} ... url: {1}", userInfo, url);
+ _logger.LogWarning("Found userInfo in url: {0} ... url: {1}", userInfo, url);
url = url.Replace(userInfo + "@", string.Empty);
}
- var request = WebRequest.Create(url);
+ var request = new HttpRequestMessage(method, url);
- if (request is HttpWebRequest httpWebRequest)
- {
- AddRequestHeaders(httpWebRequest, options);
+ AddRequestHeaders(request, options);
- if (options.EnableHttpCompression)
+ if (options.EnableHttpCompression)
+ {
+ if (options.DecompressionMethod.HasValue
+ && options.DecompressionMethod.Value == CompressionMethod.Gzip)
{
- httpWebRequest.AutomaticDecompression = DecompressionMethods.Deflate;
- if (options.DecompressionMethod.HasValue
- && options.DecompressionMethod.Value == CompressionMethod.Gzip)
- {
- httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip;
- }
+ request.Headers.Add(HeaderNames.AcceptEncoding, new[] { "gzip", "deflate" });
}
else
{
- httpWebRequest.AutomaticDecompression = DecompressionMethods.None;
+ request.Headers.Add(HeaderNames.AcceptEncoding, "deflate");
}
+ }
- httpWebRequest.KeepAlive = options.EnableKeepAlive;
+ if (options.EnableKeepAlive)
+ {
+ request.Headers.Add(HeaderNames.Connection, "Keep-Alive");
+ }
- if (!string.IsNullOrEmpty(options.Host))
- {
- httpWebRequest.Host = options.Host;
- }
+ if (!string.IsNullOrEmpty(options.Host))
+ {
+ request.Headers.Add(HeaderNames.Host, options.Host);
+ }
- if (!string.IsNullOrEmpty(options.Referer))
- {
- httpWebRequest.Referer = options.Referer;
- }
+ if (!string.IsNullOrEmpty(options.Referer))
+ {
+ request.Headers.Add(HeaderNames.Referer, options.Referer);
}
- request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
+ //request.Headers.Add(HeaderNames.CacheControl, "no-cache");
- request.Method = method;
- request.Timeout = options.TimeoutMs;
+ //request.Headers.Add(HeaderNames., options.TimeoutMs;
+ /*
if (!string.IsNullOrWhiteSpace(userInfo))
{
var parts = userInfo.Split(':');
if (parts.Length == 2)
{
- request.Credentials = GetCredential(url, parts[0], parts[1]);
- // TODO: .net core ??
- request.PreAuthenticate = true;
+ request.Headers.Add(HeaderNames., GetCredential(url, parts[0], parts[1]);
}
}
+ */
return request;
}
- private static CredentialCache GetCredential(string url, string username, string password)
- {
- //ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
- var credentialCache = new CredentialCache();
- credentialCache.Add(new Uri(url), "Basic", new NetworkCredential(username, password));
- return credentialCache;
- }
-
- private void AddRequestHeaders(HttpWebRequest request, HttpRequestOptions options)
+ private void AddRequestHeaders(HttpRequestMessage request, HttpRequestOptions options)
{
var hasUserAgent = false;
foreach (var header in options.RequestHeaders)
{
- if (string.Equals(header.Key, HeaderNames.Accept, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase))
{
- request.Accept = header.Value;
- }
- else if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase))
- {
- SetUserAgent(request, header.Value);
hasUserAgent = true;
}
- else
- {
- request.Headers.Set(header.Key, header.Value);
- }
+
+ request.Headers.Add(header.Key, header.Value);
}
if (!hasUserAgent && options.EnableDefaultUserAgent)
{
- SetUserAgent(request, _defaultUserAgentFn());
+ request.Headers.Add(HeaderNames.UserAgent, _defaultUserAgentFn());
}
}
- private static void SetUserAgent(HttpWebRequest request, string userAgent)
- {
- request.UserAgent = userAgent;
- }
-
/// <summary>
/// Gets the response internal.
/// </summary>
@@ -213,7 +189,7 @@ namespace Emby.Server.Implementations.HttpClientManager
/// <returns>Task{HttpResponseInfo}.</returns>
public Task<HttpResponseInfo> GetResponse(HttpRequestOptions options)
{
- return SendAsync(options, "GET");
+ return SendAsync(options, HttpMethod.Get);
}
/// <summary>
@@ -235,7 +211,21 @@ namespace Emby.Server.Implementations.HttpClientManager
/// <returns>Task{HttpResponseInfo}.</returns>
/// <exception cref="HttpException">
/// </exception>
- public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
+ public Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod)
+ {
+ var httpMethod2 = GetHttpMethod(httpMethod);
+ return SendAsync(options, httpMethod2);
+ }
+
+ /// <summary>
+ /// send as an asynchronous operation.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <param name="httpMethod">The HTTP method.</param>
+ /// <returns>Task{HttpResponseInfo}.</returns>
+ /// <exception cref="HttpException">
+ /// </exception>
+ public async Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod)
{
if (options.CacheMode == CacheMode.None)
{
@@ -263,6 +253,40 @@ namespace Emby.Server.Implementations.HttpClientManager
return response;
}
+ private HttpMethod GetHttpMethod(string httpMethod)
+ {
+ if (httpMethod.Equals("DELETE", StringComparison.OrdinalIgnoreCase))
+ {
+ return HttpMethod.Delete;
+ }
+ else if (httpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase))
+ {
+ return HttpMethod.Get;
+ }
+ else if (httpMethod.Equals("HEAD", StringComparison.OrdinalIgnoreCase))
+ {
+ return HttpMethod.Head;
+ }
+ else if (httpMethod.Equals("OPTIONS", StringComparison.OrdinalIgnoreCase))
+ {
+ return HttpMethod.Options;
+ }
+ else if (httpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase))
+ {
+ return HttpMethod.Post;
+ }
+ else if (httpMethod.Equals("PUT", StringComparison.OrdinalIgnoreCase))
+ {
+ return HttpMethod.Put;
+ }
+ else if (httpMethod.Equals("TRACE", StringComparison.OrdinalIgnoreCase))
+ {
+ return HttpMethod.Trace;
+ }
+
+ throw new ArgumentException("Invalid HTTP method", nameof(httpMethod));
+ }
+
private HttpResponseInfo GetCachedResponse(string responseCachePath, TimeSpan cacheLength, string url)
{
if (File.Exists(responseCachePath)
@@ -294,31 +318,23 @@ namespace Emby.Server.Implementations.HttpClientManager
}
}
- private async Task<HttpResponseInfo> SendAsyncInternal(HttpRequestOptions options, string httpMethod)
+ private async Task<HttpResponseInfo> SendAsyncInternal(HttpRequestOptions options, HttpMethod httpMethod)
{
ValidateParams(options);
options.CancellationToken.ThrowIfCancellationRequested();
- var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
+ var client = GetHttpClient(options.Url, options.EnableHttpCompression);
- if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds)
- {
- throw new HttpException(string.Format("Cancelling connection to {0} due to a previous timeout.", options.Url))
- {
- IsTimedOut = true
- };
- }
-
- var httpWebRequest = GetRequest(options, httpMethod);
+ var httpWebRequest = GetRequestMessage(options, httpMethod);
if (options.RequestContentBytes != null ||
!string.IsNullOrEmpty(options.RequestContent) ||
- string.Equals(httpMethod, "post", StringComparison.OrdinalIgnoreCase))
+ httpMethod == HttpMethod.Post)
{
try
{
- var bytes = options.RequestContentBytes ?? Encoding.UTF8.GetBytes(options.RequestContent ?? string.Empty);
+ httpWebRequest.Content = new StringContent(Encoding.UTF8.GetString(options.RequestContentBytes) ?? options.RequestContent ?? string.Empty);
var contentType = options.RequestContentType ?? "application/x-www-form-urlencoded";
@@ -327,8 +343,8 @@ namespace Emby.Server.Implementations.HttpClientManager
contentType = contentType.TrimEnd(';') + "; charset=\"utf-8\"";
}
- httpWebRequest.ContentType = contentType;
- (await httpWebRequest.GetRequestStreamAsync().ConfigureAwait(false)).Write(bytes, 0, bytes.Length);
+ httpWebRequest.Headers.Add(HeaderNames.ContentType, contentType);
+ await client.SendAsync(httpWebRequest).ConfigureAwait(false);
}
catch (Exception ex)
{
@@ -336,143 +352,96 @@ namespace Emby.Server.Implementations.HttpClientManager
}
}
- if (options.ResourcePool != null)
- {
- await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
- }
-
- if ((DateTime.UtcNow - client.LastTimeout).TotalSeconds < TimeoutSeconds)
- {
- options.ResourcePool?.Release();
-
- throw new HttpException($"Connection to {options.Url} timed out") { IsTimedOut = true };
- }
-
if (options.LogRequest)
{
- if (options.LogRequestAsDebug)
- {
- _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url);
- }
- else
- {
- _logger.LogInformation("HttpClientManager {0}: {1}", httpMethod.ToUpper(CultureInfo.CurrentCulture), options.Url);
- }
+ _logger.LogDebug("HttpClientManager {0}: {1}", httpMethod.ToString(), options.Url);
}
try
{
options.CancellationToken.ThrowIfCancellationRequested();
- if (!options.BufferContent)
+ /*if (!options.BufferContent)
{
- var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false);
+ var response = await client.HttpClient.SendAsync(httpWebRequest).ConfigureAwait(false);
- var httpResponse = (HttpWebResponse)response;
-
- EnsureSuccessStatusCode(client, httpResponse, options);
+ await EnsureSuccessStatusCode(client, response, options).ConfigureAwait(false);
options.CancellationToken.ThrowIfCancellationRequested();
- return GetResponseInfo(httpResponse, httpResponse.GetResponseStream(), GetContentLength(httpResponse), httpResponse);
- }
+ return GetResponseInfo(response, await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response.Content.Headers.ContentLength, response);
+ }*/
- using (var response = await GetResponseAsync(httpWebRequest, TimeSpan.FromMilliseconds(options.TimeoutMs)).ConfigureAwait(false))
+ using (var response = await client.SendAsync(httpWebRequest).ConfigureAwait(false))
{
- var httpResponse = (HttpWebResponse)response;
-
- EnsureSuccessStatusCode(client, httpResponse, options);
+ await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
options.CancellationToken.ThrowIfCancellationRequested();
- using (var stream = httpResponse.GetResponseStream())
+ using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
{
var memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
-
memoryStream.Position = 0;
- return GetResponseInfo(httpResponse, memoryStream, memoryStream.Length, null);
+ return GetResponseInfo(response, memoryStream, memoryStream.Length, null);
}
}
}
catch (OperationCanceledException ex)
{
- throw GetCancellationException(options, client, options.CancellationToken, ex);
- }
- catch (Exception ex)
- {
- throw GetException(ex, options, client);
- }
- finally
- {
- options.ResourcePool?.Release();
+ throw GetCancellationException(options, options.CancellationToken, ex);
}
}
- private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, Stream content, long? contentLength, IDisposable disposable)
+ private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, Stream content, long? contentLength, IDisposable disposable)
{
var responseInfo = new HttpResponseInfo(disposable)
{
Content = content,
StatusCode = httpResponse.StatusCode,
- ContentType = httpResponse.ContentType,
+ ContentType = httpResponse.Content.Headers.ContentType?.MediaType,
ContentLength = contentLength,
- ResponseUrl = httpResponse.ResponseUri.ToString()
+ ResponseUrl = httpResponse.Content.Headers.ContentLocation?.ToString()
};
if (httpResponse.Headers != null)
{
- SetHeaders(httpResponse.Headers, responseInfo);
+ SetHeaders(httpResponse.Content.Headers, responseInfo);
}
return responseInfo;
}
- private HttpResponseInfo GetResponseInfo(HttpWebResponse httpResponse, string tempFile, long? contentLength)
+ private HttpResponseInfo GetResponseInfo(HttpResponseMessage httpResponse, string tempFile, long? contentLength)
{
var responseInfo = new HttpResponseInfo
{
TempFilePath = tempFile,
StatusCode = httpResponse.StatusCode,
- ContentType = httpResponse.ContentType,
+ ContentType = httpResponse.Content.Headers.ContentType?.MediaType,
ContentLength = contentLength
};
if (httpResponse.Headers != null)
{
- SetHeaders(httpResponse.Headers, responseInfo);
+ SetHeaders(httpResponse.Content.Headers, responseInfo);
}
return responseInfo;
}
- private static void SetHeaders(WebHeaderCollection headers, HttpResponseInfo responseInfo)
+ private static void SetHeaders(HttpContentHeaders headers, HttpResponseInfo responseInfo)
{
- foreach (var key in headers.AllKeys)
+ foreach (var key in headers)
{
- responseInfo.Headers[key] = headers[key];
+ responseInfo.Headers[key.Key] = string.Join(", ", key.Value);
}
}
public Task<HttpResponseInfo> Post(HttpRequestOptions options)
{
- return SendAsync(options, "POST");
- }
-
- /// <summary>
- /// Performs a POST request
- /// </summary>
- /// <param name="options">The options.</param>
- /// <param name="postData">Params to add to the POST data.</param>
- /// <returns>stream on success, null on failure</returns>
- public async Task<Stream> Post(HttpRequestOptions options, Dictionary<string, string> postData)
- {
- options.SetPostData(postData);
-
- var response = await Post(options).ConfigureAwait(false);
-
- return response.Content;
+ return SendAsync(options, HttpMethod.Post);
}
/// <summary>
@@ -482,9 +451,10 @@ namespace Emby.Server.Implementations.HttpClientManager
/// <returns>Task{System.String}.</returns>
public async Task<string> GetTempFile(HttpRequestOptions options)
{
- var response = await GetTempFileResponse(options).ConfigureAwait(false);
-
- return response.TempFilePath;
+ using (var response = await GetTempFileResponse(options).ConfigureAwait(false))
+ {
+ return response.TempFilePath;
+ }
}
public async Task<HttpResponseInfo> GetTempFileResponse(HttpRequestOptions options)
@@ -502,44 +472,28 @@ namespace Emby.Server.Implementations.HttpClientManager
options.CancellationToken.ThrowIfCancellationRequested();
- var httpWebRequest = GetRequest(options, "GET");
-
- if (options.ResourcePool != null)
- {
- await options.ResourcePool.WaitAsync(options.CancellationToken).ConfigureAwait(false);
- }
+ var httpWebRequest = GetRequestMessage(options, HttpMethod.Get);
options.Progress.Report(0);
if (options.LogRequest)
{
- if (options.LogRequestAsDebug)
- {
- _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
- }
- else
- {
- _logger.LogInformation("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
- }
+ _logger.LogDebug("HttpClientManager.GetTempFileResponse url: {0}", options.Url);
}
- var client = GetHttpClient(GetHostFromUrl(options.Url), options.EnableHttpCompression);
+ var client = GetHttpClient(options.Url, options.EnableHttpCompression);
try
{
options.CancellationToken.ThrowIfCancellationRequested();
- using (var response = await httpWebRequest.GetResponseAsync().ConfigureAwait(false))
+ using (var response = (await client.SendAsync(httpWebRequest).ConfigureAwait(false)))
{
- var httpResponse = (HttpWebResponse)response;
-
- EnsureSuccessStatusCode(client, httpResponse, options);
+ await EnsureSuccessStatusCode(response, options).ConfigureAwait(false);
options.CancellationToken.ThrowIfCancellationRequested();
- var contentLength = GetContentLength(httpResponse);
-
- using (var stream = httpResponse.GetResponseStream())
+ using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false);
@@ -547,35 +501,22 @@ namespace Emby.Server.Implementations.HttpClientManager
options.Progress.Report(100);
- return GetResponseInfo(httpResponse, tempFile, contentLength);
+ var contentLength = response.Content.Headers.ContentLength;
+ return GetResponseInfo(response, tempFile, contentLength);
}
}
catch (Exception ex)
{
- DeleteTempFile(tempFile);
- throw GetException(ex, options, client);
- }
- finally
- {
- options.ResourcePool?.Release();
- }
- }
-
- private static long? GetContentLength(HttpWebResponse response)
- {
- var length = response.ContentLength;
+ if (File.Exists(tempFile))
+ {
+ File.Delete(tempFile);
+ }
- if (length == 0)
- {
- return null;
+ throw GetException(ex, options);
}
-
- return length;
}
- protected static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- private Exception GetException(Exception ex, HttpRequestOptions options, HttpClientInfo client)
+ private Exception GetException(Exception ex, HttpRequestOptions options)
{
if (ex is HttpException)
{
@@ -599,11 +540,6 @@ namespace Emby.Server.Implementations.HttpClientManager
if (response != null)
{
exception.StatusCode = response.StatusCode;
-
- if ((int)response.StatusCode == 429)
- {
- client.LastTimeout = DateTime.UtcNow;
- }
}
}
@@ -624,7 +560,7 @@ namespace Emby.Server.Implementations.HttpClientManager
if (operationCanceledException != null)
{
- return GetCancellationException(options, client, options.CancellationToken, operationCanceledException);
+ return GetCancellationException(options, options.CancellationToken, operationCanceledException);
}
if (options.LogErrors)
@@ -635,18 +571,6 @@ namespace Emby.Server.Implementations.HttpClientManager
return ex;
}
- private void DeleteTempFile(string file)
- {
- try
- {
- _fileSystem.DeleteFile(file);
- }
- catch (IOException)
- {
- // Might not have been created at all. No need to worry.
- }
- }
-
private void ValidateParams(HttpRequestOptions options)
{
if (string.IsNullOrEmpty(options.Url))
@@ -682,11 +606,10 @@ namespace Emby.Server.Implementations.HttpClientManager
/// Throws the cancellation exception.
/// </summary>
/// <param name="options">The options.</param>
- /// <param name="client">The client.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="exception">The exception.</param>
/// <returns>Exception.</returns>
- private Exception GetCancellationException(HttpRequestOptions options, HttpClientInfo client, CancellationToken cancellationToken, OperationCanceledException exception)
+ private Exception GetCancellationException(HttpRequestOptions options, CancellationToken cancellationToken, OperationCanceledException exception)
{
// If the HttpClient's timeout is reached, it will cancel the Task internally
if (!cancellationToken.IsCancellationRequested)
@@ -698,8 +621,6 @@ namespace Emby.Server.Implementations.HttpClientManager
_logger.LogError(msg);
}
- client.LastTimeout = DateTime.UtcNow;
-
// Throw an HttpException so that the caller doesn't think it was cancelled by user code
return new HttpException(msg, exception)
{
@@ -710,91 +631,20 @@ namespace Emby.Server.Implementations.HttpClientManager
return exception;
}
- private void EnsureSuccessStatusCode(HttpClientInfo client, HttpWebResponse response, HttpRequestOptions options)
+ private async Task EnsureSuccessStatusCode(HttpResponseMessage response, HttpRequestOptions options)
{
- var statusCode = response.StatusCode;
-
- var isSuccessful = statusCode >= HttpStatusCode.OK && statusCode <= (HttpStatusCode)299;
-
- if (isSuccessful)
+ if (response.IsSuccessStatusCode)
{
return;
}
- if (options.LogErrorResponseBody)
- {
- try
- {
- using (var stream = response.GetResponseStream())
- {
- if (stream != null)
- {
- using (var reader = new StreamReader(stream))
- {
- var msg = reader.ReadToEnd();
-
- _logger.LogError(msg);
- }
- }
- }
- }
- catch
- {
-
- }
- }
+ var msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ _logger.LogError(msg);
- throw new HttpException(response.StatusDescription)
+ throw new HttpException(response.ReasonPhrase)
{
StatusCode = response.StatusCode
};
}
-
- private static Task<WebResponse> GetResponseAsync(WebRequest request, TimeSpan timeout)
- {
- var taskCompletion = new TaskCompletionSource<WebResponse>();
-
- var asyncTask = Task.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null);
-
- ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, timeout, true);
- var callback = new TaskCallback { taskCompletion = taskCompletion };
- asyncTask.ContinueWith(callback.OnSuccess, TaskContinuationOptions.NotOnFaulted);
-
- // Handle errors
- asyncTask.ContinueWith(callback.OnError, TaskContinuationOptions.OnlyOnFaulted);
-
- return taskCompletion.Task;
- }
-
- private static void TimeoutCallback(object state, bool timedOut)
- {
- if (timedOut && state != null)
- {
- var request = (WebRequest)state;
- request.Abort();
- }
- }
-
- private class TaskCallback
- {
- public TaskCompletionSource<WebResponse> taskCompletion;
-
- public void OnSuccess(Task<WebResponse> task)
- {
- taskCompletion.TrySetResult(task.Result);
- }
-
- public void OnError(Task<WebResponse> task)
- {
- if (task.Exception == null)
- {
- taskCompletion.TrySetException(Enumerable.Empty<Exception>());
- }
- else
- {
- taskCompletion.TrySetException(task.Exception);
- }
- }
- }
}
}
diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
index 0527464ff..fe09b07ff 100644
--- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
+++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs
@@ -165,6 +165,34 @@ namespace Emby.Server.Implementations.Library
return user.Password;
}
+ public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
+ {
+ ConvertPasswordFormat(user);
+
+ if (newPassword != null)
+ {
+ newPasswordHash = string.Format("$SHA1${0}", GetHashedString(user, newPassword));
+ }
+
+ if (string.IsNullOrWhiteSpace(newPasswordHash))
+ {
+ throw new ArgumentNullException(nameof(newPasswordHash));
+ }
+
+ user.EasyPassword = newPasswordHash;
+ }
+
+ public string GetEasyPasswordHash(User user)
+ {
+ // This should be removed in the future. This was added to let user login after
+ // Jellyfin 10.3.3 failed to save a well formatted PIN.
+ ConvertPasswordFormat(user);
+
+ return string.IsNullOrEmpty(user.EasyPassword)
+ ? null
+ : (new PasswordHash(user.EasyPassword)).Hash;
+ }
+
public string GetHashedStringChangeAuth(string newPassword, PasswordHash passwordHash)
{
passwordHash.HashBytes = Encoding.UTF8.GetBytes(newPassword);
diff --git a/Emby.Server.Implementations/Library/InvalidAuthProvider.cs b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs
new file mode 100644
index 000000000..25d233137
--- /dev/null
+++ b/Emby.Server.Implementations/Library/InvalidAuthProvider.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Authentication;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Net;
+
+namespace Emby.Server.Implementations.Library
+{
+ public class InvalidAuthProvider : IAuthenticationProvider
+ {
+ public string Name => "InvalidOrMissingAuthenticationProvider";
+
+ public bool IsEnabled => true;
+
+ public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
+ {
+ throw new SecurityException("User Account cannot login with this provider. The Normal provider for this user cannot be found");
+ }
+
+ public Task<bool> HasPassword(User user)
+ {
+ return Task.FromResult(true);
+ }
+
+ public Task ChangePassword(User user, string newPassword)
+ {
+ return Task.CompletedTask;
+ }
+
+ public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
+ {
+ // Nothing here
+ }
+
+ public string GetPasswordHash(User user)
+ {
+ return string.Empty;
+ }
+
+ public string GetEasyPasswordHash(User user)
+ {
+ return string.Empty;
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index b396ee51a..ff375e590 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -79,6 +79,8 @@ namespace Emby.Server.Implementations.Library
private IAuthenticationProvider[] _authenticationProviders;
private DefaultAuthenticationProvider _defaultAuthenticationProvider;
+ private InvalidAuthProvider _invalidAuthProvider;
+
private IPasswordResetProvider[] _passwordResetProviders;
private DefaultPasswordResetProvider _defaultPasswordResetProvider;
@@ -141,6 +143,8 @@ namespace Emby.Server.Implementations.Library
_defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
+ _invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
+
_passwordResetProviders = passwordResetProviders.ToArray();
_defaultPasswordResetProvider = passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
@@ -307,8 +311,7 @@ namespace Emby.Server.Implementations.Library
user = Users
.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
- var hasNewUserPolicy = authenticationProvider as IHasNewUserPolicy;
- if (hasNewUserPolicy != null)
+ if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
{
var policy = hasNewUserPolicy.GetNewUserPolicy();
UpdateUserPolicy(user, policy, true);
@@ -400,7 +403,9 @@ namespace Emby.Server.Implementations.Library
if (providers.Length == 0)
{
- providers = new IAuthenticationProvider[] { _defaultAuthenticationProvider };
+ // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
+ _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId);
+ providers = new IAuthenticationProvider[] { _invalidAuthProvider };
}
return providers;
@@ -471,7 +476,7 @@ namespace Emby.Server.Implementations.Library
if (password == null)
{
// legacy
- success = string.Equals(_defaultAuthenticationProvider.GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ success = string.Equals(GetAuthenticationProvider(user).GetPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
}
else
{
@@ -497,11 +502,11 @@ namespace Emby.Server.Implementations.Library
if (password == null)
{
// legacy
- success = string.Equals(GetLocalPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+ success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), hashedPassword.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
}
else
{
- success = string.Equals(GetLocalPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase);
+ success = string.Equals(GetAuthenticationProvider(user).GetEasyPasswordHash(user), _defaultAuthenticationProvider.GetHashedString(user, password), StringComparison.OrdinalIgnoreCase);
}
}
}
@@ -546,13 +551,6 @@ namespace Emby.Server.Implementations.Library
}
}
- private string GetLocalPasswordHash(User user)
- {
- return string.IsNullOrEmpty(user.EasyPassword)
- ? null
- : (new PasswordHash(user.EasyPassword)).Hash;
- }
-
/// <summary>
/// Loads the users from the repository
/// </summary>
@@ -596,7 +594,7 @@ namespace Emby.Server.Implementations.Library
}
bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user).Result;
- bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetLocalPasswordHash(user));
+ bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user));
bool hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
hasConfiguredEasyPassword :
@@ -884,17 +882,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(user));
}
- if (newPassword != null)
- {
- newPasswordHash = _defaultAuthenticationProvider.GetHashedString(user, newPassword);
- }
-
- if (string.IsNullOrWhiteSpace(newPasswordHash))
- {
- throw new ArgumentNullException(nameof(newPasswordHash));
- }
-
- user.EasyPassword = newPasswordHash;
+ GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordHash);
UpdateUser(user);
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 4137760d0..f3f747718 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -96,8 +96,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Url = ApiUrl + "/schedules",
UserAgent = UserAgent,
CancellationToken = cancellationToken,
- // The data can be large so give it some extra time
- TimeoutMs = 60000,
LogErrorResponseBody = true,
RequestContent = requestString
};
@@ -115,9 +113,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Url = ApiUrl + "/programs",
UserAgent = UserAgent,
CancellationToken = cancellationToken,
- LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
+ LogErrorResponseBody = true
};
httpOptions.RequestHeaders["token"] = token;
@@ -483,8 +479,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CancellationToken = cancellationToken,
RequestContent = imageIdString,
LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
};
try
@@ -871,8 +865,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
UserAgent = UserAgent,
CancellationToken = cancellationToken,
LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
};
httpOptions.RequestHeaders["token"] = token;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 24b100edd..761275f8f 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -138,7 +138,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Url = string.Format("{0}/discover.json", GetApiUrl(info)),
CancellationToken = cancellationToken,
- TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(10).TotalMilliseconds),
BufferContent = false
}, "GET").ConfigureAwait(false))
@@ -191,7 +190,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Url = string.Format("{0}/tuners.html", GetApiUrl(info)),
CancellationToken = cancellationToken,
- TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
BufferContent = false
}))
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index d74cf3be2..e8b34da0c 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -47,13 +47,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
CancellationToken = CancellationToken.None,
BufferContent = false,
- // Increase a little bit
- TimeoutMs = 30000,
-
EnableHttpCompression = false,
-
- LogResponse = true,
- LogResponseHeaders = true
};
foreach (var header in mediaSource.RequiredHttpHeaders)
diff --git a/Emby.Server.Implementations/ResourceFileManager.cs b/Emby.Server.Implementations/ResourceFileManager.cs
index 890d848f4..6eda2b503 100644
--- a/Emby.Server.Implementations/ResourceFileManager.cs
+++ b/Emby.Server.Implementations/ResourceFileManager.cs
@@ -1,10 +1,8 @@
using System;
using System.IO;
-using System.Threading.Tasks;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations
@@ -13,34 +11,14 @@ namespace Emby.Server.Implementations
{
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
- private readonly IHttpResultFactory _resultFactory;
- public ResourceFileManager(
- IHttpResultFactory resultFactory,
- ILoggerFactory loggerFactory,
- IFileSystem fileSystem)
+ public ResourceFileManager(ILogger<ResourceFileManager> logger, IFileSystem fileSystem)
{
- _resultFactory = resultFactory;
- _logger = loggerFactory.CreateLogger("ResourceManager");
+ _logger = logger;
_fileSystem = fileSystem;
}
- public Stream GetResourceFileStream(string basePath, string virtualPath)
- {
- return _fileSystem.GetFileStream(GetResourcePath(basePath, virtualPath), FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true);
- }
-
- public Task<object> GetStaticFileResult(IRequest request, string basePath, string virtualPath, string contentType, TimeSpan? cacheDuration)
- {
- return _resultFactory.GetStaticFileResult(request, GetResourcePath(basePath, virtualPath));
- }
-
- public string ReadAllText(string basePath, string virtualPath)
- {
- return File.ReadAllText(GetResourcePath(basePath, virtualPath));
- }
-
- private string GetResourcePath(string basePath, string virtualPath)
+ public string GetResourcePath(string basePath, string virtualPath)
{
var fullPath = Path.Combine(basePath, virtualPath.Replace('/', Path.DirectorySeparatorChar));
@@ -50,7 +28,7 @@ namespace Emby.Server.Implementations
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error in Path.GetFullPath");
+ _logger.LogError(ex, "Error retrieving full path");
}
// Don't allow file system access outside of the source folder
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
index c81a93767..29b8dfd3d 100644
--- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -15,13 +15,9 @@ namespace Emby.Server.Implementations.Security
{
public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
{
- private readonly IServerConfigurationManager _config;
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
public AuthenticationRepository(ILoggerFactory loggerFactory, IServerConfigurationManager config)
: base(loggerFactory.CreateLogger(nameof(AuthenticationRepository)))
{
- _config = config;
DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "authentication.db");
}
diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
index 6a522fbef..c27eb7686 100644
--- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
+++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
@@ -48,7 +48,7 @@ namespace Emby.Server.Implementations.Services
foreach (var propertyInfo in RestPath.GetSerializableProperties(type))
{
- var propertySetFn = TypeAccessor.GetSetPropertyMethod(type, propertyInfo);
+ var propertySetFn = TypeAccessor.GetSetPropertyMethod(propertyInfo);
var propertyType = propertyInfo.PropertyType;
var propertyParseStringFn = GetParseFn(propertyType);
var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn, propertyType);
@@ -110,9 +110,9 @@ namespace Emby.Server.Implementations.Services
}
}
- internal class TypeAccessor
+ internal static class TypeAccessor
{
- public static Action<object, object> GetSetPropertyMethod(Type type, PropertyInfo propertyInfo)
+ public static Action<object, object> GetSetPropertyMethod(PropertyInfo propertyInfo)
{
if (!propertyInfo.CanWrite || propertyInfo.GetIndexParameters().Length > 0)
{
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 985748caf..53ed5fc22 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -7,7 +7,6 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Devices;
@@ -25,7 +24,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging;
@@ -53,8 +51,6 @@ namespace Emby.Server.Implementations.Session
private readonly IImageProcessor _imageProcessor;
private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IHttpClient _httpClient;
- private readonly IJsonSerializer _jsonSerializer;
private readonly IServerApplicationHost _appHost;
private readonly IAuthenticationRepository _authRepo;
@@ -96,9 +92,7 @@ namespace Emby.Server.Implementations.Session
IMusicManager musicManager,
IDtoService dtoService,
IImageProcessor imageProcessor,
- IJsonSerializer jsonSerializer,
IServerApplicationHost appHost,
- IHttpClient httpClient,
IAuthenticationRepository authRepo,
IDeviceManager deviceManager,
IMediaSourceManager mediaSourceManager)
@@ -110,9 +104,7 @@ namespace Emby.Server.Implementations.Session
_musicManager = musicManager;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
- _jsonSerializer = jsonSerializer;
_appHost = appHost;
- _httpClient = httpClient;
_authRepo = authRepo;
_deviceManager = deviceManager;
_mediaSourceManager = mediaSourceManager;
@@ -347,8 +339,7 @@ namespace Emby.Server.Implementations.Session
var runtimeTicks = libraryItem.RunTimeTicks;
MediaSourceInfo mediaSource = null;
- var hasMediaSources = libraryItem as IHasMediaSources;
- if (hasMediaSources != null)
+ if (libraryItem is IHasMediaSources hasMediaSources)
{
mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
@@ -1046,6 +1037,24 @@ namespace Emby.Server.Implementations.Session
}
}
+ private static Task SendMessageToSessions<T>(IEnumerable<SessionInfo> sessions, string name, T data, CancellationToken cancellationToken)
+ {
+ IEnumerable<Task> GetTasks()
+ {
+ var messageId = Guid.NewGuid().ToString("N");
+ foreach (var session in sessions)
+ {
+ var controllers = session.SessionControllers;
+ foreach (var controller in controllers)
+ {
+ yield return controller.SendMessage(name, messageId, data, controllers, cancellationToken);
+ }
+ }
+ }
+
+ return Task.WhenAll(GetTasks());
+ }
+
public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken)
{
CheckDisposed();
@@ -1232,12 +1241,13 @@ namespace Emby.Server.Implementations.Session
return SendMessageToSession(session, "Playstate", command, cancellationToken);
}
- private void AssertCanControl(SessionInfo session, SessionInfo controllingSession)
+ private static void AssertCanControl(SessionInfo session, SessionInfo controllingSession)
{
if (session == null)
{
throw new ArgumentNullException(nameof(session));
}
+
if (controllingSession == null)
{
throw new ArgumentNullException(nameof(controllingSession));
@@ -1249,26 +1259,11 @@ namespace Emby.Server.Implementations.Session
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
- public async Task SendRestartRequiredNotification(CancellationToken cancellationToken)
+ public Task SendRestartRequiredNotification(CancellationToken cancellationToken)
{
CheckDisposed();
- var sessions = Sessions.ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, "RestartRequired", string.Empty, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error in SendRestartRequiredNotification.", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
+ return SendMessageToSessions(Sessions, "RestartRequired", string.Empty, cancellationToken);
}
/// <summary>
@@ -1280,22 +1275,7 @@ namespace Emby.Server.Implementations.Session
{
CheckDisposed();
- var sessions = Sessions.ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, "ServerShuttingDown", string.Empty, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error in SendServerShutdownNotification.", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ return SendMessageToSessions(Sessions, "ServerShuttingDown", string.Empty, cancellationToken);
}
/// <summary>
@@ -1309,22 +1289,7 @@ namespace Emby.Server.Implementations.Session
_logger.LogDebug("Beginning SendServerRestartNotification");
- var sessions = Sessions.ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, "ServerRestarting", string.Empty, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error in SendServerRestartNotification.", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ return SendMessageToSessions(Sessions, "ServerRestarting", string.Empty, cancellationToken);
}
/// <summary>
@@ -1841,64 +1806,23 @@ namespace Emby.Server.Implementations.Session
var data = dataFn();
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken)
{
CheckDisposed();
- var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser)).ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ var sessions = Sessions.Where(i => userIds.Any(i.ContainsUser));
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
{
CheckDisposed();
- var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)).ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ var sessions = Sessions.Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase));
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
public Task SendMessageToUserDeviceAndAdminSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
@@ -1906,23 +1830,8 @@ namespace Emby.Server.Implementations.Session
CheckDisposed();
var sessions = Sessions
- .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i))
- .ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await SendMessageToSession(session, name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.LogError("Error sending message", ex);
- }
-
- }, cancellationToken)).ToArray();
-
- return Task.WhenAll(tasks);
+ .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i));
+ return SendMessageToSessions(sessions, name, data, cancellationToken);
}
private bool IsAdminSession(SessionInfo s)
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 8e6ed7a7e..b9b0cc382 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -1,7 +1,8 @@
using System.Collections.Generic;
using System.Reflection;
using Emby.Server.Implementations;
-using Emby.Server.Implementations.HttpServer;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
@@ -15,8 +16,8 @@ namespace Jellyfin.Server
ILoggerFactory loggerFactory,
StartupOptions options,
IFileSystem fileSystem,
- MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder,
- MediaBrowser.Common.Net.INetworkManager networkManager,
+ IImageEncoder imageEncoder,
+ INetworkManager networkManager,
IConfiguration configuration)
: base(
applicationPaths,
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 81f145abf..641b3f182 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -26,7 +26,7 @@
<!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.2" />
+ <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.3" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
</ItemGroup>
@@ -36,17 +36,17 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="CommandLineParser" Version="2.4.3" />
- <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.0" />
+ <PackageReference Include="CommandLineParser" Version="2.5.0" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
- <PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
- <PackageReference Include="Serilog.Sinks.Async" Version="1.3.0" />
+ <PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
+ <PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
<PackageReference Include="SkiaSharp" Version="1.68.0" />
- <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.13" />
- <PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.13" />
+ <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="1.1.14" />
+ <PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.14" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index 3c7ad1d0a..f1ae48492 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -224,7 +224,7 @@ namespace MediaBrowser.Api.UserLibrary
request.IncludeItemTypes = "Playlist";
}
- if (!user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id))
+ if (!(item is UserRootFolder) && !user.Policy.EnableAllFolders && !user.Policy.EnabledFolders.Any(i => new Guid(i) == item.Id))
{
Logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Name, item.Name);
return new QueryResult<BaseItem>
diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs
index bea178517..38e0ff0f5 100644
--- a/MediaBrowser.Common/Net/HttpRequestOptions.cs
+++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs
@@ -28,6 +28,7 @@ namespace MediaBrowser.Common.Net
get => GetHeaderValue(HeaderNames.Accept);
set => RequestHeaders[HeaderNames.Accept] = value;
}
+
/// <summary>
/// Gets or sets the cancellation token.
/// </summary>
@@ -35,12 +36,6 @@ namespace MediaBrowser.Common.Net
public CancellationToken CancellationToken { get; set; }
/// <summary>
- /// Gets or sets the resource pool.
- /// </summary>
- /// <value>The resource pool.</value>
- public SemaphoreSlim ResourcePool { get; set; }
-
- /// <summary>
/// Gets or sets the user agent.
/// </summary>
/// <value>The user agent.</value>
@@ -86,8 +81,6 @@ namespace MediaBrowser.Common.Net
public bool LogRequest { get; set; }
public bool LogRequestAsDebug { get; set; }
public bool LogErrors { get; set; }
- public bool LogResponse { get; set; }
- public bool LogResponseHeaders { get; set; }
public bool LogErrorResponseBody { get; set; }
public bool EnableKeepAlive { get; set; }
@@ -95,11 +88,9 @@ namespace MediaBrowser.Common.Net
public CacheMode CacheMode { get; set; }
public TimeSpan CacheLength { get; set; }
- public int TimeoutMs { get; set; }
public bool EnableDefaultUserAgent { get; set; }
public bool AppendCharsetToMimeType { get; set; }
- public string DownloadFilePath { get; set; }
private string GetHeaderValue(string name)
{
@@ -120,17 +111,6 @@ namespace MediaBrowser.Common.Net
LogRequest = true;
LogErrors = true;
CacheMode = CacheMode.None;
-
- TimeoutMs = 20000;
- }
-
- public void SetPostData(IDictionary<string, string> values)
- {
- var strings = values.Keys.Select(key => string.Format("{0}={1}", key, values[key]));
- var postContent = string.Join("&", strings.ToArray());
-
- RequestContent = postContent;
- RequestContentType = "application/x-www-form-urlencoded";
}
}
diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs
index 5aaf7e0be..db69c6f2c 100644
--- a/MediaBrowser.Common/Net/IHttpClient.cs
+++ b/MediaBrowser.Common/Net/IHttpClient.cs
@@ -1,5 +1,6 @@
using System.IO;
using System.Threading.Tasks;
+using System.Net.Http;
namespace MediaBrowser.Common.Net
{
@@ -23,6 +24,8 @@ namespace MediaBrowser.Common.Net
Task<Stream> Get(HttpRequestOptions options);
/// <summary>
+ /// Warning: Deprecated function,
+ /// use 'Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod);' instead
/// Sends the asynchronous.
/// </summary>
/// <param name="options">The options.</param>
@@ -31,6 +34,14 @@ namespace MediaBrowser.Common.Net
Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, string httpMethod);
/// <summary>
+ /// Sends the asynchronous.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <param name="httpMethod">The HTTP method.</param>
+ /// <returns>Task{HttpResponseInfo}.</returns>
+ Task<HttpResponseInfo> SendAsync(HttpRequestOptions options, HttpMethod httpMethod);
+
+ /// <summary>
/// Posts the specified options.
/// </summary>
/// <param name="options">The options.</param>
diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
index b9f282bd2..2cf531eed 100644
--- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
+++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
@@ -11,6 +11,9 @@ namespace MediaBrowser.Controller.Authentication
Task<ProviderAuthenticationResult> Authenticate(string username, string password);
Task<bool> HasPassword(User user);
Task ChangePassword(User user, string newPassword);
+ void ChangeEasyPassword(User user, string newPassword, string newPasswordHash);
+ string GetPasswordHash(User user);
+ string GetEasyPasswordHash(User user);
}
public interface IRequiresResolvedUser
diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs
index 0d5f508dd..9952ba418 100644
--- a/MediaBrowser.Controller/Entities/User.cs
+++ b/MediaBrowser.Controller/Entities/User.cs
@@ -228,7 +228,15 @@ namespace MediaBrowser.Controller.Entities
return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, safeFolderName);
}
- return System.IO.Path.Combine(parentPath, Id.ToString("N"));
+ // TODO: Remove idPath and just use usernamePath for future releases
+ var usernamePath = System.IO.Path.Combine(parentPath, username);
+ var idPath = System.IO.Path.Combine(parentPath, Id.ToString("N"));
+ if (!Directory.Exists(usernamePath) && Directory.Exists(idPath))
+ {
+ Directory.Move(idPath, usernamePath);
+ }
+
+ return usernamePath;
}
public bool IsParentalScheduleAllowed()
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 3e2191376..4a6d32dce 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization;
@@ -17,7 +16,6 @@ namespace MediaBrowser.Controller.Entities
public Guid? UserId { get; set; }
public static ITVSeriesManager TVSeriesManager;
- public static IPlaylistManager PlaylistManager;
[IgnoreDataMember]
public string CollectionType => ViewType;
@@ -38,6 +36,7 @@ namespace MediaBrowser.Controller.Entities
{
list.Add(Id);
}
+
return list;
}
@@ -65,7 +64,7 @@ namespace MediaBrowser.Controller.Entities
parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
}
- return new UserViewBuilder(UserViewManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager, PlaylistManager)
+ return new UserViewBuilder(UserViewManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager)
.GetUserItems(parent, this, CollectionType, query);
}
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 683218a9e..e483c8f34 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
@@ -21,9 +20,14 @@ namespace MediaBrowser.Controller.Entities
private readonly IUserDataManager _userDataManager;
private readonly ITVSeriesManager _tvSeriesManager;
private readonly IServerConfigurationManager _config;
- private readonly IPlaylistManager _playlistManager;
- public UserViewBuilder(IUserViewManager userViewManager, ILibraryManager libraryManager, ILogger logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager, IServerConfigurationManager config, IPlaylistManager playlistManager)
+ public UserViewBuilder(
+ IUserViewManager userViewManager,
+ ILibraryManager libraryManager,
+ ILogger logger,
+ IUserDataManager userDataManager,
+ ITVSeriesManager tvSeriesManager,
+ IServerConfigurationManager config)
{
_userViewManager = userViewManager;
_libraryManager = libraryManager;
@@ -31,7 +35,6 @@ namespace MediaBrowser.Controller.Entities
_userDataManager = userDataManager;
_tvSeriesManager = tvSeriesManager;
_config = config;
- _playlistManager = playlistManager;
}
public QueryResult<BaseItem> GetUserItems(Folder queryParent, Folder displayParent, string viewType, InternalItemsQuery query)
@@ -110,6 +113,7 @@ namespace MediaBrowser.Controller.Entities
{
return GetResult(GetMediaFolders(user).OfType<Folder>().SelectMany(i => i.GetChildren(user, true)), queryParent, query);
}
+
return queryParent.GetItems(query);
}
}
diff --git a/MediaBrowser.Controller/IResourceFileManager.cs b/MediaBrowser.Controller/IResourceFileManager.cs
index f70ea6a17..69a51cec8 100644
--- a/MediaBrowser.Controller/IResourceFileManager.cs
+++ b/MediaBrowser.Controller/IResourceFileManager.cs
@@ -1,16 +1,7 @@
-using System;
-using System.IO;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
namespace MediaBrowser.Controller
{
public interface IResourceFileManager
{
- Task<object> GetStaticFileResult(IRequest request, string basePath, string virtualPath, string contentType, TimeSpan? cacheDuration);
-
- Stream GetResourceFileStream(string basePath, string virtualPath);
-
- string ReadAllText(string basePath, string virtualPath);
+ string GetResourcePath(string basePath, string virtualPath);
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index e378c2b89..2984efec3 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -1083,27 +1083,51 @@ namespace MediaBrowser.Controller.MediaEncoding
{
var bitrate = request.VideoBitRate;
- // If specific values were requested, then force the caller to supply a bitrate as well
- if (request.Height.HasValue && request.Width.HasValue)
- {
- return bitrate;
- }
-
if (videoStream != null)
{
- if (bitrate.HasValue)
- {
- var inputVideoCodec = videoStream.Codec;
- bitrate = ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec);
+ var isUpscaling = request.Height.HasValue && videoStream.Height.HasValue &&
+ request.Height.Value > videoStream.Height.Value && request.Width.HasValue && videoStream.Width.HasValue &&
+ request.Width.Value > videoStream.Width.Value;
- // If a max bitrate was requested, don't let the scaled bitrate exceed it
- if (request.VideoBitRate.HasValue)
+ // Don't allow bitrate increases unless upscaling
+ if (!isUpscaling)
+ {
+ if (bitrate.HasValue && videoStream.BitRate.HasValue)
{
- bitrate = Math.Min(bitrate.Value, request.VideoBitRate.Value);
+ bitrate = GetMinBitrate(videoStream.BitRate.Value, bitrate.Value);
}
}
}
+ if (bitrate.HasValue)
+ {
+ var inputVideoCodec = videoStream.Codec;
+ bitrate = ScaleBitrate(bitrate.Value, inputVideoCodec, outputVideoCodec);
+
+ // If a max bitrate was requested, don't let the scaled bitrate exceed it
+ if (request.VideoBitRate.HasValue)
+ {
+ bitrate = Math.Min(bitrate.Value, request.VideoBitRate.Value);
+ }
+ }
+
+ return bitrate;
+ }
+
+ private int GetMinBitrate(int sourceBitrate, int requestedBitrate)
+ {
+ // these values were chosen from testing to improve low bitrate streams
+ if (sourceBitrate <= 2000000)
+ {
+ sourceBitrate = Convert.ToInt32(sourceBitrate * 2.5);
+ }
+ else if (sourceBitrate <= 3000000)
+ {
+ sourceBitrate = Convert.ToInt32(sourceBitrate * 2);
+ }
+
+ var bitrate = Math.Min(sourceBitrate, requestedBitrate);
+
return bitrate;
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 3eed891cb..b00350875 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -207,6 +207,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
"hevc_omx",
"h264_vaapi",
"hevc_vaapi",
+ "h264_v4l2m2m",
"ac3"
};
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index e4757543e..c0f92ac4a 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -18,7 +18,7 @@
<ItemGroup>
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
- <PackageReference Include="UTF.Unknown" Version="1.0.0-beta1" />
+ <PackageReference Include="UTF.Unknown" Version="1.0.0" />
</ItemGroup>
</Project>
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index d978359c7..8677b363f 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -126,8 +126,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw new ArgumentNullException(nameof(mediaSourceId));
}
- // TODO network path substition useful ?
- var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, true, cancellationToken).ConfigureAwait(false);
+ var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false);
var mediaSource = mediaSources
.First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index cfbb85ea6..5941ed436 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -14,7 +14,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.2.0" />
<PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" />
- <PackageReference Include="PlaylistsNET" Version="1.0.2" />
+ <PackageReference Include="PlaylistsNET" Version="1.0.4" />
<PackageReference Include="TvDbSharper" Version="2.0.0" />
</ItemGroup>
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
index efb8a0fe8..1d1fbd00f 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvDbClientManager.cs
@@ -33,7 +33,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
get
{
// Refresh if necessary
- if (_tokenCreatedAt > DateTime.Now.Subtract(TimeSpan.FromHours(20)))
+ if (_tokenCreatedAt < DateTime.Now.Subtract(TimeSpan.FromHours(20)))
{
try
{
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 58ab2d27b..d2ffd5efc 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -114,8 +114,6 @@ namespace MediaBrowser.WebDashboard.Api
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly IFileSystem _fileSystem;
- private readonly ILocalizationManager _localization;
- private readonly IJsonSerializer _jsonSerializer;
private IResourceFileManager _resourceFileManager;
/// <summary>
@@ -126,16 +124,12 @@ namespace MediaBrowser.WebDashboard.Api
IResourceFileManager resourceFileManager,
IServerConfigurationManager serverConfigurationManager,
IFileSystem fileSystem,
- ILocalizationManager localization,
- IJsonSerializer jsonSerializer,
ILogger logger,
IHttpResultFactory resultFactory)
{
_appHost = appHost;
_serverConfigurationManager = serverConfigurationManager;
_fileSystem = fileSystem;
- _localization = localization;
- _jsonSerializer = jsonSerializer;
_logger = logger;
_resultFactory = resultFactory;
_resourceFileManager = resourceFileManager;
@@ -205,6 +199,7 @@ namespace MediaBrowser.WebDashboard.Api
{
return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.js"), () => Task.FromResult(stream));
}
+
if (isTemplate)
{
return _resultFactory.GetStaticResult(Request, plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => Task.FromResult(stream));
@@ -316,7 +311,7 @@ namespace MediaBrowser.WebDashboard.Api
// Bounce them to the startup wizard if it hasn't been completed yet
if (!_serverConfigurationManager.Configuration.IsStartupWizardCompleted &&
Request.RawUrl.IndexOf("wizard", StringComparison.OrdinalIgnoreCase) == -1 &&
- GetPackageCreator(basePath).IsCoreHtml(path))
+ PackageCreator.IsCoreHtml(path))
{
// But don't redirect if an html import is being requested.
if (path.IndexOf("bower_components", StringComparison.OrdinalIgnoreCase) == -1)
@@ -355,7 +350,7 @@ namespace MediaBrowser.WebDashboard.Api
return await _resultFactory.GetStaticResult(Request, cacheKey, null, cacheDuration, contentType, () => GetResourceStream(basePath, path, localizationCulture)).ConfigureAwait(false);
}
- return await _resourceFileManager.GetStaticFileResult(Request, basePath, path, contentType, cacheDuration);
+ return await _resultFactory.GetStaticFileResult(Request, _resourceFileManager.GetResourcePath(basePath, path));
}
private string GetLocalizationCulture()
@@ -374,7 +369,7 @@ namespace MediaBrowser.WebDashboard.Api
private PackageCreator GetPackageCreator(string basePath)
{
- return new PackageCreator(basePath, _fileSystem, _logger, _serverConfigurationManager, _resourceFileManager);
+ return new PackageCreator(basePath, _resourceFileManager);
}
public async Task<object> Get(GetDashboardPackage request)
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index 2d0e0e188..133bf61e8 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -1,139 +1,108 @@
using System;
-using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Model.IO;
-using Microsoft.Extensions.Logging;
namespace MediaBrowser.WebDashboard.Api
{
public class PackageCreator
{
- private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
- private readonly IServerConfigurationManager _config;
private readonly string _basePath;
- private IResourceFileManager _resourceFileManager;
+ private readonly IResourceFileManager _resourceFileManager;
- public PackageCreator(string basePath, IFileSystem fileSystem, ILogger logger, IServerConfigurationManager config, IResourceFileManager resourceFileManager)
+ public PackageCreator(string basePath, IResourceFileManager resourceFileManager)
{
- _fileSystem = fileSystem;
- _logger = logger;
- _config = config;
_basePath = basePath;
_resourceFileManager = resourceFileManager;
}
- public async Task<Stream> GetResource(string virtualPath,
+ public async Task<Stream> GetResource(
+ string virtualPath,
string mode,
string localizationCulture,
string appVersion)
{
- var resourceStream = GetRawResourceStream(virtualPath);
+ var resourcePath = _resourceFileManager.GetResourcePath(_basePath, virtualPath);
+ Stream resourceStream = File.OpenRead(resourcePath);
- if (resourceStream != null)
+ if (resourceStream != null && IsCoreHtml(virtualPath))
{
- if (IsFormat(virtualPath, "html"))
- {
- if (IsCoreHtml(virtualPath))
- {
- resourceStream = await ModifyHtml(virtualPath, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false);
- }
- }
+ resourceStream = await ModifyHtml(virtualPath, resourceStream, mode, appVersion, localizationCulture).ConfigureAwait(false);
}
return resourceStream;
}
- /// <summary>
- /// Determines whether the specified path is HTML.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="format">The format.</param>
- /// <returns><c>true</c> if the specified path is HTML; otherwise, <c>false</c>.</returns>
- private static bool IsFormat(string path, string format)
- {
- return Path.GetExtension(path).EndsWith(format, StringComparison.OrdinalIgnoreCase);
- }
-
- public bool IsCoreHtml(string path)
+ public static bool IsCoreHtml(string path)
{
if (path.IndexOf(".template.html", StringComparison.OrdinalIgnoreCase) != -1)
{
return false;
}
- return IsFormat(path, "html");
+ return string.Equals(Path.GetExtension(path), ".html", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Modifies the HTML by adding common meta tags, css and js.
/// </summary>
/// <returns>Task{Stream}.</returns>
- public async Task<Stream> ModifyHtml(string path, Stream sourceStream, string mode, string appVersion, string localizationCulture)
+ public async Task<Stream> ModifyHtml(
+ string path,
+ Stream sourceStream,
+ string mode,
+ string appVersion,
+ string localizationCulture)
{
var isMainIndexPage = string.Equals(path, "index.html", StringComparison.OrdinalIgnoreCase);
- using (sourceStream)
+ string html;
+ using (var reader = new StreamReader(sourceStream, Encoding.UTF8))
{
- string html;
-
- using (var memoryStream = new MemoryStream())
- {
- await sourceStream.CopyToAsync(memoryStream).ConfigureAwait(false);
-
- var originalBytes = memoryStream.ToArray();
+ html = await reader.ReadToEndAsync().ConfigureAwait(false);
+ }
- html = Encoding.UTF8.GetString(originalBytes, 0, originalBytes.Length);
+ if (isMainIndexPage && !string.IsNullOrWhiteSpace(localizationCulture))
+ {
+ var lang = localizationCulture.Split('-')[0];
- if (isMainIndexPage)
- {
- if (!string.IsNullOrWhiteSpace(localizationCulture))
- {
- var lang = localizationCulture.Split('-').FirstOrDefault();
+ html = html.Replace("<html", "<html data-culture=\"" + localizationCulture + "\" lang=\"" + lang + "\"");
+ }
- html = html.Replace("<html", "<html data-culture=\"" + localizationCulture + "\" lang=\"" + lang + "\"");
- }
- }
- }
+ if (isMainIndexPage)
+ {
+ html = html.Replace("<head>", "<head>" + GetMetaTags(mode));
+ }
- if (isMainIndexPage)
- {
- html = html.Replace("<head>", "<head>" + GetMetaTags(mode));
- }
+ // Disable embedded scripts from plugins. We'll run them later once resources have loaded
+ if (html.IndexOf("<script", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ html = html.Replace("<script", "<!--<script");
+ html = html.Replace("</script>", "</script>-->");
+ }
- // Disable embedded scripts from plugins. We'll run them later once resources have loaded
- if (html.IndexOf("<script", StringComparison.OrdinalIgnoreCase) != -1)
- {
- html = html.Replace("<script", "<!--<script");
- html = html.Replace("</script>", "</script>-->");
- }
+ if (isMainIndexPage)
+ {
+ html = html.Replace("</body>", GetCommonJavascript(mode, appVersion) + "</body>");
+ }
- if (isMainIndexPage)
- {
- html = html.Replace("</body>", GetCommonJavascript(mode, appVersion) + "</body>");
- }
+ var bytes = Encoding.UTF8.GetBytes(html);
- var bytes = Encoding.UTF8.GetBytes(html);
+ return new MemoryStream(bytes);
- return new MemoryStream(bytes);
- }
}
/// <summary>
/// Gets the meta tags.
/// </summary>
/// <returns>System.String.</returns>
- private string GetMetaTags(string mode)
+ private static string GetMetaTags(string mode)
{
var sb = new StringBuilder();
- if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(mode, "android", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(mode, "android", StringComparison.OrdinalIgnoreCase))
{
sb.Append("<meta http-equiv=\"Content-Security-Policy\" content=\"default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: file: filesystem: ws: wss:;\">");
}
@@ -147,7 +116,7 @@ namespace MediaBrowser.WebDashboard.Api
/// <param name="mode">The mode.</param>
/// <param name="version">The version.</param>
/// <returns>System.String.</returns>
- private string GetCommonJavascript(string mode, string version)
+ private static string GetCommonJavascript(string mode, string version)
{
var builder = new StringBuilder();
@@ -156,7 +125,6 @@ namespace MediaBrowser.WebDashboard.Api
{
builder.AppendFormat("window.appMode='{0}';", mode);
}
-
else
{
builder.AppendFormat("window.dashboardVersion='{0}';", version);
@@ -164,31 +132,21 @@ namespace MediaBrowser.WebDashboard.Api
builder.Append("</script>");
- var versionString = string.IsNullOrWhiteSpace(mode) ? "?v=" + version : string.Empty;
-
- var files = new List<string>();
-
- files.Add("scripts/apploader.js" + versionString);
-
if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
{
- files.Insert(0, "cordova.js");
+ builder.Append("<script src=\"cordova.js\" defer></script>");
}
- var tags = files.Select(s => string.Format("<script src=\"{0}\" defer></script>", s)).ToArray();
+ builder.Append("<script src=\"scripts/apploader.js");
+ if (!string.IsNullOrWhiteSpace(version))
+ {
+ builder.Append("?v=");
+ builder.Append(version);
+ }
- builder.Append(string.Join(string.Empty, tags));
+ builder.Append("\" defer></script>");
return builder.ToString();
}
-
- /// <summary>
- /// Gets the raw resource stream.
- /// </summary>
- private Stream GetRawResourceStream(string virtualPath)
- {
- return _resourceFileManager.GetResourceFileStream(_basePath, virtualPath);
- }
-
}
}
diff --git a/MediaBrowser.WebDashboard/jellyfin-web b/MediaBrowser.WebDashboard/jellyfin-web
-Subproject b0f7a9b67cc72de98dc357425e9d5c3894c7f37
+Subproject 37636dae5c6c0b0711dfc7612f843b864dd5946
diff --git a/SharedVersion.cs b/SharedVersion.cs
index b249520b4..27ba1cf2c 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,4 +1,4 @@
using System.Reflection;
-[assembly: AssemblyVersion("10.3.3")]
-[assembly: AssemblyFileVersion("10.3.3")]
+[assembly: AssemblyVersion("10.3.5")]
+[assembly: AssemblyFileVersion("10.3.5")]
diff --git a/build.yaml b/build.yaml
index ed459582a..cb010ed59 100644
--- a/build.yaml
+++ b/build.yaml
@@ -1,7 +1,7 @@
---
# We just wrap `build` so this is really it
name: "jellyfin"
-version: "10.3.3"
+version: "10.3.5"
packages:
- debian-package-x64
- debian-package-armhf
diff --git a/deployment/debian-package-x64/pkg-src/changelog b/deployment/debian-package-x64/pkg-src/changelog
index a47f7e841..94d0c87df 100644
--- a/deployment/debian-package-x64/pkg-src/changelog
+++ b/deployment/debian-package-x64/pkg-src/changelog
@@ -1,3 +1,15 @@
+jellyfin (10.3.5-1) unstable; urgency=medium
+
+ * New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5
+
+ -- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 09 Jun 2019 21:47:35 -0400
+
+jellyfin (10.3.4-1) unstable; urgency=medium
+
+ * New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4
+
+ -- Jellyfin Packaging Team <packaging@jellyfin.org> Thu, 06 Jun 2019 22:45:31 -0400
+
jellyfin (10.3.3-1) unstable; urgency=medium
* New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3
diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
index e2e814b96..aeea7ecd0 100644
--- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec
+++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec
@@ -7,7 +7,7 @@
%endif
Name: jellyfin
-Version: 10.3.3
+Version: 10.3.5
Release: 1%{?dist}
Summary: The Free Software Media Browser
License: GPLv2
@@ -140,6 +140,10 @@ fi
%systemd_postun_with_restart jellyfin.service
%changelog
+* Sun Jun 09 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
+- New upstream version 10.3.5; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.5
+* Thu Jun 06 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
+- New upstream version 10.3.4; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.4
* Fri May 17 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
- New upstream version 10.3.3; release changelog at https://github.com/jellyfin/jellyfin/releases/tag/v10.3.3
* Tue Apr 30 2019 Jellyfin Packaging Team <packaging@jellyfin.org>
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 0a60c8c7a..1249a60c0 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -25,10 +25,14 @@
<Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
<!-- disable warning CA1031: Do not catch general exception types -->
<Rule Id="CA1031" Action="Info" />
+ <!-- disable warning CA1062: Validate arguments of public methods -->
+ <Rule Id="CA1062" Action="Info" />
<!-- disable warning CA1822: Member does not access instance data and can be marked as static -->
<Rule Id="CA1822" Action="Info" />
<!-- disable warning CA1054: Change the type of parameter url from string to System.Uri -->
<Rule Id="CA1054" Action="None" />
+ <!-- disable warning CA1303: Do not pass literals as localized parameters -->
+ <Rule Id="CA1303" Action="None" />
</Rules>
</RuleSet>