aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md51
-rw-r--r--.github/ISSUE_TEMPLATE/issue report.yml106
-rw-r--r--Emby.Dlna/DlnaManager.cs19
-rw-r--r--Emby.Dlna/Main/DlnaEntryPoint.cs22
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs38
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs7
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj2
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs2
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs3
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs10
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs2
-rw-r--r--Jellyfin.Api/Controllers/DynamicHlsController.cs2
-rw-r--r--Jellyfin.Api/Helpers/TranscodingJobHelper.cs6
-rw-r--r--Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs2
-rw-r--r--Jellyfin.Networking/Manager/NetworkManager.cs30
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs4
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs6
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs26
-rw-r--r--MediaBrowser.Controller/Providers/ImageRefreshOptions.cs2
-rw-r--r--MediaBrowser.Controller/Providers/ItemInfo.cs2
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs117
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs11
-rw-r--r--MediaBrowser.Model/MediaInfo/AudioCodec.cs27
-rw-r--r--MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs16
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs58
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleFormat.cs2
-rw-r--r--MediaBrowser.Model/Net/NetworkShareType.cs33
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs2
-rw-r--r--jellyfin.ruleset4
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs2
-rw-r--r--tests/Jellyfin.Networking.Tests/NetworkParseTests.cs50
-rw-r--r--tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs18
41 files changed, 244 insertions, 466 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index c1d49778e..000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,51 +0,0 @@
----
-name: Bug report
-about: Create a bug report
-title: ''
-labels: bug
-assignees: ''
-
----
-
-**Describe the bug**
-<!-- A clear and concise description of what the bug is. -->
-
-**System (please complete the following information):**
- - OS: [e.g. Debian, Windows]
- - Virtualization: [e.g. Docker, KVM, LXC]
- - Clients: [Browser, Android, Fire Stick, etc.]
- - Browser: [e.g. Firefox 91, Chrome 93, Safari 13]
- - Jellyfin Version: [e.g. 10.7.6, unstable 20191231]
- - FFmpeg Version: [e.g. 4.3.2-Jellyfin]
- - Playback: [Direct Play, Remux, Direct Stream, Transcode]
- - Hardware Acceleration: [e.g. none, VAAPI, NVENC, etc.]
- - Installed Plugins: [e.g. none, Fanart, Anime, etc.]
- - Reverse Proxy: [e.g. none, nginx, apache, etc.]
- - Base URL: [e.g. none, yes: /example]
- - Networking: [e.g. Host, Bridge/NAT]
- - Storage: [e.g. local, NFS, cloud]
-
-**To Reproduce**
-<!-- Steps to reproduce the behavior: -->
-1. Go to '...'
-2. Click on '....'
-3. Scroll down to '....'
-4. See error
-
-**Expected behavior**
-<!-- A clear and concise description of what you expected to happen. -->
-
-**Server Logs**
-<!-- Please paste any log errors. -->
-
-**FFmpeg Logs**
-<!-- Please paste any log errors. -->
-
-**Browser Console Logs**
-<!-- Please paste any log errors. -->
-
-**Screenshots**
-<!-- If applicable, add screenshots to help explain your problem. -->
-
-**Additional context**
-<!-- Add any other context about the problem here. -->
diff --git a/.github/ISSUE_TEMPLATE/issue report.yml b/.github/ISSUE_TEMPLATE/issue report.yml
new file mode 100644
index 000000000..63e0f0e22
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/issue report.yml
@@ -0,0 +1,106 @@
+name: Issue Report
+description: File an issue report
+title: "[Issue]: "
+labels: [bug, triage]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report! Please provide as much detail as necessary, most questions may not be applicable to you. If you need real-time help, join us on [Matrix](https://matrix.to/#/#jellyfin-troubleshooting:matrix.org) or [Discord](https://discord.gg/zHBxVSXdBV).
+ - type: textarea
+ id: what-happened
+ attributes:
+ label: Please describe your bug
+ description: Also tell us, what did you expect to happen?
+ placeholder: |
+ The more information that you are able to provide, the better. Did you do anything before this happened? Did you upgrade or change anything? Any screenshots or logs you can provide will be helpful.
+
+ This is my issue.
+
+ Steps to Reproduce
+ 1. In this environment...
+ 2. With this config...
+ 3. Run '...'
+ 4. See error...
+ validations:
+ required: true
+ - type: dropdown
+ id: version
+ attributes:
+ label: Jellyfin Version
+ description: What version of Jellyfin are you running?
+ options:
+ - 10.7.7
+ - 10.7.z
+ - 10.6.4
+ - Other
+ validations:
+ required: true
+ - type: input
+ id: version-other
+ attributes:
+ label: "if other:"
+ placeholder: Other
+ - type: textarea
+ attributes:
+ label: Environment
+ description: |
+ Examples:
+ - **OS**: [e.g. Debian, Windows]
+ - **Virtualization**: [e.g. Docker, KVM, LXC]
+ - **Clients**: [Browser, Android, Fire Stick, etc.]
+ - **Browser**: [e.g. Firefox 91, Chrome 93, Safari 13]
+ - **FFmpeg Version**: [e.g. 4.3.2-Jellyfin]
+ - **Playback**: [Direct Play, Remux, Direct Stream, Transcode]
+ - **Hardware Acceleration**: [e.g. none, VAAPI, NVENC, etc.]
+ - **Installed Plugins**: [e.g. none, Fanart, Anime, etc.]
+ - **Reverse Proxy**: [e.g. none, nginx, apache, etc.]
+ - **Base URL**: [e.g. none, yes: /example]
+ - **Networking**: [e.g. Host, Bridge/NAT]
+ - **Storage**: [e.g. local, NFS, cloud]
+ value: |
+ - OS:
+ - Virtualization:
+ - Clients:
+ - Browser:
+ - FFmpeg Version:
+ - Playback Method:
+ - Hardware Acceleration:
+ - Plugins:
+ - Reverse Proxy:
+ - Base URL:
+ - Networking:
+ - Storage:
+ render: markdown
+ - type: textarea
+ id: logs
+ attributes:
+ label: Jellyfin logs
+ description: Please copy and paste any relevant log output. This can be found in Dashboard > Logs.
+ placeholder: For playback issues, browser/client and FFmpeg logs may be more useful.
+ render: shell
+ - type: textarea
+ id: ffmpeg-logs
+ attributes:
+ label: FFmpeg logs
+ description: Please copy and paste any relevant log output. This can be found in Dashboard > Logs.
+ placeholder: It's important to include the specific codec details. If no FFmpeg logs appear, the file was Direct Played and did not use FFmpeg.
+ render: shell
+ - type: textarea
+ id: browserlogs
+ attributes:
+ label: Please attach any browser or client logs here
+ placeholder: Access browser logs by using the F12 to bring up the console. Screenshots are typically easier to read than raw logs. For clients such as Android or iOS, please see our documentation.
+ - type: textarea
+ id: screenshots
+ attributes:
+ label: Please attach any screenshots here
+ placeholder: Images can be pasted directly into the textbox and will be hosted by github.
+ - type: checkboxes
+ id: terms
+ attributes:
+ label: Code of Conduct
+ description: By submitting this issue, you agree to follow our [Code of Conduct](https://jellyfin.org/docs/general/community-standards.html#code-of-conduct)
+ options:
+ - label: I agree to follow this project's Code of Conduct
+ required: true
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index f37d2d7d7..277a0e678 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -112,7 +112,7 @@ namespace Emby.Dlna
if (profile == null)
{
- LogUnmatchedProfile(deviceInfo);
+ _logger.LogInformation("No matching device profile found. The default will need to be used. \n{@Profile}", deviceInfo);
}
else
{
@@ -122,23 +122,6 @@ namespace Emby.Dlna
return profile;
}
- private void LogUnmatchedProfile(DeviceIdentification profile)
- {
- var builder = new StringBuilder();
-
- builder.AppendLine("No matching device profile found. The default will need to be used.");
- builder.Append("FriendlyName: ").AppendLine(profile.FriendlyName);
- builder.Append("Manufacturer: ").AppendLine(profile.Manufacturer);
- builder.Append("ManufacturerUrl: ").AppendLine(profile.ManufacturerUrl);
- builder.Append("ModelDescription: ").AppendLine(profile.ModelDescription);
- builder.Append("ModelName: ").AppendLine(profile.ModelName);
- builder.Append("ModelNumber: ").AppendLine(profile.ModelNumber);
- builder.Append("ModelUrl: ").AppendLine(profile.ModelUrl);
- builder.Append("SerialNumber: ").AppendLine(profile.SerialNumber);
-
- _logger.LogInformation(builder.ToString());
- }
-
/// <summary>
/// Attempts to match a device with a profile.
/// Rules:
diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs
index 5d252d8dc..722428c73 100644
--- a/Emby.Dlna/Main/DlnaEntryPoint.cs
+++ b/Emby.Dlna/Main/DlnaEntryPoint.cs
@@ -52,7 +52,6 @@ namespace Emby.Dlna.Main
private readonly ISocketFactory _socketFactory;
private readonly INetworkManager _networkManager;
private readonly object _syncLock = new object();
- private readonly NetworkConfiguration _netConfig;
private readonly bool _disabled;
private PlayToManager _manager;
@@ -125,8 +124,8 @@ namespace Emby.Dlna.Main
config);
Current = this;
- _netConfig = config.GetConfiguration<NetworkConfiguration>("network");
- _disabled = appHost.ListenWithHttps && _netConfig.RequireHttps;
+ var netConfig = config.GetConfiguration<NetworkConfiguration>("network");
+ _disabled = appHost.ListenWithHttps && netConfig.RequireHttps;
if (_disabled && _config.GetDlnaConfiguration().EnableServer)
{
@@ -219,11 +218,6 @@ namespace Emby.Dlna.Main
}
}
- private void LogMessage(string msg)
- {
- _logger.LogDebug(msg);
- }
-
private void StartDeviceDiscovery(ISsdpCommunicationsServer communicationsServer)
{
try
@@ -273,7 +267,7 @@ namespace Emby.Dlna.Main
Environment.OSVersion.VersionString,
_config.GetDlnaConfiguration().SendOnlyMatchedHost)
{
- LogFunction = LogMessage,
+ LogFunction = (msg) => _logger.LogDebug("{Msg}", msg),
SupportPnpRootDevice = false
};
@@ -318,15 +312,9 @@ namespace Emby.Dlna.Main
var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
- _logger.LogInformation("Registering publisher for {0} on {1}", fullService, address);
+ _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, address);
- var uri = new UriBuilder(_appHost.GetSmartApiUrl(address.Address) + descriptorUri);
- if (!string.IsNullOrEmpty(_appHost.PublishedServerUrl))
- {
- // DLNA will only work over http, so we must reset to http:// : {port}.
- uri.Scheme = "http";
- uri.Port = _netConfig.HttpServerPortNumber;
- }
+ var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(false) + descriptorUri);
var device = new SsdpRootDevice
{
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index f359ee44c..73919f306 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -1123,12 +1123,6 @@ namespace Emby.Server.Implementations
}
string smart = NetManager.GetBindInterface(remoteAddr, out port);
- // If the smartAPI doesn't start with http then treat it as a host or ip.
- if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return smart.Trim('/');
- }
-
return GetLocalApiUrl(smart.Trim('/'), null, port);
}
@@ -1155,12 +1149,6 @@ namespace Emby.Server.Implementations
}
string smart = NetManager.GetBindInterface(request, out port);
- // If the smartAPI doesn't start with http then treat it as a host or ip.
- if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return smart.Trim('/');
- }
-
return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port);
}
@@ -1175,30 +1163,28 @@ namespace Emby.Server.Implementations
}
string smart = NetManager.GetBindInterface(hostname, out port);
-
- // If the smartAPI doesn't start with http then treat it as a host or ip.
- if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return smart.Trim('/');
- }
-
return GetLocalApiUrl(smart.Trim('/'), null, port);
}
/// <inheritdoc/>
- public string GetLoopbackHttpApiUrl()
+ public string GetApiUrlForLocalAccess(bool allowHttps)
{
- if (NetManager.IsIP6Enabled)
- {
- return GetLocalApiUrl("::1", Uri.UriSchemeHttp, HttpPort);
- }
-
- return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort);
+ // With an empty source, the port will be null
+ string smart = NetManager.GetBindInterface(string.Empty, out _);
+ var scheme = allowHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp;
+ var port = allowHttps ? HttpsPort : HttpPort;
+ return GetLocalApiUrl(smart.Trim('/'), scheme, port);
}
/// <inheritdoc/>
public string GetLocalApiUrl(string hostname, string scheme = null, int? port = null)
{
+ // If the smartAPI doesn't start with http then treat it as a host or ip.
+ if (hostname.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ return hostname.TrimEnd('/');
+ }
+
// NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
// not. For consistency, always trim the trailing slash.
return new UriBuilder
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index c6b32a52c..67ecd04e0 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -134,14 +134,11 @@ namespace Emby.Server.Implementations.Dto
var dto = GetBaseItemDtoInternal(item, options, user, owner);
if (item is LiveTvChannel tvChannel)
{
- var list = new List<(BaseItemDto, LiveTvChannel)>(1) { (dto, tvChannel) };
- LivetvManager.AddChannelInfo(list, options, user);
+ LivetvManager.AddChannelInfo(new[] { (dto, tvChannel) }, options, user);
}
else if (item is LiveTvProgram)
{
- var list = new List<(BaseItem, BaseItemDto)>(1) { (item, dto) };
- var task = LivetvManager.AddInfoToProgramDto(list, options.Fields, user);
- Task.WaitAll(task);
+ LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult();
}
if (item is IItemByName itemByName
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 03f9f50ea..042b8f71a 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -30,7 +30,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />
- <PackageReference Include="Mono.Nat" Version="3.0.1" />
+ <PackageReference Include="Mono.Nat" Version="3.0.2" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" />
<PackageReference Include="sharpcompress" Version="0.30.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index 7ebc800b9..b525f5a2f 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -267,7 +267,7 @@ namespace Emby.Server.Implementations.IO
if (_fileSystemWatchers.TryAdd(path, newWatcher))
{
newWatcher.EnableRaisingEvents = true;
- _logger.LogInformation("Watching directory " + path);
+ _logger.LogInformation("Watching directory {Path}", path);
}
else
{
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 2dbb569c6..559da7f5c 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -333,8 +333,7 @@ namespace Emby.Server.Implementations.Library
{
try
{
- var task = BaseItem.ChannelManager.DeleteItem(item);
- Task.WaitAll(task);
+ BaseItem.ChannelManager.DeleteItem(item).GetAwaiter().GetResult();
}
catch (ArgumentException)
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 60720dd2f..9e3f62276 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -151,7 +151,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
if (parser.IsMultiPart(path))
{
- logger.LogDebug("Found multi-disc folder: " + path);
+ logger.LogDebug("Found multi-disc folder: {Path}", path);
Interlocked.Increment(ref discSubfolderCount);
}
else
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index e5abb523c..644f9050d 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -957,7 +957,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public async Task<ILiveStream> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
{
- _logger.LogInformation("Streaming Channel " + channelId);
+ _logger.LogInformation("Streaming Channel {Id}", channelId);
var result = string.IsNullOrEmpty(streamId) ?
null :
@@ -1027,7 +1027,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var stream = new MediaSourceInfo
{
- EncoderPath = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
+ EncoderPath = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
EncoderProtocol = MediaProtocol.Http,
Path = info.Path,
Protocol = MediaProtocol.File,
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 8688688e9..5726d7158 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -87,8 +87,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
ErrorDialog = false
};
- var commandLineLogMessage = processStartInfo.FileName + " " + processStartInfo.Arguments;
- _logger.LogInformation(commandLineLogMessage);
+ _logger.LogInformation("{Filename} {Arguments}", processStartInfo.FileName, processStartInfo.Arguments);
var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");
Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
@@ -97,7 +96,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logFileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
await JsonSerializer.SerializeAsync(_logFileStream, mediaSource, _jsonOptions, cancellationToken).ConfigureAwait(false);
- await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);
+ await _logFileStream.WriteAsync(Encoding.UTF8.GetBytes(Environment.NewLine + Environment.NewLine + processStartInfo.FileName + " " + processStartInfo.Arguments + Environment.NewLine + Environment.NewLine), cancellationToken).ConfigureAwait(false);
_process = new Process
{
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index ecd28097d..4b7584af3 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -104,7 +104,7 @@ namespace Emby.Server.Implementations.LiveTv
// Dummy this up so that direct play checks can still run
if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http)
{
- source.Path = _appHost.GetSmartApiUrl(string.Empty);
+ source.Path = _appHost.GetApiUrlForLocalAccess();
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index b621055d8..9fba17ca3 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
// OpenedMediaSource.Path = tempFile;
// OpenedMediaSource.ReadAtNativeFramerate = true;
- MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
+ MediaSource.Path = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
MediaSource.Protocol = MediaProtocol.Http;
// OpenedMediaSource.SupportsDirectPlay = false;
// OpenedMediaSource.SupportsDirectStream = true;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index 3b69e55b0..b1ce7b2b3 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
// OpenedMediaSource.Path = tempFile;
// OpenedMediaSource.ReadAtNativeFramerate = true;
- MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
+ MediaSource.Path = _appHost.GetApiUrlForLocalAccess() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
MediaSource.Protocol = MediaProtocol.Http;
// OpenedMediaSource.Path = TempFilePath;
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 36f4e3e7c..2d7163275 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -31,7 +31,7 @@
"ItemRemovedWithName": "{0} - изъято из медиатеки",
"LabelIpAddressValue": "IP-адрес: {0}",
"LabelRunningTimeValue": "Длительность: {0}",
- "Latest": "Последнее",
+ "Latest": "Крайнее",
"MessageApplicationUpdated": "Jellyfin Server был обновлён",
"MessageApplicationUpdatedTo": "Jellyfin Server был обновлён до {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Конфигурация сервера (раздел {0}) была обновлена",
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index f2cdfeb16..21a7f4f5f 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -638,7 +638,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- _logger.LogInformation(Name + ": Cancelling");
+ _logger.LogInformation("{Name}: Cancelling", Name);
token.Cancel();
}
catch (Exception ex)
@@ -652,16 +652,16 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- _logger.LogInformation(Name + ": Waiting on Task");
+ _logger.LogInformation("{Name}: Waiting on Task", Name);
var exited = task.Wait(2000);
if (exited)
{
- _logger.LogInformation(Name + ": Task exited");
+ _logger.LogInformation("{Name}: Task exited", Name);
}
else
{
- _logger.LogInformation(Name + ": Timed out waiting for task to stop");
+ _logger.LogInformation("{Name}: Timed out waiting for task to stop", Name);
}
}
catch (Exception ex)
@@ -674,7 +674,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
{
try
{
- _logger.LogDebug(Name + ": Disposing CancellationToken");
+ _logger.LogDebug("{Name}: Disposing CancellationToken", Name);
token.Dispose();
}
catch (Exception ex)
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 4a022c5db..ef95ebf94 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -571,7 +571,7 @@ namespace Emby.Server.Implementations.Updates
?? _pluginManager.Plugins.FirstOrDefault(p => p.Name.Equals(package.Name, StringComparison.OrdinalIgnoreCase) && p.Version.Equals(package.Version));
await PerformPackageInstallation(package, plugin?.Manifest.Status ?? PluginStatus.Active, cancellationToken).ConfigureAwait(false);
- _logger.LogInformation(plugin == null ? "New plugin installed: {PluginName} {PluginVersion}" : "Plugin updated: {PluginName} {PluginVersion}", package.Name, package.Version);
+ _logger.LogInformation("Plugin {Action}: {PluginName} {PluginVersion}", plugin == null ? "installed" : "updated", package.Name, package.Version);
return plugin != null;
}
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 049fd503b..caa3d2368 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -1391,7 +1391,7 @@ namespace Jellyfin.Api.Controllers
}
else
{
- _logger.LogError("Invalid HLS segment container: " + segmentFormat);
+ _logger.LogError("Invalid HLS segment container: {SegmentFormat}", segmentFormat);
}
var maxMuxingQueueSize = _encodingOptions.MaxMuxingQueueSize > 128
diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
index f435bbf00..9d80070eb 100644
--- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
+++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
@@ -543,8 +543,7 @@ namespace Jellyfin.Api.Helpers
state,
cancellationTokenSource);
- var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
- _logger.LogInformation(commandLineLogMessage);
+ _logger.LogInformation("{Filename} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
var logFilePrefix = "FFmpeg.Transcode-";
if (state.VideoRequest != null
@@ -562,8 +561,9 @@ namespace Jellyfin.Api.Helpers
// FFmpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
+ var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(request.Path + Environment.NewLine + Environment.NewLine + JsonSerializer.Serialize(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
- await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
+ await logStream.WriteAsync(commandLineLogMessageBytes, cancellationTokenSource.Token).ConfigureAwait(false);
process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);
diff --git a/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs b/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs
index 7b32d76ba..0136d9f86 100644
--- a/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs
+++ b/Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs
@@ -197,7 +197,7 @@ namespace Jellyfin.Api.Models.PlaybackDtos
}
}
- _logger.LogDebug("No throttle data for " + path);
+ _logger.LogDebug("No throttle data for {Path}", path);
return false;
}
diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs
index 4078fd126..cf002dc73 100644
--- a/Jellyfin.Networking/Manager/NetworkManager.cs
+++ b/Jellyfin.Networking/Manager/NetworkManager.cs
@@ -455,10 +455,10 @@ namespace Jellyfin.Networking.Manager
}
// No bind address, so return all internal interfaces.
- return CreateCollection(_internalInterfaces.Where(p => !p.IsLoopback()));
+ return CreateCollection(_internalInterfaces);
}
- return new Collection<IPObject>(_bindAddresses);
+ return new Collection<IPObject>(_bindAddresses.Where(a => IsInLocalNetwork(a)).ToArray());
}
/// <inheritdoc/>
@@ -481,7 +481,7 @@ namespace Jellyfin.Networking.Manager
}
// As private addresses can be redefined by Configuration.LocalNetworkAddresses
- return _lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address);
+ return address.IsLoopback() || (_lanSubnets.ContainsAddress(address) && !_excludedSubnets.ContainsAddress(address));
}
/// <inheritdoc/>
@@ -647,16 +647,6 @@ namespace Jellyfin.Networking.Manager
_interfaceAddresses.AddItem(address, false);
_interfaceNames[parts[2]] = Math.Abs(index);
}
-
- if (IsIP4Enabled)
- {
- _interfaceAddresses.AddItem(IPNetAddress.IP4Loopback);
- }
-
- if (IsIP6Enabled)
- {
- _interfaceAddresses.AddItem(IPNetAddress.IP6Loopback);
- }
}
InitialiseLAN(config);
@@ -1037,17 +1027,14 @@ namespace Jellyfin.Networking.Manager
// Subnets are the same as the calculated internal interface.
_lanSubnets = new Collection<IPObject>();
- // We must listen on loopback for LiveTV to function regardless of the settings.
if (IsIP6Enabled)
{
- _lanSubnets.AddItem(IPNetAddress.IP6Loopback);
_lanSubnets.AddItem(IPNetAddress.Parse("fc00::/7")); // ULA
_lanSubnets.AddItem(IPNetAddress.Parse("fe80::/10")); // Site local
}
if (IsIP4Enabled)
{
- _lanSubnets.AddItem(IPNetAddress.IP4Loopback);
_lanSubnets.AddItem(IPNetAddress.Parse("10.0.0.0/8"));
_lanSubnets.AddItem(IPNetAddress.Parse("172.16.0.0/12"));
_lanSubnets.AddItem(IPNetAddress.Parse("192.168.0.0/16"));
@@ -1055,17 +1042,6 @@ namespace Jellyfin.Networking.Manager
}
else
{
- // We must listen on loopback for LiveTV to function regardless of the settings.
- if (IsIP6Enabled)
- {
- _lanSubnets.AddItem(IPNetAddress.IP6Loopback);
- }
-
- if (IsIP4Enabled)
- {
- _lanSubnets.AddItem(IPNetAddress.IP4Loopback);
- }
-
// Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet.
_internalInterfaces = CreateCollection(_interfaceAddresses.Where(IsInLocalNetwork));
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index ffd1c7f0a..ec1ebaabe 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -425,7 +425,7 @@ namespace MediaBrowser.Controller.Entities
{
if (item.IsFileProtocol)
{
- Logger.LogDebug("Removed item: " + item.Path);
+ Logger.LogDebug("Removed item: {Path}", item.Path);
item.SetParent(null);
LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }, this, false);
@@ -807,7 +807,7 @@ namespace MediaBrowser.Controller.Entities
{
if (this is not ICollectionFolder)
{
- Logger.LogDebug("Query requires post-filtering due to LinkedChildren. Type: " + GetType().Name);
+ Logger.LogDebug("{Type}: Query requires post-filtering due to LinkedChildren.", GetType().Name);
return true;
}
}
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index 7e5e742f5..7da492af3 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -81,11 +81,11 @@ namespace MediaBrowser.Controller
string GetSmartApiUrl(string hostname, int? port = null);
/// <summary>
- /// Gets a localhost URL that can be used to access the API using the loop-back IP address.
- /// over HTTP (not HTTPS).
+ /// Gets an URL that can be used to access the API over LAN.
/// </summary>
+ /// <param name="allowHttps">A value indicating whether to allow HTTPS.</param>
/// <returns>The API URL.</returns>
- string GetLoopbackHttpApiUrl();
+ string GetApiUrlForLocalAccess(bool allowHttps = true);
/// <summary>
/// Gets a local (LAN) URL that can be used to access the API.
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index e6511ca8d..7d62fb6e1 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -101,32 +101,6 @@ namespace MediaBrowser.Controller.MediaEncoding
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, string outputExtension, CancellationToken cancellationToken);
/// <summary>
- /// Extracts the video images on interval.
- /// </summary>
- /// <param name="inputFile">Input file.</param>
- /// <param name="container">Video container type.</param>
- /// <param name="videoStream">Media stream information.</param>
- /// <param name="mediaSource">Media source information.</param>
- /// <param name="threedFormat">Video 3D format.</param>
- /// <param name="interval">Time interval.</param>
- /// <param name="targetDirectory">Directory to write images.</param>
- /// <param name="filenamePrefix">Filename prefix to use.</param>
- /// <param name="maxWidth">Maximum width of image.</param>
- /// <param name="cancellationToken">CancellationToken to use for operation.</param>
- /// <returns>A task.</returns>
- Task ExtractVideoImagesOnInterval(
- string inputFile,
- string container,
- MediaStream videoStream,
- MediaSourceInfo mediaSource,
- Video3DFormat? threedFormat,
- TimeSpan interval,
- string targetDirectory,
- string filenamePrefix,
- int? maxWidth,
- CancellationToken cancellationToken);
-
- /// <summary>
/// Gets the media info.
/// </summary>
/// <param name="request">The request.</param>
diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
index 2ac4c728b..08d129a82 100644
--- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CA1819, CS1591
using System;
diff --git a/MediaBrowser.Controller/Providers/ItemInfo.cs b/MediaBrowser.Controller/Providers/ItemInfo.cs
index b8dd416a2..3a97127ea 100644
--- a/MediaBrowser.Controller/Providers/ItemInfo.cs
+++ b/MediaBrowser.Controller/Providers/ItemInfo.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
index 80eb45423..777fe6774 100644
--- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -149,7 +149,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
}
else
{
- Logger.LogWarning("Invalid Added value found: " + val);
+ Logger.LogWarning("Invalid Added value found: {Value}", val);
}
}
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index a524aeaa9..9ebc0d0cf 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -223,11 +223,10 @@ namespace MediaBrowser.MediaEncoding.Attachments
if (failed)
{
- var msg = $"ffmpeg attachment extraction failed for {inputPath} to {outputPath}";
+ _logger.LogError("ffmpeg attachment extraction failed for {InputPath} to {OutputPath}", inputPath, outputPath);
- _logger.LogError(msg);
-
- throw new InvalidOperationException(msg);
+ throw new InvalidOperationException(
+ string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputPath));
}
else
{
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index fbc7ba72f..a2bac7b49 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -682,11 +682,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (exitCode == -1 || !file.Exists || file.Length == 0)
{
- var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath);
+ _logger.LogError("ffmpeg image extraction failed for {Path}", inputPath);
- _logger.LogError(msg);
-
- throw new FfmpegException(msg);
+ throw new FfmpegException(string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputPath));
}
return tempExtractPath;
@@ -705,117 +703,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
return time.ToString(@"hh\:mm\:ss\.fff", CultureInfo.InvariantCulture);
}
- public async Task ExtractVideoImagesOnInterval(
- string inputFile,
- string container,
- MediaStream videoStream,
- MediaSourceInfo mediaSource,
- Video3DFormat? threedFormat,
- TimeSpan interval,
- string targetDirectory,
- string filenamePrefix,
- int? maxWidth,
- CancellationToken cancellationToken)
- {
- var inputArgument = GetInputArgument(inputFile, mediaSource);
-
- var vf = "fps=fps=1/" + interval.TotalSeconds.ToString(CultureInfo.InvariantCulture);
-
- if (maxWidth.HasValue)
- {
- var maxWidthParam = maxWidth.Value.ToString(CultureInfo.InvariantCulture);
-
- vf += string.Format(CultureInfo.InvariantCulture, ",scale=min(iw\\,{0}):trunc(ow/dar/2)*2", maxWidthParam);
- }
-
- Directory.CreateDirectory(targetDirectory);
- var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
-
- var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, _threads);
-
- if (!string.IsNullOrWhiteSpace(container))
- {
- var inputFormat = EncodingHelper.GetInputFormat(container);
- if (!string.IsNullOrWhiteSpace(inputFormat))
- {
- args = "-f " + inputFormat + " " + args;
- }
- }
-
- var processStartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
- FileName = _ffmpegPath,
- Arguments = args,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- };
-
- _logger.LogInformation(processStartInfo.FileName + " " + processStartInfo.Arguments);
-
- await _thumbnailResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- bool ranToCompletion = false;
-
- var process = new Process
- {
- StartInfo = processStartInfo,
- EnableRaisingEvents = true
- };
- using (var processWrapper = new ProcessWrapper(process, this))
- {
- try
- {
- StartProcess(processWrapper);
-
- // Need to give ffmpeg enough time to make all the thumbnails, which could be a while,
- // but we still need to detect if the process hangs.
- // Making the assumption that as long as new jpegs are showing up, everything is good.
-
- bool isResponsive = true;
- int lastCount = 0;
-
- while (isResponsive)
- {
- if (await process.WaitForExitAsync(TimeSpan.FromSeconds(30)).ConfigureAwait(false))
- {
- ranToCompletion = true;
- break;
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var jpegCount = _fileSystem.GetFilePaths(targetDirectory)
- .Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));
-
- isResponsive = jpegCount > lastCount;
- lastCount = jpegCount;
- }
-
- if (!ranToCompletion)
- {
- StopProcess(processWrapper, 1000);
- }
- }
- finally
- {
- _thumbnailResourcePool.Release();
- }
-
- var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
-
- if (exitCode == -1)
- {
- var msg = string.Format(CultureInfo.InvariantCulture, "ffmpeg image extraction failed for {0}", inputArgument);
-
- _logger.LogError(msg);
-
- throw new FfmpegException(msg);
- }
- }
- }
-
private void StartProcess(ProcessWrapper process)
{
process.Process.Start();
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 2b2de2ff6..89365a516 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -636,17 +636,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
if (failed)
{
- var msg = $"ffmpeg subtitle extraction failed for {inputPath} to {outputPath}";
+ _logger.LogError("ffmpeg subtitle extraction failed for {InputPath} to {OutputPath}", inputPath, outputPath);
- _logger.LogError(msg);
-
- throw new FfmpegException(msg);
+ throw new FfmpegException(
+ string.Format(CultureInfo.InvariantCulture, "ffmpeg subtitle extraction failed for {0} to {1}", inputPath, outputPath));
}
else
{
- var msg = $"ffmpeg subtitle extraction completed for {inputPath} to {outputPath}";
-
- _logger.LogInformation(msg);
+ _logger.LogInformation("ffmpeg subtitle extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath);
}
if (string.Equals(outputCodec, "ass", StringComparison.OrdinalIgnoreCase))
diff --git a/MediaBrowser.Model/MediaInfo/AudioCodec.cs b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
index 8b17757b8..7b83b1b9d 100644
--- a/MediaBrowser.Model/MediaInfo/AudioCodec.cs
+++ b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
@@ -1,13 +1,11 @@
#pragma warning disable CS1591
+using System;
+
namespace MediaBrowser.Model.MediaInfo
{
public static class AudioCodec
{
- public const string AAC = "aac";
- public const string MP3 = "mp3";
- public const string AC3 = "ac3";
-
public static string GetFriendlyName(string codec)
{
if (codec.Length == 0)
@@ -15,17 +13,20 @@ namespace MediaBrowser.Model.MediaInfo
return codec;
}
- switch (codec.ToLowerInvariant())
+ if (string.Equals(codec, "ac3", StringComparison.OrdinalIgnoreCase))
+ {
+ return "Dolby Digital";
+ }
+ else if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
{
- case "ac3":
- return "Dolby Digital";
- case "eac3":
- return "Dolby Digital+";
- case "dca":
- return "DTS";
- default:
- return codec.ToUpperInvariant();
+ return "Dolby Digital+";
}
+ else if (string.Equals(codec, "dca", StringComparison.OrdinalIgnoreCase))
+ {
+ return "DTS";
+ }
+
+ return codec.ToUpperInvariant();
}
}
}
diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
index 36a240706..24eab1a74 100644
--- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs
@@ -16,22 +16,6 @@ namespace MediaBrowser.Model.MediaInfo
DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
}
- public LiveStreamRequest(AudioOptions options)
- {
- MaxStreamingBitrate = options.MaxBitrate;
- ItemId = options.ItemId;
- DeviceProfile = options.Profile;
- MaxAudioChannels = options.MaxAudioChannels;
-
- DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
-
- if (options is VideoOptions videoOptions)
- {
- AudioStreamIndex = videoOptions.AudioStreamIndex;
- SubtitleStreamIndex = videoOptions.SubtitleStreamIndex;
- }
- }
-
public string OpenToken { get; set; }
public Guid UserId { get; set; }
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
deleted file mode 100644
index ecd9b8834..000000000
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Model.MediaInfo
-{
- public class PlaybackInfoRequest
- {
- public PlaybackInfoRequest()
- {
- EnableDirectPlay = true;
- EnableDirectStream = true;
- EnableTranscoding = true;
- AllowVideoStreamCopy = true;
- AllowAudioStreamCopy = true;
- IsPlayback = true;
- DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
- }
-
- public Guid Id { get; set; }
-
- public Guid UserId { get; set; }
-
- public long? MaxStreamingBitrate { get; set; }
-
- public long? StartTimeTicks { get; set; }
-
- public int? AudioStreamIndex { get; set; }
-
- public int? SubtitleStreamIndex { get; set; }
-
- public int? MaxAudioChannels { get; set; }
-
- public string MediaSourceId { get; set; }
-
- public string LiveStreamId { get; set; }
-
- public DeviceProfile DeviceProfile { get; set; }
-
- public bool EnableDirectPlay { get; set; }
-
- public bool EnableDirectStream { get; set; }
-
- public bool EnableTranscoding { get; set; }
-
- public bool AllowVideoStreamCopy { get; set; }
-
- public bool AllowAudioStreamCopy { get; set; }
-
- public bool IsPlayback { get; set; }
-
- public bool AutoOpenLiveStream { get; set; }
-
- public MediaProtocol[] DirectPlayProtocols { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
index 2bd45695a..9bc5c31f6 100644
--- a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
+++ b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
@@ -8,8 +8,6 @@ namespace MediaBrowser.Model.MediaInfo
public const string SSA = "ssa";
public const string ASS = "ass";
public const string VTT = "vtt";
- public const string SUB = "sub";
- public const string SMI = "smi";
public const string TTML = "ttml";
}
}
diff --git a/MediaBrowser.Model/Net/NetworkShareType.cs b/MediaBrowser.Model/Net/NetworkShareType.cs
deleted file mode 100644
index 5d985f85d..000000000
--- a/MediaBrowser.Model/Net/NetworkShareType.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Enum NetworkShareType.
- /// </summary>
- public enum NetworkShareType
- {
- /// <summary>
- /// Disk share.
- /// </summary>
- Disk,
-
- /// <summary>
- /// Printer share.
- /// </summary>
- Printer,
-
- /// <summary>
- /// Device share.
- /// </summary>
- Device,
-
- /// <summary>
- /// IPC share.
- /// </summary>
- Ipc,
-
- /// <summary>
- /// Special share.
- /// </summary>
- Special
- }
-}
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
index 2c893ac9f..3011d65a6 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
@@ -103,7 +103,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
else
{
- Logger.LogInformation("Unrecognized series status: " + status);
+ Logger.LogInformation("Unrecognized series status: {Status}", status);
}
}
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 3bced438c..e14c1c427 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -44,9 +44,13 @@
<Rule Id="CA1725" Action="Error" />
<!-- error on CA1725: Call async methods when in an async method -->
<Rule Id="CA1727" Action="Error" />
+ <!-- error on CA1843: Do not use 'WaitAll' with a single task -->
+ <Rule Id="CA1843" Action="Error" />
<!-- error on CA2016: Forward the CancellationToken parameter to methods that take one
or pass in 'CancellationToken.None' explicitly to indicate intentionally not propagating the token -->
<Rule Id="CA2016" Action="Error" />
+ <!-- error on CA2254: Template should be a static expression -->
+ <Rule Id="CA2254" Action="Error" />
<!-- disable warning CA1014: Mark assemblies with CLSCompliantAttribute -->
<Rule Id="CA1014" Action="Info" />
diff --git a/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs b/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs
index 1cad625b7..61f913252 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs
+++ b/tests/Jellyfin.Networking.Tests/NetworkManagerTests.cs
@@ -34,7 +34,7 @@ namespace Jellyfin.Networking.Tests
}
/// <summary>
- /// Checks that thge given IP address is not in the network provided.
+ /// Checks that the given IP address is not in the network provided.
/// </summary>
/// <param name="network">Network address(es).</param>
/// <param name="value">The IP to check.</param>
diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
index a24eee693..6b9397437 100644
--- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
+++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs
@@ -35,9 +35,9 @@ namespace Jellyfin.Networking.Tests
// eth16 only
[InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")]
// All interfaces excluded. (including loopbacks)
- [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[127.0.0.1/8,::1/128]")]
+ [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")]
// vEthernet1 and vEthernet212 should be excluded.
- [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24,127.0.0.1/8,::1/128]")]
+ [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")]
// Overlapping interface,
[InlineData("192.168.1.110/24,-20,br0|192.168.1.10/24,-16,br0|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.110/24,192.168.1.10/24]")]
public void IgnoreVirtualInterfaces(string interfaces, string lan, string value)
@@ -476,5 +476,51 @@ namespace Jellyfin.Networking.Tests
Assert.NotEqual(nm.HasRemoteAccess(IPAddress.Parse(remoteIp)), denied);
}
+
+ [Theory]
+ [InlineData("192.168.1.209/24,-16,eth16", "192.168.1.0/24", "", "192.168.1.209")] // Only 1 address so use it.
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "192.168.1.208")] // LAN address is specified by default.
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "10.0.0.1", "10.0.0.1")] // return bind address
+
+ public void GetBindInterface_NoSourceGiven_Success(string interfaces, string lan, string bind, string result)
+ {
+ var conf = new NetworkConfiguration
+ {
+ EnableIPV4 = true,
+ LocalNetworkSubnets = lan.Split(','),
+ LocalNetworkAddresses = bind.Split(',')
+ };
+
+ NetworkManager.MockNetworkSettings = interfaces;
+ using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
+
+ var interfaceToUse = nm.GetBindInterface(string.Empty, out _);
+
+ Assert.Equal(result, interfaceToUse);
+ }
+
+ [Theory]
+ [InlineData("192.168.1.209/24,-16,eth16", "192.168.1.0/24", "", "192.168.1.210", "192.168.1.209")] // Source on LAN
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "192.168.1.209", "192.168.1.208")] // Source on LAN
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "", "8.8.8.8", "10.0.0.1")] // Source external.
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "10.0.0.1", "192.168.1.209", "10.0.0.1")] // LAN not bound, so return external.
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "8.8.8.8", "10.0.0.1")] // return external bind address
+ [InlineData("192.168.1.208/24,-16,eth16|10.0.0.1/24,10,eth7", "192.168.1.0/24", "192.168.1.208,10.0.0.1", "192.168.1.210", "192.168.1.208")] // return LAN bind address
+ public void GetBindInterface_ValidSourceGiven_Success(string interfaces, string lan, string bind, string source, string result)
+ {
+ var conf = new NetworkConfiguration
+ {
+ EnableIPV4 = true,
+ LocalNetworkSubnets = lan.Split(','),
+ LocalNetworkAddresses = bind.Split(',')
+ };
+
+ NetworkManager.MockNetworkSettings = interfaces;
+ using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger<NetworkManager>());
+
+ var interfaceToUse = nm.GetBindInterface(source, out _);
+
+ Assert.Equal(result, interfaceToUse);
+ }
}
}
diff --git a/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
index 9f73ed7fc..2ba5c47d7 100644
--- a/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
+++ b/tests/Jellyfin.Providers.Tests/Manager/ItemImageProviderTests.cs
@@ -231,12 +231,12 @@ namespace Jellyfin.Providers.Tests.Manager
.ReturnsAsync(imageResponse);
var refreshOptions = forceRefresh
- ? new ImageRefreshOptions(null)
+ ? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
{
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllImages = true
}
- : new ImageRefreshOptions(null);
+ : new ImageRefreshOptions(Mock.Of<IDirectoryService>());
var itemImageProvider = GetItemImageProvider(null, new Mock<IFileSystem>());
var result = await itemImageProvider.RefreshImages(item, libraryOptions, new List<IImageProvider> { dynamicProvider.Object }, refreshOptions, CancellationToken.None);
@@ -286,7 +286,7 @@ namespace Jellyfin.Providers.Tests.Manager
dynamicProvider.Setup(rp => rp.GetImage(item, imageType, It.IsAny<CancellationToken>()))
.ReturnsAsync(imageResponse);
- var refreshOptions = new ImageRefreshOptions(null);
+ var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>());
var providerManager = new Mock<IProviderManager>(MockBehavior.Strict);
providerManager.Setup(pm => pm.SaveImage(item, It.IsAny<Stream>(), It.IsAny<string>(), imageType, null, It.IsAny<CancellationToken>()))
@@ -323,12 +323,12 @@ namespace Jellyfin.Providers.Tests.Manager
.Returns(new[] { imageType });
var refreshOptions = forceRefresh
- ? new ImageRefreshOptions(null)
+ ? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
{
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllImages = true
}
- : new ImageRefreshOptions(null);
+ : new ImageRefreshOptions(Mock.Of<IDirectoryService>());
var remoteInfo = new RemoteImageInfo[imageCount];
for (int i = 0; i < imageCount; i++)
@@ -393,12 +393,12 @@ namespace Jellyfin.Providers.Tests.Manager
});
var refreshOptions = fullRefresh
- ? new ImageRefreshOptions(null)
+ ? new ImageRefreshOptions(Mock.Of<IDirectoryService>())
{
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllImages = true
}
- : new ImageRefreshOptions(null);
+ : new ImageRefreshOptions(Mock.Of<IDirectoryService>());
var remoteInfo = new RemoteImageInfo[targetImageCount];
for (int i = 0; i < targetImageCount; i++)
@@ -442,7 +442,7 @@ namespace Jellyfin.Providers.Tests.Manager
remoteProvider.Setup(rp => rp.GetSupportedImages(item))
.Returns(new[] { imageType });
- var refreshOptions = new ImageRefreshOptions(null);
+ var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>());
// populate remote with double the required images to verify count is trimmed to the library option count
var remoteInfoCount = imageCount * 2;
@@ -487,7 +487,7 @@ namespace Jellyfin.Providers.Tests.Manager
remoteProvider.Setup(rp => rp.GetSupportedImages(item))
.Returns(new[] { imageType });
- var refreshOptions = new ImageRefreshOptions(null)
+ var refreshOptions = new ImageRefreshOptions(Mock.Of<IDirectoryService>())
{
ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllImages = true