aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs4
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs8
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs34
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs2
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs20
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs8
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs41
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj5
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs4
-rw-r--r--Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs3
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketConnection.cs4
-rw-r--r--Emby.Server.Implementations/IStartupOptions.cs2
-rw-r--r--Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs12
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs13
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs12
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs43
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs (renamed from Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs)0
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs4
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs3
-rw-r--r--Emby.Server.Implementations/Library/Validators/StudiosValidator.cs13
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs8
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs47
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs12
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs622
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs36
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs24
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs48
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs31
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs24
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs42
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs39
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs24
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs24
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs25
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs18
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs24
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs37
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs72
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs42
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs37
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs36
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs48
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs30
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs18
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs42
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs31
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs24
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs157
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs91
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs42
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs24
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs24
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs25
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs25
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs67
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs18
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs36
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs1
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs38
-rw-r--r--Emby.Server.Implementations/Localization/Core/af.json22
-rw-r--r--Emby.Server.Implementations/Localization/Core/bg-BG.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json12
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json18
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/ja.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/ml.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/pl.json7
-rw-r--r--Emby.Server.Implementations/Localization/Core/pr.json1
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-BR.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json10
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json9
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json6
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs63
-rw-r--r--Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs5
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs4
-rw-r--r--Emby.Server.Implementations/Properties/AssemblyInfo.cs1
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs3
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs2
-rw-r--r--Emby.Server.Implementations/Sorting/ArtistComparer.cs2
97 files changed, 1698 insertions, 788 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 19542c5ae..39e59a073 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -453,6 +453,7 @@ namespace Emby.Server.Implementations
/// <summary>
/// Runs the startup tasks.
/// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns><see cref="Task" />.</returns>
public async Task RunStartupTasksAsync(CancellationToken cancellationToken)
{
@@ -466,7 +467,7 @@ namespace Emby.Server.Implementations
_mediaEncoder.SetFFmpegPath();
- Logger.LogInformation("ServerId: {0}", SystemId);
+ Logger.LogInformation("ServerId: {ServerId}", SystemId);
var entryPoints = GetExports<IServerEntryPoint>();
@@ -1089,7 +1090,6 @@ namespace Emby.Server.Implementations
ServerName = FriendlyName,
LocalAddress = GetSmartApiUrl(source),
SupportsLibraryMonitor = true,
- EncoderLocation = _mediaEncoder.EncoderLocation,
SystemArchitecture = RuntimeInformation.OSArchitecture,
PackageName = _startupOptions.PackageName
};
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 093607dd5..41d1f9b39 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -102,7 +102,7 @@ namespace Emby.Server.Implementations.Channels
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
- return !(channel is IDisableMediaSourceDisplay);
+ return channel is not IDisableMediaSourceDisplay;
}
/// <inheritdoc />
@@ -880,7 +880,7 @@ namespace Emby.Server.Implementations.Channels
}
}
- private async Task CacheResponse(object result, string path)
+ private async Task CacheResponse(ChannelItemResult result, string path)
{
try
{
@@ -1079,11 +1079,11 @@ namespace Emby.Server.Implementations.Channels
// was used for status
// if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
- //{
+ // {
// item.ExternalEtag = info.Etag;
// forceUpdate = true;
// _logger.LogDebug("Forcing update due to ExternalEtag {0}", item.Name);
- //}
+ // }
if (!internalChannelId.Equals(item.ChannelId))
{
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 08acd1767..b00a51922 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.IO;
@@ -63,13 +61,13 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
- public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
+ public event EventHandler<CollectionCreatedEventArgs>? CollectionCreated;
/// <inheritdoc />
- public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
+ public event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection;
/// <inheritdoc />
- public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
+ public event EventHandler<CollectionModifiedEventArgs>? ItemsRemovedFromCollection;
private IEnumerable<Folder> FindFolders(string path)
{
@@ -80,7 +78,7 @@ namespace Emby.Server.Implementations.Collections
.Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path));
}
- internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded)
+ internal async Task<Folder?> EnsureLibraryFolder(string path, bool createIfNeeded)
{
var existingFolder = FindFolders(path).FirstOrDefault();
if (existingFolder != null)
@@ -97,7 +95,7 @@ namespace Emby.Server.Implementations.Collections
var libraryOptions = new LibraryOptions
{
- PathInfos = new[] { new MediaPathInfo { Path = path } },
+ PathInfos = new[] { new MediaPathInfo(path) },
EnableRealtimeMonitor = false,
SaveLocalMetadata = true
};
@@ -114,7 +112,7 @@ namespace Emby.Server.Implementations.Collections
return Path.Combine(_appPaths.DataPath, "collections");
}
- private Task<Folder> GetCollectionsFolder(bool createIfNeeded)
+ private Task<Folder?> GetCollectionsFolder(bool createIfNeeded)
{
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
}
@@ -203,8 +201,7 @@ namespace Emby.Server.Implementations.Collections
private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
{
- var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
- if (collection == null)
+ if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
{
throw new ArgumentException("No collection exists with the supplied Id");
}
@@ -256,9 +253,7 @@ namespace Emby.Server.Implementations.Collections
/// <inheritdoc />
public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
{
- var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
-
- if (collection == null)
+ if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
{
throw new ArgumentException("No collection exists with the supplied Id");
}
@@ -312,11 +307,7 @@ namespace Emby.Server.Implementations.Collections
foreach (var item in items)
{
- if (item is not ISupportsBoxSetGrouping)
- {
- results[item.Id] = item;
- }
- else
+ if (item is ISupportsBoxSetGrouping)
{
var itemId = item.Id;
@@ -340,6 +331,7 @@ namespace Emby.Server.Implementations.Collections
}
var alreadyInResults = false;
+
// this is kind of a performance hack because only Video has alternate versions that should be in a box set?
if (item is Video video)
{
@@ -355,11 +347,13 @@ namespace Emby.Server.Implementations.Collections
}
}
- if (!alreadyInResults)
+ if (alreadyInResults)
{
- results[itemId] = item;
+ continue;
}
}
+
+ results[item.Id] = item;
}
return results.Values;
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 6f23a0888..01c9fbca8 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Data
protected virtual int? CacheSize => null;
/// <summary>
- /// Gets the journal mode. <see href="https://www.sqlite.org/pragma.html#pragma_journal_mode" />
+ /// Gets the journal mode. <see href="https://www.sqlite.org/pragma.html#pragma_journal_mode" />.
/// </summary>
/// <value>The journal mode.</value>
protected virtual string JournalMode => "TRUNCATE";
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 2cb10765f..0e6b7fb82 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -75,6 +75,12 @@ namespace Emby.Server.Implementations.Data
/// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary>
+ /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
+ /// <param name="appHost">Instance of the <see cref="IServerApplicationHost"/> interface.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{SqliteItemRepository}"/> interface.</param>
+ /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
+ /// <param name="imageProcessor">Instance of the <see cref="IImageProcessor"/> interface.</param>
+ /// <exception cref="ArgumentNullException">config is null.</exception>
public SqliteItemRepository(
IServerConfigurationManager config,
IServerApplicationHost appHost,
@@ -1135,15 +1141,25 @@ namespace Emby.Server.Implementations.Data
Path = RestorePath(path.ToString())
};
- if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks))
+ if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks)
+ && ticks >= DateTime.MinValue.Ticks
+ && ticks <= DateTime.MaxValue.Ticks)
{
image.DateModified = new DateTime(ticks, DateTimeKind.Utc);
}
+ else
+ {
+ return null;
+ }
if (Enum.TryParse(imageType.ToString(), true, out ImageType type))
{
image.Type = type;
}
+ else
+ {
+ return null;
+ }
// Optional parameters: width*height*blurhash
if (nextSegment + 1 < value.Length - 1)
@@ -4879,7 +4895,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
foreach (var t in _knownTypes)
{
- dict[t.Name] = t.FullName ;
+ dict[t.Name] = t.FullName;
}
dict["Program"] = typeof(LiveTvProgram).FullName;
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index ef9af1dcd..613d07d77 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -174,7 +174,6 @@ namespace Emby.Server.Implementations.Data
/// <param name="key">The key.</param>
/// <param name="userData">The user data.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
public void PersistUserData(long internalUserId, string key, UserItemData userData, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -319,8 +318,8 @@ namespace Emby.Server.Implementations.Data
/// <summary>
/// Return all user-data associated with the given user.
/// </summary>
- /// <param name="internalUserId"></param>
- /// <returns></returns>
+ /// <param name="internalUserId">The internal user id.</param>
+ /// <returns>The list of user item data.</returns>
public List<UserItemData> GetAllUserData(long internalUserId)
{
if (internalUserId <= 0)
@@ -349,7 +348,8 @@ namespace Emby.Server.Implementations.Data
/// <summary>
/// Read a row from the specified reader into the provided userData object.
/// </summary>
- /// <param name="reader"></param>
+ /// <param name="reader">The list of result set values.</param>
+ /// <returns>The user item data.</returns>
private UserItemData ReadRow(IReadOnlyList<ResultSetValue> reader)
{
var userData = new UserItemData();
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 7411239a1..7c2d02037 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -807,7 +807,7 @@ namespace Emby.Server.Implementations.Dto
dto.MediaType = item.MediaType;
- if (!(item is LiveTvProgram))
+ if (item is not LiveTvProgram)
{
dto.LocationType = item.LocationType;
}
@@ -928,9 +928,9 @@ namespace Emby.Server.Implementations.Dto
}
// if (options.ContainsField(ItemFields.MediaSourceCount))
- //{
+ // {
// Songs always have one
- //}
+ // }
}
if (item is IHasArtist hasArtist)
@@ -938,10 +938,10 @@ namespace Emby.Server.Implementations.Dto
dto.Artists = hasArtist.Artists;
// var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
- //{
+ // {
// EnableTotalRecordCount = false,
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
- //});
+ // });
// dto.ArtistItems = artistItems.Items
// .Select(i =>
@@ -958,7 +958,7 @@ namespace Emby.Server.Implementations.Dto
// Include artists that are not in the database yet, e.g., just added via metadata editor
// var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
dto.ArtistItems = hasArtist.Artists
- //.Except(foundArtists, new DistinctNameComparer())
+ // .Except(foundArtists, new DistinctNameComparer())
.Select(i =>
{
// This should not be necessary but we're seeing some cases of it
@@ -990,10 +990,10 @@ namespace Emby.Server.Implementations.Dto
dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
// var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
- //{
+ // {
// EnableTotalRecordCount = false,
// ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
- //});
+ // });
// dto.AlbumArtists = artistItems.Items
// .Select(i =>
@@ -1008,7 +1008,7 @@ namespace Emby.Server.Implementations.Dto
// .ToList();
dto.AlbumArtists = hasAlbumArtist.AlbumArtists
- //.Except(foundArtists, new DistinctNameComparer())
+ // .Except(foundArtists, new DistinctNameComparer())
.Select(i =>
{
// This should not be necessary but we're seeing some cases of it
@@ -1035,8 +1035,7 @@ namespace Emby.Server.Implementations.Dto
}
// Add video info
- var video = item as Video;
- if (video != null)
+ if (item is Video video)
{
dto.VideoType = video.VideoType;
dto.Video3DFormat = video.Video3DFormat;
@@ -1075,9 +1074,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.MediaStreams))
{
// Add VideoInfo
- var iHasMediaSources = item as IHasMediaSources;
-
- if (iHasMediaSources != null)
+ if (item is IHasMediaSources)
{
MediaStream[] mediaStreams;
@@ -1146,7 +1143,7 @@ namespace Emby.Server.Implementations.Dto
// TODO maybe remove the if statement entirely
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
- episodeSeries = episodeSeries ?? episode.Series;
+ episodeSeries ??= episode.Series;
if (episodeSeries != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
@@ -1159,7 +1156,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.SeriesStudio))
{
- episodeSeries = episodeSeries ?? episode.Series;
+ episodeSeries ??= episode.Series;
if (episodeSeries != null)
{
dto.SeriesStudio = episodeSeries.Studios.FirstOrDefault();
@@ -1172,7 +1169,7 @@ namespace Emby.Server.Implementations.Dto
{
dto.AirDays = series.AirDays;
dto.AirTime = series.AirTime;
- dto.Status = series.Status.HasValue ? series.Status.Value.ToString() : null;
+ dto.Status = series.Status?.ToString();
}
// Add SeasonInfo
@@ -1185,7 +1182,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.SeriesStudio))
{
- series = series ?? season.Series;
+ series ??= season.Series;
if (series != null)
{
dto.SeriesStudio = series.Studios.FirstOrDefault();
@@ -1196,7 +1193,7 @@ namespace Emby.Server.Implementations.Dto
// TODO maybe remove the if statement entirely
// if (options.ContainsField(ItemFields.SeriesPrimaryImage))
{
- series = series ?? season.Series;
+ series ??= season.Series;
if (series != null)
{
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
@@ -1283,7 +1280,7 @@ namespace Emby.Server.Implementations.Dto
var parent = currentItem.DisplayParent ?? currentItem.GetOwner() ?? currentItem.GetParent();
- if (parent == null && !(originalItem is UserRootFolder) && !(originalItem is UserView) && !(originalItem is AggregateFolder) && !(originalItem is ICollectionFolder) && !(originalItem is Channel))
+ if (parent == null && originalItem is not UserRootFolder && originalItem is not UserView && originalItem is not AggregateFolder && originalItem is not ICollectionFolder && originalItem is not Channel)
{
parent = _libraryManager.GetCollectionFolders(originalItem).FirstOrDefault();
}
@@ -1317,7 +1314,7 @@ namespace Emby.Server.Implementations.Dto
var imageTags = dto.ImageTags;
while (((!(imageTags != null && imageTags.ContainsKey(ImageType.Logo)) && logoLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && artLimit > 0) || (!(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && thumbLimit > 0) || parent is Series) &&
- (parent = parent ?? (isFirst ? GetImageDisplayParent(item, item) ?? owner : parent)) != null)
+ (parent ??= (isFirst ? GetImageDisplayParent(item, item) ?? owner : parent)) != null)
{
if (parent == null)
{
@@ -1348,7 +1345,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
+ if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && parent is not ICollectionFolder && parent is not UserView)
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 4c9e05821..fa24e9dd1 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -23,14 +23,15 @@
</ItemGroup>
<ItemGroup>
+ <PackageReference Include="DiscUtils.Udf" Version="0.16.4" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.8" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.9" />
<PackageReference Include="Mono.Nat" Version="3.0.1" />
- <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
+ <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.0" />
<PackageReference Include="sharpcompress" Version="0.28.3" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.1.2" />
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 5bb4100ba..df48346e3 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -149,7 +149,7 @@ namespace Emby.Server.Implementations.EntryPoints
private static bool EnableRefreshMessage(BaseItem item)
{
- if (!(item is Folder folder))
+ if (item is not Folder folder)
{
return false;
}
@@ -403,7 +403,7 @@ namespace Emby.Server.Implementations.EntryPoints
return false;
}
- if (item is IItemByName && !(item is MusicArtist))
+ if (item is IItemByName && item is not MusicArtist)
{
return false;
}
diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index 2e72b18f5..feaccf9fa 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -37,6 +37,9 @@ namespace Emby.Server.Implementations.EntryPoints
/// <summary>
/// Initializes a new instance of the <see cref="UdpServerEntryPoint" /> class.
/// </summary>
+ /// <param name="logger">Instance of the <see cref="ILogger{UdpServerEntryPoint}"/> interface.</param>
+ /// <param name="appHost">Instance of the <see cref="IServerApplicationHost"/> interface.</param>
+ /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param>
public UdpServerEntryPoint(
ILogger<UdpServerEntryPoint> logger,
IServerApplicationHost appHost,
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index 5d38ea0ca..7010a6fb0 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.HttpServer
public event EventHandler<EventArgs>? Closed;
/// <summary>
- /// Gets or sets the remote end point.
+ /// Gets the remote end point.
/// </summary>
public IPAddress? RemoteEndPoint { get; }
@@ -82,7 +82,7 @@ namespace Emby.Server.Implementations.HttpServer
public DateTime LastKeepAliveDate { get; set; }
/// <summary>
- /// Gets or sets the query string.
+ /// Gets the query string.
/// </summary>
/// <value>The query string.</value>
public IQueryCollection QueryString { get; }
diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs
index a430b9e72..1d97882db 100644
--- a/Emby.Server.Implementations/IStartupOptions.cs
+++ b/Emby.Server.Implementations/IStartupOptions.cs
@@ -10,7 +10,7 @@ namespace Emby.Server.Implementations
string? FFmpegPath { get; }
/// <summary>
- /// Gets the value of the --service command line option.
+ /// Gets a value value indicating whether to run as service by the --service command line option.
/// </summary>
bool IsService { get; }
diff --git a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
index ff5f26ce0..0229fbae7 100644
--- a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
@@ -30,27 +30,27 @@ namespace Emby.Server.Implementations.Images
string[] includeItemTypes;
- if (string.Equals(viewType, CollectionType.Movies))
+ if (string.Equals(viewType, CollectionType.Movies, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "Movie" };
}
- else if (string.Equals(viewType, CollectionType.TvShows))
+ else if (string.Equals(viewType, CollectionType.TvShows, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "Series" };
}
- else if (string.Equals(viewType, CollectionType.Music))
+ else if (string.Equals(viewType, CollectionType.Music, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "MusicAlbum" };
}
- else if (string.Equals(viewType, CollectionType.Books))
+ else if (string.Equals(viewType, CollectionType.Books, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "Book", "AudioBook" };
}
- else if (string.Equals(viewType, CollectionType.BoxSets))
+ else if (string.Equals(viewType, CollectionType.BoxSets, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "BoxSet" };
}
- else if (string.Equals(viewType, CollectionType.HomeVideos) || string.Equals(viewType, CollectionType.Photos))
+ else if (string.Equals(viewType, CollectionType.HomeVideos, StringComparison.Ordinal) || string.Equals(viewType, CollectionType.Photos, StringComparison.Ordinal))
{
includeItemTypes = new string[] { "Video", "Photo" };
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 13fb8b2fd..a0a6bb292 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -287,14 +287,14 @@ namespace Emby.Server.Implementations.Library
if (item is IItemByName)
{
- if (!(item is MusicArtist))
+ if (item is not MusicArtist)
{
return;
}
}
else if (!item.IsFolder)
{
- if (!(item is Video) && !(item is LiveTvChannel))
+ if (item is not Video && item is not LiveTvChannel)
{
return;
}
@@ -866,7 +866,7 @@ namespace Emby.Server.Implementations.Library
{
var path = Person.GetPath(name);
var id = GetItemByNameId<Person>(path);
- if (!(GetItemById(id) is Person item))
+ if (GetItemById(id) is not Person item)
{
item = new Person
{
@@ -2118,7 +2118,7 @@ namespace Emby.Server.Implementations.Library
public LibraryOptions GetLibraryOptions(BaseItem item)
{
- if (!(item is CollectionFolder collectionFolder))
+ if (item is not CollectionFolder collectionFolder)
{
// List.Find is more performant than FirstOrDefault due to enumerator allocation
collectionFolder = GetCollectionFolders(item)
@@ -3173,10 +3173,7 @@ namespace Emby.Server.Implementations.Library
{
if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal)))
{
- list.Add(new MediaPathInfo
- {
- Path = location
- });
+ list.Add(new MediaPathInfo(location));
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index e893d6335..fd9747b4b 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -21,11 +21,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// </summary>
public class AudioResolver : ItemResolver<MediaBrowser.Controller.Entities.Audio.Audio>, IMultiItemResolver
{
- private readonly ILibraryManager LibraryManager;
+ private readonly ILibraryManager _libraryManager;
public AudioResolver(ILibraryManager libraryManager)
{
- LibraryManager = libraryManager;
+ _libraryManager = libraryManager;
}
/// <summary>
@@ -88,13 +88,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
var files = args.FileSystemChildren
- .Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
+ .Where(i => !_libraryManager.IgnoreFile(i, args.Parent))
.ToList();
return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
}
- if (LibraryManager.IsAudioFile(args.Path))
+ if (_libraryManager.IsAudioFile(args.Path))
{
var extension = Path.GetExtension(args.Path);
@@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var isMixedCollectionType = string.IsNullOrEmpty(collectionType);
// For conflicting extensions, give priority to videos
- if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path))
+ if (isMixedCollectionType && _libraryManager.IsVideoFile(args.Path))
{
return null;
}
@@ -182,7 +182,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
}
}
- var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
+ var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
var resolver = new AudioBookListResolver(namingOptions);
var resolverResult = resolver.Resolve(files).ToList();
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index cdb492022..01e89302e 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -5,6 +5,7 @@
using System;
using System.IO;
using System.Linq;
+using DiscUtils.Udf;
using Emby.Naming.Video;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -16,7 +17,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// <summary>
/// Resolves a Path into a Video or Video subclass.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The type of item to resolve.</typeparam>
public abstract class BaseVideoResolver<T> : MediaBrowser.Controller.Resolvers.ItemResolver<T>
where T : Video, new()
{
@@ -80,7 +81,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
break;
}
- if (IsBluRayDirectory(child.FullName, filename, args.DirectoryService))
+ if (IsBluRayDirectory(filename))
{
videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions);
@@ -201,6 +202,22 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
video.IsoType = IsoType.BluRay;
}
+ else
+ {
+ // use disc-utils, both DVDs and BDs use UDF filesystem
+ using (var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read))
+ {
+ UdfReader udfReader = new UdfReader(videoFileStream);
+ if (udfReader.DirectoryExists("VIDEO_TS"))
+ {
+ video.IsoType = IsoType.Dvd;
+ }
+ else if (udfReader.DirectoryExists("BDMV"))
+ {
+ video.IsoType = IsoType.BluRay;
+ }
+ }
+ }
}
}
@@ -279,25 +296,13 @@ namespace Emby.Server.Implementations.Library.Resolvers
}
/// <summary>
- /// Determines whether [is blu ray directory] [the specified directory name].
+ /// Determines whether [is bluray directory] [the specified directory name].
/// </summary>
- protected bool IsBluRayDirectory(string fullPath, string directoryName, IDirectoryService directoryService)
+ /// <param name="directoryName">The directory name.</param>
+ /// <returns>Whether the directory is a bluray directory.</returns>
+ protected bool IsBluRayDirectory(string directoryName)
{
- if (!string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return true;
- // var blurayExtensions = new[]
- //{
- // ".mts",
- // ".m2ts",
- // ".bdmv",
- // ".mpls"
- //};
-
- // return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
+ return string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase);
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
index 9599faea4..9599faea4 100644
--- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
diff --git a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
index fa45ccf84..3f29ab191 100644
--- a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
@@ -9,7 +9,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// <summary>
/// Class ItemResolver.
/// </summary>
- /// <typeparam name="T"></typeparam>
+ /// <typeparam name="T">The type of BaseItem.</typeparam>
public abstract class ItemResolver<T> : IItemResolver
where T : BaseItem, new()
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 889e29a6b..8b55a7744 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -400,7 +400,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return movie;
}
- if (IsBluRayDirectory(child.FullName, filename, directoryService))
+ if (IsBluRayDirectory(filename))
{
var movie = new T
{
@@ -481,7 +481,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return true;
}
- if (subfolders.Any(s => IsBluRayDirectory(s.FullName, s.Name, directoryService)))
+ if (subfolders.Any(s => IsBluRayDirectory(s.Name)))
{
videoTypes.Add(VideoType.BluRay);
return true;
diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index ecd44be47..2c4ead719 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -18,7 +18,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// </summary>
public class PlaylistResolver : FolderResolver<Playlist>
{
- private string[] _musicPlaylistCollectionTypes = new string[] {
+ private string[] _musicPlaylistCollectionTypes =
+ {
string.Empty,
CollectionType.Music
};
diff --git a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
index 9a8c5f39d..8577d722e 100644
--- a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -87,12 +87,15 @@ namespace Emby.Server.Implementations.Library.Validators
foreach (var item in deadEntities)
{
- _logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name);
+ _logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
- _libraryManager.DeleteItem(item, new DeleteOptions
- {
- DeleteFileLocation = false
- }, false);
+ _libraryManager.DeleteItem(
+ item,
+ new DeleteOptions
+ {
+ DeleteFileLocation = false
+ },
+ false);
}
progress.Report(100);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index 3fcadf5b1..bb3d635d1 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -33,7 +31,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return targetFile;
}
- public Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
+ public Task Record(IDirectStreamProvider? directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
if (directStreamProvider != null)
{
@@ -45,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private async Task RecordFromDirectStreamProvider(IDirectStreamProvider directStreamProvider, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
{
- Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
+ Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None))
@@ -71,7 +69,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.LogInformation("Opened recording stream from tuner provider");
- Directory.CreateDirectory(Path.GetDirectoryName(targetFile));
+ Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
await using var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.None);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 797063120..f2b9f3cb9 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -159,8 +159,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
try
{
var recordingFolders = GetRecordingFolders().ToArray();
- var virtualFolders = _libraryManager.GetVirtualFolders()
- .ToList();
+ var virtualFolders = _libraryManager.GetVirtualFolders();
var allExistingPaths = virtualFolders.SelectMany(i => i.Locations).ToList();
@@ -177,7 +176,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
continue;
}
- var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo { Path = i }).ToArray();
+ var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo(i)).ToArray();
var libraryOptions = new LibraryOptions
{
@@ -210,7 +209,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var path in pathsToRemove)
{
- await RemovePathFromLibrary(path).ConfigureAwait(false);
+ await RemovePathFromLibraryAsync(path).ConfigureAwait(false);
}
}
catch (Exception ex)
@@ -219,13 +218,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private async Task RemovePathFromLibrary(string path)
+ private async Task RemovePathFromLibraryAsync(string path)
{
_logger.LogDebug("Removing path from library: {0}", path);
var requiresRefresh = false;
- var virtualFolders = _libraryManager.GetVirtualFolders()
- .ToList();
+ var virtualFolders = _libraryManager.GetVirtualFolders();
foreach (var virtualFolder in virtualFolders)
{
@@ -460,7 +458,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (!string.IsNullOrWhiteSpace(tunerChannel.TunerChannelId))
{
var tunerChannelId = tunerChannel.TunerChannelId;
- if (tunerChannelId.IndexOf(".json.schedulesdirect.org", StringComparison.OrdinalIgnoreCase) != -1)
+ if (tunerChannelId.Contains(".json.schedulesdirect.org", StringComparison.OrdinalIgnoreCase))
{
tunerChannelId = tunerChannelId.Replace(".json.schedulesdirect.org", string.Empty, StringComparison.OrdinalIgnoreCase).TrimStart('I');
}
@@ -620,8 +618,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (existingTimer != null)
{
- if (existingTimer.Status == RecordingStatus.Cancelled ||
- existingTimer.Status == RecordingStatus.Completed)
+ if (existingTimer.Status == RecordingStatus.Cancelled
+ || existingTimer.Status == RecordingStatus.Completed)
{
existingTimer.Status = RecordingStatus.New;
existingTimer.IsManual = true;
@@ -913,18 +911,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var epgChannel = await GetEpgChannelFromTunerChannel(provider.Item1, provider.Item2, channel, cancellationToken).ConfigureAwait(false);
- List<ProgramInfo> programs;
-
if (epgChannel == null)
{
_logger.LogDebug("EPG channel not found for tuner channel {0}-{1} from {2}-{3}", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty);
- programs = new List<ProgramInfo>();
+ continue;
}
- else
- {
- programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken)
+
+ List<ProgramInfo> programs = (await provider.Item1.GetProgramsAsync(provider.Item2, epgChannel.Id, startDateUtc, endDateUtc, cancellationToken)
.ConfigureAwait(false)).ToList();
- }
// Replace the value that came from the provider with a normalized value
foreach (var program in programs)
@@ -940,7 +934,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- return new List<ProgramInfo>();
+ return Enumerable.Empty<ProgramInfo>();
}
private List<Tuple<IListingsProvider, ListingsProviderInfo>> GetListingProviders()
@@ -1292,7 +1286,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.LogInformation("Beginning recording. Will record for {0} minutes.", duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
- _logger.LogInformation("Writing file to path: " + recordPath);
+ _logger.LogInformation("Writing file to: {Path}", recordPath);
Action onStarted = async () =>
{
@@ -1417,13 +1411,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void TriggerRefresh(string path)
{
- _logger.LogInformation("Triggering refresh on {path}", path);
+ _logger.LogInformation("Triggering refresh on {Path}", path);
var item = GetAffectedBaseItem(Path.GetDirectoryName(path));
if (item != null)
{
- _logger.LogInformation("Refreshing recording parent {path}", item.Path);
+ _logger.LogInformation("Refreshing recording parent {Path}", item.Path);
_providerManager.QueueRefresh(
item.Id,
@@ -1458,7 +1452,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (item.GetType() == typeof(Folder) && string.Equals(item.Path, parentPath, StringComparison.OrdinalIgnoreCase))
{
var parentItem = item.GetParent();
- if (parentItem != null && !(parentItem is AggregateFolder))
+ if (parentItem != null && parentItem is not AggregateFolder)
{
item = parentItem;
}
@@ -1512,8 +1506,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
DeleteLibraryItemsForTimers(timersToDelete);
- var librarySeries = _libraryManager.FindByPath(seriesPath, true) as Folder;
- if (librarySeries == null)
+ if (_libraryManager.FindByPath(seriesPath, true) is not Folder librarySeries)
{
return;
}
@@ -1667,7 +1660,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.LogInformation("Running recording post processor {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
- process.Exited += Process_Exited;
+ process.Exited += OnProcessExited;
process.Start();
}
catch (Exception ex)
@@ -1681,7 +1674,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return arguments.Replace("{path}", path, StringComparison.OrdinalIgnoreCase);
}
- private void Process_Exited(object sender, EventArgs e)
+ private void OnProcessExited(object sender, EventArgs e)
{
using (var process = (Process)sender)
{
@@ -2239,7 +2232,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var enabledTimersForSeries = new List<TimerInfo>();
foreach (var timer in allTimers)
{
- var existingTimer = _timerProvider.GetTimer(timer.Id)
+ var existingTimer = _timerProvider.GetTimer(timer.Id)
?? (string.IsNullOrWhiteSpace(timer.ProgramId)
? null
: _timerProvider.GetTimerByProgramId(timer.ProgramId));
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 93781cb7b..e10bc7647 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -319,11 +319,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
}
- catch (ObjectDisposedException)
- {
- // TODO Investigate and properly fix.
- // Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
- }
catch (Exception ex)
{
_logger.LogError(ex, "Error reading ffmpeg recording log");
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
index 0ec52a959..20a8213a7 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs
@@ -8,7 +8,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
internal class EpgChannelData
{
-
private readonly Dictionary<string, ChannelInfo> _channelsById;
private readonly Dictionary<string, ChannelInfo> _channelsByNumber;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
index 4712724d6..dfe3517b2 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
@@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
/// <summary>
/// Records the specified media source.
/// </summary>
- Task Record(IDirectStreamProvider directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
+ Task Record(IDirectStreamProvider? directStreamProvider, MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
string GetOutputPath(MediaSourceInfo mediaSource, string targetFile);
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index 6c52a9a73..a861e6ae4 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -23,7 +21,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
}
- public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
+ public event EventHandler<GenericEventArgs<TimerInfo>>? TimerFired;
public void RestartTimers()
{
@@ -145,9 +143,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private void TimerCallback(object state)
+ private void TimerCallback(object? state)
{
- var timerId = (string)state;
+ var timerId = (string?)state ?? throw new ArgumentNullException(nameof(state));
var timer = GetAll().FirstOrDefault(i => string.Equals(i.Id, timerId, StringComparison.OrdinalIgnoreCase));
if (timer != null)
@@ -156,12 +154,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- public TimerInfo GetTimer(string id)
+ public TimerInfo? GetTimer(string id)
{
return GetAll().FirstOrDefault(r => string.Equals(r.Id, id, StringComparison.OrdinalIgnoreCase));
}
- public TimerInfo GetTimerByProgramId(string programId)
+ public TimerInfo? GetTimerByProgramId(string programId)
{
return GetAll().FirstOrDefault(r => string.Equals(r.ProgramId, programId, StringComparison.OrdinalIgnoreCase));
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index b7639a51c..8125ed57d 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -14,8 +14,9 @@ using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common;
+using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos;
using Jellyfin.Extensions.Json;
+using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Cryptography;
@@ -96,12 +97,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var dates = GetScheduleRequestDates(startDateUtc, endDateUtc);
_logger.LogInformation("Channel Station ID is: {ChannelID}", channelId);
- var requestList = new List<ScheduleDirect.RequestScheduleForChannel>()
+ var requestList = new List<RequestScheduleForChannelDto>()
{
- new ScheduleDirect.RequestScheduleForChannel()
+ new RequestScheduleForChannelDto()
{
- stationID = channelId,
- date = dates
+ StationId = channelId,
+ Date = dates
}
};
@@ -113,61 +114,61 @@ namespace Emby.Server.Implementations.LiveTv.Listings
options.Headers.TryAddWithoutValidation("token", token);
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var dailySchedules = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Day>>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var dailySchedules = await JsonSerializer.DeserializeAsync<List<DayDto>>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
programRequestOptions.Headers.TryAddWithoutValidation("token", token);
- var programsID = dailySchedules.SelectMany(d => d.programs.Select(s => s.programID)).Distinct();
+ var programsID = dailySchedules.SelectMany(d => d.Programs.Select(s => s.ProgramId)).Distinct();
programRequestOptions.Content = new StringContent("[\"" + string.Join("\", \"", programsID) + "\"]", Encoding.UTF8, MediaTypeNames.Application.Json);
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var programDetails = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- var programDict = programDetails.ToDictionary(p => p.programID, y => y);
+ var programDetails = await JsonSerializer.DeserializeAsync<List<ProgramDetailsDto>>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var programDict = programDetails.ToDictionary(p => p.ProgramId, y => y);
var programIdsWithImages = programDetails
- .Where(p => p.hasImageArtwork).Select(p => p.programID)
+ .Where(p => p.HasImageArtwork).Select(p => p.ProgramId)
.ToList();
var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
var programsInfo = new List<ProgramInfo>();
- foreach (ScheduleDirect.Program schedule in dailySchedules.SelectMany(d => d.programs))
+ foreach (ProgramDto schedule in dailySchedules.SelectMany(d => d.Programs))
{
// _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
// " which corresponds to channel " + channelNumber + " and program id " +
- // schedule.programID + " which says it has images? " +
- // programDict[schedule.programID].hasImageArtwork);
+ // schedule.ProgramId + " which says it has images? " +
+ // programDict[schedule.ProgramId].hasImageArtwork);
if (images != null)
{
- var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
+ var imageIndex = images.FindIndex(i => i.ProgramId == schedule.ProgramId[..10]);
if (imageIndex > -1)
{
- var programEntry = programDict[schedule.programID];
+ var programEntry = programDict[schedule.ProgramId];
- var allImages = images[imageIndex].data ?? new List<ScheduleDirect.ImageData>();
- var imagesWithText = allImages.Where(i => string.Equals(i.text, "yes", StringComparison.OrdinalIgnoreCase));
- var imagesWithoutText = allImages.Where(i => string.Equals(i.text, "no", StringComparison.OrdinalIgnoreCase));
+ var allImages = images[imageIndex].Data ?? new List<ImageDataDto>();
+ var imagesWithText = allImages.Where(i => string.Equals(i.Text, "yes", StringComparison.OrdinalIgnoreCase));
+ var imagesWithoutText = allImages.Where(i => string.Equals(i.Text, "no", StringComparison.OrdinalIgnoreCase));
const double DesiredAspect = 2.0 / 3;
- programEntry.primaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
+ programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, true, DesiredAspect) ??
GetProgramImage(ApiUrl, allImages, true, DesiredAspect);
const double WideAspect = 16.0 / 9;
- programEntry.thumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
+ programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, true, WideAspect);
// Don't supply the same image twice
- if (string.Equals(programEntry.primaryImage, programEntry.thumbImage, StringComparison.Ordinal))
+ if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal))
{
- programEntry.thumbImage = null;
+ programEntry.ThumbImage = null;
}
- programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
+ programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
@@ -176,15 +177,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.programID]));
+ programsInfo.Add(GetProgram(channelId, schedule, programDict[schedule.ProgramId]));
}
return programsInfo;
}
- private static int GetSizeOrder(ScheduleDirect.ImageData image)
+ private static int GetSizeOrder(ImageDataDto image)
{
- if (int.TryParse(image.height, out int value))
+ if (int.TryParse(image.Height, out int value))
{
return value;
}
@@ -192,53 +193,53 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return 0;
}
- private static string GetChannelNumber(ScheduleDirect.Map map)
+ private static string GetChannelNumber(MapDto map)
{
- var channelNumber = map.logicalChannelNumber;
+ var channelNumber = map.LogicalChannelNumber;
if (string.IsNullOrWhiteSpace(channelNumber))
{
- channelNumber = map.channel;
+ channelNumber = map.Channel;
}
if (string.IsNullOrWhiteSpace(channelNumber))
{
- channelNumber = map.atscMajor + "." + map.atscMinor;
+ channelNumber = map.AtscMajor + "." + map.AtscMinor;
}
return channelNumber.TrimStart('0');
}
- private static bool IsMovie(ScheduleDirect.ProgramDetails programInfo)
+ private static bool IsMovie(ProgramDetailsDto programInfo)
{
- return string.Equals(programInfo.entityType, "movie", StringComparison.OrdinalIgnoreCase);
+ return string.Equals(programInfo.EntityType, "movie", StringComparison.OrdinalIgnoreCase);
}
- private ProgramInfo GetProgram(string channelId, ScheduleDirect.Program programInfo, ScheduleDirect.ProgramDetails details)
+ private ProgramInfo GetProgram(string channelId, ProgramDto programInfo, ProgramDetailsDto details)
{
- var startAt = GetDate(programInfo.airDateTime);
- var endAt = startAt.AddSeconds(programInfo.duration);
+ var startAt = GetDate(programInfo.AirDateTime);
+ var endAt = startAt.AddSeconds(programInfo.Duration);
var audioType = ProgramAudio.Stereo;
- var programId = programInfo.programID ?? string.Empty;
+ var programId = programInfo.ProgramId ?? string.Empty;
string newID = programId + "T" + startAt.Ticks + "C" + channelId;
- if (programInfo.audioProperties != null)
+ if (programInfo.AudioProperties != null)
{
- if (programInfo.audioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
+ if (programInfo.AudioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
{
audioType = ProgramAudio.Atmos;
}
- else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
+ else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
{
audioType = ProgramAudio.DolbyDigital;
}
- else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
+ else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
{
audioType = ProgramAudio.DolbyDigital;
}
- else if (programInfo.audioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
+ else if (programInfo.AudioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
{
audioType = ProgramAudio.Stereo;
}
@@ -249,9 +250,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
string episodeTitle = null;
- if (details.episodeTitle150 != null)
+ if (details.EpisodeTitle150 != null)
{
- episodeTitle = details.episodeTitle150;
+ episodeTitle = details.EpisodeTitle150;
}
var info = new ProgramInfo
@@ -260,22 +261,22 @@ namespace Emby.Server.Implementations.LiveTv.Listings
Id = newID,
StartDate = startAt,
EndDate = endAt,
- Name = details.titles[0].title120 ?? "Unknown",
+ Name = details.Titles[0].Title120 ?? "Unknown",
OfficialRating = null,
CommunityRating = null,
EpisodeTitle = episodeTitle,
Audio = audioType,
// IsNew = programInfo.@new ?? false,
- IsRepeat = programInfo.@new == null,
- IsSeries = string.Equals(details.entityType, "episode", StringComparison.OrdinalIgnoreCase),
- ImageUrl = details.primaryImage,
- ThumbImageUrl = details.thumbImage,
- IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase),
- IsSports = string.Equals(details.entityType, "sports", StringComparison.OrdinalIgnoreCase),
+ IsRepeat = programInfo.New == null,
+ IsSeries = string.Equals(details.EntityType, "episode", StringComparison.OrdinalIgnoreCase),
+ ImageUrl = details.PrimaryImage,
+ ThumbImageUrl = details.ThumbImage,
+ IsKids = string.Equals(details.Audience, "children", StringComparison.OrdinalIgnoreCase),
+ IsSports = string.Equals(details.EntityType, "sports", StringComparison.OrdinalIgnoreCase),
IsMovie = IsMovie(details),
- Etag = programInfo.md5,
- IsLive = string.Equals(programInfo.liveTapeDelay, "live", StringComparison.OrdinalIgnoreCase),
- IsPremiere = programInfo.premiere || (programInfo.isPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1
+ Etag = programInfo.Md5,
+ IsLive = string.Equals(programInfo.LiveTapeDelay, "live", StringComparison.OrdinalIgnoreCase),
+ IsPremiere = programInfo.Premiere || (programInfo.IsPremiereOrFinale ?? string.Empty).IndexOf("premiere", StringComparison.OrdinalIgnoreCase) != -1
};
var showId = programId;
@@ -298,15 +299,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
info.ShowId = showId;
- if (programInfo.videoProperties != null)
+ if (programInfo.VideoProperties != null)
{
- info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
- info.Is3D = programInfo.videoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
+ info.IsHD = programInfo.VideoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
+ info.Is3D = programInfo.VideoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
}
- if (details.contentRating != null && details.contentRating.Count > 0)
+ if (details.ContentRating != null && details.ContentRating.Count > 0)
{
- info.OfficialRating = details.contentRating[0].code.Replace("TV", "TV-", StringComparison.Ordinal)
+ info.OfficialRating = details.ContentRating[0].Code.Replace("TV", "TV-", StringComparison.Ordinal)
.Replace("--", "-", StringComparison.Ordinal);
var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
@@ -316,15 +317,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- if (details.descriptions != null)
+ if (details.Descriptions != null)
{
- if (details.descriptions.description1000 != null && details.descriptions.description1000.Count > 0)
+ if (details.Descriptions.Description1000 != null && details.Descriptions.Description1000.Count > 0)
{
- info.Overview = details.descriptions.description1000[0].description;
+ info.Overview = details.Descriptions.Description1000[0].Description;
}
- else if (details.descriptions.description100 != null && details.descriptions.description100.Count > 0)
+ else if (details.Descriptions.Description100 != null && details.Descriptions.Description100.Count > 0)
{
- info.Overview = details.descriptions.description100[0].description;
+ info.Overview = details.Descriptions.Description100[0].Description;
}
}
@@ -334,18 +335,18 @@ namespace Emby.Server.Implementations.LiveTv.Listings
info.SeriesProviderIds[MetadataProvider.Zap2It.ToString()] = info.SeriesId;
- if (details.metadata != null)
+ if (details.Metadata != null)
{
- foreach (var metadataProgram in details.metadata)
+ foreach (var metadataProgram in details.Metadata)
{
var gracenote = metadataProgram.Gracenote;
if (gracenote != null)
{
- info.SeasonNumber = gracenote.season;
+ info.SeasonNumber = gracenote.Season;
- if (gracenote.episode > 0)
+ if (gracenote.Episode > 0)
{
- info.EpisodeNumber = gracenote.episode;
+ info.EpisodeNumber = gracenote.Episode;
}
break;
@@ -354,25 +355,25 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- if (!string.IsNullOrWhiteSpace(details.originalAirDate))
+ if (!string.IsNullOrWhiteSpace(details.OriginalAirDate))
{
- info.OriginalAirDate = DateTime.Parse(details.originalAirDate, CultureInfo.InvariantCulture);
+ info.OriginalAirDate = DateTime.Parse(details.OriginalAirDate, CultureInfo.InvariantCulture);
info.ProductionYear = info.OriginalAirDate.Value.Year;
}
- if (details.movie != null)
+ if (details.Movie != null)
{
- if (!string.IsNullOrEmpty(details.movie.year)
- && int.TryParse(details.movie.year, out int year))
+ if (!string.IsNullOrEmpty(details.Movie.Year)
+ && int.TryParse(details.Movie.Year, out int year))
{
info.ProductionYear = year;
}
}
- if (details.genres != null)
+ if (details.Genres != null)
{
- info.Genres = details.genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
- info.IsNews = details.genres.Contains("news", StringComparer.OrdinalIgnoreCase);
+ info.Genres = details.Genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
+ info.IsNews = details.Genres.Contains("news", StringComparer.OrdinalIgnoreCase);
if (info.Genres.Contains("children", StringComparer.OrdinalIgnoreCase))
{
@@ -395,11 +396,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return date;
}
- private string GetProgramImage(string apiUrl, IEnumerable<ScheduleDirect.ImageData> images, bool returnDefaultImage, double desiredAspect)
+ private string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, bool returnDefaultImage, double desiredAspect)
{
var match = images
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
- .ThenByDescending(GetSizeOrder)
+ .ThenByDescending(i => GetSizeOrder(i))
.FirstOrDefault();
if (match == null)
@@ -407,7 +408,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return null;
}
- var uri = match.uri;
+ var uri = match.Uri;
if (string.IsNullOrWhiteSpace(uri))
{
@@ -423,19 +424,19 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
- private static double GetAspectRatio(ScheduleDirect.ImageData i)
+ private static double GetAspectRatio(ImageDataDto i)
{
int width = 0;
int height = 0;
- if (!string.IsNullOrWhiteSpace(i.width))
+ if (!string.IsNullOrWhiteSpace(i.Width))
{
- int.TryParse(i.width, out width);
+ _ = int.TryParse(i.Width, out width);
}
- if (!string.IsNullOrWhiteSpace(i.height))
+ if (!string.IsNullOrWhiteSpace(i.Height))
{
- int.TryParse(i.height, out height);
+ _ = int.TryParse(i.Height, out height);
}
if (height == 0 || width == 0)
@@ -448,14 +449,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return result;
}
- private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
+ private async Task<List<ShowImagesDto>> GetImageForPrograms(
ListingsProviderInfo info,
IReadOnlyList<string> programIds,
CancellationToken cancellationToken)
{
if (programIds.Count == 0)
{
- return new List<ScheduleDirect.ShowImages>();
+ return new List<ShowImagesDto>();
}
StringBuilder str = new StringBuilder("[", 1 + (programIds.Count * 13));
@@ -479,13 +480,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
await using var response = await innerResponse2.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- return await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ShowImages>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ return await JsonSerializer.DeserializeAsync<List<ShowImagesDto>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting image info from schedules direct");
- return new List<ScheduleDirect.ShowImages>();
+ return new List<ShowImagesDto>();
}
}
@@ -508,18 +509,18 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var httpResponse = await Send(options, false, info, cancellationToken).ConfigureAwait(false);
await using var response = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var root = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Headends>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var root = await JsonSerializer.DeserializeAsync<List<HeadendsDto>>(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
if (root != null)
{
- foreach (ScheduleDirect.Headends headend in root)
+ foreach (HeadendsDto headend in root)
{
- foreach (ScheduleDirect.Lineup lineup in headend.lineups)
+ foreach (LineupDto lineup in headend.Lineups)
{
lineups.Add(new NameIdPair
{
- Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
- Id = lineup.uri.Substring(18)
+ Name = string.IsNullOrWhiteSpace(lineup.Name) ? lineup.Lineup : lineup.Name,
+ Id = lineup.Uri[18..]
});
}
}
@@ -649,14 +650,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var response = await Send(options, false, null, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Token>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- if (string.Equals(root.message, "OK", StringComparison.Ordinal))
+ var root = await JsonSerializer.DeserializeAsync<TokenDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ if (string.Equals(root.Message, "OK", StringComparison.Ordinal))
{
- _logger.LogInformation("Authenticated with Schedules Direct token: " + root.token);
- return root.token;
+ _logger.LogInformation("Authenticated with Schedules Direct token: {Token}", root.Token);
+ return root.Token;
}
- throw new Exception("Could not authenticate with Schedules Direct Error: " + root.message);
+ throw new Exception("Could not authenticate with Schedules Direct Error: " + root.Message);
}
private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken)
@@ -705,9 +706,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
httpResponse.EnsureSuccessStatusCode();
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
using var response = httpResponse.Content;
- var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Lineups>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var root = await JsonSerializer.DeserializeAsync<LineupsDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
+ return root.Lineups.Any(i => string.Equals(info.ListingsId, i.Lineup, StringComparison.OrdinalIgnoreCase));
}
catch (HttpRequestException ex)
{
@@ -777,35 +778,35 @@ namespace Emby.Server.Implementations.LiveTv.Listings
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Channel>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
+ var root = await JsonSerializer.DeserializeAsync<ChannelDto>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ _logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.Map.Count);
_logger.LogInformation("Mapping Stations to Channel");
- var allStations = root.stations ?? new List<ScheduleDirect.Station>();
+ var allStations = root.Stations ?? new List<StationDto>();
- var map = root.map;
+ var map = root.Map;
var list = new List<ChannelInfo>(map.Count);
foreach (var channel in map)
{
var channelNumber = GetChannelNumber(channel);
- var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase))
- ?? new ScheduleDirect.Station
+ var station = allStations.Find(item => string.Equals(item.StationId, channel.StationId, StringComparison.OrdinalIgnoreCase))
+ ?? new StationDto
{
- stationID = channel.stationID
+ StationId = channel.StationId
};
var channelInfo = new ChannelInfo
{
- Id = station.stationID,
- CallSign = station.callsign,
+ Id = station.StationId,
+ CallSign = station.Callsign,
Number = channelNumber,
- Name = string.IsNullOrWhiteSpace(station.name) ? channelNumber : station.name
+ Name = string.IsNullOrWhiteSpace(station.Name) ? channelNumber : station.Name
};
- if (station.logo != null)
+ if (station.Logo != null)
{
- channelInfo.ImageUrl = station.logo.URL;
+ channelInfo.ImageUrl = station.Logo.Url;
}
list.Add(channelInfo);
@@ -818,402 +819,5 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal);
}
-
- public class ScheduleDirect
- {
- public class Token
- {
- public int code { get; set; }
-
- public string message { get; set; }
-
- public string serverID { get; set; }
-
- public string token { get; set; }
- }
-
- public class Lineup
- {
- public string lineup { get; set; }
-
- public string name { get; set; }
-
- public string transport { get; set; }
-
- public string location { get; set; }
-
- public string uri { get; set; }
- }
-
- public class Lineups
- {
- public int code { get; set; }
-
- public string serverID { get; set; }
-
- public string datetime { get; set; }
-
- public List<Lineup> lineups { get; set; }
- }
-
- public class Headends
- {
- public string headend { get; set; }
-
- public string transport { get; set; }
-
- public string location { get; set; }
-
- public List<Lineup> lineups { get; set; }
- }
-
- public class Map
- {
- public string stationID { get; set; }
-
- public string channel { get; set; }
-
- public string logicalChannelNumber { get; set; }
-
- public int uhfVhf { get; set; }
-
- public int atscMajor { get; set; }
-
- public int atscMinor { get; set; }
- }
-
- public class Broadcaster
- {
- public string city { get; set; }
-
- public string state { get; set; }
-
- public string postalcode { get; set; }
-
- public string country { get; set; }
- }
-
- public class Logo
- {
- public string URL { get; set; }
-
- public int height { get; set; }
-
- public int width { get; set; }
-
- public string md5 { get; set; }
- }
-
- public class Station
- {
- public string stationID { get; set; }
-
- public string name { get; set; }
-
- public string callsign { get; set; }
-
- public List<string> broadcastLanguage { get; set; }
-
- public List<string> descriptionLanguage { get; set; }
-
- public Broadcaster broadcaster { get; set; }
-
- public string affiliate { get; set; }
-
- public Logo logo { get; set; }
-
- public bool? isCommercialFree { get; set; }
- }
-
- public class Metadata
- {
- public string lineup { get; set; }
-
- public string modified { get; set; }
-
- public string transport { get; set; }
- }
-
- public class Channel
- {
- public List<Map> map { get; set; }
-
- public List<Station> stations { get; set; }
-
- public Metadata metadata { get; set; }
- }
-
- public class RequestScheduleForChannel
- {
- public string stationID { get; set; }
-
- public List<string> date { get; set; }
- }
-
- public class Rating
- {
- public string body { get; set; }
-
- public string code { get; set; }
- }
-
- public class Multipart
- {
- public int partNumber { get; set; }
-
- public int totalParts { get; set; }
- }
-
- public class Program
- {
- public string programID { get; set; }
-
- public string airDateTime { get; set; }
-
- public int duration { get; set; }
-
- public string md5 { get; set; }
-
- public List<string> audioProperties { get; set; }
-
- public List<string> videoProperties { get; set; }
-
- public List<Rating> ratings { get; set; }
-
- public bool? @new { get; set; }
-
- public Multipart multipart { get; set; }
-
- public string liveTapeDelay { get; set; }
-
- public bool premiere { get; set; }
-
- public bool repeat { get; set; }
-
- public string isPremiereOrFinale { get; set; }
- }
-
- public class MetadataSchedule
- {
- public string modified { get; set; }
-
- public string md5 { get; set; }
-
- public string startDate { get; set; }
-
- public string endDate { get; set; }
-
- public int days { get; set; }
- }
-
- public class Day
- {
- public string stationID { get; set; }
-
- public List<Program> programs { get; set; }
-
- public MetadataSchedule metadata { get; set; }
-
- public Day()
- {
- programs = new List<Program>();
- }
- }
-
- public class Title
- {
- public string title120 { get; set; }
- }
-
- public class EventDetails
- {
- public string subType { get; set; }
- }
-
- public class Description100
- {
- public string descriptionLanguage { get; set; }
-
- public string description { get; set; }
- }
-
- public class Description1000
- {
- public string descriptionLanguage { get; set; }
-
- public string description { get; set; }
- }
-
- public class DescriptionsProgram
- {
- public List<Description100> description100 { get; set; }
-
- public List<Description1000> description1000 { get; set; }
- }
-
- public class Gracenote
- {
- public int season { get; set; }
-
- public int episode { get; set; }
- }
-
- public class MetadataPrograms
- {
- public Gracenote Gracenote { get; set; }
- }
-
- public class ContentRating
- {
- public string body { get; set; }
-
- public string code { get; set; }
- }
-
- public class Cast
- {
- public string billingOrder { get; set; }
-
- public string role { get; set; }
-
- public string nameId { get; set; }
-
- public string personId { get; set; }
-
- public string name { get; set; }
-
- public string characterName { get; set; }
- }
-
- public class Crew
- {
- public string billingOrder { get; set; }
-
- public string role { get; set; }
-
- public string nameId { get; set; }
-
- public string personId { get; set; }
-
- public string name { get; set; }
- }
-
- public class QualityRating
- {
- public string ratingsBody { get; set; }
-
- public string rating { get; set; }
-
- public string minRating { get; set; }
-
- public string maxRating { get; set; }
-
- public string increment { get; set; }
- }
-
- public class Movie
- {
- public string year { get; set; }
-
- public int duration { get; set; }
-
- public List<QualityRating> qualityRating { get; set; }
- }
-
- public class Recommendation
- {
- public string programID { get; set; }
-
- public string title120 { get; set; }
- }
-
- public class ProgramDetails
- {
- public string audience { get; set; }
-
- public string programID { get; set; }
-
- public List<Title> titles { get; set; }
-
- public EventDetails eventDetails { get; set; }
-
- public DescriptionsProgram descriptions { get; set; }
-
- public string originalAirDate { get; set; }
-
- public List<string> genres { get; set; }
-
- public string episodeTitle150 { get; set; }
-
- public List<MetadataPrograms> metadata { get; set; }
-
- public List<ContentRating> contentRating { get; set; }
-
- public List<Cast> cast { get; set; }
-
- public List<Crew> crew { get; set; }
-
- public string entityType { get; set; }
-
- public string showType { get; set; }
-
- public bool hasImageArtwork { get; set; }
-
- public string primaryImage { get; set; }
-
- public string thumbImage { get; set; }
-
- public string backdropImage { get; set; }
-
- public string bannerImage { get; set; }
-
- public string imageID { get; set; }
-
- public string md5 { get; set; }
-
- public List<string> contentAdvisory { get; set; }
-
- public Movie movie { get; set; }
-
- public List<Recommendation> recommendations { get; set; }
- }
-
- public class Caption
- {
- public string content { get; set; }
-
- public string lang { get; set; }
- }
-
- public class ImageData
- {
- public string width { get; set; }
-
- public string height { get; set; }
-
- public string uri { get; set; }
-
- public string size { get; set; }
-
- public string aspect { get; set; }
-
- public string category { get; set; }
-
- public string text { get; set; }
-
- public string primary { get; set; }
-
- public string tier { get; set; }
-
- public Caption caption { get; set; }
- }
-
- public class ShowImages
- {
- public string programID { get; set; }
-
- public List<ImageData> data { get; set; }
- }
- }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs
new file mode 100644
index 000000000..b881b307c
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/BroadcasterDto.cs
@@ -0,0 +1,36 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Broadcaster dto.
+ /// </summary>
+ public class BroadcasterDto
+ {
+ /// <summary>
+ /// Gets or sets the city.
+ /// </summary>
+ [JsonPropertyName("city")]
+ public string City { get; set; }
+
+ /// <summary>
+ /// Gets or sets the state.
+ /// </summary>
+ [JsonPropertyName("state")]
+ public string State { get; set; }
+
+ /// <summary>
+ /// Gets or sets the postal code.
+ /// </summary>
+ [JsonPropertyName("postalCode")]
+ public string Postalcode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the country.
+ /// </summary>
+ [JsonPropertyName("country")]
+ public string Country { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs
new file mode 100644
index 000000000..96b67d1eb
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CaptionDto.cs
@@ -0,0 +1,24 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Caption dto.
+ /// </summary>
+ public class CaptionDto
+ {
+ /// <summary>
+ /// Gets or sets the content.
+ /// </summary>
+ [JsonPropertyName("content")]
+ public string Content { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lang.
+ /// </summary>
+ [JsonPropertyName("lang")]
+ public string Lang { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs
new file mode 100644
index 000000000..dac6f5f3e
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CastDto.cs
@@ -0,0 +1,48 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Cast dto.
+ /// </summary>
+ public class CastDto
+ {
+ /// <summary>
+ /// Gets or sets the billing order.
+ /// </summary>
+ [JsonPropertyName("billingOrder")]
+ public string BillingOrder { get; set; }
+
+ /// <summary>
+ /// Gets or sets the role.
+ /// </summary>
+ [JsonPropertyName("role")]
+ public string Role { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name id.
+ /// </summary>
+ [JsonPropertyName("nameId")]
+ public string NameId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the person id.
+ /// </summary>
+ [JsonPropertyName("personId")]
+ public string PersonId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the character name.
+ /// </summary>
+ [JsonPropertyName("characterName")]
+ public string CharacterName { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs
new file mode 100644
index 000000000..8c9c2c1fc
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ChannelDto.cs
@@ -0,0 +1,31 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Channel dto.
+ /// </summary>
+ public class ChannelDto
+ {
+ /// <summary>
+ /// Gets or sets the list of maps.
+ /// </summary>
+ [JsonPropertyName("map")]
+ public List<MapDto> Map { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of stations.
+ /// </summary>
+ [JsonPropertyName("stations")]
+ public List<StationDto> Stations { get; set; }
+
+ /// <summary>
+ /// Gets or sets the metadata.
+ /// </summary>
+ [JsonPropertyName("metadata")]
+ public MetadataDto Metadata { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs
new file mode 100644
index 000000000..135b5bb08
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ContentRatingDto.cs
@@ -0,0 +1,24 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Content rating dto.
+ /// </summary>
+ public class ContentRatingDto
+ {
+ /// <summary>
+ /// Gets or sets the body.
+ /// </summary>
+ [JsonPropertyName("body")]
+ public string Body { get; set; }
+
+ /// <summary>
+ /// Gets or sets the code.
+ /// </summary>
+ [JsonPropertyName("code")]
+ public string Code { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs
new file mode 100644
index 000000000..82d1001c8
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/CrewDto.cs
@@ -0,0 +1,42 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Crew dto.
+ /// </summary>
+ public class CrewDto
+ {
+ /// <summary>
+ /// Gets or sets the billing order.
+ /// </summary>
+ [JsonPropertyName("billingOrder")]
+ public string BillingOrder { get; set; }
+
+ /// <summary>
+ /// Gets or sets the role.
+ /// </summary>
+ [JsonPropertyName("role")]
+ public string Role { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name id.
+ /// </summary>
+ [JsonPropertyName("nameId")]
+ public string NameId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the person id.
+ /// </summary>
+ [JsonPropertyName("personId")]
+ public string PersonId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs
new file mode 100644
index 000000000..68876b068
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DayDto.cs
@@ -0,0 +1,39 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Day dto.
+ /// </summary>
+ public class DayDto
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DayDto"/> class.
+ /// </summary>
+ public DayDto()
+ {
+ Programs = new List<ProgramDto>();
+ }
+
+ /// <summary>
+ /// Gets or sets the station id.
+ /// </summary>
+ [JsonPropertyName("stationID")]
+ public string StationId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of programs.
+ /// </summary>
+ [JsonPropertyName("programs")]
+ public List<ProgramDto> Programs { get; set; }
+
+ /// <summary>
+ /// Gets or sets the metadata schedule.
+ /// </summary>
+ [JsonPropertyName("metadata")]
+ public MetadataScheduleDto Metadata { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs
new file mode 100644
index 000000000..d3e6ff393
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description1000Dto.cs
@@ -0,0 +1,24 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Description 1_000 dto.
+ /// </summary>
+ public class Description1000Dto
+ {
+ /// <summary>
+ /// Gets or sets the description language.
+ /// </summary>
+ [JsonPropertyName("descriptionLanguage")]
+ public string DescriptionLanguage { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonPropertyName("description")]
+ public string Description { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs
new file mode 100644
index 000000000..04360266c
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/Description100Dto.cs
@@ -0,0 +1,24 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Description 100 dto.
+ /// </summary>
+ public class Description100Dto
+ {
+ /// <summary>
+ /// Gets or sets the description language.
+ /// </summary>
+ [JsonPropertyName("descriptionLanguage")]
+ public string DescriptionLanguage { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonPropertyName("description")]
+ public string Description { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs
new file mode 100644
index 000000000..3af36ae96
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/DescriptionsProgramDto.cs
@@ -0,0 +1,25 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Descriptions program dto.
+ /// </summary>
+ public class DescriptionsProgramDto
+ {
+ /// <summary>
+ /// Gets or sets the list of description 100.
+ /// </summary>
+ [JsonPropertyName("description100")]
+ public List<Description100Dto> Description100 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of description1000.
+ /// </summary>
+ [JsonPropertyName("description1000")]
+ public List<Description1000Dto> Description1000 { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs
new file mode 100644
index 000000000..c3b2bd9c1
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/EventDetailsDto.cs
@@ -0,0 +1,18 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Event details dto.
+ /// </summary>
+ public class EventDetailsDto
+ {
+ /// <summary>
+ /// Gets or sets the sub type.
+ /// </summary>
+ [JsonPropertyName("subType")]
+ public string SubType { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs
new file mode 100644
index 000000000..3d8bea362
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/GracenoteDto.cs
@@ -0,0 +1,24 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Gracenote dto.
+ /// </summary>
+ public class GracenoteDto
+ {
+ /// <summary>
+ /// Gets or sets the season.
+ /// </summary>
+ [JsonPropertyName("season")]
+ public int Season { get; set; }
+
+ /// <summary>
+ /// Gets or sets the episode.
+ /// </summary>
+ [JsonPropertyName("episode")]
+ public int Episode { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs
new file mode 100644
index 000000000..1fb3decb2
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/HeadendsDto.cs
@@ -0,0 +1,37 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Headends dto.
+ /// </summary>
+ public class HeadendsDto
+ {
+ /// <summary>
+ /// Gets or sets the headend.
+ /// </summary>
+ [JsonPropertyName("headend")]
+ public string Headend { get; set; }
+
+ /// <summary>
+ /// Gets or sets the transport.
+ /// </summary>
+ [JsonPropertyName("transport")]
+ public string Transport { get; set; }
+
+ /// <summary>
+ /// Gets or sets the location.
+ /// </summary>
+ [JsonPropertyName("location")]
+ public string Location { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of lineups.
+ /// </summary>
+ [JsonPropertyName("lineups")]
+ public List<LineupDto> Lineups { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs
new file mode 100644
index 000000000..912e680dd
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ImageDataDto.cs
@@ -0,0 +1,72 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Image data dto.
+ /// </summary>
+ public class ImageDataDto
+ {
+ /// <summary>
+ /// Gets or sets the width.
+ /// </summary>
+ [JsonPropertyName("width")]
+ public string Width { get; set; }
+
+ /// <summary>
+ /// Gets or sets the height.
+ /// </summary>
+ [JsonPropertyName("height")]
+ public string Height { get; set; }
+
+ /// <summary>
+ /// Gets or sets the uri.
+ /// </summary>
+ [JsonPropertyName("uri")]
+ public string Uri { get; set; }
+
+ /// <summary>
+ /// Gets or sets the size.
+ /// </summary>
+ [JsonPropertyName("size")]
+ public string Size { get; set; }
+
+ /// <summary>
+ /// Gets or sets the aspect.
+ /// </summary>
+ [JsonPropertyName("aspect")]
+ public string aspect { get; set; }
+
+ /// <summary>
+ /// Gets or sets the category.
+ /// </summary>
+ [JsonPropertyName("category")]
+ public string Category { get; set; }
+
+ /// <summary>
+ /// Gets or sets the text.
+ /// </summary>
+ [JsonPropertyName("text")]
+ public string Text { get; set; }
+
+ /// <summary>
+ /// Gets or sets the primary.
+ /// </summary>
+ [JsonPropertyName("primary")]
+ public string Primary { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tier.
+ /// </summary>
+ [JsonPropertyName("tier")]
+ public string Tier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the caption.
+ /// </summary>
+ [JsonPropertyName("caption")]
+ public CaptionDto Caption { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs
new file mode 100644
index 000000000..52e920aa6
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupDto.cs
@@ -0,0 +1,42 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// The lineup dto.
+ /// </summary>
+ public class LineupDto
+ {
+ /// <summary>
+ /// Gets or sets the linup.
+ /// </summary>
+ [JsonPropertyName("lineup")]
+ public string Lineup { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lineup name.
+ /// </summary>
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the transport.
+ /// </summary>
+ [JsonPropertyName("transport")]
+ public string Transport { get; set; }
+
+ /// <summary>
+ /// Gets or sets the location.
+ /// </summary>
+ [JsonPropertyName("location")]
+ public string Location { get; set; }
+
+ /// <summary>
+ /// Gets or sets the uri.
+ /// </summary>
+ [JsonPropertyName("uri")]
+ public string Uri { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs
new file mode 100644
index 000000000..15139ba3b
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LineupsDto.cs
@@ -0,0 +1,37 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Lineups dto.
+ /// </summary>
+ public class LineupsDto
+ {
+ /// <summary>
+ /// Gets or sets the response code.
+ /// </summary>
+ [JsonPropertyName("code")]
+ public int Code { get; set; }
+
+ /// <summary>
+ /// Gets or sets the server id.
+ /// </summary>
+ [JsonPropertyName("serverID")]
+ public string ServerId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the datetime.
+ /// </summary>
+ [JsonPropertyName("datetime")]
+ public string Datetime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of lineups.
+ /// </summary>
+ [JsonPropertyName("lineups")]
+ public List<LineupDto> Lineups { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs
new file mode 100644
index 000000000..7b235ed7f
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/LogoDto.cs
@@ -0,0 +1,36 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Logo dto.
+ /// </summary>
+ public class LogoDto
+ {
+ /// <summary>
+ /// Gets or sets the url.
+ /// </summary>
+ [JsonPropertyName("URL")]
+ public string Url { get; set; }
+
+ /// <summary>
+ /// Gets or sets the height.
+ /// </summary>
+ [JsonPropertyName("height")]
+ public int Height { get; set; }
+
+ /// <summary>
+ /// Gets or sets the width.
+ /// </summary>
+ [JsonPropertyName("width")]
+ public int Width { get; set; }
+
+ /// <summary>
+ /// Gets or sets the md5.
+ /// </summary>
+ [JsonPropertyName("md5")]
+ public string Md5 { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs
new file mode 100644
index 000000000..5140277b2
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MapDto.cs
@@ -0,0 +1,48 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Map dto.
+ /// </summary>
+ public class MapDto
+ {
+ /// <summary>
+ /// Gets or sets the station id.
+ /// </summary>
+ [JsonPropertyName("stationID")]
+ public string StationId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channel.
+ /// </summary>
+ [JsonPropertyName("channel")]
+ public string Channel { get; set; }
+
+ /// <summary>
+ /// Gets or sets the logical channel number.
+ /// </summary>
+ [JsonPropertyName("logicalChannelNumber")]
+ public string LogicalChannelNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets the uhfvhf.
+ /// </summary>
+ [JsonPropertyName("uhfVhf")]
+ public int UhfVhf { get; set; }
+
+ /// <summary>
+ /// Gets or sets the atsc major.
+ /// </summary>
+ [JsonPropertyName("atscMajor")]
+ public int AtscMajor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the atsc minor.
+ /// </summary>
+ [JsonPropertyName("atscMinor")]
+ public int AtscMinor { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs
new file mode 100644
index 000000000..5a3893a35
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataDto.cs
@@ -0,0 +1,30 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Metadata dto.
+ /// </summary>
+ public class MetadataDto
+ {
+ /// <summary>
+ /// Gets or sets the linup.
+ /// </summary>
+ [JsonPropertyName("lineup")]
+ public string Lineup { get; set; }
+
+ /// <summary>
+ /// Gets or sets the modified timestamp.
+ /// </summary>
+ [JsonPropertyName("modified")]
+ public string Modified { get; set; }
+
+ /// <summary>
+ /// Gets or sets the transport.
+ /// </summary>
+ [JsonPropertyName("transport")]
+ public string Transport { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs
new file mode 100644
index 000000000..4057e9802
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataProgramsDto.cs
@@ -0,0 +1,18 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Metadata programs dto.
+ /// </summary>
+ public class MetadataProgramsDto
+ {
+ /// <summary>
+ /// Gets or sets the gracenote object.
+ /// </summary>
+ [JsonPropertyName("gracenote")]
+ public GracenoteDto Gracenote { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs
new file mode 100644
index 000000000..4979296da
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MetadataScheduleDto.cs
@@ -0,0 +1,42 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Metadata schedule dto.
+ /// </summary>
+ public class MetadataScheduleDto
+ {
+ /// <summary>
+ /// Gets or sets the modified timestamp.
+ /// </summary>
+ [JsonPropertyName("modified")]
+ public string Modified { get; set; }
+
+ /// <summary>
+ /// Gets or sets the md5.
+ /// </summary>
+ [JsonPropertyName("md5")]
+ public string Md5 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the start date.
+ /// </summary>
+ [JsonPropertyName("startDate")]
+ public string StartDate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the end date.
+ /// </summary>
+ [JsonPropertyName("endDate")]
+ public string EndDate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the days count.
+ /// </summary>
+ [JsonPropertyName("days")]
+ public int Days { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs
new file mode 100644
index 000000000..48d731d89
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MovieDto.cs
@@ -0,0 +1,31 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Movie dto.
+ /// </summary>
+ public class MovieDto
+ {
+ /// <summary>
+ /// Gets or sets the year.
+ /// </summary>
+ [JsonPropertyName("year")]
+ public string Year { get; set; }
+
+ /// <summary>
+ /// Gets or sets the duration.
+ /// </summary>
+ [JsonPropertyName("duration")]
+ public int Duration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of quality rating.
+ /// </summary>
+ [JsonPropertyName("qualityRating")]
+ public List<QualityRatingDto> QualityRating { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs
new file mode 100644
index 000000000..42eddfff2
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/MultipartDto.cs
@@ -0,0 +1,24 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Multipart dto.
+ /// </summary>
+ public class MultipartDto
+ {
+ /// <summary>
+ /// Gets or sets the part number.
+ /// </summary>
+ [JsonPropertyName("partNumber")]
+ public int PartNumber { get; set; }
+
+ /// <summary>
+ /// Gets or sets the total parts.
+ /// </summary>
+ [JsonPropertyName("totalParts")]
+ public int TotalParts { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs
new file mode 100644
index 000000000..a84c47c12
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDetailsDto.cs
@@ -0,0 +1,157 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Program details dto.
+ /// </summary>
+ public class ProgramDetailsDto
+ {
+ /// <summary>
+ /// Gets or sets the audience.
+ /// </summary>
+ [JsonPropertyName("audience")]
+ public string Audience { get; set; }
+
+ /// <summary>
+ /// Gets or sets the program id.
+ /// </summary>
+ [JsonPropertyName("programID")]
+ public string ProgramId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of titles.
+ /// </summary>
+ [JsonPropertyName("titles")]
+ public List<TitleDto> Titles { get; set; }
+
+ /// <summary>
+ /// Gets or sets the event details object.
+ /// </summary>
+ [JsonPropertyName("eventDetails")]
+ public EventDetailsDto EventDetails { get; set; }
+
+ /// <summary>
+ /// Gets or sets the descriptions.
+ /// </summary>
+ [JsonPropertyName("descriptions")]
+ public DescriptionsProgramDto Descriptions { get; set; }
+
+ /// <summary>
+ /// Gets or sets the original air date.
+ /// </summary>
+ [JsonPropertyName("originalAirDate")]
+ public string OriginalAirDate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of genres.
+ /// </summary>
+ [JsonPropertyName("genres")]
+ public List<string> Genres { get; set; }
+
+ /// <summary>
+ /// Gets or sets the episode title.
+ /// </summary>
+ [JsonPropertyName("episodeTitle150")]
+ public string EpisodeTitle150 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of metadata.
+ /// </summary>
+ [JsonPropertyName("metadata")]
+ public List<MetadataProgramsDto> Metadata { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of content raitings.
+ /// </summary>
+ [JsonPropertyName("contentRating")]
+ public List<ContentRatingDto> ContentRating { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of cast.
+ /// </summary>
+ [JsonPropertyName("cast")]
+ public List<CastDto> Cast { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of crew.
+ /// </summary>
+ [JsonPropertyName("crew")]
+ public List<CrewDto> Crew { get; set; }
+
+ /// <summary>
+ /// Gets or sets the entity type.
+ /// </summary>
+ [JsonPropertyName("entityType")]
+ public string EntityType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the show type.
+ /// </summary>
+ [JsonPropertyName("showType")]
+ public string ShowType { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether there is image artwork.
+ /// </summary>
+ [JsonPropertyName("hasImageArtwork")]
+ public bool HasImageArtwork { get; set; }
+
+ /// <summary>
+ /// Gets or sets the primary image.
+ /// </summary>
+ [JsonPropertyName("primaryImage")]
+ public string PrimaryImage { get; set; }
+
+ /// <summary>
+ /// Gets or sets the thumb image.
+ /// </summary>
+ [JsonPropertyName("thumbImage")]
+ public string ThumbImage { get; set; }
+
+ /// <summary>
+ /// Gets or sets the backdrop image.
+ /// </summary>
+ [JsonPropertyName("backdropImage")]
+ public string BackdropImage { get; set; }
+
+ /// <summary>
+ /// Gets or sets the banner image.
+ /// </summary>
+ [JsonPropertyName("bannerImage")]
+ public string BannerImage { get; set; }
+
+ /// <summary>
+ /// Gets or sets the image id.
+ /// </summary>
+ [JsonPropertyName("imageID")]
+ public string ImageId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the md5.
+ /// </summary>
+ [JsonPropertyName("md5")]
+ public string Md5 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of content advisory.
+ /// </summary>
+ [JsonPropertyName("contentAdvisory")]
+ public List<string> ContentAdvisory { get; set; }
+
+ /// <summary>
+ /// Gets or sets the movie object.
+ /// </summary>
+ [JsonPropertyName("movie")]
+ public MovieDto Movie { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of recommendations.
+ /// </summary>
+ [JsonPropertyName("recommendations")]
+ public List<RecommendationDto> Recommendations { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs
new file mode 100644
index 000000000..ad5389100
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ProgramDto.cs
@@ -0,0 +1,91 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Program dto.
+ /// </summary>
+ public class ProgramDto
+ {
+ /// <summary>
+ /// Gets or sets the program id.
+ /// </summary>
+ [JsonPropertyName("programID")]
+ public string ProgramId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the air date time.
+ /// </summary>
+ [JsonPropertyName("airDateTime")]
+ public string AirDateTime { get; set; }
+
+ /// <summary>
+ /// Gets or sets the duration.
+ /// </summary>
+ [JsonPropertyName("duration")]
+ public int Duration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the md5.
+ /// </summary>
+ [JsonPropertyName("md5")]
+ public string Md5 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of audio properties.
+ /// </summary>
+ [JsonPropertyName("audioProperties")]
+ public List<string> AudioProperties { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of video properties.
+ /// </summary>
+ [JsonPropertyName("videoProperties")]
+ public List<string> VideoProperties { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of ratings.
+ /// </summary>
+ [JsonPropertyName("ratings")]
+ public List<RatingDto> Ratings { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this program is new.
+ /// </summary>
+ [JsonPropertyName("new")]
+ public bool? New { get; set; }
+
+ /// <summary>
+ /// Gets or sets the multipart object.
+ /// </summary>
+ [JsonPropertyName("multipart")]
+ public MultipartDto Multipart { get; set; }
+
+ /// <summary>
+ /// Gets or sets the live tape delay.
+ /// </summary>
+ [JsonPropertyName("liveTapeDelay")]
+ public string LiveTapeDelay { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this is the premiere.
+ /// </summary>
+ [JsonPropertyName("premiere")]
+ public bool Premiere { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this is a repeat.
+ /// </summary>
+ [JsonPropertyName("repeat")]
+ public bool Repeat { get; set; }
+
+ /// <summary>
+ /// Gets or sets the premiere or finale.
+ /// </summary>
+ [JsonPropertyName("isPremiereOrFinale")]
+ public string IsPremiereOrFinale { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs
new file mode 100644
index 000000000..5cd0a7459
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/QualityRatingDto.cs
@@ -0,0 +1,42 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Quality rating dto.
+ /// </summary>
+ public class QualityRatingDto
+ {
+ /// <summary>
+ /// Gets or sets the ratings body.
+ /// </summary>
+ [JsonPropertyName("ratingsBody")]
+ public string RatingsBody { get; set; }
+
+ /// <summary>
+ /// Gets or sets the rating.
+ /// </summary>
+ [JsonPropertyName("rating")]
+ public string Rating { get; set; }
+
+ /// <summary>
+ /// Gets or sets the min rating.
+ /// </summary>
+ [JsonPropertyName("minRating")]
+ public string MinRating { get; set; }
+
+ /// <summary>
+ /// Gets or sets the max rating.
+ /// </summary>
+ [JsonPropertyName("maxRating")]
+ public string MaxRating { get; set; }
+
+ /// <summary>
+ /// Gets or sets the increment.
+ /// </summary>
+ [JsonPropertyName("increment")]
+ public string Increment { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs
new file mode 100644
index 000000000..948b83144
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RatingDto.cs
@@ -0,0 +1,24 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Rating dto.
+ /// </summary>
+ public class RatingDto
+ {
+ /// <summary>
+ /// Gets or sets the body.
+ /// </summary>
+ [JsonPropertyName("body")]
+ public string Body { get; set; }
+
+ /// <summary>
+ /// Gets or sets the code.
+ /// </summary>
+ [JsonPropertyName("code")]
+ public string Code { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs
new file mode 100644
index 000000000..1308f45ce
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RecommendationDto.cs
@@ -0,0 +1,24 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Recommendation dto.
+ /// </summary>
+ public class RecommendationDto
+ {
+ /// <summary>
+ /// Gets or sets the program id.
+ /// </summary>
+ [JsonPropertyName("programID")]
+ public string ProgramId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the title.
+ /// </summary>
+ [JsonPropertyName("title120")]
+ public string Title120 { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs
new file mode 100644
index 000000000..fb7a31ac8
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/RequestScheduleForChannelDto.cs
@@ -0,0 +1,25 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Request schedule for channel dto.
+ /// </summary>
+ public class RequestScheduleForChannelDto
+ {
+ /// <summary>
+ /// Gets or sets the station id.
+ /// </summary>
+ [JsonPropertyName("stationID")]
+ public string StationId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of dates.
+ /// </summary>
+ [JsonPropertyName("date")]
+ public List<string> Date { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs
new file mode 100644
index 000000000..34302370d
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/ShowImagesDto.cs
@@ -0,0 +1,25 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Show image dto.
+ /// </summary>
+ public class ShowImagesDto
+ {
+ /// <summary>
+ /// Gets or sets the program id.
+ /// </summary>
+ [JsonPropertyName("programID")]
+ public string ProgramId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of data.
+ /// </summary>
+ [JsonPropertyName("data")]
+ public List<ImageDataDto> Data { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs
new file mode 100644
index 000000000..12f3576c6
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/StationDto.cs
@@ -0,0 +1,67 @@
+#nullable disable
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Station dto.
+ /// </summary>
+ public class StationDto
+ {
+ /// <summary>
+ /// Gets or sets the station id.
+ /// </summary>
+ [JsonPropertyName("stationID")]
+ public string StationId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the callsign.
+ /// </summary>
+ [JsonPropertyName("callsign")]
+ public string Callsign { get; set; }
+
+ /// <summary>
+ /// Gets or sets the broadcast language.
+ /// </summary>
+ [JsonPropertyName("broadcastLanguage")]
+ public List<string> BroadcastLanguage { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description language.
+ /// </summary>
+ [JsonPropertyName("descriptionLanguage")]
+ public List<string> DescriptionLanguage { get; set; }
+
+ /// <summary>
+ /// Gets or sets the broadcaster.
+ /// </summary>
+ [JsonPropertyName("broadcaster")]
+ public BroadcasterDto Broadcaster { get; set; }
+
+ /// <summary>
+ /// Gets or sets the affiliate.
+ /// </summary>
+ [JsonPropertyName("affiliate")]
+ public string Affiliate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the logo.
+ /// </summary>
+ [JsonPropertyName("logo")]
+ public LogoDto Logo { get; set; }
+
+ /// <summary>
+ /// Gets or set a value indicating whether it is commercial free.
+ /// </summary>
+ [JsonPropertyName("isCommercialFree")]
+ public bool? IsCommercialFree { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs
new file mode 100644
index 000000000..06c95524b
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TitleDto.cs
@@ -0,0 +1,18 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// Title dto.
+ /// </summary>
+ public class TitleDto
+ {
+ /// <summary>
+ /// Gets or sets the title.
+ /// </summary>
+ [JsonPropertyName("title120")]
+ public string Title120 { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs
new file mode 100644
index 000000000..c3ec1c7d6
--- /dev/null
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirectDtos/TokenDto.cs
@@ -0,0 +1,36 @@
+#nullable disable
+
+using System.Text.Json.Serialization;
+
+namespace Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos
+{
+ /// <summary>
+ /// The token dto.
+ /// </summary>
+ public class TokenDto
+ {
+ /// <summary>
+ /// Gets or sets the response code.
+ /// </summary>
+ [JsonPropertyName("code")]
+ public int Code { get; set; }
+
+ /// <summary>
+ /// Gets or sets the response message.
+ /// </summary>
+ [JsonPropertyName("message")]
+ public string Message { get; set; }
+
+ /// <summary>
+ /// Gets or sets the server id.
+ /// </summary>
+ [JsonPropertyName("serverID")]
+ public string ServerId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ [JsonPropertyName("token")]
+ public string Token { get; set; }
+ }
+}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index d964769b5..ce585d0fb 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -403,7 +403,7 @@ namespace Emby.Server.Implementations.LiveTv
// Set the total bitrate if not already supplied
mediaSource.InferTotalBitrate();
- if (!(service is EmbyTV.EmbyTV))
+ if (service is not EmbyTV.EmbyTV)
{
// We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
// mediaSource.SupportsDirectPlay = false;
@@ -1724,7 +1724,7 @@ namespace Emby.Server.Implementations.LiveTv
await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
- if (!(service is EmbyTV.EmbyTV))
+ if (service is not EmbyTV.EmbyTV)
{
TimerCancelled?.Invoke(this, new GenericEventArgs<TimerEventInfo>(new TimerEventInfo(id)));
}
@@ -2050,7 +2050,7 @@ namespace Emby.Server.Implementations.LiveTv
_logger.LogInformation("New recording scheduled");
- if (!(service is EmbyTV.EmbyTV))
+ if (service is not EmbyTV.EmbyTV)
{
TimerCreated?.Invoke(this, new GenericEventArgs<TimerEventInfo>(
new TimerEventInfo(newTimerId)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 3016eeda2..b2e555c7d 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -27,6 +27,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
private string _channel;
private string _program;
+
public LegacyHdHomerunChannelCommands(string url)
{
// parse url for channel and program
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index c9657f605..d28c39e21 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -13,7 +13,6 @@ using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.LiveTv;
using Microsoft.Extensions.Logging;
@@ -44,22 +43,29 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
{
- if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ if (info == null)
{
- using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
- if (!string.IsNullOrEmpty(info.UserAgent))
- {
- requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
- }
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ return File.OpenRead(info.Url);
+ }
- var response = await _httpClientFactory.CreateClient(NamedClient.Default)
- .SendAsync(requestMessage, cancellationToken)
- .ConfigureAwait(false);
- response.EnsureSuccessStatusCode();
- return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
+ if (!string.IsNullOrEmpty(info.UserAgent))
+ {
+ requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
}
- return File.OpenRead(info.Url);
+ // Set HttpCompletionOption.ResponseHeadersRead to prevent timeouts on larger files
+ var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
+ .ConfigureAwait(false);
+ response.EnsureSuccessStatusCode();
+
+ return await response.Content.ReadAsStreamAsync(cancellationToken);
}
private async Task<List<ChannelInfo>> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId)
@@ -83,7 +89,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
if (trimmedLine.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase))
{
extInf = trimmedLine.Substring(ExtInfPrefix.Length).Trim();
- _logger.LogInformation("Found m3u channel: {0}", extInf);
}
else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#'))
{
@@ -99,6 +104,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
channel.Path = trimmedLine;
channels.Add(channel);
+ _logger.LogInformation("Parsed channel: {ChannelName}", channel.Name);
extInf = string.Empty;
}
}
@@ -289,11 +295,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
- attributes.TryGetValue("tvg-name", out string name);
+ string name = nameInExtInf;
if (string.IsNullOrWhiteSpace(name))
{
- name = nameInExtInf;
+ attributes.TryGetValue("tvg-name", out name);
}
if (string.IsNullOrWhiteSpace(name))
diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json
index 4f21c66bc..18f17dda9 100644
--- a/Emby.Server.Implementations/Localization/Core/af.json
+++ b/Emby.Server.Implementations/Localization/Core/af.json
@@ -2,24 +2,24 @@
"Artists": "Kunstenare",
"Channels": "Kanale",
"Folders": "Lêergidse",
- "Favorites": "Gunstellinge",
+ "Favorites": "Gunstelinge",
"HeaderFavoriteShows": "Gunsteling Vertonings",
"ValueSpecialEpisodeName": "Spesiale - {0}",
- "HeaderAlbumArtists": "Album Kunstenaars",
+ "HeaderAlbumArtists": "Kunstenaars se Album",
"Books": "Boeke",
"HeaderNextUp": "Volgende",
"Movies": "Flieks",
"Shows": "Televisie Reekse",
"HeaderContinueWatching": "Kyk Verder",
"HeaderFavoriteEpisodes": "Gunsteling Episodes",
- "Photos": "Fotos",
+ "Photos": "Foto's",
"Playlists": "Snitlyste",
"HeaderFavoriteArtists": "Gunsteling Kunstenaars",
"HeaderFavoriteAlbums": "Gunsteling Albums",
"Sync": "Sinkroniseer",
"HeaderFavoriteSongs": "Gunsteling Liedjies",
"Songs": "Liedjies",
- "DeviceOnlineWithName": "{0} gekoppel is",
+ "DeviceOnlineWithName": "{0} is gekoppel",
"DeviceOfflineWithName": "{0} is ontkoppel",
"Collections": "Versamelings",
"Inherit": "Ontvang",
@@ -71,7 +71,7 @@
"NameSeasonUnknown": "Seisoen Onbekend",
"NameSeasonNumber": "Seisoen {0}",
"NameInstallFailed": "{0} installering het misluk",
- "MusicVideos": "Musiek videos",
+ "MusicVideos": "Musiek Videos",
"Music": "Musiek",
"MixedContent": "Gemengde inhoud",
"MessageServerConfigurationUpdated": "Bediener konfigurasie is opgedateer",
@@ -79,15 +79,15 @@
"MessageApplicationUpdatedTo": "Jellyfin Bediener is opgedateer na {0}",
"MessageApplicationUpdated": "Jellyfin Bediener is opgedateer",
"Latest": "Nuutste",
- "LabelRunningTimeValue": "Lopende tyd: {0}",
+ "LabelRunningTimeValue": "Werktyd: {0}",
"LabelIpAddressValue": "IP adres: {0}",
"ItemRemovedWithName": "{0} is uit versameling verwyder",
- "ItemAddedWithName": "{0} is in die versameling",
- "HomeVideos": "Tuis opnames",
+ "ItemAddedWithName": "{0} is by die versameling gevoeg",
+ "HomeVideos": "Tuis Videos",
"HeaderRecordingGroups": "Groep Opnames",
"Genres": "Genres",
"FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}",
- "ChapterNameValue": "Hoofstuk",
+ "ChapterNameValue": "Hoofstuk {0}",
"CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}",
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
"Albums": "Albums",
@@ -117,5 +117,7 @@
"Forced": "Geforseer",
"Default": "Oorspronklik",
"TaskCleanActivityLogDescription": "Verwyder aktiwiteitsaantekeninge ouer as die opgestelde ouderdom.",
- "TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon"
+ "TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon",
+ "TaskOptimizeDatabaseDescription": "Komprimeer databasis en verkort vrye ruimte. As hierdie taak uitgevoer word nadat die media versameling geskandeer is of ander veranderings aangebring is wat databasisaanpassings impliseer, kan dit die prestasie verbeter.",
+ "TaskOptimizeDatabase": "Optimaliseer databasis"
}
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index f55287d15..e1c923308 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Телевизия на живо",
"HeaderNextUp": "Следва",
"HeaderRecordingGroups": "Запис групи",
- "HomeVideos": "Домашни клипове",
+ "HomeVideos": "Домашни Клипове",
"Inherit": "Наследяване",
"ItemAddedWithName": "{0} е добавено към библиотеката",
"ItemRemovedWithName": "{0} е премахнато от библиотеката",
@@ -39,7 +39,7 @@
"MixedContent": "Смесено съдържание",
"Movies": "Филми",
"Music": "Музика",
- "MusicVideos": "Музикални видеа",
+ "MusicVideos": "Музикални Видеа",
"NameInstallFailed": "{0} не можа да се инсталира",
"NameSeasonNumber": "Сезон {0}",
"NameSeasonUnknown": "Неразпознат сезон",
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 1b612dc71..7715daa7c 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -5,7 +5,7 @@
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
"Books": "Llibres",
- "CameraImageUploadedFrom": "Una nova imatge de la càmera ha estat pujada des de {0}",
+ "CameraImageUploadedFrom": "S'ha pujat una nova imatge des de la camera desde {0}",
"Channels": "Canals",
"ChapterNameValue": "Capítol {0}",
"Collections": "Col·leccions",
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index 62b2b6328..4f1d231a4 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -15,7 +15,7 @@
"Favorites": "Oblíbené",
"Folders": "Složky",
"Genres": "Žánry",
- "HeaderAlbumArtists": "Umělci alba",
+ "HeaderAlbumArtists": "Album umělce",
"HeaderContinueWatching": "Pokračovat ve sledování",
"HeaderFavoriteAlbums": "Oblíbená alba",
"HeaderFavoriteArtists": "Oblíbení interpreti",
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 23d45b473..697063f26 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -1,5 +1,5 @@
{
- "Albums": "Άλμπουμς",
+ "Albums": "Άλμπουμ",
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
"Application": "Εφαρμογή",
"Artists": "Καλλιτέχνες",
@@ -15,7 +15,7 @@
"Favorites": "Αγαπημένα",
"Folders": "Φάκελοι",
"Genres": "Είδη",
- "HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
+ "HeaderAlbumArtists": "Άλμπουμ Καλλιτέχνη",
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
@@ -39,7 +39,7 @@
"MixedContent": "Ανάμεικτο Περιεχόμενο",
"Movies": "Ταινίες",
"Music": "Μουσική",
- "MusicVideos": "Μουσικά βίντεο",
+ "MusicVideos": "Μουσικά Βίντεο",
"NameInstallFailed": "{0} η εγκατάσταση απέτυχε",
"NameSeasonNumber": "Κύκλος {0}",
"NameSeasonUnknown": "Άγνωστος Κύκλος",
@@ -62,7 +62,7 @@
"NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε",
"Photos": "Φωτογραφίες",
"Playlists": "Λίστες αναπαραγωγής",
- "Plugin": "Plugin",
+ "Plugin": "Πρόσθετο",
"PluginInstalledWithName": "{0} εγκαταστήθηκε",
"PluginUninstalledWithName": "{0} έχει απεγκατασταθεί",
"PluginUpdatedWithName": "{0} έχει αναβαθμιστεί",
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Καθαρό Αρχείο Καταγραφής Δραστηριοτήτων",
"Undefined": "Απροσδιόριστο",
"Forced": "Εξαναγκασμένο",
- "Default": "Προεπιλογή"
+ "Default": "Προεπιλογή",
+ "TaskOptimizeDatabaseDescription": "Συμπιέζει τη βάση δεδομένων και δημιουργεί ελεύθερο χώρο. Η εκτέλεση αυτής της εργασίας μετά τη σάρωση της βιβλιοθήκης ή την πραγματοποίηση άλλων αλλαγών που συνεπάγονται τροποποιήσεις της βάσης δεδομένων μπορεί να βελτιώσει την απόδοση.",
+ "TaskOptimizeDatabase": "Βελτιστοποίηση βάσης δεδομένων"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index 65964f6d9..ca127cdb8 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -17,7 +17,7 @@
"Folders": "Folders",
"Forced": "Forced",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album Artists",
+ "HeaderAlbumArtists": "Artist's Album",
"HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists",
@@ -27,7 +27,7 @@
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Next Up",
"HeaderRecordingGroups": "Recording Groups",
- "HomeVideos": "Home videos",
+ "HomeVideos": "Home Videos",
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
"ItemRemovedWithName": "{0} was removed from the library",
@@ -41,7 +41,7 @@
"MixedContent": "Mixed content",
"Movies": "Movies",
"Music": "Music",
- "MusicVideos": "Music videos",
+ "MusicVideos": "Music Videos",
"NameInstallFailed": "{0} installation failed",
"NameSeasonNumber": "Season {0}",
"NameSeasonUnknown": "Season Unknown",
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 5d7ed243f..432814dac 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
@@ -15,7 +15,7 @@
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas del álbum",
+ "HeaderAlbumArtists": "Artistas del Álbum",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
@@ -25,7 +25,7 @@
"HeaderLiveTV": "TV en vivo",
"HeaderNextUp": "A continuación",
"HeaderRecordingGroups": "Grupos de grabación",
- "HomeVideos": "Videos caseros",
+ "HomeVideos": "Videos Caseros",
"Inherit": "Heredar",
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
@@ -39,7 +39,7 @@
"MixedContent": "Contenido mezclado",
"Movies": "Películas",
"Music": "Música",
- "MusicVideos": "Videos musicales",
+ "MusicVideos": "Videos Musicales",
"NameInstallFailed": "Falló la instalación de {0}",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada desconocida",
@@ -49,7 +49,7 @@
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
- "NotificationOptionInstallationFailed": "Falla de instalación",
+ "NotificationOptionInstallationFailed": "Fallo en la instalación",
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
"NotificationOptionPluginError": "Falla de complemento",
"NotificationOptionPluginInstalled": "Complemento instalado",
@@ -69,7 +69,7 @@
"ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciado",
- "ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
+ "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Programas",
"Songs": "Canciones",
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
@@ -94,9 +94,9 @@
"VersionNumber": "Versión {0}",
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
- "TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.",
+ "TaskRefreshChannelsDescription": "Actualiza la información de los canales de Internet.",
"TaskRefreshChannels": "Actualizar canales",
- "TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día.",
+ "TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día de antigüedad.",
"TaskCleanTranscode": "Limpiar directorio de transcodificado",
"TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.",
"TaskUpdatePlugins": "Actualizar complementos",
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Limpiar registro de actividades",
"Undefined": "Sin definir",
"Forced": "Forzado",
- "Default": "Predeterminado"
+ "Default": "Predeterminado",
+ "TaskOptimizeDatabase": "Optimizar base de datos",
+ "TaskOptimizeDatabaseDescription": "Compacta la base de datos y trunca el espacio libre. Puede mejorar el rendimiento si se realiza esta tarea después de escanear la biblioteca o después de realizar otros cambios que impliquen modificar la base de datos."
}
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index 7d42182b0..d3d9d2703 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -15,7 +15,7 @@
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas del álbum",
+ "HeaderAlbumArtists": "Artista del álbum",
"HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos",
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index 255d5427a..85ab1511a 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -15,7 +15,7 @@
"Favorites": "Kedvencek",
"Folders": "Könyvtárak",
"Genres": "Műfajok",
- "HeaderAlbumArtists": "Album előadók",
+ "HeaderAlbumArtists": "Előadó albumai",
"HeaderContinueWatching": "Megtekintés folytatása",
"HeaderFavoriteAlbums": "Kedvenc albumok",
"HeaderFavoriteArtists": "Kedvenc előadók",
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index 0bde76e74..5e28cf09f 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -15,7 +15,7 @@
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
- "HeaderAlbumArtists": "Artisti degli Album",
+ "HeaderAlbumArtists": "Artisti dell'Album",
"HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album Preferiti",
"HeaderFavoriteArtists": "Artisti Preferiti",
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Diretta TV",
"HeaderNextUp": "Prossimo",
"HeaderRecordingGroups": "Gruppi di Registrazione",
- "HomeVideos": "Video personali",
+ "HomeVideos": "Video Personali",
"Inherit": "Eredita",
"ItemAddedWithName": "{0} è stato aggiunto alla libreria",
"ItemRemovedWithName": "{0} è stato rimosso dalla libreria",
@@ -39,7 +39,7 @@
"MixedContent": "Contenuto misto",
"Movies": "Film",
"Music": "Musica",
- "MusicVideos": "Video musicali",
+ "MusicVideos": "Video Musicali",
"NameInstallFailed": "{0} installazione fallita",
"NameSeasonNumber": "Stagione {0}",
"NameSeasonUnknown": "Stagione sconosciuta",
diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json
index 0d90ad31c..c689bc58a 100644
--- a/Emby.Server.Implementations/Localization/Core/ja.json
+++ b/Emby.Server.Implementations/Localization/Core/ja.json
@@ -15,7 +15,7 @@
"Favorites": "お気に入り",
"Folders": "フォルダー",
"Genres": "ジャンル",
- "HeaderAlbumArtists": "アルバムアーティスト",
+ "HeaderAlbumArtists": "アーティストのアルバム",
"HeaderContinueWatching": "視聴を続ける",
"HeaderFavoriteAlbums": "お気に入りのアルバム",
"HeaderFavoriteArtists": "お気に入りのアーティスト",
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index 1b4a18deb..d28564a7c 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -15,7 +15,7 @@
"Favorites": "Tañdaulylar",
"Folders": "Qaltalar",
"Genres": "Janrlar",
- "HeaderAlbumArtists": "Älbom oryndauşylary",
+ "HeaderAlbumArtists": "Oryndauşynyñ älbomy",
"HeaderContinueWatching": "Qaraudy jalğastyru",
"HeaderFavoriteAlbums": "Tañdauly älbomdar",
"HeaderFavoriteArtists": "Tañdauly oryndauşylar",
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 409b4d26b..a37de0748 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -15,7 +15,7 @@
"Favorites": "즐겨찾기",
"Folders": "폴더",
"Genres": "장르",
- "HeaderAlbumArtists": "앨범 아티스트",
+ "HeaderAlbumArtists": "아티스트의 앨범",
"HeaderContinueWatching": "계속 시청하기",
"HeaderFavoriteAlbums": "즐겨찾는 앨범",
"HeaderFavoriteArtists": "즐겨찾는 아티스트",
diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json
index 435f9b630..09ef34913 100644
--- a/Emby.Server.Implementations/Localization/Core/ml.json
+++ b/Emby.Server.Implementations/Localization/Core/ml.json
@@ -103,7 +103,7 @@
"ValueSpecialEpisodeName": "പ്രത്യേക - {0}",
"Collections": "ശേഖരങ്ങൾ",
"Folders": "ഫോൾഡറുകൾ",
- "HeaderAlbumArtists": "ആൽബം ആർട്ടിസ്റ്റുകൾ",
+ "HeaderAlbumArtists": "കലാകാരന്റെ ആൽബം",
"Sync": "സമന്വയിപ്പിക്കുക",
"Movies": "സിനിമകൾ",
"Photos": "ഫോട്ടോകൾ",
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index 275bdec6e..e8a32a13e 100644
--- a/Emby.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
@@ -15,7 +15,7 @@
"Favorites": "Ulubione",
"Folders": "Foldery",
"Genres": "Gatunki",
- "HeaderAlbumArtists": "Wykonawcy albumów",
+ "HeaderAlbumArtists": "Album artysty",
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
"HeaderFavoriteAlbums": "Ulubione albumy",
"HeaderFavoriteArtists": "Ulubieni wykonawcy",
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Telewizja",
"HeaderNextUp": "Do obejrzenia",
"HeaderRecordingGroups": "Grupy nagrań",
- "HomeVideos": "Nagrania prywatne",
+ "HomeVideos": "Nagrania domowe",
"Inherit": "Dziedzicz",
"ItemAddedWithName": "{0} zostało dodane do biblioteki",
"ItemRemovedWithName": "{0} zostało usunięte z biblioteki",
@@ -119,5 +119,6 @@
"Undefined": "Nieustalony",
"Forced": "Wymuszony",
"Default": "Domyślne",
- "TaskOptimizeDatabase": "Optymalizuj bazę danych"
+ "TaskOptimizeDatabase": "Optymalizuj bazę danych",
+ "TaskOptimizeDatabaseDescription": "Kompaktuje bazę danych i obcina wolne miejsce. Uruchomienie tego zadania po przeskanowaniu biblioteki lub dokonaniu innych zmian, które pociągają za sobą modyfikacje bazy danych, może poprawić wydajność."
}
diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json
new file mode 100644
index 000000000..0967ef424
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/pr.json
@@ -0,0 +1 @@
+{}
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index 323dcced0..be71289b1 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Limpar Registro de Atividades",
"Undefined": "Indefinido",
"Forced": "Forçado",
- "Default": "Padrão"
+ "Default": "Padrão",
+ "TaskOptimizeDatabaseDescription": "Compactar base de dados e liberar espaço livre. Executar esta tarefa após realizar mudanças que impliquem em modificações da base de dados pode trazer melhorias de desempenho.",
+ "TaskOptimizeDatabase": "Otimizar base de dados"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index b435672ad..474dacd7c 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -117,5 +117,6 @@
"Undefined": "Indefinido",
"Forced": "Forçado",
"Default": "Predefinição",
- "TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado."
+ "TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado.",
+ "TaskOptimizeDatabase": "Otimizar base de dados"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 248f06c4b..cd016b51b 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Эфир",
"HeaderNextUp": "Очередное",
"HeaderRecordingGroups": "Группы записей",
- "HomeVideos": "Домашнее видео",
+ "HomeVideos": "Домашние видео",
"Inherit": "Наследуемое",
"ItemAddedWithName": "{0} - добавлено в медиатеку",
"ItemRemovedWithName": "{0} - изъято из медиатеки",
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 37da7d5ab..ad90bd813 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -39,7 +39,7 @@
"MixedContent": "Zmiešaný obsah",
"Movies": "Filmy",
"Music": "Hudba",
- "MusicVideos": "Hudobné videoklipy",
+ "MusicVideos": "Hudobné videá",
"NameInstallFailed": "Inštalácia {0} zlyhala",
"NameSeasonNumber": "Séria {0}",
"NameSeasonUnknown": "Neznáma séria",
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index d992bf79b..6c772c6a2 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -15,7 +15,7 @@
"Favorites": "Favoriter",
"Folders": "Mappar",
"Genres": "Genrer",
- "HeaderAlbumArtists": "Albumartister",
+ "HeaderAlbumArtists": "Artistens album",
"HeaderContinueWatching": "Fortsätt kolla",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritartister",
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Live-TV",
"HeaderNextUp": "Nästa",
"HeaderRecordingGroups": "Inspelningsgrupper",
- "HomeVideos": "Hemvideor",
+ "HomeVideos": "Hemmavideor",
"Inherit": "Ärv",
"ItemAddedWithName": "{0} lades till i biblioteket",
"ItemRemovedWithName": "{0} togs bort från biblioteket",
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Rensa Aktivitets Logg",
"Undefined": "odefinierad",
"Forced": "Tvingad",
- "Default": "Standard"
+ "Default": "Standard",
+ "TaskOptimizeDatabase": "Optimera databasen",
+ "TaskOptimizeDatabaseDescription": "Komprimerar databasen och trunkerar ledigt utrymme. Prestandan kan förbättras genom att köra denna task efter att du har skannat biblioteket eller gjort andra förändringar som indikerar att databasen har modifierats."
}
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index 771c91d59..e661299c4 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -25,7 +25,7 @@
"HeaderLiveTV": "Canlı TV",
"HeaderNextUp": "Gelecek Hafta",
"HeaderRecordingGroups": "Kayıt Grupları",
- "HomeVideos": "Ev videoları",
+ "HomeVideos": "Ana sayfa videoları",
"Inherit": "Devral",
"ItemAddedWithName": "{0} kütüphaneye eklendi",
"ItemRemovedWithName": "{0} kütüphaneden silindi",
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index 20ab1dd7d..3d69e418b 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -3,7 +3,7 @@
"Favorites": "Yêu Thích",
"Folders": "Thư Mục",
"Genres": "Thể Loại",
- "HeaderAlbumArtists": "Tuyển Tập Nghệ sĩ",
+ "HeaderAlbumArtists": "Album Nghệ sĩ",
"HeaderContinueWatching": "Xem Tiếp",
"HeaderLiveTV": "TV Trực Tiếp",
"Movies": "Phim",
@@ -82,7 +82,7 @@
"NameSeasonUnknown": "Không Rõ Mùa",
"NameSeasonNumber": "Phần {0}",
"NameInstallFailed": "{0} cài đặt thất bại",
- "MusicVideos": "Video Nhạc",
+ "MusicVideos": "Videos Nhạc",
"Music": "Nhạc",
"MixedContent": "Nội dung hỗn hợp",
"MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index faa9c40e2..f9df62724 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -7,7 +7,7 @@
"Books": "书籍",
"CameraImageUploadedFrom": "新的相机图像已从 {0} 上传",
"Channels": "频道",
- "ChapterNameValue": "第 {0} 集",
+ "ChapterNameValue": "章节 {0}",
"Collections": "合集",
"DeviceOfflineWithName": "{0} 已断开",
"DeviceOnlineWithName": "{0} 已连接",
@@ -15,8 +15,8 @@
"Favorites": "我的最爱",
"Folders": "文件夹",
"Genres": "风格",
- "HeaderAlbumArtists": "专辑作家",
- "HeaderContinueWatching": "继续观影",
+ "HeaderAlbumArtists": "专辑艺术家",
+ "HeaderContinueWatching": "继续观看",
"HeaderFavoriteAlbums": "收藏的专辑",
"HeaderFavoriteArtists": "最爱的艺术家",
"HeaderFavoriteEpisodes": "最爱的剧集",
@@ -108,8 +108,8 @@
"TaskCleanLogs": "清理日志目录",
"TaskRefreshLibraryDescription": "扫描你的媒体库以获取新文件并刷新元数据。",
"TaskRefreshLibrary": "扫描媒体库",
- "TaskRefreshChapterImagesDescription": "为包含剧集的视频提取缩略图。",
- "TaskRefreshChapterImages": "提取剧集图片",
+ "TaskRefreshChapterImagesDescription": "为包含章节的视频提取缩略图。",
+ "TaskRefreshChapterImages": "提取章节图片",
"TaskCleanCacheDescription": "删除系统不再需要的缓存文件。",
"TaskCleanCache": "清理缓存目录",
"TasksApplicationCategory": "应用程序",
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index 3dad21dcb..1cc97bc27 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -13,7 +13,7 @@
"DeviceOnlineWithName": "{0} 已經連接",
"FailedLoginAttemptWithUserName": "來自 {0} 的登入失敗",
"Favorites": "我的最愛",
- "Folders": "檔案夾",
+ "Folders": "資料夾",
"Genres": "風格",
"HeaderAlbumArtists": "專輯藝人",
"HeaderContinueWatching": "繼續觀看",
@@ -39,7 +39,7 @@
"MixedContent": "混合內容",
"Movies": "電影",
"Music": "音樂",
- "MusicVideos": "音樂視頻",
+ "MusicVideos": "音樂影片",
"NameInstallFailed": "{0} 安裝失敗",
"NameSeasonNumber": "第 {0} 季",
"NameSeasonUnknown": "未知季數",
@@ -117,5 +117,8 @@
"TaskCleanActivityLog": "清理活動記錄",
"Undefined": "未定義",
"Forced": "強制",
- "Default": "預設"
+ "Default": "預設",
+ "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。",
+ "TaskOptimizeDatabase": "最佳化數據庫",
+ "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index c3b223f63..585d81450 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -24,7 +24,7 @@
"HeaderFavoriteSongs": "最愛歌曲",
"HeaderLiveTV": "電視直播",
"HeaderNextUp": "接下來",
- "HomeVideos": "自製影片",
+ "HomeVideos": "家庭影片",
"ItemAddedWithName": "{0} 已新增至媒體庫",
"ItemRemovedWithName": "{0} 已從媒體庫移除",
"LabelIpAddressValue": "IP 位址:{0}",
@@ -117,5 +117,7 @@
"TaskCleanActivityLog": "清除活動紀錄",
"Undefined": "未定義的",
"Forced": "強制",
- "Default": "原本"
+ "Default": "原本",
+ "TaskOptimizeDatabaseDescription": "縮小資料庫並釋放可用空間。在掃描資料庫或進行資料庫相關的更動後使用此功能會增加效能。",
+ "TaskOptimizeDatabase": "最佳化資料庫"
}
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 9808e47de..03919197e 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -38,10 +36,10 @@ namespace Emby.Server.Implementations.Localization
private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
- private List<CultureDto> _cultures;
-
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
+ private List<CultureDto> _cultures = new List<CultureDto>();
+
/// <summary>
/// Initializes a new instance of the <see cref="LocalizationManager" /> class.
/// </summary>
@@ -72,8 +70,8 @@ namespace Emby.Server.Implementations.Localization
string countryCode = resource.Substring(RatingsPath.Length, 2);
var dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase);
- await using var str = _assembly.GetManifestResourceStream(resource);
- using var reader = new StreamReader(str);
+ await using var stream = _assembly.GetManifestResourceStream(resource);
+ using var reader = new StreamReader(stream!); // shouldn't be null here, we just got the resource path from Assembly.GetManifestResourceNames()
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
{
if (string.IsNullOrWhiteSpace(line))
@@ -113,7 +111,8 @@ namespace Emby.Server.Implementations.Localization
{
List<CultureDto> list = new List<CultureDto>();
- await using var stream = _assembly.GetManifestResourceStream(CulturesPath);
+ await using var stream = _assembly.GetManifestResourceStream(CulturesPath)
+ ?? throw new InvalidOperationException($"Invalid resource path: '{CulturesPath}'");
using var reader = new StreamReader(stream);
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
{
@@ -162,7 +161,7 @@ namespace Emby.Server.Implementations.Localization
}
/// <inheritdoc />
- public CultureDto FindLanguageInfo(string language)
+ public CultureDto? FindLanguageInfo(string language)
{
// TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs
for (var i = 0; i < _cultures.Count; i++)
@@ -183,9 +182,10 @@ namespace Emby.Server.Implementations.Localization
/// <inheritdoc />
public IEnumerable<CountryInfo> GetCountries()
{
- using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream(CountriesPath));
-
- return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions);
+ using StreamReader reader = new StreamReader(
+ _assembly.GetManifestResourceStream(CountriesPath) ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'"));
+ return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions)
+ ?? throw new InvalidOperationException($"Resource contains invalid data: '{CountriesPath}'");
}
/// <inheritdoc />
@@ -205,7 +205,9 @@ namespace Emby.Server.Implementations.Localization
countryCode = "us";
}
- return GetRatings(countryCode) ?? GetRatings("us");
+ return GetRatings(countryCode)
+ ?? GetRatings("us")
+ ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'");
}
/// <summary>
@@ -213,7 +215,7 @@ namespace Emby.Server.Implementations.Localization
/// </summary>
/// <param name="countryCode">The country code.</param>
/// <returns>The ratings.</returns>
- private Dictionary<string, ParentalRating> GetRatings(string countryCode)
+ private Dictionary<string, ParentalRating>? GetRatings(string countryCode)
{
_allParentalRatings.TryGetValue(countryCode, out var value);
@@ -238,7 +240,7 @@ namespace Emby.Server.Implementations.Localization
var ratingsDictionary = GetParentalRatingsDictionary();
- if (ratingsDictionary.TryGetValue(rating, out ParentalRating value))
+ if (ratingsDictionary.TryGetValue(rating, out ParentalRating? value))
{
return value.Value;
}
@@ -269,20 +271,6 @@ namespace Emby.Server.Implementations.Localization
}
/// <inheritdoc />
- public bool HasUnicodeCategory(string value, UnicodeCategory category)
- {
- foreach (var chr in value)
- {
- if (char.GetUnicodeCategory(chr) == category)
- {
- return true;
- }
- }
-
- return false;
- }
-
- /// <inheritdoc />
public string GetLocalizedString(string phrase)
{
return GetLocalizedString(phrase, _configurationManager.Configuration.UICulture);
@@ -347,18 +335,21 @@ namespace Emby.Server.Implementations.Localization
{
await using var stream = _assembly.GetManifestResourceStream(resourcePath);
// If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
- if (stream != null)
+ if (stream == null)
{
- var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
+ _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
+ return;
+ }
- foreach (var key in dict.Keys)
- {
- dictionary[key] = dict[key];
- }
+ var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
+ if (dict == null)
+ {
+ throw new InvalidOperationException($"Resource contains invalid data: '{stream}'");
}
- else
+
+ foreach (var key in dict.Keys)
{
- _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
+ dictionary[key] = dict[key];
}
}
diff --git a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
index 358606b0d..4160f3a50 100644
--- a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
+++ b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
@@ -49,5 +49,10 @@ namespace Emby.Server.Implementations.Playlists
query.Parent = null;
return LibraryManager.GetItemsResult(query);
}
+
+ public override string GetClientTypeName()
+ {
+ return "ManualPlaylistsFolder";
+ }
}
}
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 8cafde38e..b07798fa4 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -260,7 +260,7 @@ namespace Emby.Server.Implementations.Playlists
public async Task RemoveFromPlaylistAsync(string playlistId, IEnumerable<string> entryIds)
{
- if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
+ if (_libraryManager.GetItemById(playlistId) is not Playlist playlist)
{
throw new ArgumentException("No Playlist exists with the supplied Id");
}
@@ -293,7 +293,7 @@ namespace Emby.Server.Implementations.Playlists
public async Task MoveItemAsync(string playlistId, string entryId, int newIndex)
{
- if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
+ if (_libraryManager.GetItemById(playlistId) is not Playlist playlist)
{
throw new ArgumentException("No Playlist exists with the supplied Id");
}
diff --git a/Emby.Server.Implementations/Properties/AssemblyInfo.cs b/Emby.Server.Implementations/Properties/AssemblyInfo.cs
index cb7972173..41c396ac1 100644
--- a/Emby.Server.Implementations/Properties/AssemblyInfo.cs
+++ b/Emby.Server.Implementations/Properties/AssemblyInfo.cs
@@ -16,6 +16,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")]
+[assembly: InternalsVisibleTo("Emby.Server.Implementations.Fuzz")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index b34325041..fb93c375d 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -24,7 +24,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// </summary>
public class ScheduledTaskWorker : IScheduledTaskWorker
{
-
/// <summary>
/// Gets or sets the application paths.
/// </summary>
@@ -267,7 +266,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
}
/// <summary>
- /// Gets the triggers that define when the task will run.
+ /// Gets or sets the triggers that define when the task will run.
/// </summary>
/// <value>The triggers.</value>
/// <exception cref="ArgumentNullException"><c>value</c> is <c>null</c>.</exception>
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
index 19600b1e6..79886cb52 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
@@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
var retentionDays = _serverConfigurationManager.Configuration.ActivityLogRetentionDays;
- if (!retentionDays.HasValue || retentionDays <= 0)
+ if (!retentionDays.HasValue || retentionDays < 0)
{
throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
}
diff --git a/Emby.Server.Implementations/Sorting/ArtistComparer.cs b/Emby.Server.Implementations/Sorting/ArtistComparer.cs
index 98bee3fd9..7b7ba5753 100644
--- a/Emby.Server.Implementations/Sorting/ArtistComparer.cs
+++ b/Emby.Server.Implementations/Sorting/ArtistComparer.cs
@@ -27,7 +27,7 @@ namespace Emby.Server.Implementations.Sorting
/// <returns>System.String.</returns>
private static string? GetValue(BaseItem? x)
{
- if (!(x is Audio audio))
+ if (x is not Audio audio)
{
return string.Empty;
}