aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/LiveTv
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2022-12-03 12:44:45 +0100
committerShadowghost <Ghost_of_Stone@web.de>2022-12-03 12:44:45 +0100
commitbcd992fb0604839c18a628850df095777d6cf6f9 (patch)
treeabd5da0cd5ff6a7043f00969b912a00067f344f7 /Emby.Server.Implementations/LiveTv
parent36994c17bf5f71f37a5002a51840306fa09fb0ef (diff)
parentdb2c0d4c91a952407ab7709d9ff5c86017e5753e (diff)
Merge remote-tracking branch 'upstream/master' into network-rewrite
Diffstat (limited to 'Emby.Server.Implementations/LiveTv')
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs53
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs23
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs15
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs119
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs2
5 files changed, 78 insertions, 134 deletions
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 4da6776369..cf9be5a54a 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -2192,16 +2192,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void HandleDuplicateShowIds(List<TimerInfo> timers)
{
- foreach (var timer in timers.Skip(1))
+ // sort showings by HD channels first, then by startDate, record earliest showing possible
+ foreach (var timer in timers.OrderByDescending(t => _liveTvManager.GetLiveTvChannel(t, this).IsHD).ThenBy(t => t.StartDate).Skip(1))
{
- // TODO: Get smarter, prefer HD, etc
-
timer.Status = RecordingStatus.Cancelled;
_timerProvider.Update(timer);
}
}
- private void SearchForDuplicateShowIds(List<TimerInfo> timers)
+ private void SearchForDuplicateShowIds(IEnumerable<TimerInfo> timers)
{
var groups = timers.ToLookup(i => i.ShowId ?? string.Empty).ToList();
@@ -2219,6 +2218,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
continue;
}
+ // Skip ShowId without SubKey from duplicate removal actions - https://github.com/jellyfin/jellyfin/issues/5856
+ if (group.Key.EndsWith("0000", StringComparison.Ordinal))
+ {
+ continue;
+ }
+
HandleDuplicateShowIds(groupTimers);
}
}
@@ -2276,39 +2281,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (updateTimerSettings)
{
- // Only update if not currently active - test both new timer and existing in case Id's are different
- // Id's could be different if the timer was created manually prior to series timer creation
- if (!_activeRecordings.TryGetValue(timer.Id, out _) && !_activeRecordings.TryGetValue(existingTimer.Id, out _))
- {
- UpdateExistingTimerWithNewMetadata(existingTimer, timer);
-
- // Needed by ShouldCancelTimerForSeriesTimer
- timer.IsManual = existingTimer.IsManual;
-
- if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
- {
- existingTimer.Status = RecordingStatus.Cancelled;
- }
- else if (!existingTimer.IsManual)
- {
- existingTimer.Status = RecordingStatus.New;
- }
-
- if (existingTimer.Status != RecordingStatus.Cancelled)
- {
- enabledTimersForSeries.Add(existingTimer);
- }
-
- existingTimer.KeepUntil = seriesTimer.KeepUntil;
- existingTimer.IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired;
- existingTimer.IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired;
- existingTimer.PostPaddingSeconds = seriesTimer.PostPaddingSeconds;
- existingTimer.PrePaddingSeconds = seriesTimer.PrePaddingSeconds;
- existingTimer.Priority = seriesTimer.Priority;
- existingTimer.SeriesTimerId = seriesTimer.Id;
-
- _timerProvider.Update(existingTimer);
- }
+ existingTimer.KeepUntil = seriesTimer.KeepUntil;
+ existingTimer.IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired;
+ existingTimer.IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired;
+ existingTimer.PostPaddingSeconds = seriesTimer.PostPaddingSeconds;
+ existingTimer.PrePaddingSeconds = seriesTimer.PrePaddingSeconds;
+ existingTimer.Priority = seriesTimer.Priority;
+ existingTimer.SeriesTimerId = seriesTimer.Id;
}
existingTimer.SeriesTimerId = seriesTimer.Id;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index a861e6ae44..f612565d1b 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -122,11 +122,28 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (_timers.TryAdd(item.Id, timer))
{
- Logger.LogInformation(
- "Creating recording timer for {Id}, {Name}. Timer will fire in {Minutes} minutes",
+ if (item.IsSeries)
+ {
+ Logger.LogInformation(
+ "Creating recording timer for {Id}, {Name} {SeasonNumber}x{EpisodeNumber:D2} on channel {ChannelId}. Timer will fire in {Minutes} minutes at {StartDate}",
item.Id,
item.Name,
- dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+ item.SeasonNumber,
+ item.EpisodeNumber,
+ item.ChannelId,
+ dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture),
+ item.StartDate);
+ }
+ else
+ {
+ Logger.LogInformation(
+ "Creating recording timer for {Id}, {Name} on channel {ChannelId}. Timer will fire in {Minutes} minutes at {StartDate}",
+ item.Id,
+ item.Name,
+ item.ChannelId,
+ dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture),
+ item.StartDate);
+ }
}
else
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 4311db28d2..b981ad81a7 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -166,12 +166,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
const double DesiredAspect = 2.0 / 3;
- programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, DesiredAspect) ??
- GetProgramImage(ApiUrl, allImages, DesiredAspect);
+ programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, DesiredAspect, token) ??
+ GetProgramImage(ApiUrl, allImages, DesiredAspect, token);
const double WideAspect = 16.0 / 9;
- programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, WideAspect);
+ programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, WideAspect, token);
// Don't supply the same image twice
if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal))
@@ -179,7 +179,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programEntry.ThumbImage = null;
}
- programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, WideAspect);
+ programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, WideAspect, token);
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
@@ -400,7 +400,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return info;
}
- private static string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, double desiredAspect)
+ private static string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, double desiredAspect, string token)
{
var match = images
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
@@ -424,7 +424,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
else
{
- return apiUrl + "/image/" + uri;
+ return apiUrl + "/image/" + uri + "?token=" + token;
}
}
@@ -458,6 +458,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
IReadOnlyList<string> programIds,
CancellationToken cancellationToken)
{
+ var token = await GetToken(info, cancellationToken).ConfigureAwait(false);
+
if (programIds.Count == 0)
{
return Array.Empty<ShowImagesDto>();
@@ -479,6 +481,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
Content = new StringContent(str.ToString(), Encoding.UTF8, MediaTypeNames.Application.Json)
};
+ message.Headers.TryAddWithoutValidation("token", token);
try
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index bd1cd1e1de..82f0baf32e 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -6,9 +6,9 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.IO.Compression;
using System.Linq;
using System.Net.Http;
-using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions;
@@ -32,21 +32,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly IServerConfigurationManager _config;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<XmlTvListingsProvider> _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IZipClient _zipClient;
public XmlTvListingsProvider(
IServerConfigurationManager config,
IHttpClientFactory httpClientFactory,
- ILogger<XmlTvListingsProvider> logger,
- IFileSystem fileSystem,
- IZipClient zipClient)
+ ILogger<XmlTvListingsProvider> logger)
{
_config = config;
_httpClientFactory = httpClientFactory;
_logger = logger;
- _fileSystem = fileSystem;
- _zipClient = zipClient;
}
public string Name => "XmlTV";
@@ -67,16 +61,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
_logger.LogInformation("xmltv path: {Path}", info.Path);
- if (!info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return UnzipIfNeeded(info.Path, info.Path);
- }
-
string cacheFilename = info.Id + ".xml";
string cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename);
+
if (File.Exists(cacheFile) && File.GetLastWriteTimeUtc(cacheFile) >= DateTime.UtcNow.Subtract(_maxCacheAge))
{
- return UnzipIfNeeded(info.Path, cacheFile);
+ return cacheFile;
}
// Must check if file exists as parent directory may not exist.
@@ -84,93 +74,48 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
File.Delete(cacheFile);
}
+ else
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
+ }
- _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path);
-
- Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
+ if (info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path);
- using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false);
- await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous))
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false);
+ }
+ else
{
- await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
+ await using var stream = AsyncFile.OpenRead(info.Path);
+ return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false);
}
-
- return UnzipIfNeeded(info.Path, cacheFile);
}
- private string UnzipIfNeeded(ReadOnlySpan<char> originalUrl, string file)
+ private async Task<string> UnzipIfNeededAndCopy(string originalUrl, Stream stream, string file, CancellationToken cancellationToken)
{
- ReadOnlySpan<char> ext = Path.GetExtension(originalUrl.LeftPart('?'));
+ await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
- if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase))
+ if (Path.GetExtension(originalUrl.AsSpan().LeftPart('?')).Equals(".gz", StringComparison.OrdinalIgnoreCase))
{
try
{
- string tempFolder = ExtractGz(file);
- return FindXmlFile(tempFolder);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error extracting from gz file {File}", file);
- }
-
- try
- {
- string tempFolder = ExtractFirstFileFromGz(file);
- return FindXmlFile(tempFolder);
+ using var reader = new GZipStream(stream, CompressionMode.Decompress);
+ await reader.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
- _logger.LogError(ex, "Error extracting from zip file {File}", file);
+ _logger.LogError(ex, "Error extracting from gz file {File}", originalUrl);
}
}
-
- return file;
- }
-
- private string ExtractFirstFileFromGz(string file)
- {
- using (var stream = File.OpenRead(file))
- {
- string tempFolder = GetTempFolderPath(stream);
- Directory.CreateDirectory(tempFolder);
-
- _zipClient.ExtractFirstFileFromGz(stream, tempFolder, "data.xml");
-
- return tempFolder;
- }
- }
-
- private string ExtractGz(string file)
- {
- using (var stream = File.OpenRead(file))
+ else
{
- string tempFolder = GetTempFolderPath(stream);
- Directory.CreateDirectory(tempFolder);
-
- _zipClient.ExtractAllFromGz(stream, tempFolder, true);
-
- return tempFolder;
+ await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
}
- }
- private string GetTempFolderPath(Stream stream)
- {
-#pragma warning disable CA5351
- using var md5 = MD5.Create();
-#pragma warning restore CA5351
- var checksum = Convert.ToHexString(md5.ComputeHash(stream));
- stream.Position = 0;
- return Path.Combine(_config.ApplicationPaths.TempDirectory, checksum);
- }
-
- private string FindXmlFile(string directory)
- {
- return _fileSystem.GetFiles(directory, true)
- .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
- .Select(i => i.FullName)
- .FirstOrDefault();
+ return file;
}
public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
@@ -213,16 +158,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings
IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
- ImageUrl = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source) ? program.Icon.Source : null,
- HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source),
- OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null,
+ ImageUrl = string.IsNullOrEmpty(program.Icon?.Source) ? null : program.Icon.Source,
+ HasImage = !string.IsNullOrEmpty(program.Icon?.Source),
+ OfficialRating = string.IsNullOrEmpty(program.Rating?.Value) ? null : program.Rating.Value,
CommunityRating = program.StarRating,
- SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture)
+ SeriesId = program.Episode == null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture)
};
if (string.IsNullOrWhiteSpace(program.ProgramId))
{
- string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty) /*+ (p.IceTvEpisodeNumber ?? string.Empty)*/;
+ string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty);
if (programInfo.SeasonNumber.HasValue)
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 48d9e316de..e67b5846aa 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
- return VerifyReturnValueOfGetSet(buffer.AsSpan(receivedBytes), "none");
+ return VerifyReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), "none");
}
finally
{