aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/Channels/ChannelManager.cs8
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs2
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs165
-rw-r--r--Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs1
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketConnection.cs11
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketManager.cs6
-rw-r--r--Emby.Server.Implementations/IO/StreamHelper.cs20
-rw-r--r--Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs4
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs110
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs7
-rw-r--r--Emby.Server.Implementations/Library/PathExtensions.cs2
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs93
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs24
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/VideoExtraResolver.cs55
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs37
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs8
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs48
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs14
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json24
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/gsw.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/he.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/lt-LT.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/ml.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/my.json123
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/sr.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/th.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/ur_PK.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json2
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs2
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs37
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs6
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs12
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs6
43 files changed, 486 insertions, 388 deletions
diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs
index 8702691d1..43c8a451b 100644
--- a/Emby.Server.Implementations/Channels/ChannelManager.cs
+++ b/Emby.Server.Implementations/Channels/ChannelManager.cs
@@ -130,16 +130,14 @@ namespace Emby.Server.Implementations.Channels
var internalChannel = _libraryManager.GetItemById(item.ChannelId);
if (internalChannel == null)
{
- throw new ArgumentException();
+ throw new ArgumentException(nameof(item.ChannelId));
}
var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id));
- var supportsDelete = channel as ISupportsDelete;
-
- if (supportsDelete == null)
+ if (channel is not ISupportsDelete supportsDelete)
{
- throw new ArgumentException();
+ throw new ArgumentException(nameof(channel));
}
return supportsDelete.DeleteItem(item.ExternalId, CancellationToken.None);
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 79ef70fff..b5b8fea65 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.Collections
if (parentFolder == null)
{
- throw new ArgumentException();
+ throw new ArgumentException(nameof(parentFolder));
}
var path = Path.Combine(parentFolder.Path, folderName);
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index a6b48b212..5ab9e02fe 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -248,40 +248,6 @@ namespace Emby.Server.Implementations.Data
BaseItemKind.AudioBook
};
- private static readonly Type[] _knownTypes =
- {
- typeof(LiveTvProgram),
- typeof(LiveTvChannel),
- typeof(Series),
- typeof(Audio),
- typeof(MusicAlbum),
- typeof(MusicArtist),
- typeof(MusicGenre),
- typeof(MusicVideo),
- typeof(Movie),
- typeof(Playlist),
- typeof(AudioBook),
- typeof(Trailer),
- typeof(BoxSet),
- typeof(Episode),
- typeof(Season),
- typeof(Series),
- typeof(Book),
- typeof(CollectionFolder),
- typeof(Folder),
- typeof(Genre),
- typeof(Person),
- typeof(Photo),
- typeof(PhotoAlbum),
- typeof(Studio),
- typeof(UserRootFolder),
- typeof(UserView),
- typeof(Video),
- typeof(Year),
- typeof(Channel),
- typeof(AggregateFolder)
- };
-
private static readonly Dictionary<BaseItemKind, string> _baseItemKindNames = new()
{
{ BaseItemKind.AggregateFolder, typeof(AggregateFolder).FullName },
@@ -688,13 +654,13 @@ namespace Emby.Server.Implementations.Data
connection.RunInTransaction(
db =>
{
- SaveItemsInTranscation(db, tuples);
+ SaveItemsInTransaction(db, tuples);
},
TransactionMode);
}
}
- private void SaveItemsInTranscation(IDatabaseConnection db, IEnumerable<(BaseItem, List<Guid>, BaseItem, string, List<string>)> tuples)
+ private void SaveItemsInTransaction(IDatabaseConnection db, IEnumerable<(BaseItem Item, List<Guid> AncestorIds, BaseItem TopParent, string UserDataKey, List<string> InheritedTags)> tuples)
{
var statements = PrepareAll(db, new string[]
{
@@ -713,17 +679,17 @@ namespace Emby.Server.Implementations.Data
saveItemStatement.Reset();
}
- var item = tuple.Item1;
- var topParent = tuple.Item3;
- var userDataKey = tuple.Item4;
+ var item = tuple.Item;
+ var topParent = tuple.TopParent;
+ var userDataKey = tuple.UserDataKey;
SaveItem(item, topParent, userDataKey, saveItemStatement);
- var inheritedTags = tuple.Item5;
+ var inheritedTags = tuple.InheritedTags;
if (item.SupportsAncestors)
{
- UpdateAncestors(item.Id, tuple.Item2, db, deleteAncestorsStatement);
+ UpdateAncestors(item.Id, tuple.AncestorIds, db, deleteAncestorsStatement);
}
UpdateItemValues(item.Id, GetItemValuesToSave(item, inheritedTags), db);
@@ -2201,7 +2167,7 @@ namespace Emby.Server.Implementations.Data
return false;
}
- var sortingFields = new HashSet<string>(query.OrderBy.Select(i => i.Item1), StringComparer.OrdinalIgnoreCase);
+ var sortingFields = new HashSet<string>(query.OrderBy.Select(i => i.OrderBy), StringComparer.OrdinalIgnoreCase);
return sortingFields.Contains(ItemSortBy.IsFavoriteOrLiked)
|| sortingFields.Contains(ItemSortBy.IsPlayed)
@@ -3049,88 +3015,101 @@ namespace Emby.Server.Implementations.Data
return " ORDER BY " + string.Join(',', orderBy.Select(i =>
{
- var columnMap = MapOrderByField(i.Item1, query);
-
- var sortOrder = i.Item2 == SortOrder.Ascending ? "ASC" : "DESC";
-
- return columnMap.Item1 + " " + sortOrder;
+ var sortBy = MapOrderByField(i.OrderBy, query);
+ var sortOrder = i.SortOrder == SortOrder.Ascending ? "ASC" : "DESC";
+ return sortBy + " " + sortOrder;
}));
}
- private (string, bool) MapOrderByField(string name, InternalItemsQuery query)
+ private string MapOrderByField(string name, InternalItemsQuery query)
{
if (string.Equals(name, ItemSortBy.AirTime, StringComparison.OrdinalIgnoreCase))
{
// TODO
- return ("SortName", false);
+ return "SortName";
}
- else if (string.Equals(name, ItemSortBy.Runtime, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.Runtime, StringComparison.OrdinalIgnoreCase))
{
- return ("RuntimeTicks", false);
+ return "RuntimeTicks";
}
- else if (string.Equals(name, ItemSortBy.Random, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.Random, StringComparison.OrdinalIgnoreCase))
{
- return ("RANDOM()", false);
+ return "RANDOM()";
}
- else if (string.Equals(name, ItemSortBy.DatePlayed, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.DatePlayed, StringComparison.OrdinalIgnoreCase))
{
if (query.GroupBySeriesPresentationUniqueKey)
{
- return ("MAX(LastPlayedDate)", false);
+ return "MAX(LastPlayedDate)";
}
- return ("LastPlayedDate", false);
+ return "LastPlayedDate";
}
- else if (string.Equals(name, ItemSortBy.PlayCount, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.PlayCount, StringComparison.OrdinalIgnoreCase))
{
- return ("PlayCount", false);
+ return "PlayCount";
}
- else if (string.Equals(name, ItemSortBy.IsFavoriteOrLiked, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.IsFavoriteOrLiked, StringComparison.OrdinalIgnoreCase))
{
- return ("(Select Case When IsFavorite is null Then 0 Else IsFavorite End )", true);
+ return "(Select Case When IsFavorite is null Then 0 Else IsFavorite End )";
}
- else if (string.Equals(name, ItemSortBy.IsFolder, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.IsFolder, StringComparison.OrdinalIgnoreCase))
{
- return ("IsFolder", true);
+ return "IsFolder";
}
- else if (string.Equals(name, ItemSortBy.IsPlayed, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.IsPlayed, StringComparison.OrdinalIgnoreCase))
{
- return ("played", true);
+ return "played";
}
- else if (string.Equals(name, ItemSortBy.IsUnplayed, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.IsUnplayed, StringComparison.OrdinalIgnoreCase))
{
- return ("played", false);
+ return "played";
}
- else if (string.Equals(name, ItemSortBy.DateLastContentAdded, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.DateLastContentAdded, StringComparison.OrdinalIgnoreCase))
{
- return ("DateLastMediaAdded", false);
+ return "DateLastMediaAdded";
}
- else if (string.Equals(name, ItemSortBy.Artist, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.Artist, StringComparison.OrdinalIgnoreCase))
{
- return ("(select CleanValue from itemvalues where ItemId=Guid and Type=0 LIMIT 1)", false);
+ return "(select CleanValue from itemvalues where ItemId=Guid and Type=0 LIMIT 1)";
}
- else if (string.Equals(name, ItemSortBy.AlbumArtist, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.AlbumArtist, StringComparison.OrdinalIgnoreCase))
{
- return ("(select CleanValue from itemvalues where ItemId=Guid and Type=1 LIMIT 1)", false);
+ return "(select CleanValue from itemvalues where ItemId=Guid and Type=1 LIMIT 1)";
}
- else if (string.Equals(name, ItemSortBy.OfficialRating, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.OfficialRating, StringComparison.OrdinalIgnoreCase))
{
- return ("InheritedParentalRatingValue", false);
+ return "InheritedParentalRatingValue";
}
- else if (string.Equals(name, ItemSortBy.Studio, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.Studio, StringComparison.OrdinalIgnoreCase))
{
- return ("(select CleanValue from itemvalues where ItemId=Guid and Type=3 LIMIT 1)", false);
+ return "(select CleanValue from itemvalues where ItemId=Guid and Type=3 LIMIT 1)";
}
- else if (string.Equals(name, ItemSortBy.SeriesDatePlayed, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.SeriesDatePlayed, StringComparison.OrdinalIgnoreCase))
{
- return ("(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where Played=1 and B.SeriesPresentationUniqueKey=A.PresentationUniqueKey)", false);
+ return "(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where Played=1 and B.SeriesPresentationUniqueKey=A.PresentationUniqueKey)";
}
- else if (string.Equals(name, ItemSortBy.SeriesSortName, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(name, ItemSortBy.SeriesSortName, StringComparison.OrdinalIgnoreCase))
{
- return ("SeriesName", false);
+ return "SeriesName";
}
- return (name, false);
+ return name;
}
public List<Guid> GetItemIdsList(InternalItemsQuery query)
@@ -5230,32 +5209,32 @@ AND Type = @InternalPersonType)");
}
}
- public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 0, 1 }, typeof(MusicArtist).FullName);
}
- public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 0 }, typeof(MusicArtist).FullName);
}
- public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 1 }, typeof(MusicArtist).FullName);
}
- public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 3 }, typeof(Studio).FullName);
}
- public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 2 }, typeof(Genre).FullName);
}
- public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
return GetItemValues(query, new[] { 2 }, typeof(MusicGenre).FullName);
}
@@ -5351,7 +5330,7 @@ AND Type = @InternalPersonType)");
return list;
}
- private QueryResult<(BaseItem, ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
+ private QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
{
if (query == null)
{
@@ -5676,7 +5655,7 @@ AND Type = @InternalPersonType)");
return counts;
}
- private List<(int, string)> GetItemValuesToSave(BaseItem item, List<string> inheritedTags)
+ private List<(int MagicNumber, string Value)> GetItemValuesToSave(BaseItem item, List<string> inheritedTags)
{
var list = new List<(int, string)>();
@@ -5701,7 +5680,7 @@ AND Type = @InternalPersonType)");
return list;
}
- private void UpdateItemValues(Guid itemId, List<(int, string)> values, IDatabaseConnection db)
+ private void UpdateItemValues(Guid itemId, List<(int MagicNumber, string Value)> values, IDatabaseConnection db)
{
if (itemId.Equals(Guid.Empty))
{
@@ -5723,7 +5702,7 @@ AND Type = @InternalPersonType)");
InsertItemValues(guidBlob, values, db);
}
- private void InsertItemValues(byte[] idBlob, List<(int, string)> values, IDatabaseConnection db)
+ private void InsertItemValues(byte[] idBlob, List<(int MagicNumber, string Value)> values, IDatabaseConnection db)
{
const int Limit = 100;
var startIndex = 0;
@@ -5755,7 +5734,7 @@ AND Type = @InternalPersonType)");
var currentValueInfo = values[i];
- var itemValue = currentValueInfo.Item2;
+ var itemValue = currentValueInfo.Value;
// Don't save if invalid
if (string.IsNullOrWhiteSpace(itemValue))
@@ -5763,7 +5742,7 @@ AND Type = @InternalPersonType)");
continue;
}
- statement.TryBind("@Type" + index, currentValueInfo.Item1);
+ statement.TryBind("@Type" + index, currentValueInfo.MagicNumber);
statement.TryBind("@Value" + index, itemValue);
statement.TryBind("@CleanValue" + index, GetCleanValue(itemValue));
}
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 4a5f72327..d43996c69 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -464,6 +464,7 @@ namespace Emby.Server.Implementations.EntryPoints
public void Dispose()
{
Dispose(true);
+ GC.SuppressFinalize(this);
}
/// <summary>
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index b3bd3421a..b87f1bc22 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -42,17 +42,14 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="logger">The logger.</param>
/// <param name="socket">The socket.</param>
/// <param name="remoteEndPoint">The remote end point.</param>
- /// <param name="query">The query.</param>
public WebSocketConnection(
ILogger<WebSocketConnection> logger,
WebSocket socket,
- IPAddress? remoteEndPoint,
- IQueryCollection query)
+ IPAddress? remoteEndPoint)
{
_logger = logger;
_socket = socket;
RemoteEndPoint = remoteEndPoint;
- QueryString = query;
_jsonOptions = JsonDefaults.Options;
LastActivityDate = DateTime.Now;
@@ -82,12 +79,6 @@ namespace Emby.Server.Implementations.HttpServer
public DateTime LastKeepAliveDate { get; set; }
/// <summary>
- /// Gets the query string.
- /// </summary>
- /// <value>The query string.</value>
- public IQueryCollection QueryString { get; }
-
- /// <summary>
/// Gets the state.
/// </summary>
/// <value>The state.</value>
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
index e99876dce..4f7d1c40a 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
@@ -50,8 +51,7 @@ namespace Emby.Server.Implementations.HttpServer
using var connection = new WebSocketConnection(
_loggerFactory.CreateLogger<WebSocketConnection>(),
webSocket,
- context.Connection.RemoteIpAddress,
- context.Request.Query)
+ context.GetNormalizedRemoteIp())
{
OnReceive = ProcessWebSocketMessageReceived
};
@@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.HttpServer
var tasks = new Task[_webSocketListeners.Length];
for (var i = 0; i < _webSocketListeners.Length; ++i)
{
- tasks[i] = _webSocketListeners[i].ProcessWebSocketConnectedAsync(connection);
+ tasks[i] = _webSocketListeners[i].ProcessWebSocketConnectedAsync(connection, context);
}
await Task.WhenAll(tasks).ConfigureAwait(false);
diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/Emby.Server.Implementations/IO/StreamHelper.cs
index e4f5f4cf0..f55c16d6d 100644
--- a/Emby.Server.Implementations/IO/StreamHelper.cs
+++ b/Emby.Server.Implementations/IO/StreamHelper.cs
@@ -17,11 +17,11 @@ namespace Emby.Server.Implementations.IO
try
{
int read;
- while ((read = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
+ while ((read = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0)
{
cancellationToken.ThrowIfCancellationRequested();
- await destination.WriteAsync(buffer, 0, read, cancellationToken).ConfigureAwait(false);
+ await destination.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false);
if (onStarted != null)
{
@@ -44,11 +44,11 @@ namespace Emby.Server.Implementations.IO
if (emptyReadLimit <= 0)
{
int read;
- while ((read = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
+ while ((read = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0)
{
cancellationToken.ThrowIfCancellationRequested();
- await destination.WriteAsync(buffer, 0, read, cancellationToken).ConfigureAwait(false);
+ await destination.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false);
}
return;
@@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.IO
{
cancellationToken.ThrowIfCancellationRequested();
- var bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
+ var bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
if (bytesRead == 0)
{
@@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.IO
{
eofCount = 0;
- await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
+ await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
}
}
}
@@ -88,13 +88,13 @@ namespace Emby.Server.Implementations.IO
{
int bytesRead;
- while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
+ while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0)
{
var bytesToWrite = Math.Min(bytesRead, copyLength);
if (bytesToWrite > 0)
{
- await destination.WriteAsync(buffer, 0, Convert.ToInt32(bytesToWrite), cancellationToken).ConfigureAwait(false);
+ await destination.WriteAsync(buffer.AsMemory(0, Convert.ToInt32(bytesToWrite)), cancellationToken).ConfigureAwait(false);
}
copyLength -= bytesToWrite;
@@ -137,9 +137,9 @@ namespace Emby.Server.Implementations.IO
int bytesRead;
int totalBytesRead = 0;
- while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
+ while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0)
{
- await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
+ await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
totalBytesRead += bytesRead;
}
diff --git a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
index 7e12ebb08..7958eb8f5 100644
--- a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
@@ -68,9 +68,9 @@ namespace Emby.Server.Implementations.Images
DtoOptions = new DtoOptions(false),
ImageTypes = new ImageType[] { ImageType.Primary },
Limit = 8,
- OrderBy = new ValueTuple<string, SortOrder>[]
+ OrderBy = new[]
{
- new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending)
+ (ItemSortBy.Random, SortOrder.Ascending)
},
IncludeItemTypes = includeItemTypes
});
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index e30fa7097..bd0c178fd 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -13,7 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Naming.Common;
using Emby.Naming.TV;
-using Emby.Naming.Video;
+using Emby.Server.Implementations.Library.Resolvers;
using Emby.Server.Implementations.Library.Validators;
using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.ScheduledTasks.Tasks;
@@ -78,6 +78,7 @@ namespace Emby.Server.Implementations.Library
private readonly IItemRepository _itemRepository;
private readonly IImageProcessor _imageProcessor;
private readonly NamingOptions _namingOptions;
+ private readonly ExtraResolver _extraResolver;
/// <summary>
/// The _root folder sync lock.
@@ -146,6 +147,8 @@ namespace Emby.Server.Implementations.Library
_memoryCache = memoryCache;
_namingOptions = namingOptions;
+ _extraResolver = new ExtraResolver(namingOptions);
+
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
RecordConfigurationValues(configurationManager.Configuration);
@@ -1373,7 +1376,7 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetItemIdsList(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1384,7 +1387,7 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetStudios(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1395,7 +1398,7 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetGenres(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1406,7 +1409,7 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetMusicGenres(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1417,7 +1420,7 @@ namespace Emby.Server.Implementations.Library
return _itemRepository.GetAllArtists(query);
}
- public QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1441,7 +1444,7 @@ namespace Emby.Server.Implementations.Library
for (int i = 0; i < len; i++)
{
parents[i] = GetItemById(ancestorIds[i]);
- if (!(parents[i] is ICollectionFolder || parents[i] is UserView))
+ if (parents[i] is not (ICollectionFolder or UserView))
{
return;
}
@@ -1458,7 +1461,7 @@ namespace Emby.Server.Implementations.Library
}
}
- public QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
+ public QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery query)
{
if (query.User != null)
{
@@ -1757,7 +1760,7 @@ namespace Emby.Server.Implementations.Library
return orderedItems ?? items;
}
- public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderBy)
+ public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<(string OrderBy, SortOrder SortOrder)> orderBy)
{
var isFirst = true;
@@ -2692,95 +2695,55 @@ namespace Emby.Server.Implementations.Library
}
var count = fileSystemChildren.Count;
- var files = new List<VideoFileInfo>();
- var nonVideoFiles = new List<FileSystemMetadata>();
for (var i = 0; i < count; i++)
{
var current = fileSystemChildren[i];
if (current.IsDirectory && _namingOptions.AllExtrasTypesFolderNames.ContainsKey(current.Name))
{
- var filesInSubFolder = _fileSystem.GetFiles(current.FullName, _namingOptions.VideoFileExtensions, false, false);
+ var filesInSubFolder = _fileSystem.GetFiles(current.FullName, null, false, false);
foreach (var file in filesInSubFolder)
{
- var videoInfo = VideoResolver.Resolve(file.FullName, file.IsDirectory, _namingOptions);
- if (videoInfo == null)
+ if (!_extraResolver.TryGetExtraTypeForOwner(file.FullName, ownerVideoInfo, out var extraType))
{
- nonVideoFiles.Add(file);
continue;
}
- files.Add(videoInfo);
+ var extra = GetExtra(file, extraType.Value);
+ if (extra != null)
+ {
+ yield return extra;
+ }
}
}
- else if (!current.IsDirectory)
+ else if (!current.IsDirectory && _extraResolver.TryGetExtraTypeForOwner(current.FullName, ownerVideoInfo, out var extraType))
{
- var videoInfo = VideoResolver.Resolve(current.FullName, current.IsDirectory, _namingOptions);
- if (videoInfo == null)
+ var extra = GetExtra(current, extraType.Value);
+ if (extra != null)
{
- nonVideoFiles.Add(current);
- continue;
+ yield return extra;
}
-
- files.Add(videoInfo);
}
}
- if (files.Count == 0)
+ BaseItem GetExtra(FileSystemMetadata file, ExtraType extraType)
{
- yield break;
- }
-
- var videos = VideoListResolver.Resolve(files, _namingOptions);
- // owner video info cannot be null as that implies it has no path
- var extras = ExtraResolver.GetExtras(videos, ownerVideoInfo, _namingOptions.VideoFlagDelimiters);
- for (var i = 0; i < extras.Count; i++)
- {
- var currentExtra = extras[i];
- var resolved = ResolvePath(_fileSystem.GetFileInfo(currentExtra.Path), null, directoryService);
- if (resolved is not Video video)
+ var extra = ResolvePath(_fileSystem.GetFileInfo(file.FullName), directoryService, _extraResolver.GetResolversForExtraType(extraType));
+ if (extra is not Video && extra is not Audio)
{
- continue;
- }
-
- // Try to retrieve it from the db. If we don't find it, use the resolved version
- if (GetItemById(resolved.Id) is Video dbItem)
- {
- video = dbItem;
- }
-
- video.ExtraType = currentExtra.ExtraType;
- video.ParentId = Guid.Empty;
- video.OwnerId = owner.Id;
- yield return video;
- }
-
- // TODO: theme songs must be handled "manually" (but should we?) since they aren't video files
- for (var i = 0; i < nonVideoFiles.Count; i++)
- {
- var current = nonVideoFiles[i];
- var extraInfo = ExtraResolver.GetExtraInfo(current.FullName, _namingOptions);
- if (extraInfo.ExtraType != ExtraType.ThemeSong)
- {
- continue;
- }
-
- var resolved = ResolvePath(current, null, directoryService);
- if (resolved is not Audio themeSong)
- {
- continue;
+ return null;
}
// Try to retrieve it from the db. If we don't find it, use the resolved version
- if (GetItemById(themeSong.Id) is Audio dbItem)
+ var itemById = GetItemById(extra.Id);
+ if (itemById != null)
{
- themeSong = dbItem;
+ extra = itemById;
}
- themeSong.ExtraType = ExtraType.ThemeSong;
- themeSong.OwnerId = owner.Id;
- themeSong.ParentId = Guid.Empty;
-
- yield return themeSong;
+ extra.ExtraType = extraType;
+ extra.ParentId = Guid.Empty;
+ extra.OwnerId = owner.Id;
+ return extra;
}
}
@@ -3044,7 +3007,10 @@ namespace Emby.Server.Implementations.Library
}
}
- CreateItems(personsToSave, null, CancellationToken.None);
+ if (personsToSave.Count > 0)
+ {
+ CreateItems(personsToSave, null, CancellationToken.None);
+ }
}
private void StartScanInBackground()
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 972d4ebbb..a414e7e16 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -464,12 +464,11 @@ namespace Emby.Server.Implementations.Library
try
{
- var tuple = GetProvider(request.OpenToken);
- var provider = tuple.Item1;
+ var (provider, keyId) = GetProvider(request.OpenToken);
var currentLiveStreams = _openStreams.Values.ToList();
- liveStream = await provider.OpenMediaSource(tuple.Item2, currentLiveStreams, cancellationToken).ConfigureAwait(false);
+ liveStream = await provider.OpenMediaSource(keyId, currentLiveStreams, cancellationToken).ConfigureAwait(false);
mediaSource = liveStream.MediaSource;
@@ -829,7 +828,7 @@ namespace Emby.Server.Implementations.Library
}
}
- private (IMediaSourceProvider, string) GetProvider(string key)
+ private (IMediaSourceProvider MediaSourceProvider, string KeyId) GetProvider(string key)
{
if (string.IsNullOrEmpty(key))
{
diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs
index 6f61dc713..64e7d5446 100644
--- a/Emby.Server.Implementations/Library/PathExtensions.cs
+++ b/Emby.Server.Implementations/Library/PathExtensions.cs
@@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.Library
var attributeEnd = attributeIndex + attribute.Length;
if (attributeIndex > 0
&& str[attributeIndex - 1] == '['
- && str[attributeEnd] == '=')
+ && (str[attributeEnd] == '=' || str[attributeEnd] == '-'))
{
var closingIndex = str[attributeEnd..].IndexOf(']');
// Must be at least 1 character before the closing bracket.
diff --git a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
new file mode 100644
index 000000000..807913b5d
--- /dev/null
+++ b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using Emby.Naming.Common;
+using Emby.Naming.Video;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Resolvers;
+using MediaBrowser.Model.Entities;
+using static Emby.Naming.Video.ExtraRuleResolver;
+
+namespace Emby.Server.Implementations.Library.Resolvers
+{
+ /// <summary>
+ /// Resolves a Path into a Video or Video subclass.
+ /// </summary>
+ internal class ExtraResolver
+ {
+ private readonly NamingOptions _namingOptions;
+ private readonly IItemResolver[] _trailerResolvers;
+ private readonly IItemResolver[] _videoResolvers;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ExtraResolver"/> class.
+ /// </summary>
+ /// <param name="namingOptions">An instance of <see cref="NamingOptions"/>.</param>
+ public ExtraResolver(NamingOptions namingOptions)
+ {
+ _namingOptions = namingOptions;
+ _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(namingOptions) };
+ _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(namingOptions) };
+ }
+
+ /// <summary>
+ /// Gets the resolvers for the extra type.
+ /// </summary>
+ /// <param name="extraType">The extra type.</param>
+ /// <returns>The resolvers for the extra type.</returns>
+ public IItemResolver[]? GetResolversForExtraType(ExtraType extraType) => extraType switch
+ {
+ ExtraType.Trailer => _trailerResolvers,
+ // For audio we'll have to rely on the AudioResolver, which is a "built-in"
+ ExtraType.ThemeSong => null,
+ _ => _videoResolvers
+ };
+
+ public bool TryGetExtraTypeForOwner(string path, VideoFileInfo ownerVideoFileInfo, [NotNullWhen(true)] out ExtraType? extraType)
+ {
+ var extraResult = GetExtraInfo(path, _namingOptions);
+ if (extraResult.ExtraType == null)
+ {
+ extraType = null;
+ return false;
+ }
+
+ var cleanDateTimeResult = CleanDateTimeParser.Clean(Path.GetFileNameWithoutExtension(path), _namingOptions.CleanDateTimeRegexes);
+ var name = cleanDateTimeResult.Name;
+ var year = cleanDateTimeResult.Year;
+
+ var parentDir = ownerVideoFileInfo.IsDirectory ? ownerVideoFileInfo.Path : Path.GetDirectoryName(ownerVideoFileInfo.Path.AsSpan());
+
+ var trimmedFileNameWithoutExtension = TrimFilenameDelimiters(ownerVideoFileInfo.FileNameWithoutExtension, _namingOptions.VideoFlagDelimiters);
+ var trimmedVideoInfoName = TrimFilenameDelimiters(ownerVideoFileInfo.Name, _namingOptions.VideoFlagDelimiters);
+ var trimmedExtraFileName = TrimFilenameDelimiters(name, _namingOptions.VideoFlagDelimiters);
+
+ // first check filenames
+ bool isValid = StartsWith(trimmedExtraFileName, trimmedFileNameWithoutExtension)
+ || (StartsWith(trimmedExtraFileName, trimmedVideoInfoName) && year == ownerVideoFileInfo.Year);
+
+ if (!isValid)
+ {
+ // When the extra rule type is DirectoryName we must go one level higher to get the "real" dir name
+ var currentParentDir = extraResult.Rule?.RuleType == ExtraRuleType.DirectoryName
+ ? Path.GetDirectoryName(Path.GetDirectoryName(path.AsSpan()))
+ : Path.GetDirectoryName(path.AsSpan());
+
+ isValid = !currentParentDir.IsEmpty && !parentDir.IsEmpty && currentParentDir.Equals(parentDir, StringComparison.OrdinalIgnoreCase);
+ }
+
+ extraType = extraResult.ExtraType;
+ return isValid;
+ }
+
+ private static ReadOnlySpan<char> TrimFilenameDelimiters(ReadOnlySpan<char> name, ReadOnlySpan<char> videoFlagDelimiters)
+ {
+ return name.IsEmpty ? name : name.TrimEnd().TrimEnd(videoFlagDelimiters).TrimEnd();
+ }
+
+ private static bool StartsWith(ReadOnlySpan<char> fileName, ReadOnlySpan<char> baseName)
+ {
+ return !baseName.IsEmpty && fileName.StartsWith(baseName, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
new file mode 100644
index 000000000..b8554bd51
--- /dev/null
+++ b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs
@@ -0,0 +1,24 @@
+#nullable disable
+
+using Emby.Naming.Common;
+using MediaBrowser.Controller.Entities;
+
+namespace Emby.Server.Implementations.Library.Resolvers
+{
+ /// <summary>
+ /// Resolves a Path into an instance of the <see cref="Video"/> class.
+ /// </summary>
+ /// <typeparam name="T">The type of item to resolve.</typeparam>
+ public class GenericVideoResolver<T> : BaseVideoResolver<T>
+ where T : Video, new()
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GenericVideoResolver{T}"/> class.
+ /// </summary>
+ /// <param name="namingOptions">The naming options.</param>
+ public GenericVideoResolver(NamingOptions namingOptions)
+ : base(namingOptions)
+ {
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoExtraResolver.cs
deleted file mode 100644
index 9aadce88c..000000000
--- a/Emby.Server.Implementations/Library/Resolvers/VideoExtraResolver.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-#nullable disable
-
-using Emby.Naming.Common;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Entities;
-
-namespace Emby.Server.Implementations.Library.Resolvers
-{
- /// <summary>
- /// Resolves a Path into a Video or Video subclass.
- /// </summary>
- public class VideoExtraResolver : BaseVideoResolver<Video>
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="VideoExtraResolver"/> class.
- /// </summary>
- /// <param name="namingOptions">The naming options.</param>
- public VideoExtraResolver(NamingOptions namingOptions)
- : base(namingOptions)
- {
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override ResolverPriority Priority => ResolverPriority.Last;
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>The video extra or null if not handled by this resolver.</returns>
- public override Video Resolve(ItemResolveArgs args)
- {
- // Only handle owned items
- if (args.Parent != null)
- {
- return null;
- }
-
- var ownedItem = base.Resolve(args);
-
- // Re-resolve items that have their own type
- if (ownedItem.ExtraType == ExtraType.Trailer)
- {
- ownedItem = ResolveVideo<Trailer>(args, false);
- }
-
- return ownedItem;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
index 4aacf7774..55911933a 100644
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ b/Emby.Server.Implementations/Library/SearchEngine.cs
@@ -190,7 +190,7 @@ namespace Emby.Server.Implementations.Library
searchQuery.ParentId = Guid.Empty;
searchQuery.IncludeItemsByName = true;
searchQuery.IncludeItemTypes = Array.Empty<BaseItemKind>();
- mediaItems = _libraryManager.GetAllArtists(searchQuery).Items.Select(i => i.Item1).ToList();
+ mediaItems = _libraryManager.GetAllArtists(searchQuery).Items.Select(i => i.Item).ToList();
}
else
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 2fbd47bc7..a8440102d 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -643,7 +643,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
CancellationToken cancellationToken)
{
using var options = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/token");
+#pragma warning disable CA5350 // SchedulesDirect is always SHA1.
var hashedPasswordBytes = SHA1.HashData(Encoding.ASCII.GetBytes(password));
+#pragma warning restore CA5350
// TODO: remove ToLower when Convert.ToHexString supports lowercase
// Schedules Direct requires the hex to be lowercase
string hashedPassword = Convert.ToHexString(hashedPasswordBytes).ToLowerInvariant();
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 047d8e98c..aa3598c8b 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.LiveTv
orderBy.Insert(0, (ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
}
- if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
+ if (!internalQuery.OrderBy.Any(i => string.Equals(i.OrderBy, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
{
orderBy.Add((ItemSortBy.SortName, SortOrder.Ascending));
}
@@ -520,7 +520,7 @@ namespace Emby.Server.Implementations.LiveTv
return item;
}
- private (LiveTvProgram item, bool isNew, bool isUpdated) GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel)
+ private (LiveTvProgram Item, bool IsNew, bool IsUpdated) GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel)
{
var id = _tvDtoService.GetInternalProgramId(info.Id);
@@ -779,9 +779,9 @@ namespace Emby.Server.Implementations.LiveTv
var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
- var list = new List<Tuple<BaseItemDto, string, string>>
+ var list = new List<(BaseItemDto ItemDto, string ExternalId, string ExternalSeriesId)>
{
- new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, program.ExternalSeriesId)
+ (dto, program.ExternalId, program.ExternalSeriesId)
};
await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false);
@@ -976,16 +976,16 @@ namespace Emby.Server.Implementations.LiveTv
return score;
}
- private async Task AddRecordingInfo(IEnumerable<Tuple<BaseItemDto, string, string>> programs, CancellationToken cancellationToken)
+ private async Task AddRecordingInfo(IEnumerable<(BaseItemDto ItemDto, string ExternalId, string ExternalSeriesId)> programs, CancellationToken cancellationToken)
{
IReadOnlyList<TimerInfo> timerList = null;
IReadOnlyList<SeriesTimerInfo> seriesTimerList = null;
foreach (var programTuple in programs)
{
- var program = programTuple.Item1;
- var externalProgramId = programTuple.Item2;
- string externalSeriesId = programTuple.Item3;
+ var program = programTuple.ItemDto;
+ var externalProgramId = programTuple.ExternalId;
+ string externalSeriesId = programTuple.ExternalSeriesId;
timerList ??= (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items;
@@ -1186,13 +1186,13 @@ namespace Emby.Server.Implementations.LiveTv
foreach (var program in channelPrograms)
{
var programTuple = GetProgram(program, existingPrograms, currentChannel);
- var programItem = programTuple.item;
+ var programItem = programTuple.Item;
- if (programTuple.isNew)
+ if (programTuple.IsNew)
{
newPrograms.Add(programItem);
}
- else if (programTuple.isUpdated)
+ else if (programTuple.IsUpdated)
{
updatedPrograms.Add(programItem);
}
@@ -1423,9 +1423,9 @@ namespace Emby.Server.Implementations.LiveTv
return result;
}
- public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> programs, IReadOnlyList<ItemFields> fields, User user = null)
+ public Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem Item, BaseItemDto ItemDto)> programs, IReadOnlyList<ItemFields> fields, User user = null)
{
- var programTuples = new List<Tuple<BaseItemDto, string, string>>();
+ var programTuples = new List<(BaseItemDto Dto, string ExternalId, string ExternalSeriesId)>();
var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
var hasChannelInfo = fields.Contains(ItemFields.ChannelInfo);
@@ -1461,7 +1461,7 @@ namespace Emby.Server.Implementations.LiveTv
}
}
- programTuples.Add(new Tuple<BaseItemDto, string, string>(dto, program.ExternalId, program.ExternalSeriesId));
+ programTuples.Add((dto, program.ExternalId, program.ExternalSeriesId));
}
return AddRecordingInfo(programTuples, CancellationToken.None);
@@ -1868,11 +1868,11 @@ namespace Emby.Server.Implementations.LiveTv
return _libraryManager.GetItemById(internalChannelId);
}
- public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> items, DtoOptions options, User user)
+ public void AddChannelInfo(IReadOnlyCollection<(BaseItemDto ItemDto, LiveTvChannel Channel)> items, DtoOptions options, User user)
{
var now = DateTime.UtcNow;
- var channelIds = items.Select(i => i.Item2.Id).Distinct().ToArray();
+ var channelIds = items.Select(i => i.Channel.Id).Distinct().ToArray();
var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user)
{
@@ -1893,11 +1893,8 @@ namespace Emby.Server.Implementations.LiveTv
var addCurrentProgram = options.AddCurrentProgram;
- foreach (var tuple in items)
+ foreach (var (dto, channel) in items)
{
- var dto = tuple.Item1;
- var channel = tuple.Item2;
-
dto.Number = channel.Number;
dto.ChannelNumber = channel.Number;
dto.ChannelType = channel.ChannelType;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs
index 069b4fab6..aae33503f 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunChannelCommands.cs
@@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_profile = profile;
}
- public IEnumerable<(string, string)> GetCommands()
+ public IEnumerable<(string CommandName, string CommandValue)> GetCommands()
{
if (!string.IsNullOrEmpty(_channel))
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index f009c77cf..48d9e316d 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_remoteEndPoint = new IPEndPoint(remoteIp, HdHomeRunPort);
_tcpClient = new TcpClient();
- _tcpClient.Connect(_remoteEndPoint);
+ await _tcpClient.ConnectAsync(_remoteEndPoint, cancellationToken).ConfigureAwait(false);
if (!_lockkey.HasValue)
{
@@ -114,7 +114,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
foreach (var command in commands.GetCommands())
{
- var channelMsgLen = WriteSetMessage(buffer, i, command.Item1, command.Item2, lockKeyValue);
+ var channelMsgLen = WriteSetMessage(buffer, i, command.CommandName, command.CommandValue, lockKeyValue);
await stream.WriteAsync(buffer.AsMemory(0, channelMsgLen), cancellationToken).ConfigureAwait(false);
receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
@@ -158,7 +158,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
using var tcpClient = new TcpClient();
- tcpClient.Connect(_remoteEndPoint);
+ await tcpClient.ConnectAsync(_remoteEndPoint, cancellationToken).ConfigureAwait(false);
using var stream = tcpClient.GetStream();
var commandList = commands.GetCommands();
@@ -167,7 +167,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
foreach (var command in commandList)
{
- var channelMsgLen = WriteSetMessage(buffer, _activeTuner, command.Item1, command.Item2, _lockkey);
+ var channelMsgLen = WriteSetMessage(buffer, _activeTuner, command.CommandName, command.CommandValue, _lockkey);
await stream.WriteAsync(buffer.AsMemory(0, channelMsgLen), cancellationToken).ConfigureAwait(false);
int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index d2f033439..a5edd35cc 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -165,7 +165,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
await CopyTo(udpClient, TempFilePath, openTaskCompletionSource, cancellationToken).ConfigureAwait(false);
}
- catch (OperationCanceledException ex)
+ catch (Exception ex) when (ex is OperationCanceledException || ex is TimeoutException)
{
Logger.LogInformation("HDHR UDP stream cancelled or timed out from {0}", remoteAddress);
openTaskCompletionSource.TrySetException(ex);
@@ -191,36 +191,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
- using (var timeOutSource = new CancellationTokenSource())
- using (var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(
- cancellationToken,
- timeOutSource.Token))
+ var res = await udpClient.ReceiveAsync(cancellationToken)
+ .AsTask()
+ .WaitAsync(TimeSpan.FromMilliseconds(30000), CancellationToken.None)
+ .ConfigureAwait(false);
+ var buffer = res.Buffer;
+
+ var read = buffer.Length - RtpHeaderBytes;
+
+ if (read > 0)
+ {
+ await fileStream.WriteAsync(buffer.AsMemory(RtpHeaderBytes, read), cancellationToken).ConfigureAwait(false);
+ }
+
+ if (!resolved)
{
- var resTask = udpClient.ReceiveAsync(linkedSource.Token).AsTask();
- if (await Task.WhenAny(resTask, Task.Delay(30000, linkedSource.Token)).ConfigureAwait(false) != resTask)
- {
- resTask.Dispose();
- break;
- }
-
- // We don't want all these delay tasks to keep running
- timeOutSource.Cancel();
- var res = await resTask.ConfigureAwait(false);
- var buffer = res.Buffer;
-
- var read = buffer.Length - RtpHeaderBytes;
-
- if (read > 0)
- {
- fileStream.Write(buffer, RtpHeaderBytes, read);
- }
-
- if (!resolved)
- {
- resolved = true;
- DateOpened = DateTime.UtcNow;
- openTaskCompletionSource.TrySetResult(true);
- }
+ resolved = true;
+ DateOpened = DateTime.UtcNow;
+ openTaskCompletionSource.TrySetResult(true);
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs
index 153354932..11bd40ab1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/IHdHomerunChannelCommands.cs
@@ -6,6 +6,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
public interface IHdHomerunChannelCommands
{
- IEnumerable<(string, string)> GetCommands();
+ IEnumerable<(string CommandName, string CommandValue)> GetCommands();
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs
index 26627b8aa..80d9d0724 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
- public IEnumerable<(string, string)> GetCommands()
+ public IEnumerable<(string CommandName, string CommandValue)> GetCommands()
{
if (!string.IsNullOrEmpty(_channel))
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
index b1ce7b2b3..ab4beb15b 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -51,7 +49,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
var url = mediaSource.Path;
- Directory.CreateDirectory(Path.GetDirectoryName(TempFilePath));
+ Directory.CreateDirectory(Path.GetDirectoryName(TempFilePath) ?? throw new InvalidOperationException("Path can't be a root directory."));
var typeName = GetType().Name;
Logger.LogInformation("Opening {StreamType} Live stream from {Url}", typeName, url);
@@ -94,14 +92,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
// OpenedMediaSource.SupportsDirectPlay = false;
// OpenedMediaSource.SupportsDirectStream = true;
// OpenedMediaSource.SupportsTranscoding = true;
- await taskCompletionSource.Task.ConfigureAwait(false);
- if (taskCompletionSource.Task.Exception != null)
- {
- // Error happened while opening the stream so raise the exception again to inform the caller
- throw taskCompletionSource.Task.Exception;
- }
-
- if (!taskCompletionSource.Task.Result)
+ var res = await taskCompletionSource.Task.ConfigureAwait(false);
+ if (!res)
{
Logger.LogWarning("Zero bytes copied from stream {StreamType} to {FilePath} but no exception raised", GetType().Name, TempFilePath);
throw new EndOfStreamException(string.Format(CultureInfo.InvariantCulture, "Zero bytes copied from stream {0}", GetType().Name));
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 2dee5e327..9bab3b9a9 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -15,7 +15,7 @@
"Favorites": "Preferits",
"Folders": "Carpetes",
"Genres": "Gèneres",
- "HeaderAlbumArtists": "Àlbum de l'artista",
+ "HeaderAlbumArtists": "Artistes de l'àlbum",
"HeaderContinueWatching": "Continua Veient",
"HeaderFavoriteAlbums": "Àlbums Preferits",
"HeaderFavoriteArtists": "Artistes Predilectes",
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index c924e5c15..115f36e7c 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -71,7 +71,7 @@
"ScheduledTaskStartedWithName": "{0} wurde gestartet",
"ServerNameNeedsToBeRestarted": "{0} muss neu gestartet werden",
"Shows": "Serien",
- "Songs": "Songs",
+ "Songs": "Lieder",
"StartupEmbyServerIsLoading": "Jellyfin-Server startet, bitte versuche es gleich noch einmal.",
"SubtitleDownloadFailureForItem": "Download der Untertitel fehlgeschlagen für {0}",
"SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden",
@@ -92,25 +92,25 @@
"ValueHasBeenAddedToLibrary": "{0} wurde deiner Bibliothek hinzugefügt",
"ValueSpecialEpisodeName": "Extra - {0}",
"VersionNumber": "Version {0}",
- "TaskDownloadMissingSubtitlesDescription": "Durchsucht das Internet nach fehlenden Untertiteln, basierend auf den Meta Einstellungen.",
+ "TaskDownloadMissingSubtitlesDescription": "Suche im Internet basierend auf den Metadaten-Einstellungen nach fehlenden Untertiteln.",
"TaskDownloadMissingSubtitles": "Lade fehlende Untertitel herunter",
- "TaskRefreshChannelsDescription": "Aktualisiere Internet Kanal Informationen.",
+ "TaskRefreshChannelsDescription": "Aktualisiere Internet-Kanal-Informationen.",
"TaskRefreshChannels": "Aktualisiere Kanäle",
- "TaskCleanTranscodeDescription": "Löscht Transkodierdateien, welche älter als einen Tag sind.",
- "TaskCleanTranscode": "Lösche Transkodier-Pfad",
+ "TaskCleanTranscodeDescription": "Löscht Transkodierdateien, die älter als einen Tag sind.",
+ "TaskCleanTranscode": "Räume Transkodierungs-Verzeichnis auf",
"TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche für automatische Updates konfiguriert sind und installiert diese.",
"TaskUpdatePlugins": "Aktualisiere Plugins",
"TaskRefreshPeopleDescription": "Aktualisiert Metadaten für Schauspieler und Regisseure in deinen Bibliotheken.",
- "TaskRefreshPeople": "Aktualisiere Schauspieler",
- "TaskCleanLogsDescription": "Lösche Log Dateien, die älter als {0} Tage sind.",
- "TaskCleanLogs": "Lösche Log-Verzeichnis",
- "TaskRefreshLibraryDescription": "Scanne alle Bibliotheken nach neu hinzugefügten Dateien und aktualisiere Metadaten.",
+ "TaskRefreshPeople": "Aktualisiere Personen",
+ "TaskCleanLogsDescription": "Lösche Log-Dateien, die älter als {0} Tage sind.",
+ "TaskCleanLogs": "Räumt Log-Verzeichnis auf",
+ "TaskRefreshLibraryDescription": "Scannt alle Bibliotheken nach neu hinzugefügten Dateien und aktualisiere Metadaten.",
"TaskRefreshLibrary": "Scanne Medien-Bibliothek",
- "TaskRefreshChapterImagesDescription": "Erstellt Vorschaubilder für Videos, welche Kapitel besitzen.",
- "TaskRefreshChapterImages": "Extrahiert Kapitel-Bilder",
+ "TaskRefreshChapterImagesDescription": "Erstellt Vorschaubilder für Videos, die Kapitel besitzen.",
+ "TaskRefreshChapterImages": "Extrahiere Kapitel-Bilder",
"TaskCleanCacheDescription": "Löscht nicht mehr benötigte Zwischenspeicherdateien.",
"TaskCleanCache": "Leere Zwischenspeicher",
- "TasksChannelsCategory": "Internet Kanäle",
+ "TasksChannelsCategory": "Internet-Kanäle",
"TasksApplicationCategory": "Anwendung",
"TasksLibraryCategory": "Bibliothek",
"TasksMaintenanceCategory": "Wartung",
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 697063f26..9952c05ca 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -15,7 +15,7 @@
"Favorites": "Αγαπημένα",
"Folders": "Φάκελοι",
"Genres": "Είδη",
- "HeaderAlbumArtists": "Άλμπουμ Καλλιτέχνη",
+ "HeaderAlbumArtists": "Καλλιτέχνες άλμπουμ",
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json
index 3364ee333..5bfe8c0b1 100644
--- a/Emby.Server.Implementations/Localization/Core/gsw.json
+++ b/Emby.Server.Implementations/Localization/Core/gsw.json
@@ -118,5 +118,6 @@
"TaskCleanActivityLog": "Aktivitätsprotokoll aufräumen",
"Undefined": "Undefiniert",
"Forced": "Erzwungen",
- "Default": "Standard"
+ "Default": "Standard",
+ "TaskOptimizeDatabase": "Datenbank optimieren"
}
diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json
index 981e8a06e..e32ab4ca8 100644
--- a/Emby.Server.Implementations/Localization/Core/he.json
+++ b/Emby.Server.Implementations/Localization/Core/he.json
@@ -118,5 +118,7 @@
"TaskCleanActivityLog": "נקה רשומת פעילות",
"Undefined": "לא מוגדר",
"Forced": "כפוי",
- "Default": "ברירת מחדל"
+ "Default": "ברירת מחדל",
+ "TaskOptimizeDatabase": "מיטוב מסד נתונים",
+ "TaskOptimizeDatabaseDescription": "דוחס את מסד הנתונים ומוריד את שטח האחסון שבשימוש. הרצה של פעולה זו לאחר סריקת הספרייה או שינויים אחרים שמשפיעים על מסד הנתונים יכולה לשפר ביצועים."
}
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index a37de0748..50d019f90 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -15,7 +15,7 @@
"Favorites": "즐겨찾기",
"Folders": "폴더",
"Genres": "장르",
- "HeaderAlbumArtists": "아티스트의 앨범",
+ "HeaderAlbumArtists": "앨범 음악가",
"HeaderContinueWatching": "계속 시청하기",
"HeaderFavoriteAlbums": "즐겨찾는 앨범",
"HeaderFavoriteArtists": "즐겨찾는 아티스트",
diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json
index f0a07f604..881cd4a93 100644
--- a/Emby.Server.Implementations/Localization/Core/lt-LT.json
+++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json
@@ -118,5 +118,6 @@
"Undefined": "Neapibrėžtas",
"Forced": "Priverstas",
"Default": "Numatytas",
- "TaskCleanActivityLogDescription": "Ištrina veiklos žuranlo įrašus, kurie yra senesni nei nustatytas amžius."
+ "TaskCleanActivityLogDescription": "Ištrina veiklos žuranlo įrašus, kurie yra senesni nei nustatytas amžius.",
+ "TaskOptimizeDatabase": "Optimizuoti duomenų bazės"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json
index 09ef34913..acc7746c1 100644
--- a/Emby.Server.Implementations/Localization/Core/ml.json
+++ b/Emby.Server.Implementations/Localization/Core/ml.json
@@ -6,7 +6,7 @@
"ChapterNameValue": "അധ്യായം {0}",
"DeviceOfflineWithName": "{0} വിച്ഛേദിച്ചു",
"DeviceOnlineWithName": "{0} ബന്ധിപ്പിച്ചു",
- "FailedLoginAttemptWithUserName": "Log 0 from എന്നതിൽ നിന്നുള്ള പ്രവേശന ശ്രമം പരാജയപ്പെട്ടു",
+ "FailedLoginAttemptWithUserName": "{0} - എന്നതിൽ നിന്നുള്ള പ്രവേശന ശ്രമം പരാജയപ്പെട്ടു",
"Forced": "നിർബന്ധിച്ചു",
"HeaderFavoriteAlbums": "പ്രിയപ്പെട്ട ആൽബങ്ങൾ",
"HeaderFavoriteArtists": "പ്രിയപ്പെട്ട കലാകാരന്മാർ",
diff --git a/Emby.Server.Implementations/Localization/Core/my.json b/Emby.Server.Implementations/Localization/Core/my.json
new file mode 100644
index 000000000..418376c4e
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/my.json
@@ -0,0 +1,123 @@
+{
+ "Default": "ပုံသေ",
+ "Collections": "စုစည်းမှုများ",
+ "Channels": "ချန်နယ်များ",
+ "Books": "စာအုပ်များ",
+ "Artists": "အနုပညာရှင်များ",
+ "Albums": "အခွေများ",
+ "TaskOptimizeDatabaseDescription": "ဒေတာဘေ့စ်ကို ကျစ်လစ်စေပြီး နေရာလွတ်များကို ဖြတ်တောက်ပေးသည်။ စာကြည့်တိုက်ကို စကင်န်ဖတ်ပြီးနောက် ဤလုပ်ငန်းကို လုပ်ဆောင်ခြင်း သို့မဟုတ် ဒေတာဘေ့စ်မွမ်းမံမှုများ စွမ်းဆောင်ရည်ကို မြှင့်တင်ပေးနိုင်သည်ဟု ရည်ညွှန်းသော အခြားပြောင်းလဲမှုများကို လုပ်ဆောင်ခြင်း။.",
+ "TaskOptimizeDatabase": "ဒေတာဘေ့စ်ကို အကောင်းဆုံးဖြစ်အောင်လုပ်ပါ။",
+ "TaskDownloadMissingSubtitlesDescription": "မက်တာဒေတာ ဖွဲ့စည်းမှုပုံစံအပေါ် အခြေခံ၍ ပျောက်ဆုံးနေသော စာတန်းထိုးများအတွက် အင်တာနက်ကို ရှာဖွေသည်။",
+ "TaskDownloadMissingSubtitles": "ပျောက်ဆုံးနေသော စာတန်းထိုးများကို ဒေါင်းလုဒ်လုပ်ပါ။",
+ "TaskRefreshChannelsDescription": "အင်တာနက်ချန်နယ်အချက်အလက်ကို ပြန်လည်စတင်သည်။",
+ "TaskRefreshChannels": "ချန်နယ်များကို ပြန်လည်စတင်ပါ။",
+ "TaskCleanTranscodeDescription": "သက်တမ်း တစ်ရက်ထက်ပိုသော အသွင်ပြောင်းကုဒ်ဖိုင်များကို ဖျက်ပါ။",
+ "TaskCleanTranscode": "Transcode လမ်းညွှန်ကို သန့်ရှင်းပါ။",
+ "TaskUpdatePluginsDescription": "အလိုအလျောက် အပ်ဒိတ်လုပ်ရန် စီစဉ်ထားသော ပလပ်အင်များအတွက် အပ်ဒိတ်များကို ဒေါင်းလုဒ်လုပ်ပြီး ထည့်သွင်းပါ။",
+ "TaskUpdatePlugins": "ပလပ်အင်များကို အပ်ဒိတ်လုပ်ပါ။",
+ "TaskRefreshPeopleDescription": "သင့်မီဒီယာစာကြည့်တိုက်ရှိ သရုပ်ဆောင်များနှင့် ဒါရိုက်တာများအတွက် မက်တာဒေတာကို အပ်ဒိတ်လုပ်ပါ။",
+ "TaskRefreshPeople": "လူများကို ပြန်လည်ဆန်းသစ်ပါ။",
+ "TaskCleanLogsDescription": "{0} ရက်ထက်ပိုသော မှတ်တမ်းဖိုင်များကို ဖျက်သည်။",
+ "TaskCleanLogs": "မှတ်တမ်းလမ်းညွှန်ကို သန့်ရှင်းပါ။",
+ "TaskRefreshLibraryDescription": "ဖိုင်အသစ်များအတွက် သင့်မီဒီယာဒစ်ဂျစ်တိုက်ကို စကင်န်ဖတ်ပြီး မက်တာဒေတာကို ပြန်လည်စတင်ပါ။",
+ "TaskRefreshLibrary": "မီဒီယာစာကြည့်တိုက်ကို စကင်န်ဖတ်ပါ။",
+ "TaskRefreshChapterImagesDescription": "အခန်းများပါရှိသော ဗီဒီယိုများအတွက် ပုံသေးများကို ဖန်တီးပါ။",
+ "TaskRefreshChapterImages": "အခန်းပုံများကို ထုတ်ယူပါ။",
+ "TaskCleanCacheDescription": "စနစ်မှ မလိုအပ်တော့သော ကက်ရှ်ဖိုင်များကို ဖျက်ပါ။.",
+ "TaskCleanCache": "Cache Directory ကို ရှင်းပါ။",
+ "TaskCleanActivityLogDescription": "စီစဉ်သတ်မှတ်ထားသော အသက်ထက် ပိုကြီးသော လုပ်ဆောင်ချက်မှတ်တမ်းများကို ဖျက်ပါ။",
+ "TaskCleanActivityLog": "လုပ်ဆောင်ချက်မှတ်တမ်းကို ရှင်းလင်းပါ။",
+ "TasksChannelsCategory": "အင်တာနက်ချန်နယ်များ",
+ "TasksApplicationCategory": "အပလီကေးရှင်း",
+ "TasksLibraryCategory": "စာကြည့်တိုက်",
+ "TasksMaintenanceCategory": "ထိန်းသိမ်းခြင်း",
+ "VersionNumber": "ဗားရှင်း {0}",
+ "ValueSpecialEpisodeName": "အထူး- {0}",
+ "ValueHasBeenAddedToLibrary": "{0} ကို သင့်မီဒီယာဒစ်ဂျစ်တိုက်သို့ ပေါင်းထည့်လိုက်ပါပြီ။",
+ "UserStoppedPlayingItemWithValues": "{0} သည် {1} ကို {2} တွင် ဖွင့်ပြီးပါပြီ",
+ "UserStartedPlayingItemWithValues": "{0} သည် {1} ကို {2} တွင် ပြသနေသည်",
+ "UserPolicyUpdatedWithName": "{0} အတွက် အသုံးပြုသူမူဝါဒကို အပ်ဒိတ်လုပ်ပြီးပါပြီ",
+ "UserPasswordChangedWithName": "အသုံးပြုသူ {0} အတွက် စကားဝှက်ကို ပြောင်းထားသည်",
+ "UserOnlineFromDevice": "{0} သည် {1} မှ အွန်လိုင်းဖြစ်သည်",
+ "UserOfflineFromDevice": "{0} သည် {1} မှ ချိတ်ဆက်မှုပြတ်တောက်သွားသည်",
+ "UserLockedOutWithName": "အသုံးပြုသူ {0} အား လော့ခ်ချထားသည်။",
+ "UserDownloadingItemWithValues": "{0} သည် {1} ကို ဒေါင်းလုဒ်လုပ်နေသည်",
+ "UserDeletedWithName": "အသုံးပြုသူ {0} ကို ဖျက်လိုက်ပါပြီ။",
+ "UserCreatedWithName": "အသုံးပြုသူ {0} ကို ဖန်တီးပြီးပါပြီ။",
+ "User": "အသုံးပြုသူ",
+ "Undefined": "သတ်မှတ်မထားသော",
+ "TvShows": "တီဗီရှိုးများ",
+ "System": "စနစ်",
+ "Sync": "ထပ်တူကျသည်။",
+ "SubtitleDownloadFailureFromForItem": "စာတန်းထိုးများကို {1} အတွက် {0} မှ ဒေါင်းလုဒ်လုပ်၍ မရပါ",
+ "StartupEmbyServerIsLoading": "Jellyfin ဆာဗာကို ဖွင့်နေပါသည်။ ခဏနေ ထပ်စမ်းကြည့်ပါ။",
+ "Songs": "သီချင်းများ",
+ "Shows": "ရှိုးပွဲ",
+ "ServerNameNeedsToBeRestarted": "{0} ကို ပြန်လည်စတင်ရန် လိုအပ်သည်။",
+ "ScheduledTaskStartedWithName": "{0} စတင်ခဲ့သည်။",
+ "ScheduledTaskFailedWithName": "{0} မအောင်မြင်ပါ။",
+ "ProviderValue": "ဝန်ဆောင်မှုပေးသူ- {0}",
+ "PluginUpdatedWithName": "{0} ကို အပ်ဒိတ်လုပ်ထားသည်။",
+ "PluginUninstalledWithName": "{0} ကို ဖြုတ်လိုက်ပါပြီ။",
+ "PluginInstalledWithName": "{0} ကို ထည့်သွင်းခဲ့သည်။",
+ "Plugin": "ပလပ်အင်",
+ "Playlists": "အစီအစဉ်များ",
+ "Photos": "ဓာတ်ပုံများ",
+ "NotificationOptionVideoPlaybackStopped": "ဗီဒီယိုပြန်ဖွင့်ခြင်းကို ရပ်သွားသည်။",
+ "NotificationOptionVideoPlayback": "ဗီဒီယိုဖွင့်ခြင်း စတင်ပါပြီ။",
+ "NotificationOptionUserLockedOut": "အသုံးပြုသူ ထွက်သွားသည်။",
+ "NotificationOptionTaskFailed": "စီစဉ်ထားသော အလုပ်ပျက်ကွက်",
+ "NotificationOptionServerRestartRequired": "ဆာဗာ ပြန်လည်စတင်ရန် လိုအပ်သည်။",
+ "NotificationOptionPluginUpdateInstalled": "ပလပ်အင် အပ်ဒိတ် ထည့်သွင်းပြီးပါပြီ။",
+ "NotificationOptionPluginUninstalled": "ပလပ်အင်ကို ဖြုတ်လိုက်ပါပြီ။",
+ "NotificationOptionPluginInstalled": "ပလပ်အင် ထည့်သွင်းထားသည်။",
+ "NotificationOptionPluginError": "ပလပ်အင် ချို့ယွင်းခြင်း။",
+ "NotificationOptionNewLibraryContent": "အကြောင်းအရာအသစ် ထပ်ထည့်ထားပါတယ်။",
+ "NotificationOptionInstallationFailed": "တပ်ဆင်မှု မအောင်မြင်ပါ။",
+ "NotificationOptionCameraImageUploaded": "ကင်မရာပုံ အပ်လုဒ်လုပ်ထားသည်။",
+ "NotificationOptionAudioPlaybackStopped": "အသံပြန်ဖွင့်ခြင်းကို ရပ်သွားသည်။",
+ "NotificationOptionAudioPlayback": "အသံပြန်ဖွင့်ခြင်း စတင်ပါပြီ။",
+ "NotificationOptionApplicationUpdateInstalled": "အပလီကေးရှင်း အပ်ဒိတ်ကို ထည့်သွင်းထားသည်။",
+ "NotificationOptionApplicationUpdateAvailable": "အပလီကေးရှင်း အပ်ဒိတ် ရနိုင်ပါပြီ။",
+ "NewVersionIsAvailable": "Jellyfin Server ၏ ဗားရှင်းအသစ်ကို ဒေါင်းလုဒ်လုပ်နိုင်ပါသည်။",
+ "NameSeasonUnknown": "အမည််မသိ ဇာတ်လမ်းတွဲ",
+ "NameSeasonNumber": "ဇာတ်လမ်းတွဲ {0}",
+ "NameInstallFailed": "{0} ထည့်သွင်းမှု မအောင်မြင်ပါ။",
+ "MusicVideos": "ဂီတဗီဒီယိုများ",
+ "Music": "တေးဂီတ",
+ "Movies": "ရုပ်ရှင်များ",
+ "MixedContent": "ရောနှောပါဝင်မှု",
+ "MessageServerConfigurationUpdated": "ဆာဗာဖွဲ့စည်းပုံကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။",
+ "MessageNamedServerConfigurationUpdatedWithValue": "ဆာဗာဖွဲ့စည်းပုံကဏ္ဍ {0} ကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။",
+ "MessageApplicationUpdatedTo": "Jellyfin ဆာဗာကို {0} သို့ အပ်ဒိတ်လုပ်ထားသည်",
+ "MessageApplicationUpdated": "Jellyfin ဆာဗာကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။",
+ "Latest": "နောက်ဆုံး",
+ "LabelRunningTimeValue": "လည်ပတ်ချိန်- {0}",
+ "LabelIpAddressValue": "IP လိပ်စာ- {0}",
+ "ItemRemovedWithName": "{0} ကို ဒစ်ဂျစ်တိုက်မှ ဖယ်ရှားခဲ့သည်။",
+ "ItemAddedWithName": "{0} ကို စာကြည့်တိုက်သို့ ထည့်ထားသည်။",
+ "Inherit": "ဆက်လက် လုပ်ဆောင်သည်။",
+ "HomeVideos": "ပင်မဗီဒီယိုများ",
+ "HeaderRecordingGroups": "အသံဖမ်းအဖွဲ့များ",
+ "HeaderNextUp": "နောက်ထပ်",
+ "HeaderLiveTV": "Live TV",
+ "HeaderFavoriteSongs": "အကြိုက်ဆုံးသီချင်းများ",
+ "HeaderFavoriteShows": "အကြိုက်ဆုံးရှိုးများ",
+ "HeaderFavoriteEpisodes": "အကြိုက်ဆုံးအပိုင်းများ",
+ "HeaderFavoriteArtists": "အကြိုက်ဆုံးအနုပညာရှင်များ",
+ "HeaderFavoriteAlbums": "အကြိုက်ဆုံး အယ်လ်ဘမ်များ",
+ "HeaderContinueWatching": "ဆက်လက်ကြည့်ရှုပါ။",
+ "HeaderAlbumArtists": "အယ်လ်ဘမ်အနုပညာရှင်များ",
+ "Genres": "အမျိုးအစားများ",
+ "Forced": "အတင်းအကြပ်",
+ "Folders": "ဖိုဒါများ",
+ "Favorites": "အကြိုက်ဆုံးများ",
+ "FailedLoginAttemptWithUserName": "{0} မှ အကောင့်ဝင်ရန် မအောင်မြင်ပါ",
+ "DeviceOnlineWithName": "{0} ကို ချိတ်ဆက်ထားသည်။",
+ "DeviceOfflineWithName": "{0} နှင့် အဆက်ပြတ်သွားပါပြီ။",
+ "ChapterNameValue": "အခန်း {0}",
+ "CameraImageUploadedFrom": "ကင်မရာပုံအသစ်ကို {0} မှ အပ်လုဒ်လုပ်ထားသည်",
+ "AuthenticationSucceededWithUserName": "{0} စစ်မှန်ကြောင်း အောင်မြင်စွာ အတည်ပြုပြီးပါပြီ။",
+ "Application": "အပလီကေးရှင်း",
+ "AppDeviceValues": "အက်ပ်- {0}၊ စက်- {1}"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 2d7163275..dc3793f1b 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -96,7 +96,7 @@
"TaskRefreshChannels": "Обновление каналов",
"TaskCleanTranscode": "Очистка каталога перекодировки",
"TaskUpdatePlugins": "Обновление плагинов",
- "TaskRefreshPeople": "Подновить людей",
+ "TaskRefreshPeople": "Подновление людей",
"TaskCleanLogs": "Очистка каталога журналов",
"TaskRefreshLibrary": "Сканирование медиатеки",
"TaskRefreshChapterImages": "Извлечение изображений сцен",
@@ -115,10 +115,10 @@
"TaskRefreshChapterImagesDescription": "Создаются эскизы для видео, которые содержат сцены.",
"TaskCleanCacheDescription": "Удаляются файлы кэша, которые больше не нужны системе.",
"TaskCleanActivityLogDescription": "Удаляет записи журнала активности старше установленного возраста.",
- "TaskCleanActivityLog": "Очистить журнал активности",
+ "TaskCleanActivityLog": "Очистка журнала активности",
"Undefined": "Не определено",
"Forced": "Форсир-ые",
"Default": "По умолчанию",
"TaskOptimizeDatabaseDescription": "Сжимает базу данных и вырезает свободные места. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.",
- "TaskOptimizeDatabase": "Оптимизировать базу данных"
+ "TaskOptimizeDatabase": "Оптимизация базы данных"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json
index e31208e80..72e125dfe 100644
--- a/Emby.Server.Implementations/Localization/Core/sr.json
+++ b/Emby.Server.Implementations/Localization/Core/sr.json
@@ -118,5 +118,6 @@
"Undefined": "Недефинисано",
"Forced": "Принудно",
"Default": "Подразумевано",
- "TaskOptimizeDatabase": "Оптимизуј датабазу"
+ "TaskOptimizeDatabase": "Оптимизуј датабазу",
+ "TaskOptimizeDatabaseDescription": "Сажима базу података и скраћује слободан простор. Покретање овог задатка након скенирања библиотеке или других промена које подразумевају измене базе података које могу побољшати перформансе."
}
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index f3f601661..5d05361b0 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -16,7 +16,7 @@
"Folders": "Mappar",
"Genres": "Genrer",
"HeaderAlbumArtists": "Albumsartister",
- "HeaderContinueWatching": "Fortsätt kolla",
+ "HeaderContinueWatching": "Fortsätt kolla på",
"HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritartister",
"HeaderFavoriteEpisodes": "Favoritavsnitt",
diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json
index e26010423..bed67fa4f 100644
--- a/Emby.Server.Implementations/Localization/Core/th.json
+++ b/Emby.Server.Implementations/Localization/Core/th.json
@@ -117,5 +117,7 @@
"TaskCleanActivityLogDescription": "ลบบันทึกกิจกรรมที่เก่ากว่าค่าที่กำหนดไว้",
"TaskCleanActivityLog": "ล้างบันทึกกิจกรรม",
"Undefined": "ไม่ได้กำหนด",
- "Forced": "บังคับใช้"
+ "Forced": "บังคับใช้",
+ "TaskOptimizeDatabase": "ปรับปรุงประสิทธิภาพฐานข้อมูล",
+ "TaskOptimizeDatabaseDescription": "ลดขนาดการจัดเก็บฐานข้อมูล ใช้งานคำสั่งนี้หลังจากสแกนไลบรารีหรือหลังจากการเปลี่ยนแปลงฐานข้อมูล อาจจะทำให้ระบบทำงานเร็วขึ้น"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json
index 73c60e263..e7f3e492c 100644
--- a/Emby.Server.Implementations/Localization/Core/ur_PK.json
+++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json
@@ -113,5 +113,7 @@
"CameraImageUploadedFrom": "ایک نئی کیمرہ تصویر اپ لوڈ کی گئی ہے {0}",
"Application": "پروگرام",
"AppDeviceValues": "پروگرام:{0}, ڈیوائس:{1}",
- "Forced": "جَبری"
+ "Forced": "جَبری",
+ "Undefined": "غير وضاحتى",
+ "Default": "طے شدہ"
}
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index b7ece8d5f..d80f1760d 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": "Tuyển Tập",
+ "Albums": "",
"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",
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 9481e26f7..02df2fffe 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -72,7 +72,7 @@ namespace Emby.Server.Implementations.Playlists
var parentFolder = GetPlaylistsFolder(Guid.Empty);
if (parentFolder == null)
{
- throw new ArgumentException();
+ throw new ArgumentException(nameof(parentFolder));
}
if (string.IsNullOrEmpty(options.MediaType))
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index 2a14a8c7b..a085ee546 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Net;
@@ -50,16 +51,10 @@ namespace Emby.Server.Implementations.Session
/// </summary>
private readonly object _webSocketsLock = new object();
- /// <summary>
- /// The _session manager.
- /// </summary>
private readonly ISessionManager _sessionManager;
-
- /// <summary>
- /// The _logger.
- /// </summary>
private readonly ILogger<SessionWebSocketListener> _logger;
private readonly ILoggerFactory _loggerFactory;
+ private readonly IAuthorizationContext _authorizationContext;
/// <summary>
/// The KeepAlive cancellation token.
@@ -72,14 +67,17 @@ namespace Emby.Server.Implementations.Session
/// <param name="logger">The logger.</param>
/// <param name="sessionManager">The session manager.</param>
/// <param name="loggerFactory">The logger factory.</param>
+ /// <param name="authorizationContext">The authorization context.</param>
public SessionWebSocketListener(
ILogger<SessionWebSocketListener> logger,
ISessionManager sessionManager,
- ILoggerFactory loggerFactory)
+ ILoggerFactory loggerFactory,
+ IAuthorizationContext authorizationContext)
{
_logger = logger;
_sessionManager = sessionManager;
_loggerFactory = loggerFactory;
+ _authorizationContext = authorizationContext;
}
/// <inheritdoc />
@@ -97,9 +95,9 @@ namespace Emby.Server.Implementations.Session
=> Task.CompletedTask;
/// <inheritdoc />
- public async Task ProcessWebSocketConnectedAsync(IWebSocketConnection connection)
+ public async Task ProcessWebSocketConnectedAsync(IWebSocketConnection connection, HttpContext httpContext)
{
- var session = await GetSession(connection.QueryString, connection.RemoteEndPoint.ToString()).ConfigureAwait(false);
+ var session = await GetSession(httpContext, connection.RemoteEndPoint?.ToString()).ConfigureAwait(false);
if (session != null)
{
EnsureController(session, connection);
@@ -107,25 +105,28 @@ namespace Emby.Server.Implementations.Session
}
else
{
- _logger.LogWarning("Unable to determine session based on query string: {0}", connection.QueryString);
+ _logger.LogWarning("Unable to determine session based on query string: {0}", httpContext.Request.QueryString);
}
}
- private Task<SessionInfo> GetSession(IQueryCollection queryString, string remoteEndpoint)
+ private async Task<SessionInfo> GetSession(HttpContext httpContext, string remoteEndpoint)
{
- if (queryString == null)
+ var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(httpContext)
+ .ConfigureAwait(false);
+
+ if (!authorizationInfo.IsAuthenticated)
{
return null;
}
- var token = queryString["api_key"];
- if (string.IsNullOrWhiteSpace(token))
+ var deviceId = authorizationInfo.DeviceId;
+ if (httpContext.Request.Query.TryGetValue("deviceId", out var queryDeviceId))
{
- return null;
+ deviceId = queryDeviceId;
}
- var deviceId = queryString["deviceId"];
- return _sessionManager.GetSessionByAuthenticationToken(token, deviceId, remoteEndpoint);
+ return await _sessionManager.GetSessionByAuthenticationToken(authorizationInfo.Token, deviceId, remoteEndpoint)
+ .ConfigureAwait(false);
}
private void EnsureController(SessionInfo session, IWebSocketConnection connection)
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 5dbe9f44d..c994ffc90 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -117,7 +117,7 @@ namespace Emby.Server.Implementations.TV
new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { BaseItemKind.Episode },
- OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
+ OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) },
SeriesPresentationUniqueKey = presentationUniqueKey,
Limit = limit,
DtoOptions = new DtoOptions { Fields = new[] { ItemFields.SeriesPresentationUniqueKey }, EnableImages = false },
@@ -193,7 +193,7 @@ namespace Emby.Server.Implementations.TV
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { BaseItemKind.Episode },
- OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Descending) },
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Descending) },
IsPlayed = true,
Limit = 1,
ParentIndexNumberNotEquals = 0,
@@ -211,7 +211,7 @@ namespace Emby.Server.Implementations.TV
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { BaseItemKind.Episode },
- OrderBy = new[] { new ValueTuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending) },
+ OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
Limit = 1,
IsPlayed = false,
IsVirtualItem = false,
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index 33e4e5651..c8ab99de4 100644
--- a/Emby.Server.Implementations/Udp/UdpServer.cs
+++ b/Emby.Server.Implementations/Udp/UdpServer.cs
@@ -97,21 +97,11 @@ namespace Emby.Server.Implementations.Udp
private async Task BeginReceiveAsync(CancellationToken cancellationToken)
{
- var infiniteTask = Task.Delay(-1, cancellationToken);
while (!cancellationToken.IsCancellationRequested)
{
try
{
- var task = _udpSocket.ReceiveFromAsync(_receiveBuffer, SocketFlags.None, _endpoint);
- await Task.WhenAny(task, infiniteTask).ConfigureAwait(false);
-
- if (!task.IsCompleted)
- {
- return;
- }
-
- var result = task.Result;
-
+ var result = await _udpSocket.ReceiveFromAsync(_receiveBuffer, SocketFlags.None, _endpoint, cancellationToken).ConfigureAwait(false);
var text = Encoding.UTF8.GetString(_receiveBuffer, 0, result.ReceivedBytes);
if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase))
{
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 24d592525..5eb4c9ffa 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Updates
/// <summary>
/// The current installations.
/// </summary>
- private readonly List<(InstallationInfo info, CancellationTokenSource token)> _currentInstallations;
+ private readonly List<(InstallationInfo Info, CancellationTokenSource Token)> _currentInstallations;
/// <summary>
/// The completed installations.
@@ -399,13 +399,13 @@ namespace Emby.Server.Implementations.Updates
{
lock (_currentInstallationsLock)
{
- var install = _currentInstallations.Find(x => x.info.Id == id);
+ var install = _currentInstallations.Find(x => x.Info.Id == id);
if (install == default((InstallationInfo, CancellationTokenSource)))
{
return false;
}
- install.token.Cancel();
+ install.Token.Cancel();
_currentInstallations.Remove(install);
return true;
}