aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs26
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs8
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json4
-rw-r--r--Jellyfin.Api/Controllers/DevicesController.cs6
-rw-r--r--Jellyfin.Api/Controllers/LiveTvController.cs7
-rw-r--r--Jellyfin.Api/Controllers/SuggestionsController.cs3
-rw-r--r--Jellyfin.Api/Helpers/ProgressiveFileStream.cs12
-rw-r--r--Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs2
-rw-r--r--MediaBrowser.Controller/Library/IMediaSourceManager.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs2
12 files changed, 55 insertions, 28 deletions
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 6730751d5..858c10030 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -131,6 +131,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
await taskCompletionSource.Task.ConfigureAwait(false);
}
+ public string GetFilePath()
+ {
+ return TempFilePath;
+ }
+
private Task StartStreaming(UdpClient udpClient, HdHomerunManager hdHomerunManager, IPAddress remoteAddress, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
return Task.Run(async () =>
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 8107bc427..4b170b2e4 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -65,7 +65,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var channelIdPrefix = GetFullChannelIdPrefix(info);
- return await new M3uParser(Logger, _httpClientFactory, _appHost).Parse(info.Url, channelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
+ return await new M3uParser(Logger, _httpClientFactory, _appHost)
+ .Parse(info, channelIdPrefix, cancellationToken)
+ .ConfigureAwait(false);
}
public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
@@ -126,7 +128,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task Validate(TunerHostInfo info)
{
- using (var stream = await new M3uParser(Logger, _httpClientFactory, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
+ using (var stream = await new M3uParser(Logger, _httpClientFactory, _appHost).GetListingsStream(info, CancellationToken.None).ConfigureAwait(false))
{
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index f066a749e..c064e2fe6 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -13,6 +13,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.LiveTv;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.TunerHosts
@@ -30,12 +31,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
_appHost = appHost;
}
- public async Task<List<ChannelInfo>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
+ public async Task<List<ChannelInfo>> Parse(TunerHostInfo info, string channelIdPrefix, CancellationToken cancellationToken)
{
// Read the file and display it line by line.
- using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
+ using (var reader = new StreamReader(await GetListingsStream(info, cancellationToken).ConfigureAwait(false)))
{
- return GetChannels(reader, channelIdPrefix, tunerHostId);
+ return GetChannels(reader, channelIdPrefix, info.Id);
}
}
@@ -48,15 +49,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
- public Task<Stream> GetListingsStream(string url, CancellationToken cancellationToken)
+ public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
{
- if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
- return _httpClientFactory.CreateClient(NamedClient.Default)
- .GetStreamAsync(url);
+ using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
+ if (!string.IsNullOrEmpty(info.UserAgent))
+ {
+ requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
+ }
+
+ var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .SendAsync(requestMessage, cancellationToken)
+ .ConfigureAwait(false);
+ response.EnsureSuccessStatusCode();
+ return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
}
- return Task.FromResult((Stream)File.OpenRead(url));
+ return File.OpenRead(info.Url);
}
private const string ExtInfPrefix = "#EXTINF:";
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index 6c10fca8c..2e1b89509 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -55,7 +55,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var typeName = GetType().Name;
Logger.LogInformation("Opening " + typeName + " Live stream from {0}", url);
- using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ // Response stream is disposed manually.
+ var response = await _httpClientFactory.CreateClient(NamedClient.Default)
.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, CancellationToken.None)
.ConfigureAwait(false);
@@ -121,6 +122,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
+ public string GetFilePath()
+ {
+ return TempFilePath;
+ }
+
private Task StartStreaming(HttpResponseMessage response, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
{
return Task.Run(async () =>
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index f4ca8575a..cc9243f37 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -113,5 +113,7 @@
"TaskCleanCache": "Vider le répertoire cache",
"TasksApplicationCategory": "Application",
"TasksLibraryCategory": "Bibliothèque",
- "TasksMaintenanceCategory": "Maintenance"
+ "TasksMaintenanceCategory": "Maintenance",
+ "TaskCleanActivityLogDescription": "Supprime les entrées du journal d'activité antérieures à l'âge configuré.",
+ "TaskCleanActivityLog": "Nettoyer le journal d'activité"
}
diff --git a/Jellyfin.Api/Controllers/DevicesController.cs b/Jellyfin.Api/Controllers/DevicesController.cs
index 74380c2ef..b3e3490c2 100644
--- a/Jellyfin.Api/Controllers/DevicesController.cs
+++ b/Jellyfin.Api/Controllers/DevicesController.cs
@@ -15,7 +15,7 @@ namespace Jellyfin.Api.Controllers
/// <summary>
/// Devices Controller.
/// </summary>
- [Authorize(Policy = Policies.DefaultAuthorization)]
+ [Authorize(Policy = Policies.RequiresElevation)]
public class DevicesController : BaseJellyfinApiController
{
private readonly IDeviceManager _deviceManager;
@@ -46,7 +46,6 @@ namespace Jellyfin.Api.Controllers
/// <response code="200">Devices retrieved.</response>
/// <returns>An <see cref="OkResult"/> containing the list of devices.</returns>
[HttpGet]
- [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<DeviceInfo>> GetDevices([FromQuery] bool? supportsSync, [FromQuery] Guid? userId)
{
@@ -62,7 +61,6 @@ namespace Jellyfin.Api.Controllers
/// <response code="404">Device not found.</response>
/// <returns>An <see cref="OkResult"/> containing the device info on success, or a <see cref="NotFoundResult"/> if the device could not be found.</returns>
[HttpGet("Info")]
- [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<DeviceInfo> GetDeviceInfo([FromQuery, Required] string id)
@@ -84,7 +82,6 @@ namespace Jellyfin.Api.Controllers
/// <response code="404">Device not found.</response>
/// <returns>An <see cref="OkResult"/> containing the device info on success, or a <see cref="NotFoundResult"/> if the device could not be found.</returns>
[HttpGet("Options")]
- [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<DeviceOptions> GetDeviceOptions([FromQuery, Required] string id)
@@ -107,7 +104,6 @@ namespace Jellyfin.Api.Controllers
/// <response code="404">Device not found.</response>
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the device could not be found.</returns>
[HttpPost("Options")]
- [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult UpdateDeviceOptions(
diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs
index f89c31e8b..88a7542ce 100644
--- a/Jellyfin.Api/Controllers/LiveTvController.cs
+++ b/Jellyfin.Api/Controllers/LiveTvController.cs
@@ -1220,11 +1220,8 @@ namespace Jellyfin.Api.Controllers
return NotFound();
}
- await using var memoryStream = new MemoryStream();
- await new ProgressiveFileCopier(liveStreamInfo, null, _transcodingJobHelper, CancellationToken.None)
- .WriteToAsync(memoryStream, CancellationToken.None)
- .ConfigureAwait(false);
- return File(memoryStream, MimeTypes.GetMimeType("file." + container));
+ var liveStream = new ProgressiveFileStream(liveStreamInfo.GetFilePath(), null, _transcodingJobHelper);
+ return new FileStreamResult(liveStream, MimeTypes.GetMimeType("file." + container));
}
private void AssertUserCanManageLiveTv()
diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs
index d7c81a3ab..ad64adfba 100644
--- a/Jellyfin.Api/Controllers/SuggestionsController.cs
+++ b/Jellyfin.Api/Controllers/SuggestionsController.cs
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
+using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Data.Enums;
@@ -9,6 +10,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Querying;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -18,6 +20,7 @@ namespace Jellyfin.Api.Controllers
/// The suggestions controller.
/// </summary>
[Route("")]
+ [Authorize(Policy = Policies.DefaultAuthorization)]
public class SuggestionsController : BaseJellyfinApiController
{
private readonly IDtoService _dtoService;
diff --git a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
index b3566b6f8..824870c7e 100644
--- a/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
+++ b/Jellyfin.Api/Helpers/ProgressiveFileStream.cs
@@ -82,20 +82,23 @@ namespace Jellyfin.Api.Helpers
int totalBytesRead = 0;
int remainingBytesToRead = count;
+ int newOffset = offset;
while (remainingBytesToRead > 0)
{
cancellationToken.ThrowIfCancellationRequested();
int bytesRead;
if (_allowAsyncFileRead)
{
- bytesRead = await _fileStream.ReadAsync(buffer, offset, remainingBytesToRead, cancellationToken).ConfigureAwait(false);
+ bytesRead = await _fileStream.ReadAsync(buffer, newOffset, remainingBytesToRead, cancellationToken).ConfigureAwait(false);
}
else
{
- bytesRead = _fileStream.Read(buffer, offset, remainingBytesToRead);
+ bytesRead = _fileStream.Read(buffer, newOffset, remainingBytesToRead);
}
remainingBytesToRead -= bytesRead;
+ newOffset += bytesRead;
+
if (bytesRead > 0)
{
_bytesWritten += bytesRead;
@@ -108,12 +111,13 @@ namespace Jellyfin.Api.Helpers
}
else
{
- if (_job == null || _job.HasExited)
+ // If the job is null it's a live stream and will require user action to close
+ if (_job?.HasExited ?? false)
{
break;
}
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+ await Task.Delay(50, cancellationToken).ConfigureAwait(false);
}
}
diff --git a/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs b/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs
index 208566dc8..4f012cab2 100644
--- a/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs
+++ b/Jellyfin.Api/ModelBinders/CommaDelimitedArrayModelBinder.cs
@@ -15,7 +15,7 @@ namespace Jellyfin.Api.ModelBinders
public Task BindModelAsync(ModelBindingContext bindingContext)
{
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
- var elementType = bindingContext.ModelType.GetElementType();
+ var elementType = bindingContext.ModelType.GetElementType() ?? bindingContext.ModelType.GenericTypeArguments[0];
var converter = TypeDescriptor.GetConverter(elementType);
if (valueProviderResult.Length > 1)
diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
index 22bf9488f..21c6ef2af 100644
--- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs
+++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs
@@ -115,5 +115,7 @@ namespace MediaBrowser.Controller.Library
public interface IDirectStreamProvider
{
Task CopyToAsync(Stream stream, CancellationToken cancellationToken);
+
+ string GetFilePath();
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 33256e4bf..5846a603a 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -3085,7 +3085,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- var whichCodec = videoStream.Codec.ToLowerInvariant();
+ var whichCodec = videoStream.Codec?.ToLowerInvariant();
switch (whichCodec)
{
case "avc":