aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs18
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs79
-rw-r--r--Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs2
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs347
-rw-r--r--Emby.Server.Implementations/Data/SqliteUserDataRepository.cs10
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs43
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj10
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs2
-rw-r--r--Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs13
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs4
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs16
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs2
-rw-r--r--Emby.Server.Implementations/Images/BaseFolderImageProvider.cs2
-rw-r--r--Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs4
-rw-r--r--Emby.Server.Implementations/Images/DynamicImageProvider.cs10
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs142
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs6
-rw-r--r--Emby.Server.Implementations/Library/MediaStreamSelector.cs90
-rw-r--r--Emby.Server.Implementations/Library/MusicManager.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs31
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs16
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs6
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs24
-rw-r--r--Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs79
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs20
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs132
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs35
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs33
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs10
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs124
-rw-r--r--Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs18
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs2
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json8
-rw-r--r--Emby.Server.Implementations/Localization/Core/cy.json54
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json5
-rw-r--r--Emby.Server.Implementations/Localization/Core/eo.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/fa.json5
-rw-r--r--Emby.Server.Implementations/Localization/Core/fi.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/hi.json7
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/mr.json44
-rw-r--r--Emby.Server.Implementations/Localization/Core/pr.json15
-rw-r--r--Emby.Server.Implementations/Localization/Core/ro.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ta.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ug.json9
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json2
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs8
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/au.csv1
-rw-r--r--Emby.Server.Implementations/Net/SocketFactory.cs20
-rw-r--r--Emby.Server.Implementations/Net/UdpSocket.cs4
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs6
-rw-r--r--Emby.Server.Implementations/Plugins/PluginManager.cs2
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs6
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs4
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs15
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs11
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs9
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs13
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs17
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs17
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs57
-rw-r--r--Emby.Server.Implementations/Sorting/IndexNumberComparer.cs50
-rw-r--r--Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs50
-rw-r--r--Emby.Server.Implementations/SyncPlay/Group.cs2
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs60
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs10
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs8
90 files changed, 1072 insertions, 939 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 814c10196..82294644b 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -44,9 +44,9 @@ using Emby.Server.Implementations.Serialization;
using Emby.Server.Implementations.Session;
using Emby.Server.Implementations.SyncPlay;
using Emby.Server.Implementations.TV;
-using Emby.Server.Implementations.Udp;
using Emby.Server.Implementations.Updates;
using Jellyfin.Api.Helpers;
+using Jellyfin.MediaEncoding.Hls.Playlist;
using Jellyfin.Networking.Configuration;
using Jellyfin.Networking.Manager;
using MediaBrowser.Common;
@@ -103,6 +103,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Prometheus.DotNetRuntime;
+using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager;
namespace Emby.Server.Implementations
@@ -149,7 +150,7 @@ namespace Emby.Server.Implementations
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
/// <param name="options">Instance of the <see cref="IStartupOptions"/> interface.</param>
/// <param name="startupConfig">The <see cref="IConfiguration" /> interface.</param>
- public ApplicationHost(
+ protected ApplicationHost(
IServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
IStartupOptions options,
@@ -184,6 +185,11 @@ namespace Emby.Server.Implementations
public event EventHandler HasPendingRestartChanged;
/// <summary>
+ /// Gets the value of the PublishedServerUrl setting.
+ /// </summary>
+ private string PublishedServerUrl => _startupConfig[AddressOverrideKey];
+
+ /// <summary>
/// Gets a value indicating whether this instance can self restart.
/// </summary>
public bool CanSelfRestart => _startupOptions.RestartPath != null;
@@ -259,11 +265,6 @@ namespace Emby.Server.Implementations
/// </summary>
public int HttpsPort { get; private set; }
- /// <summary>
- /// Gets the value of the PublishedServerUrl setting.
- /// </summary>
- public string PublishedServerUrl => _startupOptions.PublishedServerUrl ?? _startupConfig[UdpServer.AddressOverrideConfigKey];
-
/// <inheritdoc />
public Version ApplicationVersion { get; }
@@ -999,6 +1000,9 @@ namespace Emby.Server.Implementations
// Network
yield return typeof(NetworkManager).Assembly;
+ // Hls
+ yield return typeof(DynamicHlsPlaylistGenerator).Assembly;
+
foreach (var i in GetAssembliesWithPartsInternal())
{
yield return i;
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 43c8a451b..92a85e862 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.Channels
/// <summary>
/// The LiveTV channel manager.
/// </summary>
- public class ChannelManager : IChannelManager
+ public class ChannelManager : IChannelManager, IDisposable
{
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataManager;
@@ -52,6 +52,7 @@ namespace Emby.Server.Implementations.Channels
private readonly IMemoryCache _memoryCache;
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
+ private bool _disposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="ChannelManager"/> class.
@@ -161,7 +162,7 @@ namespace Emby.Server.Implementations.Channels
/// <inheritdoc />
public QueryResult<Channel> GetChannelsInternal(ChannelQuery query)
{
- var user = query.UserId.Equals(Guid.Empty)
+ var user = query.UserId.Equals(default)
? null
: _userManager.GetUserById(query.UserId);
@@ -264,17 +265,16 @@ namespace Emby.Server.Implementations.Channels
}
}
- return new QueryResult<Channel>
- {
- Items = all,
- TotalRecordCount = totalCount
- };
+ return new QueryResult<Channel>(
+ query.StartIndex,
+ totalCount,
+ all);
}
/// <inheritdoc />
public QueryResult<BaseItemDto> GetChannels(ChannelQuery query)
{
- var user = query.UserId.Equals(Guid.Empty)
+ var user = query.UserId.Equals(default)
? null
: _userManager.GetUserById(query.UserId);
@@ -285,11 +285,10 @@ namespace Emby.Server.Implementations.Channels
// TODO Fix The co-variant conversion (internalResult.Items) between Folder[] and BaseItem[], this can generate runtime issues.
var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user);
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnItems,
- TotalRecordCount = internalResult.TotalRecordCount
- };
+ var result = new QueryResult<BaseItemDto>(
+ query.StartIndex,
+ internalResult.TotalRecordCount,
+ returnItems);
return result;
}
@@ -333,7 +332,7 @@ namespace Emby.Server.Implementations.Channels
private Channel GetChannelEntity(IChannel channel)
{
- return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).Result;
+ return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).GetAwaiter().GetResult();
}
private MediaSourceInfo[] GetSavedMediaSources(BaseItem item)
@@ -475,7 +474,7 @@ namespace Emby.Server.Implementations.Channels
item.ChannelId = id;
- if (item.ParentId != parentFolderId)
+ if (!item.ParentId.Equals(parentFolderId))
{
forceUpdate = true;
}
@@ -620,11 +619,10 @@ namespace Emby.Server.Implementations.Channels
var returnItems = _dtoService.GetBaseItemDtos(items, query.DtoOptions, query.User);
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnItems,
- TotalRecordCount = totalRecordCount
- };
+ var result = new QueryResult<BaseItemDto>(
+ query.StartIndex,
+ totalRecordCount,
+ returnItems);
return result;
}
@@ -717,7 +715,9 @@ namespace Emby.Server.Implementations.Channels
// Find the corresponding channel provider plugin
var channelProvider = GetChannelProvider(channel);
- var parentItem = query.ParentId == Guid.Empty ? channel : _libraryManager.GetItemById(query.ParentId);
+ var parentItem = query.ParentId.Equals(default)
+ ? channel
+ : _libraryManager.GetItemById(query.ParentId);
var itemsResult = await GetChannelItems(
channelProvider,
@@ -728,7 +728,7 @@ namespace Emby.Server.Implementations.Channels
cancellationToken)
.ConfigureAwait(false);
- if (query.ParentId == Guid.Empty)
+ if (query.ParentId.Equals(default))
{
query.Parent = channel;
}
@@ -786,11 +786,10 @@ namespace Emby.Server.Implementations.Channels
var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, query.DtoOptions, query.User);
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnItems,
- TotalRecordCount = internalResult.TotalRecordCount
- };
+ var result = new QueryResult<BaseItemDto>(
+ query.StartIndex,
+ internalResult.TotalRecordCount,
+ returnItems);
return result;
}
@@ -1217,5 +1216,31 @@ namespace Emby.Server.Implementations.Channels
return result;
}
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and optionally managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ _resourcePool?.Dispose();
+ }
+
+ _disposed = true;
+ }
}
}
diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
index e5dde48d8..cfd08e653 100644
--- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Channels
public string Key => "RefreshInternetChannels";
/// <inheritdoc />
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var manager = (ChannelManager)_channelManager;
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index b5b8fea65..5fc2e39a7 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -265,7 +265,7 @@ namespace Emby.Server.Implementations.Collections
{
var childItem = _libraryManager.GetItemById(guidId);
- var child = collection.LinkedChildren.FirstOrDefault(i => (i.ItemId.HasValue && i.ItemId.Value == guidId) || (childItem != null && string.Equals(childItem.Path, i.Path, StringComparison.OrdinalIgnoreCase)));
+ var child = collection.LinkedChildren.FirstOrDefault(i => (i.ItemId.HasValue && i.ItemId.Value.Equals(guidId)) || (childItem != null && string.Equals(childItem.Path, i.Path, StringComparison.OrdinalIgnoreCase)));
if (child == null)
{
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 5ab9e02fe..85fa79cba 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -317,11 +317,6 @@ namespace Emby.Server.Implementations.Data
IImageProcessor imageProcessor)
: base(logger)
{
- if (config == null)
- {
- throw new ArgumentNullException(nameof(config));
- }
-
_config = config;
_appHost = appHost;
_localization = localization;
@@ -334,9 +329,6 @@ namespace Emby.Server.Implementations.Data
}
/// <inheritdoc />
- public string Name => "SQLite";
-
- /// <inheritdoc />
protected override int? CacheSize => 20000;
/// <inheritdoc />
@@ -573,22 +565,6 @@ namespace Emby.Server.Implementations.Data
userDataRepo.Initialize(userManager, WriteLock, WriteConnection);
}
- /// <summary>
- /// Save a standard item in the repo.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <exception cref="ArgumentNullException"><paramref name="item"/> is <c>null</c>.</exception>
- public void SaveItem(BaseItem item, CancellationToken cancellationToken)
- {
- if (item == null)
- {
- throw new ArgumentNullException(nameof(item));
- }
-
- SaveItems(new[] { item }, cancellationToken);
- }
-
public void SaveImages(BaseItem item)
{
if (item == null)
@@ -605,7 +581,7 @@ namespace Emby.Server.Implementations.Data
{
using (var saveImagesStatement = PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id"))
{
- saveImagesStatement.TryBind("@Id", item.Id.ToByteArray());
+ saveImagesStatement.TryBind("@Id", item.Id);
saveImagesStatement.TryBind("@Images", SerializeImages(item.ImageInfos));
saveImagesStatement.MoveNext();
@@ -750,7 +726,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBindNull("@EndDate");
}
- saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(Guid.Empty) ? null : item.ChannelId.ToString("N", CultureInfo.InvariantCulture));
+ saveItemStatement.TryBind("@ChannelId", item.ChannelId.Equals(default) ? null : item.ChannelId.ToString("N", CultureInfo.InvariantCulture));
if (item is IHasProgramAttributes hasProgramAttributes)
{
@@ -780,7 +756,7 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.TryBind("@ProductionYear", item.ProductionYear);
var parentId = item.ParentId;
- if (parentId.Equals(Guid.Empty))
+ if (parentId.Equals(default))
{
saveItemStatement.TryBindNull("@ParentId");
}
@@ -975,7 +951,7 @@ namespace Emby.Server.Implementations.Data
{
saveItemStatement.TryBind("@SeasonName", episode.SeasonName);
- var nullableSeasonId = episode.SeasonId == Guid.Empty ? (Guid?)null : episode.SeasonId;
+ var nullableSeasonId = episode.SeasonId.Equals(default) ? (Guid?)null : episode.SeasonId;
saveItemStatement.TryBind("@SeasonId", nullableSeasonId);
}
@@ -987,7 +963,7 @@ namespace Emby.Server.Implementations.Data
if (item is IHasSeries hasSeries)
{
- var nullableSeriesId = hasSeries.SeriesId.Equals(Guid.Empty) ? (Guid?)null : hasSeries.SeriesId;
+ var nullableSeriesId = hasSeries.SeriesId.Equals(default) ? (Guid?)null : hasSeries.SeriesId;
saveItemStatement.TryBind("@SeriesId", nullableSeriesId);
saveItemStatement.TryBind("@SeriesPresentationUniqueKey", hasSeries.SeriesPresentationUniqueKey);
@@ -1060,7 +1036,7 @@ namespace Emby.Server.Implementations.Data
}
Guid ownerId = item.OwnerId;
- if (ownerId == Guid.Empty)
+ if (ownerId.Equals(default))
{
saveItemStatement.TryBindNull("@OwnerId");
}
@@ -1198,13 +1174,15 @@ namespace Emby.Server.Implementations.Data
bldr.Append(Delimiter)
// Replace delimiters with other characters.
// This can be removed when we migrate to a proper DB.
- .Append(hash.Replace('*', '/').Replace('|', '\\'));
+ .Append(hash.Replace(Delimiter, '/').Replace('|', '\\'));
}
}
internal ItemImageInfo ItemImageInfoFromValueString(ReadOnlySpan<char> value)
{
- var nextSegment = value.IndexOf('*');
+ const char Delimiter = '*';
+
+ var nextSegment = value.IndexOf(Delimiter);
if (nextSegment == -1)
{
return null;
@@ -1212,7 +1190,7 @@ namespace Emby.Server.Implementations.Data
ReadOnlySpan<char> path = value[..nextSegment];
value = value[(nextSegment + 1)..];
- nextSegment = value.IndexOf('*');
+ nextSegment = value.IndexOf(Delimiter);
if (nextSegment == -1)
{
return null;
@@ -1220,7 +1198,7 @@ namespace Emby.Server.Implementations.Data
ReadOnlySpan<char> dateModified = value[..nextSegment];
value = value[(nextSegment + 1)..];
- nextSegment = value.IndexOf('*');
+ nextSegment = value.IndexOf(Delimiter);
if (nextSegment == -1)
{
nextSegment = value.Length;
@@ -1257,7 +1235,7 @@ namespace Emby.Server.Implementations.Data
if (nextSegment + 1 < value.Length - 1)
{
value = value[(nextSegment + 1)..];
- nextSegment = value.IndexOf('*');
+ nextSegment = value.IndexOf(Delimiter);
if (nextSegment == -1 || nextSegment == value.Length)
{
return image;
@@ -1266,7 +1244,7 @@ namespace Emby.Server.Implementations.Data
ReadOnlySpan<char> widthSpan = value[..nextSegment];
value = value[(nextSegment + 1)..];
- nextSegment = value.IndexOf('*');
+ nextSegment = value.IndexOf(Delimiter);
if (nextSegment == -1)
{
nextSegment = value.Length;
@@ -1292,7 +1270,7 @@ namespace Emby.Server.Implementations.Data
var c = value[i];
blurHashSpan[i] = c switch
{
- '/' => '*',
+ '/' => Delimiter,
'\\' => '|',
_ => c
};
@@ -1314,7 +1292,7 @@ namespace Emby.Server.Implementations.Data
/// <exception cref="ArgumentException"><paramr name="id"/> is <seealso cref="Guid.Empty"/>.</exception>
public BaseItem RetrieveItem(Guid id)
{
- if (id == Guid.Empty)
+ if (id.Equals(default))
{
throw new ArgumentException("Guid can't be empty", nameof(id));
}
@@ -2086,7 +2064,7 @@ namespace Emby.Server.Implementations.Data
{
CheckDisposed();
- if (id.Equals(Guid.Empty))
+ if (id.Equals(default))
{
throw new ArgumentNullException(nameof(id));
}
@@ -2492,12 +2470,12 @@ namespace Emby.Server.Implementations.Data
searchTerm = GetCleanValue(searchTerm);
var commandText = statement.SQL;
- if (commandText.IndexOf("@SearchTermStartsWith", StringComparison.OrdinalIgnoreCase) != -1)
+ if (commandText.Contains("@SearchTermStartsWith", StringComparison.OrdinalIgnoreCase))
{
statement.TryBind("@SearchTermStartsWith", searchTerm + "%");
}
- if (commandText.IndexOf("@SearchTermContains", StringComparison.OrdinalIgnoreCase) != -1)
+ if (commandText.Contains("@SearchTermContains", StringComparison.OrdinalIgnoreCase))
{
statement.TryBind("@SearchTermContains", "%" + searchTerm + "%");
}
@@ -2514,17 +2492,17 @@ namespace Emby.Server.Implementations.Data
var commandText = statement.SQL;
- if (commandText.IndexOf("@ItemOfficialRating", StringComparison.OrdinalIgnoreCase) != -1)
+ if (commandText.Contains("@ItemOfficialRating", StringComparison.OrdinalIgnoreCase))
{
statement.TryBind("@ItemOfficialRating", item.OfficialRating);
}
- if (commandText.IndexOf("@ItemProductionYear", StringComparison.OrdinalIgnoreCase) != -1)
+ if (commandText.Contains("@ItemProductionYear", StringComparison.OrdinalIgnoreCase))
{
statement.TryBind("@ItemProductionYear", item.ProductionYear ?? 0);
}
- if (commandText.IndexOf("@SimilarItemId", StringComparison.OrdinalIgnoreCase) != -1)
+ if (commandText.Contains("@SimilarItemId", StringComparison.OrdinalIgnoreCase))
{
statement.TryBind("@SimilarItemId", item.Id);
}
@@ -2758,12 +2736,12 @@ namespace Emby.Server.Implementations.Data
foreach (var providerId in newItem.ProviderIds)
{
- if (providerId.Key == MetadataProvider.TmdbCollection.ToString())
+ if (string.Equals(providerId.Key, nameof(MetadataProvider.TmdbCollection), StringComparison.Ordinal))
{
continue;
}
- if (item.GetProviderId(providerId.Key) == providerId.Value)
+ if (string.Equals(item.GetProviderId(providerId.Key), providerId.Value, StringComparison.Ordinal))
{
if (newItem.SourceType == SourceType.Library)
{
@@ -2810,11 +2788,10 @@ namespace Emby.Server.Implementations.Data
if (!query.EnableTotalRecordCount || (!query.Limit.HasValue && (query.StartIndex ?? 0) == 0))
{
var returnList = GetItemList(query);
- return new QueryResult<BaseItem>
- {
- Items = returnList,
- TotalRecordCount = returnList.Count
- };
+ return new QueryResult<BaseItem>(
+ query.StartIndex,
+ returnList.Count,
+ returnList);
}
var now = DateTime.UtcNow;
@@ -2978,6 +2955,7 @@ namespace Emby.Server.Implementations.Data
ReadTransactionMode);
}
+ result.StartIndex = query.StartIndex ?? 0;
result.Items = list;
return result;
}
@@ -3185,220 +3163,6 @@ namespace Emby.Server.Implementations.Data
return list;
}
- public List<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query)
- {
- if (query == null)
- {
- throw new ArgumentNullException(nameof(query));
- }
-
- CheckDisposed();
-
- var now = DateTime.UtcNow;
-
- var columns = new List<string> { "guid", "path" };
- SetFinalColumnsToSelect(query, columns);
- var commandText = "select " + string.Join(',', columns) + FromText;
-
- var whereClauses = GetWhereClauses(query, null);
- if (whereClauses.Count != 0)
- {
- commandText += " where " + string.Join(" AND ", whereClauses);
- }
-
- commandText += GetGroupBy(query)
- + GetOrderByText(query);
-
- if (query.Limit.HasValue || query.StartIndex.HasValue)
- {
- var offset = query.StartIndex ?? 0;
-
- if (query.Limit.HasValue || offset > 0)
- {
- commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
- }
-
- if (offset > 0)
- {
- commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
- }
- }
-
- var list = new List<Tuple<Guid, string>>();
- using (var connection = GetConnection(true))
- {
- using (var statement = PrepareStatement(connection, commandText))
- {
- if (EnableJoinUserData(query))
- {
- statement.TryBind("@UserId", query.User.InternalId);
- }
-
- // Running this again will bind the params
- GetWhereClauses(query, statement);
-
- foreach (var row in statement.ExecuteQuery())
- {
- var id = row.GetGuid(0);
-
- row.TryGetString(1, out var path);
-
- list.Add(new Tuple<Guid, string>(id, path));
- }
- }
- }
-
- LogQueryTime("GetItemIdsWithPath", commandText, now);
-
- return list;
- }
-
- public QueryResult<Guid> GetItemIds(InternalItemsQuery query)
- {
- if (query == null)
- {
- throw new ArgumentNullException(nameof(query));
- }
-
- CheckDisposed();
-
- if (!query.EnableTotalRecordCount || (!query.Limit.HasValue && (query.StartIndex ?? 0) == 0))
- {
- var returnList = GetItemIdsList(query);
- return new QueryResult<Guid>
- {
- Items = returnList,
- TotalRecordCount = returnList.Count
- };
- }
-
- var now = DateTime.UtcNow;
-
- var columns = new List<string> { "guid" };
- SetFinalColumnsToSelect(query, columns);
- var commandText = "select "
- + string.Join(',', columns)
- + FromText
- + GetJoinUserDataText(query);
-
- var whereClauses = GetWhereClauses(query, null);
-
- var whereText = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses);
-
- commandText += whereText
- + GetGroupBy(query)
- + GetOrderByText(query);
-
- if (query.Limit.HasValue || query.StartIndex.HasValue)
- {
- var offset = query.StartIndex ?? 0;
-
- if (query.Limit.HasValue || offset > 0)
- {
- commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture);
- }
-
- if (offset > 0)
- {
- commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture);
- }
- }
-
- var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
-
- var statementTexts = new List<string>();
- if (!isReturningZeroItems)
- {
- statementTexts.Add(commandText);
- }
-
- if (query.EnableTotalRecordCount)
- {
- commandText = string.Empty;
-
- List<string> columnsToSelect;
- if (EnableGroupByPresentationUniqueKey(query))
- {
- columnsToSelect = new List<string> { "count (distinct PresentationUniqueKey)" };
- }
- else if (query.GroupBySeriesPresentationUniqueKey)
- {
- columnsToSelect = new List<string> { "count (distinct SeriesPresentationUniqueKey)" };
- }
- else
- {
- columnsToSelect = new List<string> { "count (guid)" };
- }
-
- SetFinalColumnsToSelect(query, columnsToSelect);
- commandText += " select " + string.Join(',', columnsToSelect) + FromText;
-
- commandText += GetJoinUserDataText(query)
- + whereText;
- statementTexts.Add(commandText);
- }
-
- var list = new List<Guid>();
- var result = new QueryResult<Guid>();
- using (var connection = GetConnection(true))
- {
- connection.RunInTransaction(
- db =>
- {
- var statements = PrepareAll(db, statementTexts);
-
- if (!isReturningZeroItems)
- {
- using (var statement = statements[0])
- {
- if (EnableJoinUserData(query))
- {
- statement.TryBind("@UserId", query.User.InternalId);
- }
-
- BindSimilarParams(query, statement);
- BindSearchParams(query, statement);
-
- // Running this again will bind the params
- GetWhereClauses(query, statement);
-
- foreach (var row in statement.ExecuteQuery())
- {
- list.Add(row[0].ReadGuidFromBlob());
- }
- }
- }
-
- if (query.EnableTotalRecordCount)
- {
- using (var statement = statements[statements.Length - 1])
- {
- if (EnableJoinUserData(query))
- {
- statement.TryBind("@UserId", query.User.InternalId);
- }
-
- BindSimilarParams(query, statement);
- BindSearchParams(query, statement);
-
- // Running this again will bind the params
- GetWhereClauses(query, statement);
-
- result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
- }
- }
- },
- ReadTransactionMode);
- }
-
- LogQueryTime("GetItemIds", commandText, now);
-
- result.Items = list;
- return result;
- }
-
private bool IsAlphaNumeric(string str)
{
if (string.IsNullOrWhiteSpace(str))
@@ -3665,7 +3429,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add($"ChannelId in ({inClause})");
}
- if (!query.ParentId.Equals(Guid.Empty))
+ if (!query.ParentId.Equals(default))
{
whereClauses.Add("ParentId=@ParentId");
statement?.TryBind("@ParentId", query.ParentId);
@@ -4025,7 +3789,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))");
if (statement != null)
{
- statement.TryBind(paramName, artistId.ToByteArray());
+ statement.TryBind(paramName, artistId);
}
index++;
@@ -4046,7 +3810,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))");
if (statement != null)
{
- statement.TryBind(paramName, artistId.ToByteArray());
+ statement.TryBind(paramName, artistId);
}
index++;
@@ -4067,7 +3831,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type=1))");
if (statement != null)
{
- statement.TryBind(paramName, artistId.ToByteArray());
+ statement.TryBind(paramName, artistId);
}
index++;
@@ -4088,7 +3852,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")");
if (statement != null)
{
- statement.TryBind(paramName, albumId.ToByteArray());
+ statement.TryBind(paramName, albumId);
}
index++;
@@ -4109,7 +3873,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("(guid not in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))");
if (statement != null)
{
- statement.TryBind(paramName, artistId.ToByteArray());
+ statement.TryBind(paramName, artistId);
}
index++;
@@ -4130,7 +3894,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))");
if (statement != null)
{
- statement.TryBind(paramName, genreId.ToByteArray());
+ statement.TryBind(paramName, genreId);
}
index++;
@@ -4209,7 +3973,7 @@ namespace Emby.Server.Implementations.Data
if (statement != null)
{
- statement.TryBind(paramName, studioId.ToByteArray());
+ statement.TryBind(paramName, studioId);
}
index++;
@@ -4494,7 +4258,7 @@ namespace Emby.Server.Implementations.Data
var index = 0;
foreach (var pair in query.ExcludeProviderIds)
{
- if (string.Equals(pair.Key, MetadataProvider.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(pair.Key, nameof(MetadataProvider.TmdbCollection), StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -4524,7 +4288,7 @@ namespace Emby.Server.Implementations.Data
var index = 0;
foreach (var pair in query.HasAnyProviderId)
{
- if (string.Equals(pair.Key, MetadataProvider.TmdbCollection.ToString(), StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(pair.Key, nameof(MetadataProvider.TmdbCollection), StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -4942,7 +4706,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
public void DeleteItem(Guid id)
{
- if (id == Guid.Empty)
+ if (id.Equals(default))
{
throw new ArgumentNullException(nameof(id));
}
@@ -4954,7 +4718,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
connection.RunInTransaction(
db =>
{
- var idBlob = id.ToByteArray();
+ Span<byte> idBlob = stackalloc byte[16];
+ id.TryWriteBytes(idBlob);
// Delete people
ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", idBlob);
@@ -5003,7 +4768,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
if (whereClauses.Count != 0)
{
- commandText.Append(" where ").Append(string.Join(" AND ", whereClauses));
+ commandText.Append(" where ").AppendJoin(" AND ", whereClauses);
}
commandText.Append(" order by ListOrder");
@@ -5089,16 +4854,16 @@ AND Type = @InternalPersonType)");
statement?.TryBind("@InternalPersonType", typeof(Person).FullName);
}
- if (!query.ItemId.Equals(Guid.Empty))
+ if (!query.ItemId.Equals(default))
{
whereClauses.Add("ItemId=@ItemId");
- statement?.TryBind("@ItemId", query.ItemId.ToByteArray());
+ statement?.TryBind("@ItemId", query.ItemId);
}
- if (!query.AppearsInItemId.Equals(Guid.Empty))
+ if (!query.AppearsInItemId.Equals(default))
{
whereClauses.Add("p.Name in (Select Name from People where ItemId=@AppearsInItemId)");
- statement?.TryBind("@AppearsInItemId", query.AppearsInItemId.ToByteArray());
+ statement?.TryBind("@AppearsInItemId", query.AppearsInItemId);
}
var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
@@ -5151,7 +4916,7 @@ AND Type = @InternalPersonType)");
private void UpdateAncestors(Guid itemId, List<Guid> ancestorIds, IDatabaseConnection db, IStatement deleteAncestorsStatement)
{
- if (itemId.Equals(Guid.Empty))
+ if (itemId.Equals(default))
{
throw new ArgumentNullException(nameof(itemId));
}
@@ -5599,6 +5364,7 @@ AND Type = @InternalPersonType)");
result.TotalRecordCount = list.Count;
}
+ result.StartIndex = query.StartIndex ?? 0;
result.Items = list;
return result;
@@ -5682,7 +5448,7 @@ AND Type = @InternalPersonType)");
private void UpdateItemValues(Guid itemId, List<(int MagicNumber, string Value)> values, IDatabaseConnection db)
{
- if (itemId.Equals(Guid.Empty))
+ if (itemId.Equals(default))
{
throw new ArgumentNullException(nameof(itemId));
}
@@ -5758,7 +5524,7 @@ AND Type = @InternalPersonType)");
public void UpdatePeople(Guid itemId, List<PersonInfo> people)
{
- if (itemId.Equals(Guid.Empty))
+ if (itemId.Equals(default))
{
throw new ArgumentNullException(nameof(itemId));
}
@@ -5891,7 +5657,7 @@ AND Type = @InternalPersonType)");
using (var statement = PrepareStatement(connection, cmdText))
{
- statement.TryBind("@ItemId", query.ItemId.ToByteArray());
+ statement.TryBind("@ItemId", query.ItemId);
if (query.Type.HasValue)
{
@@ -5913,11 +5679,11 @@ AND Type = @InternalPersonType)");
}
}
- public void SaveMediaStreams(Guid id, List<MediaStream> streams, CancellationToken cancellationToken)
+ public void SaveMediaStreams(Guid id, IReadOnlyList<MediaStream> streams, CancellationToken cancellationToken)
{
CheckDisposed();
- if (id == Guid.Empty)
+ if (id.Equals(default))
{
throw new ArgumentNullException(nameof(id));
}
@@ -5945,7 +5711,7 @@ AND Type = @InternalPersonType)");
}
}
- private void InsertMediaStreams(byte[] idBlob, List<MediaStream> streams, IDatabaseConnection db)
+ private void InsertMediaStreams(byte[] idBlob, IReadOnlyList<MediaStream> streams, IDatabaseConnection db)
{
const int Limit = 10;
var startIndex = 0;
@@ -6204,6 +5970,7 @@ AND Type = @InternalPersonType)");
item.LocalizedUndefined = _localization.GetLocalizedString("Undefined");
item.LocalizedDefault = _localization.GetLocalizedString("Default");
item.LocalizedForced = _localization.GetLocalizedString("Forced");
+ item.LocalizedExternal = _localization.GetLocalizedString("External");
}
return item;
@@ -6253,7 +6020,7 @@ AND Type = @InternalPersonType)");
CancellationToken cancellationToken)
{
CheckDisposed();
- if (id == Guid.Empty)
+ if (id.Equals(default))
{
throw new ArgumentException("Guid can't be empty.", nameof(id));
}
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index 80b8f9ebf..ba86dc156 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -26,9 +26,6 @@ namespace Emby.Server.Implementations.Data
DbFilePath = Path.Combine(appPaths.DataPath, "library.db");
}
- /// <inheritdoc />
- public string Name => "SQLite";
-
/// <summary>
/// Opens the connection to the database.
/// </summary>
@@ -102,7 +99,7 @@ namespace Emby.Server.Implementations.Data
continue;
}
- statement.TryBind("@UserId", user.Id.ToByteArray());
+ statement.TryBind("@UserId", user.Id);
statement.TryBind("@InternalUserId", user.InternalId);
statement.MoveNext();
@@ -390,6 +387,7 @@ namespace Emby.Server.Implementations.Data
return userData;
}
+#pragma warning disable CA2215
/// <inheritdoc/>
/// <remarks>
/// There is nothing to dispose here since <see cref="BaseSqliteRepository.WriteLock"/> and
@@ -398,6 +396,10 @@ namespace Emby.Server.Implementations.Data
/// </remarks>
protected override void Dispose(bool dispose)
{
+ // The write lock and connection for the item repository are shared with the user data repository
+ // since they point to the same database. The item repo has responsibility for disposing these two objects,
+ // so the user data repo should not attempt to dispose them as well
}
+#pragma warning restore CA2215
}
}
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 7ba34e74a..efcfccafe 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -21,7 +21,6 @@ using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
@@ -458,11 +457,6 @@ namespace Emby.Server.Implementations.Dto
}
}
- private string GetDtoId(BaseItem item)
- {
- return item.Id.ToString("N", CultureInfo.InvariantCulture);
- }
-
private void SetMusicVideoProperties(BaseItemDto dto, MusicVideo item)
{
if (!string.IsNullOrEmpty(item.Album))
@@ -584,7 +578,7 @@ namespace Emby.Server.Implementations.Dto
if (dictionary.TryGetValue(person.Name, out Person entity))
{
baseItemPerson.PrimaryImageTag = GetTagAndFillBlurhash(dto, entity, ImageType.Primary);
- baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
+ baseItemPerson.Id = entity.Id;
if (dto.ImageBlurHashes != null)
{
// Only add BlurHash for the person's image.
@@ -743,8 +737,7 @@ namespace Emby.Server.Implementations.Dto
dto.Tags = item.Tags;
}
- var hasAspectRatio = item as IHasAspectRatio;
- if (hasAspectRatio != null)
+ if (item is IHasAspectRatio hasAspectRatio)
{
dto.AspectRatio = hasAspectRatio.AspectRatio;
}
@@ -894,15 +887,13 @@ namespace Emby.Server.Implementations.Dto
dto.CommunityRating = item.CommunityRating;
}
- var supportsPlaceHolders = item as ISupportsPlaceHolders;
- if (supportsPlaceHolders != null && supportsPlaceHolders.IsPlaceHolder)
+ if (item is ISupportsPlaceHolders supportsPlaceHolders && supportsPlaceHolders.IsPlaceHolder)
{
dto.IsPlaceHolder = supportsPlaceHolders.IsPlaceHolder;
}
// Add audio info
- var audio = item as Audio;
- if (audio != null)
+ if (item is Audio audio)
{
dto.Album = audio.Album;
if (audio.ExtraType.HasValue)
@@ -975,8 +966,7 @@ namespace Emby.Server.Implementations.Dto
}).Where(i => i != null).ToArray();
}
- var hasAlbumArtist = item as IHasAlbumArtist;
- if (hasAlbumArtist != null)
+ if (item is IHasAlbumArtist hasAlbumArtist)
{
dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
@@ -1102,12 +1092,13 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.LocalTrailerCount))
{
- allExtras ??= item.GetExtras().ToArray();
- dto.LocalTrailerCount = allExtras.Count(i => i.ExtraType == ExtraType.Trailer);
-
if (item is IHasTrailers hasTrailers)
{
- dto.LocalTrailerCount += hasTrailers.GetTrailerCount();
+ dto.LocalTrailerCount = hasTrailers.GetTrailerCount();
+ }
+ else
+ {
+ dto.LocalTrailerCount = (allExtras ?? item.GetExtras()).Count(i => i.ExtraType == ExtraType.Trailer);
}
}
@@ -1317,35 +1308,35 @@ namespace Emby.Server.Implementations.Dto
var allImages = parent.ImageInfos;
- if (logoLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Logo)) && dto.ParentLogoItemId == null)
+ if (logoLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Logo)) && dto.ParentLogoItemId is null)
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Logo);
if (image != null)
{
- dto.ParentLogoItemId = GetDtoId(parent);
+ dto.ParentLogoItemId = parent.Id;
dto.ParentLogoImageTag = GetTagAndFillBlurhash(dto, parent, image);
}
}
- if (artLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && dto.ParentArtItemId == null)
+ if (artLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && dto.ParentArtItemId is null)
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Art);
if (image != null)
{
- dto.ParentArtItemId = GetDtoId(parent);
+ dto.ParentArtItemId = parent.Id;
dto.ParentArtImageTag = GetTagAndFillBlurhash(dto, parent, image);
}
}
- if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && parent is not ICollectionFolder && parent is not UserView)
+ if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId is null || parent is Series) && parent is not ICollectionFolder && parent is not UserView)
{
var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
if (image != null)
{
- dto.ParentThumbItemId = GetDtoId(parent);
+ dto.ParentThumbItemId = parent.Id;
dto.ParentThumbImageTag = GetTagAndFillBlurhash(dto, parent, image);
}
}
@@ -1356,7 +1347,7 @@ namespace Emby.Server.Implementations.Dto
if (images.Count > 0)
{
- dto.ParentBackdropItemId = GetDtoId(parent);
+ dto.ParentBackdropItemId = parent.Id;
dto.ParentBackdropImageTags = GetTagsAndFillBlurhashes(dto, parent, ImageType.Backdrop, images);
}
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 1e09a98cf..886da1390 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -26,12 +26,12 @@
<PackageReference Include="DiscUtils.Udf" Version="0.16.13" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
- <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
- <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.1" />
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.3" />
<PackageReference Include="Mono.Nat" Version="3.0.2" />
- <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.2" />
+ <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.3" />
<PackageReference Include="sharpcompress" Version="0.30.1" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
@@ -55,6 +55,10 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+ <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3">
+ <PrivateAssets>all</PrivateAssets>
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
+ </PackageReference>
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index d43996c69..9e35d83aa 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -326,7 +326,7 @@ namespace Emby.Server.Implementations.EntryPoints
{
var userIds = _sessionManager.Sessions
.Select(i => i.UserId)
- .Where(i => !i.Equals(Guid.Empty))
+ .Where(i => !i.Equals(default))
.Distinct()
.ToArray();
diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
index feaccf9fa..e45baedd7 100644
--- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
+++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
@@ -3,6 +3,8 @@ using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Emby.Server.Implementations.Udp;
+using Jellyfin.Networking.Configuration;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Plugins;
using Microsoft.Extensions.Configuration;
@@ -26,6 +28,7 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly ILogger<UdpServerEntryPoint> _logger;
private readonly IServerApplicationHost _appHost;
private readonly IConfiguration _config;
+ private readonly IConfigurationManager _configurationManager;
/// <summary>
/// The UDP server.
@@ -40,14 +43,17 @@ namespace Emby.Server.Implementations.EntryPoints
/// <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>
+ /// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
public UdpServerEntryPoint(
ILogger<UdpServerEntryPoint> logger,
IServerApplicationHost appHost,
- IConfiguration configuration)
+ IConfiguration configuration,
+ IConfigurationManager configurationManager)
{
_logger = logger;
_appHost = appHost;
_config = configuration;
+ _configurationManager = configurationManager;
}
/// <inheritdoc />
@@ -55,6 +61,11 @@ namespace Emby.Server.Implementations.EntryPoints
{
CheckDisposed();
+ if (!_configurationManager.GetNetworkConfiguration().AutoDiscovery)
+ {
+ return Task.CompletedTask;
+ }
+
try
{
_udpServer = new UdpServer(_logger, _appHost, _config, PortNumber);
diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index bb6041f28..15ab363fe 100644
--- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -47,7 +47,9 @@ namespace Emby.Server.Implementations.HttpServer.Security
{
var session = await GetSession(requestContext).ConfigureAwait(false);
- return session.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(session.UserId);
+ return session.UserId.Equals(default)
+ ? null
+ : _userManager.GetUserById(session.UserId);
}
public Task<User?> GetUser(object requestContext)
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 5c86dbbb7..399ece7fd 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -581,7 +581,7 @@ namespace Emby.Server.Implementations.IO
}
/// <inheritdoc />
- public virtual List<FileSystemMetadata> GetDrives()
+ public virtual IEnumerable<FileSystemMetadata> GetDrives()
{
// check for ready state to avoid waiting for drives to timeout
// some drives on linux have no actual size or are used for other purposes
@@ -595,7 +595,7 @@ namespace Emby.Server.Implementations.IO
Name = d.Name,
FullName = d.RootDirectory.FullName,
IsDirectory = true
- }).ToList();
+ });
}
/// <inheritdoc />
@@ -704,6 +704,18 @@ namespace Emby.Server.Implementations.IO
return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive));
}
+ /// <inheritdoc />
+ public virtual bool DirectoryExists(string path)
+ {
+ return Directory.Exists(path);
+ }
+
+ /// <inheritdoc />
+ public virtual bool FileExists(string path)
+ {
+ return File.Exists(path);
+ }
+
private EnumerationOptions GetEnumerationOptions(bool recursive)
{
return new EnumerationOptions
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index 758986945..57c2f1a5e 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -135,7 +135,7 @@ namespace Emby.Server.Implementations.Images
protected virtual IEnumerable<string> GetStripCollageImagePaths(BaseItem primaryItem, IEnumerable<BaseItem> items)
{
- var useBackdrop = primaryItem is CollectionFolder;
+ var useBackdrop = primaryItem is CollectionFolder || primaryItem is UserView;
return items
.Select(i =>
{
diff --git a/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs b/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
index 1c69056d2..6fc7f1ac3 100644
--- a/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Images
{
private readonly ILibraryManager _libraryManager;
- public BaseFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager)
+ protected BaseFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager)
: base(fileSystem, providerManager, applicationPaths, imageProcessor)
{
_libraryManager = libraryManager;
diff --git a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
index 7958eb8f5..8a0e627b9 100644
--- a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
@@ -42,6 +42,10 @@ namespace Emby.Server.Implementations.Images
{
includeItemTypes = new[] { BaseItemKind.MusicAlbum };
}
+ else if (string.Equals(viewType, CollectionType.MusicVideos, StringComparison.Ordinal))
+ {
+ includeItemTypes = new[] { BaseItemKind.MusicVideo };
+ }
else if (string.Equals(viewType, CollectionType.Books, StringComparison.Ordinal))
{
includeItemTypes = new[] { BaseItemKind.Book, BaseItemKind.AudioBook };
diff --git a/Emby.Server.Implementations/Images/DynamicImageProvider.cs b/Emby.Server.Implementations/Images/DynamicImageProvider.cs
index 575680653..9f9a4902a 100644
--- a/Emby.Server.Implementations/Images/DynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/DynamicImageProvider.cs
@@ -84,16 +84,20 @@ namespace Emby.Server.Implementations.Images
}).GroupBy(x => x.Id)
.Select(x => x.First());
+ List<BaseItem> returnItems;
if (isUsingCollectionStrip)
{
- return items
+ returnItems = items
.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb))
.ToList();
+ returnItems.Shuffle();
+ return returnItems;
}
-
- return items
+ returnItems = items
.Where(i => i.HasImage(ImageType.Primary))
.ToList();
+ returnItems.Shuffle();
+ return returnItems;
}
protected override bool Supports(BaseItem item)
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index bd0c178fd..a9428ae9b 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -45,8 +45,8 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Tasks;
-using MediaBrowser.Providers.MediaInfo;
using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using EpisodeInfo = Emby.Naming.TV.EpisodeInfo;
@@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.Library
/// Initializes a new instance of the <see cref="LibraryManager" /> class.
/// </summary>
/// <param name="appHost">The application host.</param>
- /// <param name="logger">The logger.</param>
+ /// <param name="loggerFactory">The logger factory.</param>
/// <param name="taskManager">The task manager.</param>
/// <param name="userManager">The user manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
@@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.Library
/// <param name="namingOptions">The naming options.</param>
public LibraryManager(
IServerApplicationHost appHost,
- ILogger<LibraryManager> logger,
+ ILoggerFactory loggerFactory,
ITaskManager taskManager,
IUserManager userManager,
IServerConfigurationManager configurationManager,
@@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Library
NamingOptions namingOptions)
{
_appHost = appHost;
- _logger = logger;
+ _logger = loggerFactory.CreateLogger<LibraryManager>();
_taskManager = taskManager;
_userManager = userManager;
_configurationManager = configurationManager;
@@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.Library
_memoryCache = memoryCache;
_namingOptions = namingOptions;
- _extraResolver = new ExtraResolver(namingOptions);
+ _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions);
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -680,9 +680,7 @@ namespace Emby.Server.Implementations.Library
if (result?.Items.Count > 0)
{
- var items = new List<BaseItem>();
- items.AddRange(result.Items);
-
+ var items = result.Items;
foreach (var item in items)
{
ResolverHelper.SetInitialItemValues(item, parent, this, directoryService);
@@ -758,7 +756,7 @@ namespace Emby.Server.Implementations.Library
Path = path
};
- if (folder.Id.Equals(Guid.Empty))
+ if (folder.Id.Equals(default))
{
if (string.IsNullOrEmpty(folder.Path))
{
@@ -777,7 +775,7 @@ namespace Emby.Server.Implementations.Library
folder = dbItem;
}
- if (folder.ParentId != rootFolder.Id)
+ if (!folder.ParentId.Equals(rootFolder.Id))
{
folder.ParentId = rootFolder.Id;
folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
@@ -1007,14 +1005,8 @@ namespace Emby.Server.Implementations.Library
return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
}
- /// <summary>
- /// Validate and refresh the People sub-set of the IBN.
- /// The items are stored in the db but not loaded into memory until actually requested by an operation.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public Task ValidatePeopleAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
// Ensure the location is available.
Directory.CreateDirectory(_configurationManager.ApplicationPaths.PeoplePath);
@@ -1037,15 +1029,6 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// Queues the library scan.
- /// </summary>
- public void QueueLibraryScan()
- {
- // Just run the scheduled task so that the user can see it
- _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>();
- }
-
- /// <summary>
/// Validates the media library internal.
/// </summary>
/// <param name="progress">The progress.</param>
@@ -1270,7 +1253,7 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="ArgumentNullException"><paramref name="id"/> is <c>null</c>.</exception>
public BaseItem GetItemById(Guid id)
{
- if (id == Guid.Empty)
+ if (id.Equals(default))
{
throw new ArgumentException("Guid can't be empty", nameof(id));
}
@@ -1292,7 +1275,7 @@ namespace Emby.Server.Implementations.Library
public List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent)
{
- if (query.Recursive && query.ParentId != Guid.Empty)
+ if (query.Recursive && !query.ParentId.Equals(default))
{
var parent = GetItemById(query.ParentId);
if (parent != null)
@@ -1316,7 +1299,7 @@ namespace Emby.Server.Implementations.Library
public int GetCount(InternalItemsQuery query)
{
- if (query.Recursive && !query.ParentId.Equals(Guid.Empty))
+ if (query.Recursive && !query.ParentId.Equals(default))
{
var parent = GetItemById(query.ParentId);
if (parent != null)
@@ -1360,10 +1343,10 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetItems(query);
}
- return new QueryResult<BaseItem>
- {
- Items = _itemRepository.GetItemList(query)
- };
+ return new QueryResult<BaseItem>(
+ query.StartIndex,
+ null,
+ _itemRepository.GetItemList(query));
}
public List<Guid> GetItemIds(InternalItemsQuery query)
@@ -1474,7 +1457,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query)
{
- if (query.Recursive && !query.ParentId.Equals(Guid.Empty))
+ if (query.Recursive && !query.ParentId.Equals(default))
{
var parent = GetItemById(query.ParentId);
if (parent != null)
@@ -1493,10 +1476,10 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetItems(query);
}
- return new QueryResult<BaseItem>
- {
- Items = _itemRepository.GetItemList(query)
- };
+ return new QueryResult<BaseItem>(
+ query.StartIndex,
+ null,
+ _itemRepository.GetItemList(query));
}
private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents)
@@ -1530,7 +1513,7 @@ namespace Emby.Server.Implementations.Library
private void AddUserToQuery(InternalItemsQuery query, User user, bool allowExternalContent = true)
{
if (query.AncestorIds.Length == 0 &&
- query.ParentId.Equals(Guid.Empty) &&
+ query.ParentId.Equals(default) &&
query.ChannelIds.Count == 0 &&
query.TopParentIds.Length == 0 &&
string.IsNullOrEmpty(query.AncestorWithPresentationUniqueKey) &&
@@ -1558,7 +1541,7 @@ namespace Emby.Server.Implementations.Library
}
// Translate view into folders
- if (!view.DisplayParentId.Equals(Guid.Empty))
+ if (!view.DisplayParentId.Equals(default))
{
var displayParent = GetItemById(view.DisplayParentId);
if (displayParent != null)
@@ -1569,7 +1552,7 @@ namespace Emby.Server.Implementations.Library
return Array.Empty<Guid>();
}
- if (!view.ParentId.Equals(Guid.Empty))
+ if (!view.ParentId.Equals(default))
{
var displayParent = GetItemById(view.ParentId);
if (displayParent != null)
@@ -1651,27 +1634,6 @@ namespace Emby.Server.Implementations.Library
}
/// <summary>
- /// Gets all intro files.
- /// </summary>
- /// <returns>IEnumerable{System.String}.</returns>
- public IEnumerable<string> GetAllIntroFiles()
- {
- return IntroProviders.SelectMany(i =>
- {
- try
- {
- return i.GetAllIntroFiles().ToList();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error getting intro files");
-
- return new List<string>();
- }
- });
- }
-
- /// <summary>
/// Resolves the intro.
/// </summary>
/// <param name="info">The info.</param>
@@ -2014,16 +1976,16 @@ namespace Emby.Server.Implementations.Library
public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
=> UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken);
- public Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason)
+ public async Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason)
{
if (item.IsFileProtocol)
{
- ProviderManager.SaveMetadata(item, updateReason);
+ await ProviderManager.SaveMetadataAsync(item, updateReason).ConfigureAwait(false);
}
item.DateLastSaved = DateTime.UtcNow;
- return UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate);
+ await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false);
}
/// <summary>
@@ -2192,7 +2154,7 @@ namespace Emby.Server.Implementations.Library
return null;
}
- while (!item.ParentId.Equals(Guid.Empty))
+ while (!item.ParentId.Equals(default))
{
var parent = item.GetParent();
if (parent == null || parent is AggregateFolder)
@@ -2270,7 +2232,9 @@ namespace Emby.Server.Implementations.Library
string viewType,
string sortName)
{
- var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture);
+ var parentIdString = parentId.Equals(default)
+ ? null
+ : parentId.ToString("N", CultureInfo.InvariantCulture);
var idValues = "38_namedview_" + name + user.Id.ToString("N", CultureInfo.InvariantCulture) + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
var id = GetNewItemId(idValues, typeof(UserView));
@@ -2304,7 +2268,7 @@ namespace Emby.Server.Implementations.Library
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && !item.DisplayParentId.Equals(Guid.Empty))
+ if (!refresh && !item.DisplayParentId.Equals(default))
{
var displayParent = GetItemById(item.DisplayParentId);
refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
@@ -2371,7 +2335,7 @@ namespace Emby.Server.Implementations.Library
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && !item.DisplayParentId.Equals(Guid.Empty))
+ if (!refresh && !item.DisplayParentId.Equals(default))
{
var displayParent = GetItemById(item.DisplayParentId);
refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
@@ -2404,7 +2368,9 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(name));
}
- var parentIdString = parentId.Equals(Guid.Empty) ? null : parentId.ToString("N", CultureInfo.InvariantCulture);
+ var parentIdString = parentId.Equals(default)
+ ? null
+ : parentId.ToString("N", CultureInfo.InvariantCulture);
var idValues = "37_namedview_" + name + (parentIdString ?? string.Empty) + (viewType ?? string.Empty);
if (!string.IsNullOrEmpty(uniqueId))
{
@@ -2448,7 +2414,7 @@ namespace Emby.Server.Implementations.Library
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- if (!refresh && !item.DisplayParentId.Equals(Guid.Empty))
+ if (!refresh && !item.DisplayParentId.Equals(default))
{
var displayParent = GetItemById(item.DisplayParentId);
refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
@@ -2469,24 +2435,6 @@ namespace Emby.Server.Implementations.Library
return item;
}
- public void AddExternalSubtitleStreams(
- List<MediaStream> streams,
- string videoPath,
- string[] files)
- {
- new SubtitleResolver(BaseItem.LocalizationManager).AddExternalSubtitleStreams(streams, videoPath, streams.Count, files);
- }
-
- public BaseItem GetParentItem(string parentId, Guid? userId)
- {
- if (string.IsNullOrEmpty(parentId))
- {
- return GetParentItem((Guid?)null, userId);
- }
-
- return GetParentItem(new Guid(parentId), userId);
- }
-
public BaseItem GetParentItem(Guid? parentId, Guid? userId)
{
if (parentId.HasValue)
@@ -2494,7 +2442,7 @@ namespace Emby.Server.Implementations.Library
return GetItemById(parentId.Value);
}
- if (userId.HasValue && userId != Guid.Empty)
+ if (userId.HasValue && !userId.Equals(default))
{
return GetUserRootFolder();
}
@@ -2686,7 +2634,7 @@ namespace Emby.Server.Implementations.Library
};
}
- public IEnumerable<BaseItem> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
+ public IEnumerable<BaseItem> FindExtras(BaseItem owner, IReadOnlyList<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions);
if (ownerVideoInfo == null)
@@ -2784,16 +2732,6 @@ namespace Emby.Server.Implementations.Library
return path;
}
- public string SubstitutePath(string path, string from, string to)
- {
- if (path.TryReplaceSubPath(from, to, out var newPath))
- {
- return newPath;
- }
-
- return path;
- }
-
public List<PersonInfo> GetPeople(InternalPeopleQuery query)
{
return _itemRepository.GetPeople(query);
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index a414e7e16..c9202c264 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -344,7 +344,7 @@ namespace Emby.Server.Implementations.Library
return sources;
}
- private string[] NormalizeLanguage(string language)
+ private IReadOnlyList<string> NormalizeLanguage(string language)
{
if (string.IsNullOrEmpty(language))
{
@@ -514,10 +514,10 @@ namespace Emby.Server.Implementations.Library
_logger.LogInformation("Live stream opened: {@MediaSource}", mediaSource);
var clone = JsonSerializer.Deserialize<MediaSourceInfo>(json, _jsonOptions);
- if (!request.UserId.Equals(Guid.Empty))
+ if (!request.UserId.Equals(default))
{
var user = _userManager.GetUserById(request.UserId);
- var item = request.ItemId.Equals(Guid.Empty)
+ var item = request.ItemId.Equals(default)
? null
: _libraryManager.GetItemById(request.ItemId);
SetDefaultAudioAndSubtitleStreamIndexes(item, clone, user);
diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
index da0c89c13..c5abb9a0a 100644
--- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs
+++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -13,10 +11,9 @@ namespace Emby.Server.Implementations.Library
{
public static class MediaStreamSelector
{
- public static int? GetDefaultAudioStreamIndex(List<MediaStream> streams, string[] preferredLanguages, bool preferDefaultTrack)
+ public static int? GetDefaultAudioStreamIndex(IReadOnlyList<MediaStream> streams, IReadOnlyList<string> preferredLanguages, bool preferDefaultTrack)
{
- streams = GetSortedStreams(streams, MediaStreamType.Audio, preferredLanguages)
- .ToList();
+ var sortedStreams = GetSortedStreams(streams, MediaStreamType.Audio, preferredLanguages);
if (preferDefaultTrack)
{
@@ -28,24 +25,15 @@ namespace Emby.Server.Implementations.Library
}
}
- var stream = streams.FirstOrDefault();
-
- if (stream != null)
- {
- return stream.Index;
- }
-
- return null;
+ return sortedStreams.FirstOrDefault()?.Index;
}
public static int? GetDefaultSubtitleStreamIndex(
IEnumerable<MediaStream> streams,
- string[] preferredLanguages,
+ IReadOnlyList<string> preferredLanguages,
SubtitlePlaybackMode mode,
string audioTrackLanguage)
{
- MediaStream stream = null;
-
if (mode == SubtitlePlaybackMode.None)
{
return null;
@@ -59,6 +47,7 @@ namespace Emby.Server.Implementations.Library
.ThenByDescending(x => x.IsDefault)
.ToList();
+ MediaStream? stream = null;
if (mode == SubtitlePlaybackMode.Default)
{
// Prefer embedded metadata over smart logic
@@ -95,26 +84,27 @@ namespace Emby.Server.Implementations.Library
return stream?.Index;
}
- private static IEnumerable<MediaStream> GetSortedStreams(IEnumerable<MediaStream> streams, MediaStreamType type, string[] languagePreferences)
+ private static IEnumerable<MediaStream> GetSortedStreams(IEnumerable<MediaStream> streams, MediaStreamType type, IReadOnlyList<string> languagePreferences)
{
// Give some preference to external text subs for better performance
- return streams.Where(i => i.Type == type)
+ return streams
+ .Where(i => i.Type == type)
.OrderBy(i =>
- {
- var index = FindIndex(languagePreferences, i.Language);
-
- return index == -1 ? 100 : index;
- })
- .ThenBy(i => GetBooleanOrderBy(i.IsDefault))
- .ThenBy(i => GetBooleanOrderBy(i.SupportsExternalStream))
- .ThenBy(i => GetBooleanOrderBy(i.IsTextSubtitleStream))
- .ThenBy(i => GetBooleanOrderBy(i.IsExternal))
- .ThenBy(i => i.Index);
+ {
+ var index = languagePreferences.FindIndex(x => string.Equals(x, i.Language, StringComparison.OrdinalIgnoreCase));
+
+ return index == -1 ? 100 : index;
+ })
+ .ThenBy(i => GetBooleanOrderBy(i.IsDefault))
+ .ThenBy(i => GetBooleanOrderBy(i.SupportsExternalStream))
+ .ThenBy(i => GetBooleanOrderBy(i.IsTextSubtitleStream))
+ .ThenBy(i => GetBooleanOrderBy(i.IsExternal))
+ .ThenBy(i => i.Index);
}
public static void SetSubtitleStreamScores(
- List<MediaStream> streams,
- string[] preferredLanguages,
+ IReadOnlyList<MediaStream> streams,
+ IReadOnlyList<string> preferredLanguages,
SubtitlePlaybackMode mode,
string audioTrackLanguage)
{
@@ -123,15 +113,14 @@ namespace Emby.Server.Implementations.Library
return;
}
- streams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages)
- .ToList();
+ var sortedStreams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages);
var filteredStreams = new List<MediaStream>();
if (mode == SubtitlePlaybackMode.Default)
{
// Prefer embedded metadata over smart logic
- filteredStreams = streams.Where(s => s.IsForced || s.IsDefault)
+ filteredStreams = sortedStreams.Where(s => s.IsForced || s.IsDefault)
.ToList();
}
else if (mode == SubtitlePlaybackMode.Smart)
@@ -139,54 +128,37 @@ namespace Emby.Server.Implementations.Library
// Prefer smart logic over embedded metadata
if (!preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
{
- filteredStreams = streams.Where(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase))
+ filteredStreams = sortedStreams.Where(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase))
.ToList();
}
}
else if (mode == SubtitlePlaybackMode.Always)
{
// always load the most suitable full subtitles
- filteredStreams = streams.Where(s => !s.IsForced)
- .ToList();
+ filteredStreams = sortedStreams.Where(s => !s.IsForced).ToList();
}
else if (mode == SubtitlePlaybackMode.OnlyForced)
{
// always load the most suitable full subtitles
- filteredStreams = streams.Where(s => s.IsForced).ToList();
+ filteredStreams = sortedStreams.Where(s => s.IsForced).ToList();
}
// load forced subs if we have found no suitable full subtitles
- if (filteredStreams.Count == 0)
- {
- filteredStreams = streams
- .Where(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
- .ToList();
- }
+ var iterStreams = filteredStreams.Count == 0
+ ? sortedStreams.Where(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
+ : filteredStreams;
- foreach (var stream in filteredStreams)
+ foreach (var stream in iterStreams)
{
stream.Score = GetSubtitleScore(stream, preferredLanguages);
}
}
- private static int FindIndex(string[] list, string value)
- {
- for (var i = 0; i < list.Length; i++)
- {
- if (string.Equals(list[i], value, StringComparison.OrdinalIgnoreCase))
- {
- return i;
- }
- }
-
- return -1;
- }
-
- private static int GetSubtitleScore(MediaStream stream, string[] languagePreferences)
+ private static int GetSubtitleScore(MediaStream stream, IReadOnlyList<string> languagePreferences)
{
var values = new List<int>();
- var index = FindIndex(languagePreferences, stream.Language);
+ var index = languagePreferences.FindIndex(x => string.Equals(x, stream.Language, StringComparison.OrdinalIgnoreCase));
values.Add(index == -1 ? 0 : 100 - index);
diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs
index d35e74e7b..b2439a87e 100644
--- a/Emby.Server.Implementations/Library/MusicManager.cs
+++ b/Emby.Server.Implementations/Library/MusicManager.cs
@@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library
{
return Guid.Empty;
}
- }).Where(i => !i.Equals(Guid.Empty)).ToArray();
+ }).Where(i => !i.Equals(default)).ToArray();
return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 9222a9479..3d6b9f3b6 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -12,6 +12,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers
{
@@ -22,8 +23,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
public abstract class BaseVideoResolver<T> : MediaBrowser.Controller.Resolvers.ItemResolver<T>
where T : Video, new()
{
- protected BaseVideoResolver(NamingOptions namingOptions)
+ private readonly ILogger _logger;
+
+ protected BaseVideoResolver(ILogger logger, NamingOptions namingOptions)
{
+ _logger = logger;
NamingOptions = namingOptions;
}
@@ -156,19 +160,26 @@ namespace Emby.Server.Implementations.Library.Resolvers
}
else
{
- // use disc-utils, both DVDs and BDs use UDF filesystem
- using (var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read))
- using (UdfReader udfReader = new UdfReader(videoFileStream))
+ try
{
- if (udfReader.DirectoryExists("VIDEO_TS"))
- {
- video.IsoType = IsoType.Dvd;
- }
- else if (udfReader.DirectoryExists("BDMV"))
+ // use disc-utils, both DVDs and BDs use UDF filesystem
+ using (var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read))
+ using (UdfReader udfReader = new UdfReader(videoFileStream))
{
- video.IsoType = IsoType.BluRay;
+ if (udfReader.DirectoryExists("VIDEO_TS"))
+ {
+ video.IsoType = IsoType.Dvd;
+ }
+ else if (udfReader.DirectoryExists("BDMV"))
+ {
+ video.IsoType = IsoType.BluRay;
+ }
}
}
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error opening UDF/ISO image: {Value}", video.Path ?? video.Name);
+ }
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
index 807913b5d..408e640f9 100644
--- a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
@@ -6,6 +6,7 @@ using Emby.Naming.Video;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
+using Microsoft.Extensions.Logging;
using static Emby.Naming.Video.ExtraRuleResolver;
namespace Emby.Server.Implementations.Library.Resolvers
@@ -22,12 +23,13 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// <summary>
/// Initializes a new instance of the <see cref="ExtraResolver"/> class.
/// </summary>
+ /// <param name="logger">The logger.</param>
/// <param name="namingOptions">An instance of <see cref="NamingOptions"/>.</param>
- public ExtraResolver(NamingOptions namingOptions)
+ public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions)
{
_namingOptions = namingOptions;
- _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(namingOptions) };
- _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(namingOptions) };
+ _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions) };
+ _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(logger, namingOptions) };
}
/// <summary>
diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
index b8554bd51..5e33b402d 100644
--- a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
@@ -1,7 +1,8 @@
-#nullable disable
+#nullable disable
using Emby.Naming.Common;
using MediaBrowser.Controller.Entities;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers
{
@@ -15,9 +16,10 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// <summary>
/// Initializes a new instance of the <see cref="GenericVideoResolver{T}"/> class.
/// </summary>
+ /// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public GenericVideoResolver(NamingOptions namingOptions)
- : base(namingOptions)
+ public GenericVideoResolver(ILogger logger, NamingOptions namingOptions)
+ : base(logger, namingOptions)
{
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 1a9295dc8..140c4272e 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -17,6 +17,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers.Movies
{
@@ -32,7 +33,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
CollectionType.Movies,
CollectionType.HomeVideos,
CollectionType.MusicVideos,
- CollectionType.Movies,
+ CollectionType.TvShows,
CollectionType.Photos
};
@@ -40,9 +41,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// Initializes a new instance of the <see cref="MovieResolver"/> class.
/// </summary>
/// <param name="imageProcessor">The image processor.</param>
+ /// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public MovieResolver(IImageProcessor imageProcessor, NamingOptions namingOptions)
- : base(namingOptions)
+ public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions)
+ : base(logger, namingOptions)
{
_imageProcessor = imageProcessor;
}
@@ -128,10 +130,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return movie?.ExtraType == null ? movie : null;
}
- // Owned items will be caught by the video extra resolver
if (args.Parent == null)
{
- return null;
+ return base.Resolve(args);
}
if (IsInvalid(args.Parent, collectionType))
@@ -222,6 +223,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
return ResolveVideos<Movie>(parent, files, true, collectionType, true);
}
+ if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+ {
+ return ResolveVideos<Episode>(parent, files, true, collectionType, true);
+ }
+
return null;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index be9905647..bfa73af2f 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -6,6 +6,7 @@ using Emby.Naming.Common;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers.TV
{
@@ -17,9 +18,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <summary>
/// Initializes a new instance of the <see cref="EpisodeResolver"/> class.
/// </summary>
+ /// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public EpisodeResolver(NamingOptions namingOptions)
- : base(namingOptions)
+ public EpisodeResolver(ILogger<EpisodeResolver> logger, NamingOptions namingOptions)
+ : base(logger, namingOptions)
{
}
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 55911933a..96702d152 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library
public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
{
User user = null;
- if (query.UserId != Guid.Empty)
+ if (!query.UserId.Equals(default))
{
user = _userManager.GetUserById(query.UserId);
}
@@ -48,12 +48,10 @@ namespace Emby.Server.Implementations.Library
results = results.GetRange(0, Math.Min(query.Limit.Value, results.Count));
}
- return new QueryResult<SearchHintInfo>
- {
- TotalRecordCount = totalRecordCount,
-
- Items = results
- };
+ return new QueryResult<SearchHintInfo>(
+ query.StartIndex,
+ totalRecordCount,
+ results);
}
private static void AddIfMissing(List<BaseItemKind> list, BaseItemKind value)
@@ -170,10 +168,10 @@ namespace Emby.Server.Implementations.Library
{
Fields = new ItemFields[]
{
- ItemFields.AirTime,
- ItemFields.DateCreated,
- ItemFields.ChannelInfo,
- ItemFields.ParentId
+ ItemFields.AirTime,
+ ItemFields.DateCreated,
+ ItemFields.ChannelInfo,
+ ItemFields.ParentId
}
}
};
@@ -182,12 +180,12 @@ namespace Emby.Server.Implementations.Library
if (searchQuery.IncludeItemTypes.Length == 1 && searchQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist)
{
- if (!searchQuery.ParentId.Equals(Guid.Empty))
+ if (!searchQuery.ParentId.Equals(default))
{
searchQuery.AncestorIds = new[] { searchQuery.ParentId };
+ searchQuery.ParentId = Guid.Empty;
}
- searchQuery.ParentId = Guid.Empty;
searchQuery.IncludeItemsByName = true;
searchQuery.IncludeItemTypes = Array.Empty<BaseItemKind>();
mediaItems = _libraryManager.GetAllArtists(searchQuery).Items.Select(i => i.Item).ToList();
diff --git a/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs b/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs
new file mode 100644
index 000000000..320685b1f
--- /dev/null
+++ b/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
+using Microsoft.Extensions.Logging;
+
+namespace Emby.Server.Implementations.Library;
+
+/// <summary>
+/// The splashscreen post scan task.
+/// </summary>
+public class SplashscreenPostScanTask : ILibraryPostScanTask
+{
+ private readonly IItemRepository _itemRepository;
+ private readonly IImageEncoder _imageEncoder;
+ private readonly ILogger<SplashscreenPostScanTask> _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SplashscreenPostScanTask"/> class.
+ /// </summary>
+ /// <param name="itemRepository">Instance of the <see cref="IItemRepository"/> interface.</param>
+ /// <param name="imageEncoder">Instance of the <see cref="IImageEncoder"/> interface.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{SplashscreenPostScanTask}"/> interface.</param>
+ public SplashscreenPostScanTask(
+ IItemRepository itemRepository,
+ IImageEncoder imageEncoder,
+ ILogger<SplashscreenPostScanTask> logger)
+ {
+ _itemRepository = itemRepository;
+ _imageEncoder = imageEncoder;
+ _logger = logger;
+ }
+
+ /// <inheritdoc />
+ public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ var posters = GetItemsWithImageType(ImageType.Primary).Select(x => x.GetImages(ImageType.Primary).First().Path).ToList();
+ var backdrops = GetItemsWithImageType(ImageType.Thumb).Select(x => x.GetImages(ImageType.Thumb).First().Path).ToList();
+ if (backdrops.Count == 0)
+ {
+ // Thumb images fit better because they include the title in the image but are not provided with TMDb.
+ // Using backdrops as a fallback to generate an image at all
+ _logger.LogDebug("No thumb images found. Using backdrops to generate splashscreen");
+ backdrops = GetItemsWithImageType(ImageType.Backdrop).Select(x => x.GetImages(ImageType.Backdrop).First().Path).ToList();
+ }
+
+ _imageEncoder.CreateSplashscreen(posters, backdrops);
+ return Task.CompletedTask;
+ }
+
+ private IReadOnlyList<BaseItem> GetItemsWithImageType(ImageType imageType)
+ {
+ // TODO make included libraries configurable
+ return _itemRepository.GetItemList(new InternalItemsQuery
+ {
+ CollapseBoxSetItems = false,
+ Recursive = true,
+ DtoOptions = new DtoOptions(false),
+ ImageTypes = new[] { imageType },
+ Limit = 30,
+ // TODO max parental rating configurable
+ MaxParentalRating = 10,
+ OrderBy = new[]
+ {
+ (ItemSortBy.Random, SortOrder.Ascending)
+ },
+ IncludeItemTypes = new[] { BaseItemKind.Movie, BaseItemKind.Series }
+ });
+ }
+}
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index b00bc72e6..ec411aa3b 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -142,7 +142,7 @@ namespace Emby.Server.Implementations.Library
if (index == -1
&& i is UserView view
- && view.DisplayParentId != Guid.Empty)
+ && !view.DisplayParentId.Equals(default))
{
index = Array.IndexOf(orders, view.DisplayParentId);
}
@@ -214,7 +214,7 @@ namespace Emby.Server.Implementations.Library
}
else
{
- var current = list.FirstOrDefault(i => i.Item1 != null && i.Item1.Id == container.Id);
+ var current = list.FirstOrDefault(i => i.Item1 != null && i.Item1.Id.Equals(container.Id));
if (current != null)
{
@@ -244,7 +244,7 @@ namespace Emby.Server.Implementations.Library
var parents = new List<BaseItem>();
- if (!parentId.Equals(Guid.Empty))
+ if (!parentId.Equals(default))
{
var parentItem = _libraryManager.GetItemById(parentId);
if (parentItem is Channel)
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
index 6937cc097..b2d25fdae 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
@@ -46,7 +46,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
Directory.CreateDirectory(Path.GetDirectoryName(targetFile) ?? throw new ArgumentException("Path can't be a root directory.", nameof(targetFile)));
- using (var output = new FileStream(targetFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
+ await using (var output = new FileStream(targetFile, FileMode.CreateNew, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
{
onStarted();
@@ -56,14 +56,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
using var durationToken = new CancellationTokenSource(duration);
using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token);
var linkedCancellationToken = cancellationTokenSource.Token;
-
- await using var fileStream = new ProgressiveFileStream(directStreamProvider.GetStream());
- await _streamHelper.CopyToAsync(
- fileStream,
- output,
- IODefaults.CopyToBufferSize,
- 1000,
- linkedCancellationToken).ConfigureAwait(false);
+ var fileStream = new ProgressiveFileStream(directStreamProvider.GetStream());
+ await using (fileStream.ConfigureAwait(false))
+ {
+ await _streamHelper.CopyToAsync(
+ fileStream,
+ output,
+ IODefaults.CopyToBufferSize,
+ 1000,
+ linkedCancellationToken).ConfigureAwait(false);
+ }
}
_logger.LogInformation("Recording completed: {FilePath}", targetFile);
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index e7834ffd6..2753cf177 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -1819,16 +1819,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (timer.IsProgramSeries)
{
- SaveSeriesNfo(timer, seriesPath);
- SaveVideoNfo(timer, recordingPath, program, false);
+ await SaveSeriesNfoAsync(timer, seriesPath).ConfigureAwait(false);
+ await SaveVideoNfoAsync(timer, recordingPath, program, false).ConfigureAwait(false);
}
else if (!timer.IsMovie || timer.IsSports || timer.IsNews)
{
- SaveVideoNfo(timer, recordingPath, program, true);
+ await SaveVideoNfoAsync(timer, recordingPath, program, true).ConfigureAwait(false);
}
else
{
- SaveVideoNfo(timer, recordingPath, program, false);
+ await SaveVideoNfoAsync(timer, recordingPath, program, false).ConfigureAwait(false);
}
await SaveRecordingImages(recordingPath, program).ConfigureAwait(false);
@@ -1839,7 +1839,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
- private void SaveSeriesNfo(TimerInfo timer, string seriesPath)
+ private async Task SaveSeriesNfoAsync(TimerInfo timer, string seriesPath)
{
var nfoPath = Path.Combine(seriesPath, "tvshow.nfo");
@@ -1848,61 +1848,62 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
- using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
+ await using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
{
var settings = new XmlWriterSettings
{
Indent = true,
- Encoding = Encoding.UTF8
+ Encoding = Encoding.UTF8,
+ Async = true
};
- using (var writer = XmlWriter.Create(stream, settings))
+ await using (var writer = XmlWriter.Create(stream, settings))
{
- writer.WriteStartDocument(true);
- writer.WriteStartElement("tvshow");
+ await writer.WriteStartDocumentAsync(true).ConfigureAwait(false);
+ await writer.WriteStartElementAsync(null, "tvshow", null).ConfigureAwait(false);
string id;
if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out id))
{
- writer.WriteElementString("id", id);
+ await writer.WriteElementStringAsync(null, "id", null, id).ConfigureAwait(false);
}
if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out id))
{
- writer.WriteElementString("imdb_id", id);
+ await writer.WriteElementStringAsync(null, "imdb_id", null, id).ConfigureAwait(false);
}
if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out id))
{
- writer.WriteElementString("tmdbid", id);
+ await writer.WriteElementStringAsync(null, "tmdbid", null, id).ConfigureAwait(false);
}
if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Zap2It.ToString(), out id))
{
- writer.WriteElementString("zap2itid", id);
+ await writer.WriteElementStringAsync(null, "zap2itid", null, id).ConfigureAwait(false);
}
if (!string.IsNullOrWhiteSpace(timer.Name))
{
- writer.WriteElementString("title", timer.Name);
+ await writer.WriteElementStringAsync(null, "title", null, timer.Name).ConfigureAwait(false);
}
if (!string.IsNullOrWhiteSpace(timer.OfficialRating))
{
- writer.WriteElementString("mpaa", timer.OfficialRating);
+ await writer.WriteElementStringAsync(null, "mpaa", null, timer.OfficialRating).ConfigureAwait(false);
}
foreach (var genre in timer.Genres)
{
- writer.WriteElementString("genre", genre);
+ await writer.WriteElementStringAsync(null, "genre", null, genre).ConfigureAwait(false);
}
- writer.WriteEndElement();
- writer.WriteEndDocument();
+ await writer.WriteEndElementAsync().ConfigureAwait(false);
+ await writer.WriteEndDocumentAsync().ConfigureAwait(false);
}
}
}
- private void SaveVideoNfo(TimerInfo timer, string recordingPath, BaseItem item, bool lockData)
+ private async Task SaveVideoNfoAsync(TimerInfo timer, string recordingPath, BaseItem item, bool lockData)
{
var nfoPath = Path.ChangeExtension(recordingPath, ".nfo");
@@ -1911,29 +1912,30 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
- using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
+ await using (var stream = new FileStream(nfoPath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
{
var settings = new XmlWriterSettings
{
Indent = true,
- Encoding = Encoding.UTF8
+ Encoding = Encoding.UTF8,
+ Async = true
};
var options = _config.GetNfoConfiguration();
var isSeriesEpisode = timer.IsProgramSeries;
- using (var writer = XmlWriter.Create(stream, settings))
+ await using (var writer = XmlWriter.Create(stream, settings))
{
- writer.WriteStartDocument(true);
+ await writer.WriteStartDocumentAsync(true).ConfigureAwait(false);
if (isSeriesEpisode)
{
- writer.WriteStartElement("episodedetails");
+ await writer.WriteStartElementAsync(null, "episodedetails", null).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(timer.EpisodeTitle))
{
- writer.WriteElementString("title", timer.EpisodeTitle);
+ await writer.WriteElementStringAsync(null, "title", null, timer.EpisodeTitle).ConfigureAwait(false);
}
var premiereDate = item.PremiereDate ?? (!timer.IsRepeat ? DateTime.UtcNow : null);
@@ -1942,79 +1944,87 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var formatString = options.ReleaseDateFormat;
- writer.WriteElementString(
+ await writer.WriteElementStringAsync(
+ null,
"aired",
- premiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
+ null,
+ premiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)).ConfigureAwait(false);
}
if (item.IndexNumber.HasValue)
{
- writer.WriteElementString("episode", item.IndexNumber.Value.ToString(CultureInfo.InvariantCulture));
+ await writer.WriteElementStringAsync(null, "episode", null, item.IndexNumber.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false);
}
if (item.ParentIndexNumber.HasValue)
{
- writer.WriteElementString("season", item.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture));
+ await writer.WriteElementStringAsync(null, "season", null, item.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false);
}
}
else
{
- writer.WriteStartElement("movie");
+ await writer.WriteStartElementAsync(null, "movie", null);
if (!string.IsNullOrWhiteSpace(item.Name))
{
- writer.WriteElementString("title", item.Name);
+ await writer.WriteElementStringAsync(null, "title", null, item.Name).ConfigureAwait(false);
}
if (!string.IsNullOrWhiteSpace(item.OriginalTitle))
{
- writer.WriteElementString("originaltitle", item.OriginalTitle);
+ await writer.WriteElementStringAsync(null, "originaltitle", null, item.OriginalTitle).ConfigureAwait(false);
}
if (item.PremiereDate.HasValue)
{
var formatString = options.ReleaseDateFormat;
- writer.WriteElementString(
+ await writer.WriteElementStringAsync(
+ null,
"premiered",
- item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
- writer.WriteElementString(
+ null,
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)).ConfigureAwait(false);
+ await writer.WriteElementStringAsync(
+ null,
"releasedate",
- item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture));
+ null,
+ item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)).ConfigureAwait(false);
}
}
- writer.WriteElementString(
+ await writer.WriteElementStringAsync(
+ null,
"dateadded",
- DateTime.Now.ToString(DateAddedFormat, CultureInfo.InvariantCulture));
+ null,
+ DateTime.Now.ToString(DateAddedFormat, CultureInfo.InvariantCulture)).ConfigureAwait(false);
if (item.ProductionYear.HasValue)
{
- writer.WriteElementString("year", item.ProductionYear.Value.ToString(CultureInfo.InvariantCulture));
+ await writer.WriteElementStringAsync(null, "year", null, item.ProductionYear.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false);
}
if (!string.IsNullOrEmpty(item.OfficialRating))
{
- writer.WriteElementString("mpaa", item.OfficialRating);
+ await writer.WriteElementStringAsync(null, "mpaa", null, item.OfficialRating).ConfigureAwait(false);
}
var overview = (item.Overview ?? string.Empty)
.StripHtml()
.Replace("&quot;", "'", StringComparison.Ordinal);
- writer.WriteElementString("plot", overview);
+ await writer.WriteElementStringAsync(null, "plot", null, overview).ConfigureAwait(false);
if (item.CommunityRating.HasValue)
{
- writer.WriteElementString("rating", item.CommunityRating.Value.ToString(CultureInfo.InvariantCulture));
+ await writer.WriteElementStringAsync(null, "rating", null, item.CommunityRating.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false);
}
foreach (var genre in item.Genres)
{
- writer.WriteElementString("genre", genre);
+ await writer.WriteElementStringAsync(null, "genre", null, genre).ConfigureAwait(false);
}
- var people = item.Id.Equals(Guid.Empty) ? new List<PersonInfo>() : _libraryManager.GetPeople(item);
+ var people = item.Id.Equals(default) ? new List<PersonInfo>() : _libraryManager.GetPeople(item);
var directors = people
.Where(i => IsPersonType(i, PersonType.Director))
@@ -2023,7 +2033,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var person in directors)
{
- writer.WriteElementString("director", person);
+ await writer.WriteElementStringAsync(null, "director", null, person).ConfigureAwait(false);
}
var writers = people
@@ -2034,19 +2044,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var person in writers)
{
- writer.WriteElementString("writer", person);
+ await writer.WriteElementStringAsync(null, "writer", null, person).ConfigureAwait(false);
}
foreach (var person in writers)
{
- writer.WriteElementString("credits", person);
+ await writer.WriteElementStringAsync(null, "credits", null, person).ConfigureAwait(false);
}
var tmdbCollection = item.GetProviderId(MetadataProvider.TmdbCollection);
if (!string.IsNullOrEmpty(tmdbCollection))
{
- writer.WriteElementString("collectionnumber", tmdbCollection);
+ await writer.WriteElementStringAsync(null, "collectionnumber", null, tmdbCollection).ConfigureAwait(false);
}
var imdb = item.GetProviderId(MetadataProvider.Imdb);
@@ -2054,10 +2064,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
if (!isSeriesEpisode)
{
- writer.WriteElementString("id", imdb);
+ await writer.WriteElementStringAsync(null, "id", null, imdb).ConfigureAwait(false);
}
- writer.WriteElementString("imdbid", imdb);
+ await writer.WriteElementStringAsync(null, "imdbid", null, imdb).ConfigureAwait(false);
// No need to lock if we have identified the content already
lockData = false;
@@ -2066,7 +2076,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var tvdb = item.GetProviderId(MetadataProvider.Tvdb);
if (!string.IsNullOrEmpty(tvdb))
{
- writer.WriteElementString("tvdbid", tvdb);
+ await writer.WriteElementStringAsync(null, "tvdbid", null, tvdb).ConfigureAwait(false);
// No need to lock if we have identified the content already
lockData = false;
@@ -2075,7 +2085,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var tmdb = item.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdb))
{
- writer.WriteElementString("tmdbid", tmdb);
+ await writer.WriteElementStringAsync(null, "tmdbid", null, tmdb).ConfigureAwait(false);
// No need to lock if we have identified the content already
lockData = false;
@@ -2083,26 +2093,26 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (lockData)
{
- writer.WriteElementString("lockdata", true.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
+ await writer.WriteElementStringAsync(null, "lockdata", null, "true").ConfigureAwait(false);
}
if (item.CriticRating.HasValue)
{
- writer.WriteElementString("criticrating", item.CriticRating.Value.ToString(CultureInfo.InvariantCulture));
+ await writer.WriteElementStringAsync(null, "criticrating", null, item.CriticRating.Value.ToString(CultureInfo.InvariantCulture)).ConfigureAwait(false);
}
if (!string.IsNullOrWhiteSpace(item.Tagline))
{
- writer.WriteElementString("tagline", item.Tagline);
+ await writer.WriteElementStringAsync(null, "tagline", null, item.Tagline).ConfigureAwait(false);
}
foreach (var studio in item.Studios)
{
- writer.WriteElementString("studio", studio);
+ await writer.WriteElementStringAsync(null, "studio", null, studio).ConfigureAwait(false);
}
- writer.WriteEndElement();
- writer.WriteEndDocument();
+ await writer.WriteEndElementAsync().ConfigureAwait(false);
+ await writer.WriteEndDocumentAsync().ConfigureAwait(false);
}
}
}
@@ -2372,7 +2382,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
string channelId = seriesTimer.RecordAnyChannel ? null : seriesTimer.ChannelId;
- if (string.IsNullOrWhiteSpace(channelId) && !parent.ChannelId.Equals(Guid.Empty))
+ if (string.IsNullOrWhiteSpace(channelId) && !parent.ChannelId.Equals(default))
{
if (!tempChannelCache.TryGetValue(parent.ChannelId, out LiveTvChannel channel))
{
@@ -2431,7 +2441,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
string channelId = null;
- if (!programInfo.ChannelId.Equals(Guid.Empty))
+ if (!programInfo.ChannelId.Equals(default))
{
if (!tempChannelCache.TryGetValue(programInfo.ChannelId, out LiveTvChannel channel))
{
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 7fa47e7db..582e61d79 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -24,18 +24,19 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
- public class EncodedRecorder : IRecorder
+ public class EncodedRecorder : IRecorder, IDisposable
{
private readonly ILogger _logger;
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerApplicationPaths _appPaths;
- private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
+ private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly IServerConfigurationManager _serverConfigurationManager;
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
private bool _hasExited;
private Stream _logFileStream;
private string _targetPath;
private Process _process;
+ private bool _disposed = false;
public EncodedRecorder(
ILogger logger,
@@ -323,5 +324,35 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.LogError(ex, "Error reading ffmpeg recording log");
}
}
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and optionally managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ _logFileStream?.Dispose();
+ _process?.Dispose();
+ }
+
+ _logFileStream = null;
+ _process = null;
+
+ _disposed = true;
+ }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index a8440102d..ffa0d9b6a 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -28,7 +28,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.LiveTv.Listings
{
- public class SchedulesDirect : IListingsProvider
+ public class SchedulesDirect : IListingsProvider, IDisposable
{
private const string ApiUrl = "https://json.schedulesdirect.org/20141201";
@@ -39,6 +39,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
private DateTime _lastErrorResponse;
+ private bool _disposed = false;
public SchedulesDirect(
ILogger<SchedulesDirect> logger,
@@ -58,8 +59,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
var dates = new List<string>();
- var start = new List<DateTime> { startDateUtc, startDateUtc.ToLocalTime() }.Min().Date;
- var end = new List<DateTime> { endDateUtc, endDateUtc.ToLocalTime() }.Max().Date;
+ var start = new[] { startDateUtc, startDateUtc.ToLocalTime() }.Min().Date;
+ var end = new[] { endDateUtc, endDateUtc.ToLocalTime() }.Max().Date;
while (start <= end)
{
@@ -822,5 +823,31 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return list;
}
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// <summary>
+ /// Releases unmanaged and optionally managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ _tokenSemaphore?.Dispose();
+ }
+
+ _disposed = true;
+ }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index 323b96021..c09f9cf8d 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.LiveTv
try
{
dto.ParentThumbImageTag = _imageProcessor.GetImageCacheTag(librarySeries, image);
- dto.ParentThumbItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
+ dto.ParentThumbItemId = librarySeries.Id;
}
catch (Exception ex)
{
@@ -193,7 +193,7 @@ namespace Emby.Server.Implementations.LiveTv
{
_imageProcessor.GetImageCacheTag(librarySeries, image)
};
- dto.ParentBackdropItemId = librarySeries.Id.ToString("N", CultureInfo.InvariantCulture);
+ dto.ParentBackdropItemId = librarySeries.Id;
}
catch (Exception ex)
{
@@ -240,7 +240,7 @@ namespace Emby.Server.Implementations.LiveTv
_imageProcessor.GetImageCacheTag(program, image)
};
- dto.ParentBackdropItemId = program.Id.ToString("N", CultureInfo.InvariantCulture);
+ dto.ParentBackdropItemId = program.Id;
}
catch (Exception ex)
{
@@ -456,7 +456,7 @@ namespace Emby.Server.Implementations.LiveTv
info.Id = timer.ExternalId;
}
- if (!dto.ChannelId.Equals(Guid.Empty) && string.IsNullOrEmpty(info.ChannelId))
+ if (!dto.ChannelId.Equals(default) && string.IsNullOrEmpty(info.ChannelId))
{
var channel = _libraryManager.GetItemById(dto.ChannelId);
@@ -522,7 +522,7 @@ namespace Emby.Server.Implementations.LiveTv
info.Id = timer.ExternalId;
}
- if (!dto.ChannelId.Equals(Guid.Empty) && string.IsNullOrEmpty(info.ChannelId))
+ if (!dto.ChannelId.Equals(default) && string.IsNullOrEmpty(info.ChannelId))
{
var channel = _libraryManager.GetItemById(dto.ChannelId);
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index aa3598c8b..97c2e6e30 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.LiveTv
/// <summary>
/// Class LiveTvManager.
/// </summary>
- public class LiveTvManager : ILiveTvManager, IDisposable
+ public class LiveTvManager : ILiveTvManager
{
private const int MaxGuideDays = 14;
private const string ExternalServiceTag = "ExternalServiceId";
@@ -63,8 +63,6 @@ namespace Emby.Server.Implementations.LiveTv
private ITunerHost[] _tunerHosts = Array.Empty<ITunerHost>();
private IListingsProvider[] _listingProviders = Array.Empty<IListingsProvider>();
- private bool _disposed = false;
-
public LiveTvManager(
IServerConfigurationManager config,
ILogger<LiveTvManager> logger,
@@ -178,7 +176,9 @@ namespace Emby.Server.Implementations.LiveTv
public QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken)
{
- var user = query.UserId == Guid.Empty ? null : _userManager.GetUserById(query.UserId);
+ var user = query.UserId.Equals(default)
+ ? null
+ : _userManager.GetUserById(query.UserId);
var topFolder = GetInternalLiveTvFolder(cancellationToken);
@@ -312,7 +312,7 @@ namespace Emby.Server.Implementations.LiveTv
{
if (isVideo)
{
- mediaSource.MediaStreams.AddRange(new List<MediaStream>
+ mediaSource.MediaStreams = new MediaStream[]
{
new MediaStream
{
@@ -329,11 +329,11 @@ namespace Emby.Server.Implementations.LiveTv
// Set the index to -1 because we don't know the exact index of the audio stream within the container
Index = -1
}
- });
+ };
}
else
{
- mediaSource.MediaStreams.AddRange(new List<MediaStream>
+ mediaSource.MediaStreams = new MediaStream[]
{
new MediaStream
{
@@ -341,7 +341,7 @@ namespace Emby.Server.Implementations.LiveTv
// Set the index to -1 because we don't know the exact index of the audio stream within the container
Index = -1
}
- });
+ };
}
}
@@ -857,11 +857,10 @@ namespace Emby.Server.Implementations.LiveTv
var returnArray = _dtoService.GetBaseItemDtos(queryResult.Items, options, user);
- return new QueryResult<BaseItemDto>
- {
- Items = returnArray,
- TotalRecordCount = queryResult.TotalRecordCount
- };
+ return new QueryResult<BaseItemDto>(
+ query.StartIndex,
+ queryResult.TotalRecordCount,
+ returnArray);
}
public QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
@@ -910,29 +909,27 @@ namespace Emby.Server.Implementations.LiveTv
programs = programs.Take(query.Limit.Value);
}
- return new QueryResult<BaseItem>
- {
- Items = programs.ToArray(),
- TotalRecordCount = totalCount
- };
+ return new QueryResult<BaseItem>(
+ query.StartIndex,
+ totalCount,
+ programs.ToArray());
}
- public QueryResult<BaseItemDto> GetRecommendedPrograms(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
+ public Task<QueryResult<BaseItemDto>> GetRecommendedProgramsAsync(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken)
{
if (!(query.IsAiring ?? false))
{
- return GetPrograms(query, options, cancellationToken).Result;
+ return GetPrograms(query, options, cancellationToken);
}
RemoveFields(options);
var internalResult = GetRecommendedProgramsInternal(query, options, cancellationToken);
- return new QueryResult<BaseItemDto>
- {
- Items = _dtoService.GetBaseItemDtos(internalResult.Items, options, query.User),
- TotalRecordCount = internalResult.TotalRecordCount
- };
+ return Task.FromResult(new QueryResult<BaseItemDto>(
+ query.StartIndex,
+ internalResult.TotalRecordCount,
+ _dtoService.GetBaseItemDtos(internalResult.Items, options, query.User)));
}
private int GetRecommendationScore(LiveTvProgram program, User user, bool factorChannelWatchCount)
@@ -1273,7 +1270,7 @@ namespace Emby.Server.Implementations.LiveTv
{
cancellationToken.ThrowIfCancellationRequested();
- if (itemId.Equals(Guid.Empty))
+ if (itemId.Equals(default))
{
// Somehow some invalid data got into the db. It probably predates the boundary checking
continue;
@@ -1533,7 +1530,9 @@ namespace Emby.Server.Implementations.LiveTv
public QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options)
{
- var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId);
+ var user = query.UserId.Equals(default)
+ ? null
+ : _userManager.GetUserById(query.UserId);
RemoveFields(options);
@@ -1541,11 +1540,10 @@ namespace Emby.Server.Implementations.LiveTv
var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user);
- return new QueryResult<BaseItemDto>
- {
- Items = returnArray,
- TotalRecordCount = internalResult.TotalRecordCount
- };
+ return new QueryResult<BaseItemDto>(
+ query.StartIndex,
+ internalResult.TotalRecordCount,
+ returnArray);
}
private async Task<QueryResult<TimerInfo>> GetTimersInternal(TimerQuery query, CancellationToken cancellationToken)
@@ -1593,7 +1591,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(query.ChannelId))
{
var guid = new Guid(query.ChannelId);
- timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
+ timers = timers.Where(i => _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId).Equals(guid));
}
if (!string.IsNullOrEmpty(query.SeriesTimerId))
@@ -1601,7 +1599,7 @@ namespace Emby.Server.Implementations.LiveTv
var guid = new Guid(query.SeriesTimerId);
timers = timers
- .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId) == guid);
+ .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId).Equals(guid));
}
if (!string.IsNullOrEmpty(query.Id))
@@ -1615,11 +1613,7 @@ namespace Emby.Server.Implementations.LiveTv
.OrderBy(i => i.StartDate)
.ToArray();
- return new QueryResult<TimerInfo>
- {
- Items = returnArray,
- TotalRecordCount = returnArray.Length
- };
+ return new QueryResult<TimerInfo>(returnArray);
}
public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
@@ -1667,7 +1661,7 @@ namespace Emby.Server.Implementations.LiveTv
if (!string.IsNullOrEmpty(query.ChannelId))
{
var guid = new Guid(query.ChannelId);
- timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
+ timers = timers.Where(i => _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId).Equals(guid));
}
if (!string.IsNullOrEmpty(query.SeriesTimerId))
@@ -1675,7 +1669,7 @@ namespace Emby.Server.Implementations.LiveTv
var guid = new Guid(query.SeriesTimerId);
timers = timers
- .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId) == guid);
+ .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item1.SeriesTimerId).Equals(guid));
}
if (!string.IsNullOrEmpty(query.Id))
@@ -1701,11 +1695,7 @@ namespace Emby.Server.Implementations.LiveTv
.OrderBy(i => i.StartDate)
.ToArray();
- return new QueryResult<TimerInfoDto>
- {
- Items = returnArray,
- TotalRecordCount = returnArray.Length
- };
+ return new QueryResult<TimerInfoDto>(returnArray);
}
public async Task CancelTimer(string id)
@@ -1801,11 +1791,7 @@ namespace Emby.Server.Implementations.LiveTv
.Select(i => i.Item1)
.ToArray();
- return new QueryResult<SeriesTimerInfo>
- {
- Items = returnArray,
- TotalRecordCount = returnArray.Length
- };
+ return new QueryResult<SeriesTimerInfo>(returnArray);
}
public async Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken)
@@ -1855,11 +1841,7 @@ namespace Emby.Server.Implementations.LiveTv
})
.ToArray();
- return new QueryResult<SeriesTimerInfoDto>
- {
- Items = returnArray,
- TotalRecordCount = returnArray.Length
- };
+ return new QueryResult<SeriesTimerInfoDto>(returnArray);
}
public BaseItem GetLiveTvChannel(TimerInfo timer, ILiveTvService service)
@@ -2112,36 +2094,6 @@ namespace Emby.Server.Implementations.LiveTv
};
}
- /// <inheritdoc />
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (_disposed)
- {
- return;
- }
-
- if (dispose)
- {
- // TODO: Dispose stuff
- }
-
- _services = null;
- _listingProviders = null;
- _tunerHosts = null;
-
- _disposed = true;
- }
-
private LiveTvServiceInfo[] GetServiceInfos()
{
return Services.Select(GetServiceInfo).ToArray();
diff --git a/Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs b/Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs
index 15df0dcf1..72bbdd14a 100644
--- a/Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs
+++ b/Emby.Server.Implementations/LiveTv/RefreshGuideScheduledTask.cs
@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.LiveTv
public string Key => "RefreshGuide";
/// <inheritdoc />
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var manager = (LiveTvManager)_liveTvManager;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 532790019..e0eaa8e58 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -446,7 +446,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
Path = url,
Protocol = MediaProtocol.Udp,
- MediaStreams = new List<MediaStream>
+ MediaStreams = new MediaStream[]
{
new MediaStream
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index a5edd35cc..6195c7648 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -133,7 +133,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
- var taskCompletionSource = new TaskCompletionSource<bool>();
+ var taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
_ = StartStreaming(
udpClient,
@@ -186,7 +186,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
var resolved = false;
- using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read))
+ var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
+ await using (fileStream.ConfigureAwait(false))
{
while (true)
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
index 5581ba87c..2748794b3 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
@@ -97,7 +97,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public Stream GetStream()
{
- var stream = GetInputStream(TempFilePath);
+ var stream = new FileStream(
+ TempFilePath,
+ FileMode.Open,
+ FileAccess.Read,
+ FileShare.ReadWrite,
+ IODefaults.FileStreamBufferSize,
+ FileOptions.SequentialScan | FileOptions.Asynchronous);
+
bool seekFile = (DateTime.UtcNow - DateOpened).TotalSeconds > 10;
if (seekFile)
{
@@ -107,15 +114,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return stream;
}
- protected FileStream GetInputStream(string path)
- => new FileStream(
- path,
- FileMode.Open,
- FileAccess.Read,
- FileShare.ReadWrite,
- IODefaults.FileStreamBufferSize,
- FileOptions.SequentialScan | FileOptions.Asynchronous);
-
protected async Task DeleteTempFiles(string path, int retryCount = 0)
{
if (retryCount == 0)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index dd83f9a53..2a468e14d 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -170,7 +170,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
Path = path,
Protocol = protocol,
- MediaStreams = new List<MediaStream>
+ MediaStreams = new MediaStream[]
{
new MediaStream
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index ab4beb15b..e84e1e074 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -73,7 +73,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
SetTempFilePath("ts");
- var taskCompletionSource = new TaskCompletionSource<bool>();
+ var taskCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
_ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index 25f51db16..01a9969b4 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -23,7 +23,7 @@
"HeaderFavoriteShows": "Oblíbené seriály",
"HeaderFavoriteSongs": "Oblíbená hudba",
"HeaderLiveTV": "Televize",
- "HeaderNextUp": "Nadcházející",
+ "HeaderNextUp": "Další díly",
"HeaderRecordingGroups": "Skupiny nahrávek",
"HomeVideos": "Domácí videa",
"Inherit": "Zdědit",
@@ -77,7 +77,7 @@
"SubtitleDownloadFailureFromForItem": "Stažení titulků pro {1} z {0} selhalo",
"Sync": "Synchronizace",
"System": "Systém",
- "TvShows": "TV seriály",
+ "TvShows": "Seriály",
"User": "Uživatel",
"UserCreatedWithName": "Uživatel {0} byl vytvořen",
"UserDeletedWithName": "Uživatel {0} byl smazán",
@@ -120,5 +120,7 @@
"Forced": "Vynucené",
"Default": "Výchozí",
"TaskOptimizeDatabaseDescription": "Zmenší databázi a odstraní prázdné místo. Spuštění této úlohy po skenování knihovny či jiných změnách databáze může zlepšit výkon.",
- "TaskOptimizeDatabase": "Optimalizovat databázi"
+ "TaskOptimizeDatabase": "Optimalizovat databázi",
+ "TaskKeyframeExtractorDescription": "Vytahuje klíčové snímky ze souborů videa za účelem vytváření přesnějších seznamů přehrávání HLS. Tento úkol může trvat velmi dlouho.",
+ "TaskKeyframeExtractor": "Vytahovač klíčových snímků"
}
diff --git a/Emby.Server.Implementations/Localization/Core/cy.json b/Emby.Server.Implementations/Localization/Core/cy.json
index 7fc27e18a..981614005 100644
--- a/Emby.Server.Implementations/Localization/Core/cy.json
+++ b/Emby.Server.Implementations/Localization/Core/cy.json
@@ -54,5 +54,57 @@
"Undefined": "Heb ddiffiniad",
"TvShows": "Rhaglenni teledu",
"HeaderLiveTV": "Teledu Byw",
- "User": "Defnyddiwr"
+ "User": "Defnyddiwr",
+ "TaskCleanLogsDescription": "Dileu ffeiliau log sy'n fwy na {0} diwrnod oed.",
+ "TaskCleanLogs": "Glanhau ffolder log",
+ "TaskRefreshLibraryDescription": "Sganio'ch llyfrgell gyfryngau am ffeiliau newydd ac yn adnewyddu metaddata.",
+ "TaskRefreshLibrary": "Sganwich Llyfrgell Cyfryngau",
+ "TaskCleanActivityLogDescription": "Yn dileu cofnodion log gweithgaredd sy'n hŷn na'r oedran a nodwyd.",
+ "TaskCleanActivityLog": "Glanhau Log Gweithgaredd",
+ "SubtitleDownloadFailureFromForItem": "Methodd is-deitlau lawrlwytho o {0} ar gyfer {1}",
+ "NotificationOptionPluginError": "Methodd ategyn",
+ "NotificationOptionAudioPlaybackStopped": "Stopiwyd chwarae sain",
+ "NotificationOptionAudioPlayback": "Dechreuwyd chwarae sain",
+ "MessageServerConfigurationUpdated": "Mae ffurfweddiad gweinydd wedi'i ddiweddaru",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Mae adran ffurfweddu gweinydd {0} wedi'i diweddaru",
+ "FailedLoginAttemptWithUserName": "Cais mewngofnodi wedi methu gan {0}",
+ "ValueHasBeenAddedToLibrary": "{0} wedi'i hychwanegu at eich llyfrgell gyfryngau",
+ "UserStoppedPlayingItemWithValues": "{0} wedi gorffen chwarae {1} ar {2}",
+ "UserStartedPlayingItemWithValues": "{0} yn chwarae {1} ar {2}",
+ "UserPolicyUpdatedWithName": "Polisi defnyddiwr wedi'i newid ar gyfer {0}",
+ "UserPasswordChangedWithName": "Cyfrinair wedi'i newid ar gyfer defnyddiwr {0}",
+ "UserOnlineFromDevice": "Mae {0} ar-lein o {1}",
+ "UserOfflineFromDevice": "Mae {0} wedi datgysylltu o {1}",
+ "UserLockedOutWithName": "Mae defnyddiwr {0} wedi'i gloi allan",
+ "UserDownloadingItemWithValues": "Mae {0} yn lawrlwytho {1}",
+ "UserDeletedWithName": "Defnyddiwr {0} wedi'i ddileu",
+ "UserCreatedWithName": "Defnyddiwr {0} wedi'i greu",
+ "StartupEmbyServerIsLoading": "Gweinydd Jellyfin yn llwytho. Triwch eto mewn ychydig.",
+ "ServerNameNeedsToBeRestarted": "Mae angen ailddechrau {0}",
+ "PluginUpdatedWithName": "{0} wedi'i ddiweddaru",
+ "PluginUninstalledWithName": "{0} wedi'i ddadosod",
+ "PluginInstalledWithName": "{0} wedi'i osod",
+ "NotificationOptionVideoPlaybackStopped": "Stopiwyd chwarae fideo",
+ "NotificationOptionVideoPlayback": "Dechreuwyd chwarae fideo",
+ "NotificationOptionUserLockedOut": "Defnyddiwr wedi'i gloi allan",
+ "NotificationOptionTaskFailed": "Methwyd cyflawni y dasg a drefnwyd",
+ "NotificationOptionServerRestartRequired": "Mae angen ailgychwyn y gweinydd",
+ "NotificationOptionPluginUpdateInstalled": "Diweddariad ategyn wedi'i osod",
+ "NotificationOptionPluginUninstalled": "Ategyn wedi'i ddadosod",
+ "NotificationOptionPluginInstalled": "Ategyn wedi'i osod",
+ "NotificationOptionNewLibraryContent": "Cynnwys newydd ar gael",
+ "NotificationOptionCameraImageUploaded": "Llun camera wedi'i huwchlwytho",
+ "NotificationOptionApplicationUpdateInstalled": "Diweddariad ap wedi'i osod",
+ "NotificationOptionApplicationUpdateAvailable": "Diweddariad ap ar gael",
+ "NewVersionIsAvailable": "Mae fersiwn diweddarach o'r gweinydd Jellyfin ar gael.",
+ "NameInstallFailed": "Gosodiad {0} wedi methu",
+ "MessageApplicationUpdatedTo": "Gweinydd Jellyfin wedi'i ddiweddaru i {0}",
+ "MessageApplicationUpdated": "Gweinydd Jellyfin wedi'i ddiweddaru",
+ "LabelIpAddressValue": "Cyfeiriad IP: {0}",
+ "ItemRemovedWithName": "{0} wedi'i dynnu o'r llyfrgell",
+ "ItemAddedWithName": "{0} wedi'i adio i'r llyfrgell",
+ "HeaderRecordingGroups": "Grwpiau Recordio",
+ "HeaderFavoriteSongs": "Ffefryn Ganeuon",
+ "HeaderFavoriteShows": "Ffefryn Shoeau",
+ "HeaderFavoriteEpisodes": "Ffefryn Rhaglenni"
}
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 115f36e7c..c538d08c7 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -120,5 +120,7 @@
"Forced": "Erzwungen",
"Default": "Standard",
"TaskOptimizeDatabaseDescription": "Komprimiert die Datenbank und trimmt den freien Speicherplatz. Die Ausführung dieser Aufgabe nach dem Scannen der Bibliothek oder nach anderen Änderungen, die Datenbankänderungen implizieren, kann die Leistung verbessern.",
- "TaskOptimizeDatabase": "Datenbank optimieren"
+ "TaskOptimizeDatabase": "Datenbank optimieren",
+ "TaskKeyframeExtractorDescription": "Extrahiere Keyframes aus Videodateien, um präzisere HLS-Playlisten zu erzeugen. Diese Aufgabe kann sehr lange dauern.",
+ "TaskKeyframeExtractor": "Keyframe Extraktor"
}
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 9952c05ca..09222dc47 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -120,5 +120,7 @@
"Forced": "Εξαναγκασμένο",
"Default": "Προεπιλογή",
"TaskOptimizeDatabaseDescription": "Συμπιέζει τη βάση δεδομένων και δημιουργεί ελεύθερο χώρο. Η εκτέλεση αυτής της εργασίας μετά τη σάρωση της βιβλιοθήκης ή την πραγματοποίηση άλλων αλλαγών που συνεπάγονται τροποποιήσεις της βάσης δεδομένων μπορεί να βελτιώσει την απόδοση.",
- "TaskOptimizeDatabase": "Βελτιστοποίηση βάσης δεδομένων"
+ "TaskOptimizeDatabase": "Βελτιστοποίηση βάσης δεδομένων",
+ "TaskKeyframeExtractorDescription": "Εξάγει τα βασικά καρέ από αρχεία βίντεο για να δημιουργήσει πιο ακριβείς HLS λίστες αναπαραγωγής. Αυτή η εργασία μπορεί να διαρκέσει πολλή ώρα.",
+ "TaskKeyframeExtractor": "Εξαγωγέας βασικών καρέ βίντεο"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index 568a8e447..d8c33d51b 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -12,6 +12,7 @@
"Default": "Default",
"DeviceOfflineWithName": "{0} has disconnected",
"DeviceOnlineWithName": "{0} is connected",
+ "External": "External",
"FailedLoginAttemptWithUserName": "Failed login try from {0}",
"Favorites": "Favorites",
"Folders": "Folders",
@@ -119,5 +120,7 @@
"TaskDownloadMissingSubtitles": "Download missing subtitles",
"TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.",
"TaskOptimizeDatabase": "Optimize database",
- "TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance."
+ "TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.",
+ "TaskKeyframeExtractor": "Keyframe Extractor",
+ "TaskKeyframeExtractorDescription": "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time."
}
diff --git a/Emby.Server.Implementations/Localization/Core/eo.json b/Emby.Server.Implementations/Localization/Core/eo.json
index 8abf7fa66..7d0fca47f 100644
--- a/Emby.Server.Implementations/Localization/Core/eo.json
+++ b/Emby.Server.Implementations/Localization/Core/eo.json
@@ -119,5 +119,7 @@
"HeaderRecordingGroups": "Rikordadaj Grupoj",
"FailedLoginAttemptWithUserName": "Malsukcesa ensaluta provo de {0}",
"CameraImageUploadedFrom": "Nova kamera bildo estis alŝutita de {0}",
- "AuthenticationSucceededWithUserName": "{0} sukcese aŭtentikigis"
+ "AuthenticationSucceededWithUserName": "{0} sukcese aŭtentikigis",
+ "TaskKeyframeExtractorDescription": "Eltiras ĉefkadrojn el videodosieroj por krei pli precizajn HLS-ludlistojn. Ĉi tiu tasko povas funkcii dum longa tempo.",
+ "TaskKeyframeExtractor": "Eltiri Ĉefkadrojn"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 432814dac..80ae16c5c 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
@@ -120,5 +120,7 @@
"Forced": "Forzado",
"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."
+ "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.",
+ "TaskKeyframeExtractorDescription": "Extrae los cuadros clave de los archivos de vídeo para crear listas HLS más precisas. Esta tarea puede tardar un buen rato.",
+ "TaskKeyframeExtractor": "Extractor de Cuadros Clave"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index f8c69712e..4918f468b 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -120,5 +120,7 @@
"Forced": "Forzado",
"Default": "Predeterminado",
"TaskOptimizeDatabase": "Optimizar la base de datos",
- "TaskOptimizeDatabaseDescription": "Compacta y libera el espacio libre en la base de datos. Ejecutar esta tarea tras escanear la biblioteca o hacer cambios que impliquen modificaciones en la base de datos puede mejorar el rendimiento."
+ "TaskOptimizeDatabaseDescription": "Compacta y libera el espacio libre en la base de datos. Ejecutar esta tarea tras escanear la biblioteca o hacer cambios que impliquen modificaciones en la base de datos puede mejorar el rendimiento.",
+ "TaskKeyframeExtractorDescription": "Extrae los fotogramas clave de los archivos de vídeo para crear listas HLS más precisas. Esta tarea puede tardar mucho tiempo.",
+ "TaskKeyframeExtractor": "Extractor de Fotogramas Clave"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json
index 6960ff007..7fb560be8 100644
--- a/Emby.Server.Implementations/Localization/Core/fa.json
+++ b/Emby.Server.Implementations/Localization/Core/fa.json
@@ -119,5 +119,8 @@
"TaskCleanActivityLogDescription": "ورودی‌های قدیمی‌تر از سن تنظیم شده در سیاهه فعالیت را حذف می‌کند.",
"TaskCleanActivityLog": "پاکسازی سیاهه فعالیت",
"Undefined": "تعریف نشده",
- "TaskOptimizeDatabase": "بهینه سازی پایگاه داده"
+ "TaskOptimizeDatabase": "بهینه سازی پایگاه داده",
+ "TaskOptimizeDatabaseDescription": "فشرده سازی پایگاه داده و باز کردن فضای آزاد.اجرای این گزینه بعد از اسکن کردن کتابخانه یا تغییرات دیگر که روی پایگاه داده تأثیر میگذارند میتواند کارایی را بهبود ببخشد.",
+ "TaskKeyframeExtractorDescription": "فریم های کلیدی را از فایل های ویدئویی استخراج می کند تا لیست های پخش HLS دقیق تری ایجاد کند. این کار ممکن است برای مدت طولانی اجرا شود.",
+ "TaskKeyframeExtractor": "استخراج کننده فریم کلیدی"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
index 4a1f4f1d5..435de7363 100644
--- a/Emby.Server.Implementations/Localization/Core/fi.json
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
@@ -119,5 +119,7 @@
"TaskCleanActivityLog": "Tyhjennä toimintahistoria",
"Undefined": "Määrittelemätön",
"TaskOptimizeDatabaseDescription": "Tiivistää ja puhdistaa tietokannan. Tämän toiminnon suorittaminen kirjastojen skannauksen tai muiden tietokantaan liittyvien muutoksien jälkeen voi parantaa suorituskykyä.",
- "TaskOptimizeDatabase": "Optimoi tietokanta"
+ "TaskOptimizeDatabase": "Optimoi tietokanta",
+ "TaskKeyframeExtractorDescription": "Purkaa videotiedostojen avainkuvat tarkempien HLS-toistolistojen luomiseksi. Tehtävä saattaa kestää huomattavan pitkään.",
+ "TaskKeyframeExtractor": "Avainkuvien purkain"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index bfafe7650..2a329e74d 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -1,7 +1,7 @@
{
"Albums": "Albums",
"AppDeviceValues": "Application : {0}, Appareil : {1}",
- "Application": "Applications",
+ "Application": "Application",
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
"Books": "Livres",
@@ -120,5 +120,7 @@
"Forced": "Forcé",
"Default": "Par défaut",
"TaskOptimizeDatabaseDescription": "Réduit les espaces vides/inutiles et compacte la base de données. Utiliser cette fonction après une mise à jour de la bibliothèque ou toute autre modification de la base de données peut améliorer les performances du serveur.",
- "TaskOptimizeDatabase": "Optimiser la base de données"
+ "TaskOptimizeDatabase": "Optimiser la base de données",
+ "TaskKeyframeExtractorDescription": "Extrait les images clés des fichiers vidéo pour créer des listes de lecture HLS plus précises. Cette tâche peut durer très longtemps.",
+ "TaskKeyframeExtractor": "Extracteur d'image clé"
}
diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json
index 85de5925e..781cfcfa2 100644
--- a/Emby.Server.Implementations/Localization/Core/hi.json
+++ b/Emby.Server.Implementations/Localization/Core/hi.json
@@ -61,5 +61,10 @@
"LabelRunningTimeValue": "चलने का समय: {0}",
"ItemAddedWithName": "{0} को लाइब्रेरी में जोड़ा गया",
"Inherit": "इनहेरिट",
- "NotificationOptionVideoPlaybackStopped": "चलचित्र रुका हुआ"
+ "NotificationOptionVideoPlaybackStopped": "चलचित्र रुका हुआ",
+ "PluginUninstalledWithName": "{0} अनइंस्टॉल हुए",
+ "PluginInstalledWithName": "{0} इंस्टॉल हुए",
+ "Plugin": "प्लग-इन",
+ "Playlists": "प्लेलिस्ट",
+ "Photos": "तस्वीरें"
}
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index acde84aaf..2da936cff 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -120,5 +120,7 @@
"Forced": "Kényszerített",
"Default": "Alapértelmezett",
"TaskOptimizeDatabaseDescription": "Tömöríti az adatbázist és csonkolja a szabad helyet. A feladat futtatása a könyvtár beolvasása után, vagy egyéb, adatbázis-módosítást igénylő változtatások végrehajtása javíthatja a teljesítményt.",
- "TaskOptimizeDatabase": "Adatbázis optimalizálása"
+ "TaskOptimizeDatabase": "Adatbázis optimalizálása",
+ "TaskKeyframeExtractor": "Kulcskockák kibontása",
+ "TaskKeyframeExtractorDescription": "Kulcskockákat bont ki a videofájlokból, hogy pontosabb HLS lejátszási listákat hozzon létre. Ez a feladat hosszú ideig tarthat."
}
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index 1b4a18deb..aaaf04712 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -120,5 +120,7 @@
"TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.",
"TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teñşelgen jasynan asqan jazbalary joiady.",
"TaskOptimizeDatabaseDescription": "Derekqordy qysyp, bos oryndy qysqartady. Būl tapsyrmany tasyğyşhanany skanerlegennen keiın nemese derekqorğa meñzeitın basqa özgertuler ıstelgennen keiın oryndau önımdılıktı damytuy mümkın.",
- "TaskOptimizeDatabase": "Derekqordy oñtailandyru"
+ "TaskOptimizeDatabase": "Derekqordy oñtailandyru",
+ "TaskKeyframeExtractorDescription": "Naqtyraq HLS oynatu tızımderın jasau üşın beinefaildardan negızgı kadrlardy şyğarady. Būl tapsyrma ūzaq uaqytqa sozyluy mümkın.",
+ "TaskKeyframeExtractor": "Negızgı kadrlardy şyğaru"
}
diff --git a/Emby.Server.Implementations/Localization/Core/mr.json b/Emby.Server.Implementations/Localization/Core/mr.json
index fdb4171b5..5aad4b0ed 100644
--- a/Emby.Server.Implementations/Localization/Core/mr.json
+++ b/Emby.Server.Implementations/Localization/Core/mr.json
@@ -58,5 +58,47 @@
"Application": "अ‍ॅप्लिकेशन",
"AppDeviceValues": "अ‍ॅप: {0}, यंत्र: {1}",
"Collections": "संग्रह",
- "ChapterNameValue": "धडा {0}"
+ "ChapterNameValue": "धडा {0}",
+ "TaskDownloadMissingSubtitlesDescription": "नसलेल्या उपशिर्षकांचा मेटाडॅटा कॉन्फिग्युरेशनप्रमाणे इन्टरनेटवर शोध घेतो.",
+ "TaskRefreshChannelsDescription": "इन्टरनेट वाहिन्यांची माहिती ताजी करतो.",
+ "TaskUpdatePluginsDescription": "आपोआप अपडेट करण्यासाठी कॉन्फिगर केलेल्या प्लगइनसाठी अपडेट डाउनलोड करून इन्स्टॉल करतो.",
+ "TaskRefreshChannels": "वाहिन्या ताज्या करा",
+ "TaskRefreshPeopleDescription": "आपल्या माध्यम संग्रहातील अभिनेत्यांचा व दिग्दर्शकांचा मेटाडॅटा ताजा करतो.",
+ "TaskRefreshPeople": "लोकांची माहिती ताजी करा",
+ "TaskRefreshLibraryDescription": "माध्यम संग्रह स्कॅन करून नवीन फायली शोधतो व मेटाडॅटा ताजे करतो.",
+ "TaskRefreshLibrary": "माध्यम संग्रह स्कॅन करा",
+ "TaskRefreshChapterImagesDescription": "अध्याय असलेल्या व्हिडियोंसाठी थंबनेल चित्र बनवतो.",
+ "TaskRefreshChapterImages": "अध्याय चित्र काढून घ्या",
+ "TasksMaintenanceCategory": "देखरेख",
+ "ValueHasBeenAddedToLibrary": "{0} हे तुमच्या माध्यम संग्रहात जोडण्यात आले आहे",
+ "UserStoppedPlayingItemWithValues": "{0} यांचं {2} वर {1} पूर्णपणे प्ले करून झालं आहे",
+ "UserStartedPlayingItemWithValues": "{0} हे {2} वर {1} प्ले करत आहे",
+ "UserDownloadingItemWithValues": "{0} हे {1} डाउनलोड करत आहे",
+ "System": "प्रणाली",
+ "Undefined": "अव्याख्यात",
+ "Sync": "सिंक",
+ "ServerNameNeedsToBeRestarted": "{0} याला बंद करून पुन्हा सुरू करायची गरज आहे",
+ "SubtitleDownloadFailureFromForItem": "{0} येथून {1} यासाठी उपशिर्षक डाउनलोड करण्यात अपयश",
+ "ScheduledTaskStartedWithName": "{0} सुरू झाले",
+ "ScheduledTaskFailedWithName": "{0} अपयशी झाले",
+ "ProviderValue": "पुरवणारा: {0}",
+ "PluginUpdatedWithName": "{0} अपडेट केले",
+ "PluginUninstalledWithName": "{0} अनिन्स्टॉल केले",
+ "PluginInstalledWithName": "{0} इन्स्टॉल केले",
+ "NotificationOptionVideoPlaybackStopped": "व्हिडियो प्लेबॅक बंद केले",
+ "NotificationOptionVideoPlayback": "व्हिडियो प्लेबॅक सुरू केले",
+ "NotificationOptionTaskFailed": "अनुसूचित कार्यात अपयश",
+ "NotificationOptionServerRestartRequired": "सर्व्हर बंद करून पुन्हा सुरू करावा लागेल",
+ "NotificationOptionPluginUpdateInstalled": "प्लगइन अपडेट इन्स्टॉल झाले",
+ "NotificationOptionPluginUninstalled": "प्लगइन अनिन्स्टॉल झाले",
+ "NotificationOptionPluginInstalled": "प्लगइन इन्स्टॉल झाले",
+ "NotificationOptionPluginError": "प्लगइनमध्ये अपयश",
+ "NotificationOptionNewLibraryContent": "नवीन सामग्री जोडली गेली",
+ "NotificationOptionInstallationFailed": "इन्स्टॉल करण्यात अपयश",
+ "NotificationOptionAudioPlayback": "ऑडियो प्लेबॅक सुरू झाले",
+ "NotificationOptionAudioPlaybackStopped": "ऑडियो प्लेबॅक बंद झाले",
+ "MixedContent": "मिश्रित सामग्री",
+ "LabelRunningTimeValue": "चालू काल: {0}",
+ "HeaderContinueWatching": "बघणे चालू ठेवा",
+ "Default": "डीफॉल्ट"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json
index 81aa996d9..401e68b2a 100644
--- a/Emby.Server.Implementations/Localization/Core/pr.json
+++ b/Emby.Server.Implementations/Localization/Core/pr.json
@@ -1,7 +1,16 @@
{
- "Books": "Libros",
- "AuthenticationSucceededWithUserName": "{0} autentificado correctamente",
+ "Books": "Scrolls",
+ "AuthenticationSucceededWithUserName": "{0} passed yer trial",
"Artists": "Artistas",
"Songs": "Shantees",
- "Albums": "Ships"
+ "Albums": "Tomes",
+ "Photos": "Paintings",
+ "NotificationOptionUserLockedOut": "Crewmate sent to the brig",
+ "HeaderContinueWatching": "Continue Yer Journey",
+ "Folders": "Chests",
+ "Application": "Captain",
+ "DeviceOnlineWithName": "{0} joined yer crew",
+ "DeviceOfflineWithName": "{0} abandoned ship",
+ "AppDeviceValues": "Captain: {0}, Ship: {1}",
+ "CameraImageUploadedFrom": "Yer looking glass has glimpsed another painting from {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json
index f8fad7b63..8af5449a7 100644
--- a/Emby.Server.Implementations/Localization/Core/ro.json
+++ b/Emby.Server.Implementations/Localization/Core/ro.json
@@ -117,5 +117,7 @@
"TaskCleanActivityLog": "Curăță Jurnalul de Activitate",
"Undefined": "Nedefinit",
"Forced": "Forțat",
- "Default": "Implicit"
+ "Default": "Implicit",
+ "TaskOptimizeDatabaseDescription": "Compactează baza de date și trunchiază spațiul liber. Rularea acestei sarcini după scanarea bibliotecii sau după efectuarea altor modificări care implică modificări ale bazei de date poate îmbunătăți performanța.",
+ "TaskOptimizeDatabase": "Optimizează baza de date"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index dc3793f1b..dd1e5d0ee 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -120,5 +120,7 @@
"Forced": "Форсир-ые",
"Default": "По умолчанию",
"TaskOptimizeDatabaseDescription": "Сжимает базу данных и вырезает свободные места. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.",
- "TaskOptimizeDatabase": "Оптимизация базы данных"
+ "TaskOptimizeDatabase": "Оптимизация базы данных",
+ "TaskKeyframeExtractorDescription": "Извлекаются ключевые кадры из видеофайлов для создания более точных списков плей-листов HLS. Эта задача может выполняться в течение длительного времени.",
+ "TaskKeyframeExtractor": "Извлечение ключевых кадров"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 5d05361b0..10c6db63b 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -120,5 +120,7 @@
"Forced": "Tvingad",
"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."
+ "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.",
+ "TaskKeyframeExtractorDescription": "Expoterar nyckelram från video filer för att skapa mer exakta HLS-spellistor. Denna uppgift kan pågå under lång tid.",
+ "TaskKeyframeExtractor": "Nyckelram Extraktor"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json
index 98d763fcd..5548a74d2 100644
--- a/Emby.Server.Implementations/Localization/Core/ta.json
+++ b/Emby.Server.Implementations/Localization/Core/ta.json
@@ -119,5 +119,7 @@
"Forced": "கட்டாயப்படுத்தப்பட்டது",
"Default": "இயல்புநிலை",
"TaskOptimizeDatabaseDescription": "தரவுத்தளத்தை சுருக்கி, இலவச இடத்தை குறைக்கிறது. நூலகத்தை ஸ்கேன் செய்தபின் அல்லது தரவுத்தள மாற்றங்களைக் குறிக்கும் பிற மாற்றங்களைச் செய்தபின் இந்த பணியை இயக்குவது செயல்திறனை மேம்படுத்தக்கூடும்.",
- "TaskOptimizeDatabase": "தரவுத்தளத்தை மேம்படுத்தவும்"
+ "TaskOptimizeDatabase": "தரவுத்தளத்தை மேம்படுத்தவும்",
+ "TaskKeyframeExtractorDescription": "மிகவும் துல்லியமான HLS பிளேலிஸ்ட்களை உருவாக்க வீடியோ கோப்புகளிலிருந்து கீஃப்ரேம்களைப் பிரித்தெடுக்கிறது. இந்த பணி நீண்ட காலமாக இருக்கலாம்.",
+ "TaskKeyframeExtractor": "கீஃப்ரேம் எக்ஸ்ட்ராக்டர்"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ug.json b/Emby.Server.Implementations/Localization/Core/ug.json
new file mode 100644
index 000000000..aea93c7fa
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/ug.json
@@ -0,0 +1,9 @@
+{
+ "ChapterNameValue": "باب {0}",
+ "Channels": "قانال",
+ "CameraImageUploadedFrom": "{0} ئورۇندىن يېڭى سۈرەت چىقىرىلدى",
+ "Books": "كىتاب",
+ "AuthenticationSucceededWithUserName": "تىزىملىتىش مۇۋەپپەقىيەتلىك بول",
+ "Artists": "سەنئەتكار",
+ "Albums": "پىلاستىنكا"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index d80f1760d..a9268c7d5 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -13,7 +13,7 @@
"Songs": "Bài Hát",
"Sync": "Đồng Bộ",
"ValueSpecialEpisodeName": "Đặc Biệt - {0}",
- "Albums": "",
+ "Albums": "Album",
"Artists": "Ca Sĩ",
"TaskDownloadMissingSubtitlesDescription": "Tìm kiếm phụ đề bị thiếu trên Internet dựa trên cấu hình dữ liệu mô tả.",
"TaskDownloadMissingSubtitles": "Tải Xuống Phụ Đề Bị Thiếu",
@@ -119,5 +119,7 @@
"Forced": "Bắt Buộc",
"Default": "Mặc Định",
"TaskOptimizeDatabaseDescription": "Thu gọn cơ sở dữ liệu và cắt bớt dung lượng trống. Chạy tác vụ này sau khi quét thư viện hoặc thực hiện các thay đổi khác ngụ ý sửa đổi cơ sở dữ liệu có thể cải thiện hiệu suất.",
- "TaskOptimizeDatabase": "Tối ưu hóa cơ sở dữ liệu"
+ "TaskOptimizeDatabase": "Tối ưu hóa cơ sở dữ liệu",
+ "TaskKeyframeExtractor": "Trích Xuất Khung Hình",
+ "TaskKeyframeExtractorDescription": "Trích xuất khung hình chính từ các tệp video để tạo danh sách phát HLS chính xác hơn. Tác vụ này có thể chạy trong một thời gian dài."
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index ac4eb644b..23d2819c3 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -120,5 +120,7 @@
"Forced": "强制的",
"Default": "默认",
"TaskOptimizeDatabaseDescription": "压缩数据库并优化可用空间,在扫描库或执行其他数据库修改后运行此任务可能会提高性能。",
- "TaskOptimizeDatabase": "优化数据库"
+ "TaskOptimizeDatabase": "优化数据库",
+ "TaskKeyframeExtractorDescription": "从视频文件中提取关键帧以创建更准确的HLS播放列表。这项任务可能需要很长时间。",
+ "TaskKeyframeExtractor": "关键帧提取器"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index 1cc97bc27..ac74da67d 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -103,7 +103,7 @@
"TaskCleanTranscodeDescription": "刪除超過一天的轉碼文件。",
"TaskCleanTranscode": "清理轉碼目錄",
"TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。",
- "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的metadata。",
+ "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的元數據。",
"TaskCleanLogsDescription": "刪除超過{0}天的日誌文件。",
"TaskCleanLogs": "清理日誌目錄",
"TaskRefreshLibrary": "掃描媒體庫",
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index dbd70342a..281dbb00b 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -147,13 +147,7 @@ namespace Emby.Server.Implementations.Localization
threeletterNames = new[] { parts[0], parts[1] };
}
- list.Add(new CultureDto
- {
- DisplayName = name,
- Name = name,
- ThreeLetterISOLanguageNames = threeletterNames,
- TwoLetterISOLanguageName = twoCharName
- });
+ list.Add(new CultureDto(name, name, twoCharName, threeletterNames));
}
}
diff --git a/Emby.Server.Implementations/Localization/Ratings/au.csv b/Emby.Server.Implementations/Localization/Ratings/au.csv
index 940375e26..11f4ed94c 100644
--- a/Emby.Server.Implementations/Localization/Ratings/au.csv
+++ b/Emby.Server.Implementations/Localization/Ratings/au.csv
@@ -2,7 +2,6 @@ AU-G,1
AU-PG,5
AU-M,6
AU-MA15+,7
-AU-M15+,8
AU-R18+,9
AU-X18+,10
AU-RC,11
diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs
index fd3fc31c9..21795c8f8 100644
--- a/Emby.Server.Implementations/Net/SocketFactory.cs
+++ b/Emby.Server.Implementations/Net/SocketFactory.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -63,18 +61,13 @@ namespace Emby.Server.Implementations.Net
}
/// <inheritdoc />
- public ISocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort)
+ public ISocket CreateUdpMulticastSocket(IPAddress ipAddress, int multicastTimeToLive, int localPort)
{
if (ipAddress == null)
{
throw new ArgumentNullException(nameof(ipAddress));
}
- if (ipAddress.Length == 0)
- {
- throw new ArgumentException("ipAddress cannot be an empty string.", nameof(ipAddress));
- }
-
if (multicastTimeToLive <= 0)
{
throw new ArgumentException("multicastTimeToLive cannot be zero or less.", nameof(multicastTimeToLive));
@@ -87,14 +80,7 @@ namespace Emby.Server.Implementations.Net
var retVal = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
- try
- {
- // not supported on all platforms. throws on ubuntu with .net core 2.0
- retVal.ExclusiveAddressUse = false;
- }
- catch (SocketException)
- {
- }
+ retVal.ExclusiveAddressUse = false;
try
{
@@ -114,7 +100,7 @@ namespace Emby.Server.Implementations.Net
var localIp = IPAddress.Any;
- retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(IPAddress.Parse(ipAddress), localIp));
+ retVal.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(ipAddress, localIp));
retVal.MulticastLoopback = true;
return new UdpSocket(retVal, localPort, localIp);
diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs
index 0c451ccb6..bbbca4fc0 100644
--- a/Emby.Server.Implementations/Net/UdpSocket.cs
+++ b/Emby.Server.Implementations/Net/UdpSocket.cs
@@ -159,7 +159,7 @@ namespace Emby.Server.Implementations.Net
{
ThrowIfDisposed();
- var taskCompletion = new TaskCompletionSource<SocketReceiveResult>();
+ var taskCompletion = new TaskCompletionSource<SocketReceiveResult>(TaskCreationOptions.RunContinuationsAsynchronously);
bool isResultSet = false;
Action<IAsyncResult> callback = callbackResult =>
@@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.Net
{
ThrowIfDisposed();
- var taskCompletion = new TaskCompletionSource<int>();
+ var taskCompletion = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
bool isResultSet = false;
Action<IAsyncResult> callback = callbackResult =>
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 02df2fffe..9e7035cb3 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -139,7 +139,9 @@ namespace Emby.Server.Implementations.Playlists
{
new Share
{
- UserId = options.UserId.Equals(Guid.Empty) ? null : options.UserId.ToString("N", CultureInfo.InvariantCulture),
+ UserId = options.UserId.Equals(default)
+ ? null
+ : options.UserId.ToString("N", CultureInfo.InvariantCulture),
CanEdit = true
}
}
@@ -188,7 +190,7 @@ namespace Emby.Server.Implementations.Playlists
public Task AddToPlaylistAsync(Guid playlistId, IReadOnlyCollection<Guid> itemIds, Guid userId)
{
- var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId);
+ var user = userId.Equals(default) ? null : _userManager.GetUserById(userId);
return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
{
diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs
index a805924dd..45ef36441 100644
--- a/Emby.Server.Implementations/Plugins/PluginManager.cs
+++ b/Emby.Server.Implementations/Plugins/PluginManager.cs
@@ -483,7 +483,7 @@ namespace Emby.Server.Implementations.Plugins
var pluginStr = instance.Version.ToString();
bool changed = false;
if (string.Equals(manifest.Version, pluginStr, StringComparison.Ordinal)
- || manifest.Id != instance.Id)
+ || !manifest.Id.Equals(instance.Id))
{
// If a plugin without a manifest failed to load due to an external issue (eg config),
// this updates the manifest to the actual plugin values.
diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index 299f10544..2c4d6884d 100644
--- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -414,7 +414,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
CurrentCancellationTokenSource.CancelAfter(TimeSpan.FromTicks(options.MaxRuntimeTicks.Value));
}
- await ScheduledTask.Execute(CurrentCancellationTokenSource.Token, progress).ConfigureAwait(false);
+ await ScheduledTask.ExecuteAsync(progress, CurrentCancellationTokenSource.Token).ConfigureAwait(false);
status = TaskCompletionStatus.Completed;
}
@@ -757,6 +757,10 @@ namespace Emby.Server.Implementations.ScheduledTasks
var trigger = triggerInfo.Item2;
trigger.Triggered -= OnTriggerTriggered;
trigger.Stop();
+ if (trigger is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index a5786a3d7..0bf0838fa 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -88,13 +88,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
};
}
- /// <summary>
- /// Returns the task to be executed.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var videos = _libraryManager.GetItemList(new InternalItemsQuery
{
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
index 79886cb52..776079044 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs
@@ -57,12 +57,12 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public bool IsLogged => true;
/// <inheritdoc />
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var retentionDays = _serverConfigurationManager.Configuration.ActivityLogRetentionDays;
if (!retentionDays.HasValue || retentionDays < 0)
{
- throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
+ throw new InvalidOperationException($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
}
var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value);
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
index 0941902fc..03935b384 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs
@@ -79,19 +79,14 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
};
}
- /// <summary>
- /// Returns the task to be executed.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var minDateModified = DateTime.UtcNow.AddDays(-30);
try
{
- DeleteCacheFilesFromDirectory(cancellationToken, _applicationPaths.CachePath, minDateModified, progress);
+ DeleteCacheFilesFromDirectory(_applicationPaths.CachePath, minDateModified, progress, cancellationToken);
}
catch (DirectoryNotFoundException)
{
@@ -104,7 +99,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
try
{
- DeleteCacheFilesFromDirectory(cancellationToken, _applicationPaths.TempDirectory, minDateModified, progress);
+ DeleteCacheFilesFromDirectory(_applicationPaths.TempDirectory, minDateModified, progress, cancellationToken);
}
catch (DirectoryNotFoundException)
{
@@ -117,11 +112,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Deletes the cache files from directory with a last write time less than a given date.
/// </summary>
- /// <param name="cancellationToken">The task cancellation token.</param>
/// <param name="directory">The directory.</param>
/// <param name="minDateModified">The min date modified.</param>
/// <param name="progress">The progress.</param>
- private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
+ /// <param name="cancellationToken">The task cancellation token.</param>
+ private void DeleteCacheFilesFromDirectory(string directory, DateTime minDateModified, IProgress<double> progress, CancellationToken cancellationToken)
{
var filesToDelete = _fileSystem.GetFiles(directory, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
index fedb5deb0..9739d7327 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs
@@ -69,13 +69,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
};
}
- /// <summary>
- /// Returns the task to be executed.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
// Delete log files more than n days old
var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays);
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
index 099d781cd..e4e565c64 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs
@@ -78,18 +78,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
};
}
- /// <summary>
- /// Returns the task to be executed.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
var minDateModified = DateTime.UtcNow.AddDays(-1);
progress.Report(50);
- DeleteTempFilesFromDirectory(cancellationToken, _configurationManager.GetTranscodePath(), minDateModified, progress);
+ DeleteTempFilesFromDirectory(_configurationManager.GetTranscodePath(), minDateModified, progress, cancellationToken);
return Task.CompletedTask;
}
@@ -97,11 +92,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Deletes the transcoded temp files from directory with a last write time less than a given date.
/// </summary>
- /// <param name="cancellationToken">The task cancellation token.</param>
/// <param name="directory">The directory.</param>
/// <param name="minDateModified">The min date modified.</param>
/// <param name="progress">The progress.</param>
- private void DeleteTempFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress<double> progress)
+ /// <param name="cancellationToken">The task cancellation token.</param>
+ private void DeleteTempFilesFromDirectory(string directory, DateTime minDateModified, IProgress<double> progress, CancellationToken cancellationToken)
{
var filesToDelete = _fileSystem.GetFiles(directory, true)
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
index 35a4aeef6..98e45fa46 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
@@ -69,13 +69,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
};
}
- /// <summary>
- /// Returns the task to be executed.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
_logger.LogInformation("Optimizing and vacuuming jellyfin.db...");
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
index 34780111b..7d60ea731 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
@@ -62,15 +62,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
};
}
- /// <summary>
- /// Returns the task to be executed.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
- return _libraryManager.ValidatePeople(cancellationToken, progress);
+ return _libraryManager.ValidatePeopleAsync(progress, cancellationToken);
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
index b3973cecb..443649e6e 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs
@@ -68,13 +68,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
yield return new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks };
}
- /// <summary>
- /// Update installed plugins.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns><see cref="Task" />.</returns>
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
progress.Report(0);
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
index f7b3cfedc..065008157 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs
@@ -25,8 +25,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// <summary>
/// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class.
/// </summary>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="localization">The localization manager.</param>
+ /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
+ /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
public RefreshMediaLibraryTask(ILibraryManager libraryManager, ILocalizationManager localization)
{
_libraryManager = libraryManager;
@@ -58,13 +58,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
};
}
- /// <summary>
- /// Executes the internal.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
+ /// <inheritdoc />
+ public Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
index dc5eb7391..63f11a22c 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/DailyTrigger.cs
@@ -8,10 +8,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
/// <summary>
/// Represents a task trigger that fires everyday.
/// </summary>
- public sealed class DailyTrigger : ITaskTrigger
+ public sealed class DailyTrigger : ITaskTrigger, IDisposable
{
private readonly TimeSpan _timeOfDay;
private Timer? _timer;
+ private bool _disposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="DailyTrigger"/> class.
@@ -71,6 +72,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
private void DisposeTimer()
{
_timer?.Dispose();
+ _timer = null;
}
/// <summary>
@@ -80,5 +82,18 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
{
Triggered?.Invoke(this, EventArgs.Empty);
}
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ DisposeTimer();
+
+ _disposed = true;
+ }
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs
index 927f57e95..3eb800199 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/IntervalTrigger.cs
@@ -9,11 +9,12 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
/// <summary>
/// Represents a task trigger that runs repeatedly on an interval.
/// </summary>
- public sealed class IntervalTrigger : ITaskTrigger
+ public sealed class IntervalTrigger : ITaskTrigger, IDisposable
{
private readonly TimeSpan _interval;
private DateTime _lastStartDate;
private Timer? _timer;
+ private bool _disposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="IntervalTrigger"/> class.
@@ -89,6 +90,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
private void DisposeTimer()
{
_timer?.Dispose();
+ _timer = null;
}
/// <summary>
@@ -104,5 +106,18 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
Triggered(this, EventArgs.Empty);
}
}
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ DisposeTimer();
+
+ _disposed = true;
+ }
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs b/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
index 2392b20fd..fab49f2fb 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Triggers/WeeklyTrigger.cs
@@ -8,11 +8,12 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
/// <summary>
/// Represents a task trigger that fires on a weekly basis.
/// </summary>
- public sealed class WeeklyTrigger : ITaskTrigger
+ public sealed class WeeklyTrigger : ITaskTrigger, IDisposable
{
private readonly TimeSpan _timeOfDay;
private readonly DayOfWeek _dayOfWeek;
private Timer? _timer;
+ private bool _disposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="WeeklyTrigger"/> class.
@@ -94,6 +95,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
private void DisposeTimer()
{
_timer?.Dispose();
+ _timer = null;
}
/// <summary>
@@ -103,5 +105,18 @@ namespace Emby.Server.Implementations.ScheduledTasks.Triggers
{
Triggered?.Invoke(this, EventArgs.Empty);
}
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ DisposeTimer();
+
+ _disposed = true;
+ }
}
}
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 6c679ea20..277fdf87d 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -373,7 +373,7 @@ namespace Emby.Server.Implementations.Session
info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture);
}
- if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
+ if (!info.ItemId.Equals(default) && info.Item == null && libraryItem != null)
{
var current = session.NowPlayingItem;
@@ -424,9 +424,14 @@ namespace Emby.Server.Implementations.Session
var nowPlayingQueue = info.NowPlayingQueue;
- if (nowPlayingQueue != null)
+ if (nowPlayingQueue?.Length > 0)
{
session.NowPlayingQueue = nowPlayingQueue;
+
+ var itemIds = nowPlayingQueue.Select(queue => queue.Id).ToArray();
+ session.NowPlayingQueueFullItems = _dtoService.GetBaseItemDtos(
+ _libraryManager.GetItemList(new InternalItemsQuery { ItemIds = itemIds }),
+ new DtoOptions(true));
}
}
@@ -553,22 +558,24 @@ namespace Emby.Server.Implementations.Session
{
var users = new List<User>();
- if (session.UserId != Guid.Empty)
+ if (session.UserId.Equals(default))
{
- var user = _userManager.GetUserById(session.UserId);
-
- if (user == null)
- {
- throw new InvalidOperationException("User not found");
- }
+ return users;
+ }
- users.Add(user);
+ var user = _userManager.GetUserById(session.UserId);
- users.AddRange(session.AdditionalUsers
- .Select(i => _userManager.GetUserById(i.UserId))
- .Where(i => i != null));
+ if (user == null)
+ {
+ throw new InvalidOperationException("User not found");
}
+ users.Add(user);
+
+ users.AddRange(session.AdditionalUsers
+ .Select(i => _userManager.GetUserById(i.UserId))
+ .Where(i => i != null));
+
return users;
}
@@ -660,7 +667,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(info.SessionId);
- var libraryItem = info.ItemId == Guid.Empty
+ var libraryItem = info.ItemId.Equals(default)
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -755,7 +762,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(info.SessionId);
- var libraryItem = info.ItemId.Equals(Guid.Empty)
+ var libraryItem = info.ItemId.Equals(default)
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -892,7 +899,7 @@ namespace Emby.Server.Implementations.Session
session.StopAutomaticProgress();
- var libraryItem = info.ItemId.Equals(Guid.Empty)
+ var libraryItem = info.ItemId.Equals(default)
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -902,7 +909,7 @@ namespace Emby.Server.Implementations.Session
info.MediaSourceId = info.ItemId.ToString("N", CultureInfo.InvariantCulture);
}
- if (!info.ItemId.Equals(Guid.Empty) && info.Item == null && libraryItem != null)
+ if (!info.ItemId.Equals(default) && info.Item == null && libraryItem != null)
{
var current = session.NowPlayingItem;
@@ -1122,7 +1129,7 @@ namespace Emby.Server.Implementations.Session
var session = GetSessionToRemoteControl(sessionId);
- var user = session.UserId == Guid.Empty ? null : _userManager.GetUserById(session.UserId);
+ var user = session.UserId.Equals(default) ? null : _userManager.GetUserById(session.UserId);
List<BaseItem> items;
@@ -1177,7 +1184,7 @@ namespace Emby.Server.Implementations.Session
EnableImages = false
})
.Where(i => !i.IsVirtualItem)
- .SkipWhile(i => i.Id != episode.Id)
+ .SkipWhile(i => !i.Id.Equals(episode.Id))
.ToList();
if (episodes.Count > 0)
@@ -1191,7 +1198,7 @@ namespace Emby.Server.Implementations.Session
{
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
- if (!controllingSession.UserId.Equals(Guid.Empty))
+ if (!controllingSession.UserId.Equals(default))
{
command.ControllingUserId = controllingSession.UserId;
}
@@ -1310,7 +1317,7 @@ namespace Emby.Server.Implementations.Session
{
var controllingSession = GetSession(controllingSessionId);
AssertCanControl(session, controllingSession);
- if (!controllingSession.UserId.Equals(Guid.Empty))
+ if (!controllingSession.UserId.Equals(default))
{
command.ControllingUserId = controllingSession.UserId.ToString("N", CultureInfo.InvariantCulture);
}
@@ -1383,12 +1390,12 @@ namespace Emby.Server.Implementations.Session
var session = GetSession(sessionId);
- if (session.UserId == userId)
+ if (session.UserId.Equals(userId))
{
throw new ArgumentException("The requested user is already the primary user of the session.");
}
- if (session.AdditionalUsers.All(i => i.UserId != userId))
+ if (session.AdditionalUsers.All(i => !i.UserId.Equals(userId)))
{
var user = _userManager.GetUserById(userId);
@@ -1458,7 +1465,7 @@ namespace Emby.Server.Implementations.Session
CheckDisposed();
User user = null;
- if (request.UserId != Guid.Empty)
+ if (!request.UserId.Equals(default))
{
user = _userManager.GetUserById(request.UserId);
}
@@ -1787,7 +1794,7 @@ namespace Emby.Server.Implementations.Session
throw new ArgumentNullException(nameof(info));
}
- var user = info.UserId == Guid.Empty
+ var user = info.UserId.Equals(default)
? null
: _userManager.GetUserById(info.UserId);
diff --git a/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs b/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs
new file mode 100644
index 000000000..e39280a10
--- /dev/null
+++ b/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs
@@ -0,0 +1,50 @@
+using System;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Sorting;
+using MediaBrowser.Model.Querying;
+
+namespace Emby.Server.Implementations.Sorting
+{
+ /// <summary>
+ /// Class IndexNumberComparer.
+ /// </summary>
+ public class IndexNumberComparer : IBaseItemComparer
+ {
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.IndexNumber;
+
+ /// <summary>
+ /// Compares the specified x.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <param name="y">The y.</param>
+ /// <returns>System.Int32.</returns>
+ public int Compare(BaseItem? x, BaseItem? y)
+ {
+ if (x == null)
+ {
+ throw new ArgumentNullException(nameof(x));
+ }
+
+ if (y == null)
+ {
+ throw new ArgumentNullException(nameof(y));
+ }
+
+ if (!x.IndexNumber.HasValue)
+ {
+ return -1;
+ }
+
+ if (!y.IndexNumber.HasValue)
+ {
+ return 1;
+ }
+
+ return x.IndexNumber.Value.CompareTo(y.IndexNumber.Value);
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs b/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs
new file mode 100644
index 000000000..ffc4e0cad
--- /dev/null
+++ b/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs
@@ -0,0 +1,50 @@
+using System;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Sorting;
+using MediaBrowser.Model.Querying;
+
+namespace Emby.Server.Implementations.Sorting
+{
+ /// <summary>
+ /// Class ParentIndexNumberComparer.
+ /// </summary>
+ public class ParentIndexNumberComparer : IBaseItemComparer
+ {
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name => ItemSortBy.ParentIndexNumber;
+
+ /// <summary>
+ /// Compares the specified x.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <param name="y">The y.</param>
+ /// <returns>System.Int32.</returns>
+ public int Compare(BaseItem? x, BaseItem? y)
+ {
+ if (x == null)
+ {
+ throw new ArgumentNullException(nameof(x));
+ }
+
+ if (y == null)
+ {
+ throw new ArgumentNullException(nameof(y));
+ }
+
+ if (!x.ParentIndexNumber.HasValue)
+ {
+ return -1;
+ }
+
+ if (!y.ParentIndexNumber.HasValue)
+ {
+ return 1;
+ }
+
+ return x.ParentIndexNumber.Value.CompareTo(y.ParentIndexNumber.Value);
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/SyncPlay/Group.cs b/Emby.Server.Implementations/SyncPlay/Group.cs
index 75cf890e5..52becfec6 100644
--- a/Emby.Server.Implementations/SyncPlay/Group.cs
+++ b/Emby.Server.Implementations/SyncPlay/Group.cs
@@ -553,7 +553,7 @@ namespace Emby.Server.Implementations.SyncPlay
if (playingItemRemoved)
{
var itemId = PlayQueue.GetPlayingItemId();
- if (!itemId.Equals(Guid.Empty))
+ if (!itemId.Equals(default))
{
var item = _libraryManager.GetItemById(itemId);
RunTimeTicks = item.RunTimeTicks ?? 0;
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index c994ffc90..727b9d4b5 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -126,7 +126,8 @@ namespace Emby.Server.Implementations.TV
parentsFolders.ToList())
.Cast<Episode>()
.Where(episode => !string.IsNullOrEmpty(episode.SeriesPresentationUniqueKey))
- .Select(GetUniqueSeriesKey);
+ .Select(GetUniqueSeriesKey)
+ .ToList();
// Avoid implicitly captured closure
var episodes = GetNextUpEpisodes(request, user, items, options);
@@ -134,13 +135,21 @@ namespace Emby.Server.Implementations.TV
return GetResult(episodes, request);
}
- public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<string> seriesKeys, DtoOptions dtoOptions)
+ public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList<string> seriesKeys, DtoOptions dtoOptions)
{
// Avoid implicitly captured closure
var currentUser = user;
var allNextUp = seriesKeys
- .Select(i => GetNextUp(i, currentUser, dtoOptions));
+ .Select(i => GetNextUp(i, currentUser, dtoOptions, false));
+
+ if (request.EnableRewatching)
+ {
+ allNextUp = allNextUp.Concat(
+ seriesKeys.Select(i => GetNextUp(i, currentUser, dtoOptions, true))
+ )
+ .OrderByDescending(i => i.Item1);
+ }
// If viewing all next up for all series, remove first episodes
// But if that returns empty, keep those first episodes (avoid completely empty view)
@@ -186,9 +195,9 @@ namespace Emby.Server.Implementations.TV
/// Gets the next up.
/// </summary>
/// <returns>Task{Episode}.</returns>
- private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions)
+ private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching)
{
- var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
+ var lastQuery = new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
@@ -202,23 +211,43 @@ namespace Emby.Server.Implementations.TV
Fields = new[] { ItemFields.SortName },
EnableImages = false
}
- }).Cast<Episode>().FirstOrDefault();
+ };
+
+ if (rewatching)
+ {
+ // find last watched by date played, not by newest episode watched
+ lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) };
+ }
+
+ var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast<Episode>().FirstOrDefault();
Func<Episode> getEpisode = () =>
{
- var nextEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
+ var nextQuery = new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { BaseItemKind.Episode },
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
Limit = 1,
- IsPlayed = false,
+ IsPlayed = rewatching,
IsVirtualItem = false,
ParentIndexNumberNotEquals = 0,
MinSortName = lastWatchedEpisode?.SortName,
DtoOptions = dtoOptions
- }).Cast<Episode>().FirstOrDefault();
+ };
+
+ Episode nextEpisode;
+ if (rewatching)
+ {
+ nextQuery.Limit = 2;
+ // get watched episode after most recently watched
+ nextEpisode = _libraryManager.GetItemList(nextQuery).Cast<Episode>().ElementAtOrDefault(1);
+ }
+ else
+ {
+ nextEpisode = _libraryManager.GetItemList(nextQuery).Cast<Episode>().FirstOrDefault();
+ }
if (_configurationManager.Configuration.DisplaySpecialsWithinSeasons)
{
@@ -228,7 +257,7 @@ namespace Emby.Server.Implementations.TV
SeriesPresentationUniqueKey = seriesKey,
ParentIndexNumber = 0,
IncludeItemTypes = new[] { BaseItemKind.Episode },
- IsPlayed = false,
+ IsPlayed = rewatching,
IsVirtualItem = false,
DtoOptions = dtoOptions
})
@@ -251,7 +280,7 @@ namespace Emby.Server.Implementations.TV
.Cast<Episode>();
if (lastWatchedEpisode != null)
{
- sortedConsideredEpisodes = sortedConsideredEpisodes.SkipWhile(episode => episode.Id != lastWatchedEpisode.Id).Skip(1);
+ sortedConsideredEpisodes = sortedConsideredEpisodes.SkipWhile(episode => !episode.Id.Equals(lastWatchedEpisode.Id)).Skip(1);
}
nextEpisode = sortedConsideredEpisodes.FirstOrDefault();
@@ -304,11 +333,10 @@ namespace Emby.Server.Implementations.TV
items = items.Take(query.Limit.Value);
}
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = totalCount,
- Items = items.ToArray()
- };
+ return new QueryResult<BaseItem>(
+ query.StartIndex,
+ totalCount,
+ items.ToArray());
}
}
}
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index c8ab99de4..937e792f5 100644
--- a/Emby.Server.Implementations/Udp/UdpServer.cs
+++ b/Emby.Server.Implementations/Udp/UdpServer.cs
@@ -9,6 +9,7 @@ using MediaBrowser.Controller;
using MediaBrowser.Model.ApiClient;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
+using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
namespace Emby.Server.Implementations.Udp
{
@@ -18,11 +19,6 @@ namespace Emby.Server.Implementations.Udp
public sealed class UdpServer : IDisposable
{
/// <summary>
- /// Address Override Configuration Key.
- /// </summary>
- public const string AddressOverrideConfigKey = "PublishedServerUrl";
-
- /// <summary>
/// The _logger.
/// </summary>
private readonly ILogger _logger;
@@ -60,7 +56,7 @@ namespace Emby.Server.Implementations.Udp
private async Task RespondToV2Message(EndPoint endpoint, CancellationToken cancellationToken)
{
- string? localUrl = _config[AddressOverrideConfigKey];
+ string? localUrl = _config[AddressOverrideKey];
if (string.IsNullOrEmpty(localUrl))
{
localUrl = _appHost.GetSmartApiUrl(((IPEndPoint)endpoint).Address);
@@ -68,7 +64,7 @@ namespace Emby.Server.Implementations.Udp
if (string.IsNullOrEmpty(localUrl))
{
- _logger.LogWarning("Unable to respond to udp request because the local ip address could not be determined.");
+ _logger.LogWarning("Unable to respond to server discovery request because the local ip address could not be determined.");
return;
}
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 5eb4c9ffa..40c386e82 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -227,9 +227,9 @@ namespace Emby.Server.Implementations.Updates
availablePackages = availablePackages.Where(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
}
- if (id != default)
+ if (!id.Equals(default))
{
- availablePackages = availablePackages.Where(x => x.Id == id);
+ availablePackages = availablePackages.Where(x => x.Id.Equals(id));
}
if (specificVersion != null)
@@ -399,7 +399,7 @@ namespace Emby.Server.Implementations.Updates
{
lock (_currentInstallationsLock)
{
- var install = _currentInstallations.Find(x => x.Info.Id == id);
+ var install = _currentInstallations.Find(x => x.Info.Id.Equals(id));
if (install == default((InstallationInfo, CancellationTokenSource)))
{
return false;
@@ -498,7 +498,7 @@ namespace Emby.Server.Implementations.Updates
var compatibleVersions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, minVersion: plugin.Version);
var version = compatibleVersions.FirstOrDefault(y => y.Version > plugin.Version);
- if (version != null && CompletedInstallations.All(x => x.Id != version.Id))
+ if (version != null && CompletedInstallations.All(x => !x.Id.Equals(version.Id)))
{
yield return version;
}