From 03ecf57548b3bd6058b9427ff48f06050725ad4a Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Fri, 4 Oct 2019 13:23:08 -0400 Subject: Store MediaAttachments in DB. --- .../Data/SqliteItemRepository.cs | 179 +++++++++++++++++++++ 1 file changed, 179 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 31a661c5d..58f04e4fc 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -94,6 +94,8 @@ namespace Emby.Server.Implementations.Data { const string CreateMediaStreamsTableCommand = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + const string CreateMediaAttachmentsTableCommand + = "create table if not exists mediaattachments (ItemId GUID, AttachmentIndex INT, Codec TEXT, CodecTag TEXT NULL, Comment TEXT NULL, Filename TEXT NULL, MIMEType TEXT NULL, PRIMARY KEY (ItemId, AttachmentIndex))"; string[] queries = { @@ -116,6 +118,7 @@ namespace Emby.Server.Implementations.Data "create table if not exists " + ChaptersTableName + " (ItemId GUID, ChapterIndex INT NOT NULL, StartPositionTicks BIGINT NOT NULL, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))", CreateMediaStreamsTableCommand, + CreateMediaAttachmentsTableCommand, "pragma shrink_memory" }; @@ -423,6 +426,17 @@ namespace Emby.Server.Implementations.Data "ColorTransfer" }; + private static readonly string[] _mediaAttachmentSaveColumns = + { + "ItemId", + "AttachmentIndex", + "Codec", + "CodecTag", + "Comment", + "Filename", + "MIMEType" + }; + private static string GetSaveItemCommandText() { var saveColumns = new [] @@ -6130,5 +6144,170 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type return item; } + + public List GetMediaAttachments(MediaAttachmentQuery query) + { + CheckDisposed(); + + if (query == null) + { + throw new ArgumentNullException(nameof(query)); + } + + var cmdText = "select " + + string.Join(",", _mediaAttachmentSaveColumns) + + " from mediaattachments where" + + " ItemId=@ItemId"; + + if (query.Index.HasValue) + { + cmdText += "AND AttachmentIndex=@AttachmentIndex"; + } + + cmdText += " order by AttachmentIndex ASC"; + + using (var connection = GetConnection(true)) + { + var list = new List(); + + using (var statement = PrepareStatement(connection, cmdText)) + { + statement.TryBind("@ItemId", query.ItemId.ToGuidBlob()); + + if (query.Index.HasValue) + { + statement.TryBind("@AttachmentIndex", query.Index.Value); + } + + foreach (var row in statement.ExecuteQuery()) { + list.Add(GetMediaAttachment(row)); + } + } + + return list; + } + } + + public void SaveMediaAttachments(Guid id, List attachments, CancellationToken cancellationToken) + { + CheckDisposed(); + if (id == Guid.Empty) + { + throw new ArgumentNullException(nameof(id)); + } + + if (attachments == null) + { + throw new ArgumentNullException(nameof(attachments)); + } + + using (var connection = GetConnection()) + { + connection.RunInTransaction(db => + { + var itemIdBlob = id.ToGuidBlob(); + + db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob); + + InsertMediaAttachments(itemIdBlob, attachments, db); + + }, TransactionMode); + } + } + + private void InsertMediaAttachments(byte[] idBlob, List attachments, IDatabaseConnection db) + { + var startIndex = 0; + var limit = 10; + + while (startIndex < attachments.Count) + { + var insertText = new StringBuilder(string.Format("insert into mediaattachments ({0}) values ", string.Join(",", _mediaAttachmentSaveColumns))); + + var endIndex = Math.Min(attachments.Count, startIndex + limit); + + for (var i = startIndex; i < endIndex; i++) + { + if (i != startIndex) + { + insertText.Append(","); + } + + var index = i.ToString(CultureInfo.InvariantCulture); + insertText.Append("(@ItemId, "); + + foreach (var column in _mediaAttachmentSaveColumns.Skip(1)) + { + insertText.Append("@" + column + index + ","); + } + insertText.Length -= 1; + + insertText.Append(")"); + } + + using (var statement = PrepareStatement(db, insertText.ToString())) + { + statement.TryBind("@ItemId", idBlob); + + for (var i = startIndex; i < endIndex; i++) + { + var index = i.ToString(CultureInfo.InvariantCulture); + + var attachment = attachments[i]; + + statement.TryBind("@AttachmentIndex" + index, attachment.Index); + statement.TryBind("@Codec" + index, attachment.Codec); + statement.TryBind("@CodecTag" + index, attachment.CodecTag); + statement.TryBind("@Comment" + index, attachment.Comment); + statement.TryBind("@Filename" + index, attachment.Filename); + statement.TryBind("@MIMEType" + index, attachment.MIMEType); + } + + statement.Reset(); + statement.MoveNext(); + } + startIndex += limit; + } + } + + /// + /// Gets the attachment. + /// + /// The reader. + /// MediaAttachment + private MediaAttachment GetMediaAttachment(IReadOnlyList reader) + { + var item = new MediaAttachment + { + Index = reader[1].ToInt() + }; + + if (reader[2].SQLiteType != SQLiteType.Null) + { + item.Codec = reader[2].ToString(); + } + + if (reader[2].SQLiteType != SQLiteType.Null) + { + item.CodecTag = reader[3].ToString(); + } + + if (reader[4].SQLiteType != SQLiteType.Null) + { + item.Comment = reader[4].ToString(); + } + + if (reader[6].SQLiteType != SQLiteType.Null) + { + item.Filename = reader[5].ToString(); + } + + if (reader[6].SQLiteType != SQLiteType.Null) + { + item.MIMEType = reader[6].ToString(); + } + + return item; + } } } -- cgit v1.2.3 From 1513c76a3e0b8f84def98df3fe99432370076497 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 7 Oct 2019 07:53:28 -0400 Subject: Support MediaAttachment retrieval in MediaSourceManager. --- .../Library/MediaSourceManager.cs | 26 ++++++++++++++++++++++ .../Library/IMediaSourceManager.cs | 19 ++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index d83e1fc02..e069ebd17 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -128,6 +128,32 @@ namespace Emby.Server.Implementations.Library return streams; } + public List GetMediaAttachments(MediaAttachmentQuery query) + { + var list = _itemRepo.GetMediaAttachments(query); + return list; + } + + public List GetMediaAttachments(string mediaSourceId) + { + var list = GetMediaAttachments(new MediaAttachmentQuery + { + ItemId = new Guid(mediaSourceId) + }); + + return list; + } + + public List GetMediaAttachments(Guid itemId) + { + var list = GetMediaAttachments(new MediaAttachmentQuery + { + ItemId = itemId + }); + + return list; + } + public async Task> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken) { var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index fbae4edb0..961028b04 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -38,6 +38,25 @@ namespace MediaBrowser.Controller.Library /// IEnumerable<MediaStream>. List GetMediaStreams(MediaStreamQuery query); + /// + /// Gets the media attachments. + /// + /// The item identifier. + /// IEnumerable<MediaAttachment>. + List GetMediaAttachments(Guid itemId); + /// + /// Gets the media attachments. + /// + /// The The media source identifier. + /// IEnumerable<MediaAttachment>. + List GetMediaAttachments(string mediaSourceId); + /// + /// Gets the media attachments. + /// + /// The query. + /// IEnumerable<MediaAttachment>. + List GetMediaAttachments(MediaAttachmentQuery query); + /// /// Gets the playack media sources. /// -- cgit v1.2.3 From a9a85f251e77cf1670e69733e1d7b5ab9116aaaf Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Fri, 18 Oct 2019 07:56:53 -0400 Subject: Instantiate AttachmentExtractor in ApplicationHost. --- Emby.Server.Implementations/ApplicationHost.cs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 04904fc4a..bcd99ffe4 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -282,6 +282,8 @@ namespace Emby.Server.Implementations private ISubtitleEncoder SubtitleEncoder { get; set; } + private IAttachmentExtractor AttachmentExtractor { get; set; } + private ISessionManager SessionManager { get; set; } private ILiveTvManager LiveTvManager { get; set; } @@ -904,6 +906,10 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager)); + AttachmentExtractor = new MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, MediaSourceManager, ProcessFactory); + + serviceCollection.AddSingleton(AttachmentExtractor); + _displayPreferencesRepository.Initialize(); var userDataRepo = new SqliteUserDataRepository(LoggerFactory, ApplicationPaths); -- cgit v1.2.3 From 90dfe729bb9f8929bf584b93f93aabd84abbef3d Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 08:48:27 -0500 Subject: Add space when building query string for attachments. Co-Authored-By: Vasily --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 58f04e4fc..d02aa9b1f 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6161,7 +6161,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type if (query.Index.HasValue) { - cmdText += "AND AttachmentIndex=@AttachmentIndex"; + cmdText += " AND AttachmentIndex=@AttachmentIndex"; } cmdText += " order by AttachmentIndex ASC"; -- cgit v1.2.3 From 4573fb5301b02e8b57a15a57f909b1b63d6b3900 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:19:53 -0500 Subject: Use ToByteArray instead of ToGuidBlob. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 08f53f0f5..2b07844e7 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6175,7 +6175,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var statement = PrepareStatement(connection, cmdText)) { - statement.TryBind("@ItemId", query.ItemId.ToGuidBlob()); + statement.TryBind("@ItemId", query.ItemId.ToByteArray()); if (query.Index.HasValue) { @@ -6208,7 +6208,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { connection.RunInTransaction(db => { - var itemIdBlob = id.ToGuidBlob(); + var itemIdBlob = id.ToByteArray(); db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob); -- cgit v1.2.3 From 0dde5e46df13f63158288ce73b98b25a6440de43 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:28:33 -0500 Subject: Flatten using connection in GetMediaAttachments/SaveMediaAttachments --- .../Data/SqliteItemRepository.cs | 39 ++++++++++------------ 1 file changed, 17 insertions(+), 22 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2b07844e7..62837933c 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6169,26 +6169,23 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type cmdText += " order by AttachmentIndex ASC"; - using (var connection = GetConnection(true)) + var list = new List(); + using var connection = GetConnection(true); + using (var statement = PrepareStatement(connection, cmdText)) { - var list = new List(); + statement.TryBind("@ItemId", query.ItemId.ToByteArray()); - using (var statement = PrepareStatement(connection, cmdText)) + if (query.Index.HasValue) { - statement.TryBind("@ItemId", query.ItemId.ToByteArray()); - - if (query.Index.HasValue) - { - statement.TryBind("@AttachmentIndex", query.Index.Value); - } - - foreach (var row in statement.ExecuteQuery()) { - list.Add(GetMediaAttachment(row)); - } + statement.TryBind("@AttachmentIndex", query.Index.Value); } - return list; + foreach (var row in statement.ExecuteQuery()) { + list.Add(GetMediaAttachment(row)); + } } + + return list; } public void SaveMediaAttachments(Guid id, List attachments, CancellationToken cancellationToken) @@ -6204,18 +6201,16 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type throw new ArgumentNullException(nameof(attachments)); } - using (var connection = GetConnection()) + using var connection = GetConnection(); + connection.RunInTransaction(db => { - connection.RunInTransaction(db => - { - var itemIdBlob = id.ToByteArray(); + var itemIdBlob = id.ToByteArray(); - db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob); + db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob); - InsertMediaAttachments(itemIdBlob, attachments, db); + InsertMediaAttachments(itemIdBlob, attachments, db); - }, TransactionMode); - } + }, TransactionMode); } private void InsertMediaAttachments(byte[] idBlob, List attachments, IDatabaseConnection db) -- cgit v1.2.3 From c2d8f210b1c8daa86a37b530a7da05370d3b209a Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:31:32 -0500 Subject: Check for cancellation in SaveMediaAttachments. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 62837933c..8d39cdb8f 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6201,6 +6201,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type throw new ArgumentNullException(nameof(attachments)); } + cancellationToken.ThrowIfCancellationRequested(); + using var connection = GetConnection(); connection.RunInTransaction(db => { -- cgit v1.2.3 From ad2101ce52999fccf51e52510729fd36e3495369 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:33:50 -0500 Subject: Rename "limit" to "insertAtOnce" in InsertMediaAttachments. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 8d39cdb8f..7c0a58596 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6218,13 +6218,13 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type private void InsertMediaAttachments(byte[] idBlob, List attachments, IDatabaseConnection db) { var startIndex = 0; - var limit = 10; + var insertAtOnce = 10; while (startIndex < attachments.Count) { var insertText = new StringBuilder(string.Format("insert into mediaattachments ({0}) values ", string.Join(",", _mediaAttachmentSaveColumns))); - var endIndex = Math.Min(attachments.Count, startIndex + limit); + var endIndex = Math.Min(attachments.Count, startIndex + insertAtOnce); for (var i = startIndex; i < endIndex; i++) { @@ -6266,7 +6266,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statement.Reset(); statement.MoveNext(); } - startIndex += limit; + startIndex += insertAtOnce; } } -- cgit v1.2.3 From 7110069b399496979aa8fdc701d0e1af536425f7 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:37:19 -0500 Subject: Return list result directly for MediaAttachments. --- Emby.Server.Implementations/Library/MediaSourceManager.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 2e24b847b..27eb39530 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -130,28 +130,23 @@ namespace Emby.Server.Implementations.Library public List GetMediaAttachments(MediaAttachmentQuery query) { - var list = _itemRepo.GetMediaAttachments(query); - return list; + return _itemRepo.GetMediaAttachments(query); } public List GetMediaAttachments(string mediaSourceId) { - var list = GetMediaAttachments(new MediaAttachmentQuery + return GetMediaAttachments(new MediaAttachmentQuery { ItemId = new Guid(mediaSourceId) }); - - return list; } public List GetMediaAttachments(Guid itemId) { - var list = GetMediaAttachments(new MediaAttachmentQuery + return GetMediaAttachments(new MediaAttachmentQuery { ItemId = itemId }); - - return list; } public async Task> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken) -- cgit v1.2.3 From bd4da93d1e871f7d58075b6780c4c1c30fc34fd7 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 07:48:32 -0500 Subject: Throw ArgumentException instead of ArgumentNullException on empty Guid. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 7c0a58596..e7e93d0db 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6193,7 +6193,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type CheckDisposed(); if (id == Guid.Empty) { - throw new ArgumentNullException(nameof(id)); + throw new ArgumentException(nameof(id)); } if (attachments == null) -- cgit v1.2.3 From 24a460dc939b8dab2aa7e3e4db7ce47373a50d1d Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:13:22 -0500 Subject: Update Emby.Server.Implementations/Data/SqliteItemRepository.cs formatting Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 7c0a58596..27a29318b 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6240,6 +6240,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { insertText.Append("@" + column + index + ","); } + insertText.Length -= 1; insertText.Append(")"); -- cgit v1.2.3 From 25bc7b81c37b635109e5c14baabf64954a517512 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:13:35 -0500 Subject: Update Emby.Server.Implementations/Data/SqliteItemRepository.cs formatting Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 27a29318b..de95adfa1 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6267,6 +6267,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statement.Reset(); statement.MoveNext(); } + startIndex += insertAtOnce; } } -- cgit v1.2.3 From d33e0a4e2c59783c785c992ea0c3a31596ae3058 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:17:34 -0500 Subject: Simplify AttachmentExtractor instantiation. Co-Authored-By: Bond-009 --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ac37cfe07..9330d5719 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -908,7 +908,7 @@ namespace Emby.Server.Implementations AttachmentExtractor = new MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, MediaSourceManager, ProcessFactory); - serviceCollection.AddSingleton(AttachmentExtractor); + serviceCollection.AddSingleton(); _displayPreferencesRepository.Initialize(); -- cgit v1.2.3 From e5b65ed034c6ca0fcee9f73cc991a0962a44278f Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:19:07 -0500 Subject: Update Emby.Server.Implementations/Data/SqliteItemRepository.cs formatting Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index de95adfa1..a7e779251 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6180,7 +6180,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statement.TryBind("@AttachmentIndex", query.Index.Value); } - foreach (var row in statement.ExecuteQuery()) { + foreach (var row in statement.ExecuteQuery()) + { list.Add(GetMediaAttachment(row)); } } -- cgit v1.2.3 From 3602251cf53c636e3006605e2b48a77d7952d029 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:36:17 -0500 Subject: Update Emby.Server.Implementations/Data/SqliteItemRepository.cs Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index a7e779251..2152ed605 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6231,7 +6231,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type { if (i != startIndex) { - insertText.Append(","); + insertText.Append(','); } var index = i.ToString(CultureInfo.InvariantCulture); -- cgit v1.2.3 From 8505ee9d6c0b2f073fd39c538cbdb0ee9ee95354 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 14:53:46 -0500 Subject: Extract the prefix for MediaAttachment insertions to a static member instead of generating it per-query. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2b9c6a52f..dd8620f9f 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -51,6 +51,19 @@ namespace Emby.Server.Implementations.Data private readonly TypeMapper _typeMapper; private readonly JsonSerializerOptions _jsonOptions; + static SqliteItemRepository() { + var queryPrefixText = new StringBuilder(); + queryPrefixText.Append("insert into mediaattachments ("); + foreach (var column in _mediaAttachmentSaveColumns) + { + queryPrefixText.Append(column); + queryPrefixText.Append(','); + } + queryPrefixText.Length -= 1; + queryPrefixText.Append(") values "); + _mediaAttachmentInsertPrefix = queryPrefixText.ToString(); + } + /// /// Initializes a new instance of the class. /// @@ -436,6 +449,7 @@ namespace Emby.Server.Implementations.Data "Filename", "MIMEType" }; + private static readonly string _mediaAttachmentInsertPrefix; private static string GetSaveItemCommandText() { @@ -6223,7 +6237,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type while (startIndex < attachments.Count) { - var insertText = new StringBuilder(string.Format("insert into mediaattachments ({0}) values ", string.Join(",", _mediaAttachmentSaveColumns))); + var insertText = new StringBuilder(_mediaAttachmentInsertPrefix); var endIndex = Math.Min(attachments.Count, startIndex + insertAtOnce); -- cgit v1.2.3 From 79bbf09ecba6d3c2ddb0955bdbd51164a6c7a236 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Wed, 6 Nov 2019 08:43:49 -0500 Subject: Revert "Simplify AttachmentExtractor instantiation." This reverts commit d33e0a4e2c59783c785c992ea0c3a31596ae3058. --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9330d5719..ac37cfe07 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -908,7 +908,7 @@ namespace Emby.Server.Implementations AttachmentExtractor = new MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, MediaSourceManager, ProcessFactory); - serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(AttachmentExtractor); _displayPreferencesRepository.Initialize(); -- cgit v1.2.3 From 74fb63a8989fe19c5e985a09f9cb45e42022320f Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Wed, 6 Nov 2019 10:43:14 -0500 Subject: Use block rather than local using statement. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index dd8620f9f..f7774000c 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6184,7 +6184,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type cmdText += " order by AttachmentIndex ASC"; var list = new List(); - using var connection = GetConnection(true); + using (var connection = GetConnection(true)) using (var statement = PrepareStatement(connection, cmdText)) { statement.TryBind("@ItemId", query.ItemId.ToByteArray()); @@ -6218,16 +6218,18 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type cancellationToken.ThrowIfCancellationRequested(); - using var connection = GetConnection(); - connection.RunInTransaction(db => + using (var connection = GetConnection()) { - var itemIdBlob = id.ToByteArray(); + connection.RunInTransaction(db => + { + var itemIdBlob = id.ToByteArray(); - db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob); + db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob); - InsertMediaAttachments(itemIdBlob, attachments, db); + InsertMediaAttachments(itemIdBlob, attachments, db); - }, TransactionMode); + }, TransactionMode); + } } private void InsertMediaAttachments(byte[] idBlob, List attachments, IDatabaseConnection db) -- cgit v1.2.3 From 6defe80b62483350111dc6ba2f18cfd83542c4b1 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 08:38:36 -0500 Subject: Check for cancellation between each batch of MediaAttachment inserts. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index f7774000c..195671168 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6226,13 +6226,13 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type db.Execute("delete from mediaattachments where ItemId=@ItemId", itemIdBlob); - InsertMediaAttachments(itemIdBlob, attachments, db); + InsertMediaAttachments(itemIdBlob, attachments, db, cancellationToken); }, TransactionMode); } } - private void InsertMediaAttachments(byte[] idBlob, List attachments, IDatabaseConnection db) + private void InsertMediaAttachments(byte[] idBlob, List attachments, IDatabaseConnection db, CancellationToken cancellationToken) { var startIndex = 0; var insertAtOnce = 10; @@ -6263,6 +6263,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type insertText.Append(")"); } + cancellationToken.ThrowIfCancellationRequested(); + using (var statement = PrepareStatement(db, insertText.ToString())) { statement.TryBind("@ItemId", idBlob); -- cgit v1.2.3 From f60e9b0b62388f6e758870b53f3fb59b842fecd2 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:20:29 -0500 Subject: formatting Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 195671168..ba75488fb 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -51,7 +51,8 @@ namespace Emby.Server.Implementations.Data private readonly TypeMapper _typeMapper; private readonly JsonSerializerOptions _jsonOptions; - static SqliteItemRepository() { + static SqliteItemRepository() + { var queryPrefixText = new StringBuilder(); queryPrefixText.Append("insert into mediaattachments ("); foreach (var column in _mediaAttachmentSaveColumns) -- cgit v1.2.3 From d6aa02ff09e262bc57a4dcf6dbbc11e6d1c963dd Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:22:07 -0500 Subject: Update Emby.Server.Implementations/Data/SqliteItemRepository.cs formatting Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index ba75488fb..a63052a68 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -60,6 +60,7 @@ namespace Emby.Server.Implementations.Data queryPrefixText.Append(column); queryPrefixText.Append(','); } + queryPrefixText.Length -= 1; queryPrefixText.Append(") values "); _mediaAttachmentInsertPrefix = queryPrefixText.ToString(); -- cgit v1.2.3 From 193a1fa474202ec0933c1a937c56ccecf58c4410 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:23:28 -0500 Subject: Update Emby.Server.Implementations/Library/MediaSourceManager.cs docs Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Library/MediaSourceManager.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 27eb39530..833a5c88f 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -128,6 +128,7 @@ namespace Emby.Server.Implementations.Library return streams; } + /// public List GetMediaAttachments(MediaAttachmentQuery query) { return _itemRepo.GetMediaAttachments(query); -- cgit v1.2.3 From 8b2d7062c43ff6618d5bf4a5e26d0ba5bb70bd07 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:24:30 -0500 Subject: Update Emby.Server.Implementations/Library/MediaSourceManager.cs docs Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Library/MediaSourceManager.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 833a5c88f..df6f0e77f 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -134,6 +134,7 @@ namespace Emby.Server.Implementations.Library return _itemRepo.GetMediaAttachments(query); } + /// public List GetMediaAttachments(string mediaSourceId) { return GetMediaAttachments(new MediaAttachmentQuery -- cgit v1.2.3 From d73f46dcda8db877edae4918a6053cad924808fa Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:27:55 -0500 Subject: Update Emby.Server.Implementations/Library/MediaSourceManager.cs docs Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Library/MediaSourceManager.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index df6f0e77f..9574a28e9 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -143,6 +143,7 @@ namespace Emby.Server.Implementations.Library }); } + /// public List GetMediaAttachments(Guid itemId) { return GetMediaAttachments(new MediaAttachmentQuery -- cgit v1.2.3 From c6855e6a2a347db17f2b8b2f18dd89d5b47a8816 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:53:39 -0500 Subject: Simplify AttachmentExtractor instantiation. --- Emby.Server.Implementations/ApplicationHost.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ac37cfe07..97da5b437 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -282,8 +282,6 @@ namespace Emby.Server.Implementations private ISubtitleEncoder SubtitleEncoder { get; set; } - private IAttachmentExtractor AttachmentExtractor { get; set; } - private ISessionManager SessionManager { get; set; } private ILiveTvManager LiveTvManager { get; set; } @@ -906,9 +904,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager)); - AttachmentExtractor = new MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor(LibraryManager, LoggerFactory, ApplicationPaths, FileSystemManager, MediaEncoder, MediaSourceManager, ProcessFactory); - - serviceCollection.AddSingleton(AttachmentExtractor); + serviceCollection.AddSingleton(typeof(MediaBrowser.Controller.MediaEncoding.IAttachmentExtractor),typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor)); _displayPreferencesRepository.Initialize(); -- cgit v1.2.3 From 5cab79c839d2212ae638db403ec4d7ba0699f9a1 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 4 Dec 2019 21:39:27 +0100 Subject: Clean up Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs --- .../Library/MediaSourceManager.cs | 4 +- .../LiveTv/EmbyTV/DirectRecorder.cs | 3 +- .../LiveTv/EmbyTV/EmbyTV.cs | 504 +++++++++------------ .../LiveTv/EmbyTV/EpgChannelData.cs | 68 +++ .../LiveTv/EmbyTV/NfoConfigurationExtensions.cs | 19 + .../LiveTv/LiveTvManager.cs | 5 +- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- .../Library/IMediaSourceManager.cs | 4 +- .../Subtitles/SubtitleEncoder.cs | 2 +- jellyfin.ruleset | 4 +- 11 files changed, 319 insertions(+), 298 deletions(-) create mode 100644 Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs create mode 100644 Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 7a26e0c37..059b27e87 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -128,7 +128,7 @@ namespace Emby.Server.Implementations.Library return streams; } - public async Task> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken) + public async Task> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken) { var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); @@ -290,7 +290,7 @@ namespace Emby.Server.Implementations.Library return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false); } - var sources = await GetPlayackMediaSources(item, null, false, enablePathSubstitution, cancellationToken).ConfigureAwait(false); + var sources = await GetPlaybackMediaSources(item, null, false, enablePathSubstitution, cancellationToken).ConfigureAwait(false); return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 8dee7046e..84e8c31f9 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -74,7 +75,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV DecompressionMethod = CompressionMethod.None }; - using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false)) + using (var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false)) { _logger.LogInformation("Opened recording stream from tuner provider"); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index ef5928e48..420209eda 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1,3 +1,6 @@ +#pragma warning disable SA1600 +#pragma warning disable CS1591 + using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -40,6 +43,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable { + public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss"; + + private const int TunerDiscoveryDurationMs = 3000; + private readonly IServerApplicationHost _appHost; private readonly ILogger _logger; private readonly IHttpClient _httpClient; @@ -57,19 +64,21 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private readonly IProviderManager _providerManager; private readonly IMediaEncoder _mediaEncoder; private readonly IProcessFactory _processFactory; - private IMediaSourceManager _mediaSourceManager; - - public static EmbyTV Current; - - public event EventHandler> TimerCreated; - public event EventHandler> TimerCancelled; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly IStreamHelper _streamHelper; private readonly ConcurrentDictionary _activeRecordings = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private readonly IStreamHelper _streamHelper; + private readonly ConcurrentDictionary _epgChannels = + new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1); - public EmbyTV(IServerApplicationHost appHost, + private bool _disposed = false; + + public EmbyTV( + IServerApplicationHost appHost, IStreamHelper streamHelper, IMediaSourceManager mediaSourceManager, ILogger logger, @@ -103,12 +112,40 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _seriesTimerProvider = new SeriesTimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers.json")); _timerProvider = new TimerManager(jsonSerializer, _logger, Path.Combine(DataPath, "timers.json")); - _timerProvider.TimerFired += _timerProvider_TimerFired; + _timerProvider.TimerFired += OnTimerProviderTimerFired; - _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; + _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated; } - private void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) + public event EventHandler> TimerCreated; + + public event EventHandler> TimerCancelled; + + public static EmbyTV Current { get; private set; } + + /// + public string Name => "Emby"; + + public string DataPath => Path.Combine(_config.CommonApplicationPaths.DataPath, "livetv"); + + /// + public string HomePageUrl => "https://github.com/jellyfin/jellyfin"; + + private string DefaultRecordingPath => Path.Combine(DataPath, "recordings"); + + private string RecordingPath + { + get + { + var path = GetConfiguration().RecordingPath; + + return string.IsNullOrWhiteSpace(path) + ? DefaultRecordingPath + : path; + } + } + + private void OnNamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e) { if (string.Equals(e.Key, "livetv", StringComparison.OrdinalIgnoreCase)) { @@ -116,11 +153,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - public async Task Start() + public Task Start() { _timerProvider.RestartTimers(); - await CreateRecordingFolders().ConfigureAwait(false); + return CreateRecordingFolders(); } private async void OnRecordingFoldersChanged() @@ -132,8 +169,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { try { - var recordingFolders = GetRecordingFolders(); - + var recordingFolders = GetRecordingFolders().ToArray(); var virtualFolders = _libraryManager.GetVirtualFolders() .ToList(); @@ -241,26 +277,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - public string Name => "Emby"; - - public string DataPath => Path.Combine(_config.CommonApplicationPaths.DataPath, "livetv"); - - private string DefaultRecordingPath => Path.Combine(DataPath, "recordings"); - - private string RecordingPath - { - get - { - var path = GetConfiguration().RecordingPath; - - return string.IsNullOrWhiteSpace(path) - ? DefaultRecordingPath - : path; - } - } - - public string HomePageUrl => "https://github.com/jellyfin/jellyfin"; - public async Task RefreshSeriesTimers(CancellationToken cancellationToken) { var seriesTimers = await GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false); @@ -339,7 +355,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } catch (NotSupportedException) { - } catch (Exception ex) { @@ -351,7 +366,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return list; } - private async Task AddMetadata(IListingsProvider provider, ListingsProviderInfo info, List tunerChannels, bool enableCache, CancellationToken cancellationToken) + private async Task AddMetadata( + IListingsProvider provider, + ListingsProviderInfo info, + IEnumerable tunerChannels, + bool enableCache, + CancellationToken cancellationToken) { var epgChannels = await GetEpgChannels(provider, info, enableCache, cancellationToken).ConfigureAwait(false); @@ -363,8 +383,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { if (!string.IsNullOrWhiteSpace(epgChannel.Name)) { - //tunerChannel.Name = epgChannel.Name; + // tunerChannel.Name = epgChannel.Name; } + if (!string.IsNullOrWhiteSpace(epgChannel.ImageUrl)) { tunerChannel.ImageUrl = epgChannel.ImageUrl; @@ -373,10 +394,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - private readonly ConcurrentDictionary _epgChannels = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - - private async Task GetEpgChannels(IListingsProvider provider, ListingsProviderInfo info, bool enableCache, CancellationToken cancellationToken) + private async Task GetEpgChannels( + IListingsProvider provider, + ListingsProviderInfo info, + bool enableCache, + CancellationToken cancellationToken) { if (!enableCache || !_epgChannels.TryGetValue(info.Id, out var result)) { @@ -394,59 +416,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return result; } - private class EpgChannelData - { - public EpgChannelData(List channels) - { - ChannelsById = new Dictionary(StringComparer.OrdinalIgnoreCase); - ChannelsByNumber = new Dictionary(StringComparer.OrdinalIgnoreCase); - ChannelsByName = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var channel in channels) - { - ChannelsById[channel.Id] = channel; - - if (!string.IsNullOrEmpty(channel.Number)) - { - ChannelsByNumber[channel.Number] = channel; - } - - var normalizedName = NormalizeName(channel.Name ?? string.Empty); - if (!string.IsNullOrWhiteSpace(normalizedName)) - { - ChannelsByName[normalizedName] = channel; - } - } - } - - private Dictionary ChannelsById { get; set; } - private Dictionary ChannelsByNumber { get; set; } - private Dictionary ChannelsByName { get; set; } - - public ChannelInfo GetChannelById(string id) - { - ChannelInfo result = null; - - ChannelsById.TryGetValue(id, out result); - - return result; - } - - public ChannelInfo GetChannelByNumber(string number) - { - ChannelsByNumber.TryGetValue(number, out var result); - - return result; - } - - public ChannelInfo GetChannelByName(string name) - { - ChannelsByName.TryGetValue(name, out var result); - - return result; - } - } - private async Task GetEpgChannelFromTunerChannel(IListingsProvider provider, ListingsProviderInfo info, ChannelInfo tunerChannel, CancellationToken cancellationToken) { var epgChannels = await GetEpgChannels(provider, info, true, cancellationToken).ConfigureAwait(false); @@ -463,6 +432,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return mapping.Value; } } + return channelId; } @@ -476,7 +446,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return GetEpgChannelFromTunerChannel(info.ChannelMappings, tunerChannel, epgChannels); } - private ChannelInfo GetEpgChannelFromTunerChannel(NameValuePair[] mappings, ChannelInfo tunerChannel, EpgChannelData epgChannelData) + private ChannelInfo GetEpgChannelFromTunerChannel( + NameValuePair[] mappings, + ChannelInfo tunerChannel, + EpgChannelData epgChannelData) { if (!string.IsNullOrWhiteSpace(tunerChannel.Id)) { @@ -537,7 +510,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (!string.IsNullOrWhiteSpace(tunerChannel.Name)) { - var normalizedName = NormalizeName(tunerChannel.Name); + var normalizedName = EpgChannelData.NormalizeName(tunerChannel.Name); var channel = epgChannelData.GetChannelByName(normalizedName); @@ -550,11 +523,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return null; } - private static string NormalizeName(string value) - { - return value.Replace(" ", string.Empty).Replace("-", string.Empty); - } - public async Task> GetChannelsForListingsProvider(ListingsProviderInfo listingsProvider, CancellationToken cancellationToken) { var list = new List(); @@ -600,6 +568,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { _seriesTimerProvider.Delete(remove); } + return Task.CompletedTask; } @@ -689,6 +658,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { programInfo = GetProgramInfoFromCache(timer); } + if (programInfo == null) { _logger.LogInformation("Unable to find program with Id {0}. Will search using start date", timer.ProgramId); @@ -703,10 +673,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV timer.IsManual = true; _timerProvider.Add(timer); - if (TimerCreated != null) - { - TimerCreated(this, new GenericEventArgs(timer)); - } + TimerCreated?.Invoke(this, new GenericEventArgs(timer)); return Task.FromResult(timer.Id); } @@ -800,7 +767,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } // Only update if not currently active - if (!_activeRecordings.TryGetValue(updatedTimer.Id, out var activeRecordingInfo)) + if (!_activeRecordings.TryGetValue(updatedTimer.Id, out _)) { existingTimer.PrePaddingSeconds = updatedTimer.PrePaddingSeconds; existingTimer.PostPaddingSeconds = updatedTimer.PostPaddingSeconds; @@ -846,6 +813,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { return info.Path; } + return null; } @@ -870,9 +838,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { return null; } + return recording; } } + return null; } @@ -1061,13 +1031,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV mediaSource.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture) + "_" + mediaSource.Id; - //if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing) - //{ - // var ticks = (DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value).Ticks - TimeSpan.FromSeconds(10).Ticks; - // ticks = Math.Max(0, ticks); - // mediaSource.Path += "?t=" + ticks.ToString(CultureInfo.InvariantCulture) + "&s=" + mediaSource.DateLiveStreamOpened.Value.Ticks.ToString(CultureInfo.InvariantCulture); - //} - return mediaSource; } @@ -1091,7 +1054,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } catch (NotImplementedException) { - } } @@ -1142,7 +1104,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return Task.CompletedTask; } - async void _timerProvider_TimerFired(object sender, GenericEventArgs e) + private async void OnTimerProviderTimerFired(object sender, GenericEventArgs e) { var timer = e.Argument; @@ -1177,7 +1139,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } catch (OperationCanceledException) { - } catch (Exception ex) { @@ -1221,7 +1182,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (timer.SeasonNumber.HasValue) { - folderName = string.Format("Season {0}", timer.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture)); + folderName = string.Format( + CultureInfo.InvariantCulture, + "Season {0}", + timer.SeasonNumber.Value); recordPath = Path.Combine(recordPath, folderName); } } @@ -1275,6 +1239,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { recordPath = Path.Combine(recordPath, "Sports"); } + recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim()); } else @@ -1283,6 +1248,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { recordPath = Path.Combine(recordPath, "Other"); } + recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim()); } @@ -1304,6 +1270,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { programInfo = GetProgramInfoFromCache(timer); } + if (programInfo == null) { _logger.LogInformation("Unable to find program with Id {0}. Will search using start date", timer.ProgramId); @@ -1315,9 +1282,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV CopyProgramInfoToTimerInfo(programInfo, timer); } - string seriesPath = null; var remoteMetadata = await FetchInternetMetadata(timer, CancellationToken.None).ConfigureAwait(false); - var recordPath = GetRecordingPath(timer, remoteMetadata, out seriesPath); + var recordPath = GetRecordingPath(timer, remoteMetadata, out string seriesPath); var recordingStatus = RecordingStatus.New; string liveStreamId = null; @@ -1326,19 +1292,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV try { - var allMediaSources = await _mediaSourceManager.GetPlayackMediaSources(channelItem, null, true, false, CancellationToken.None).ConfigureAwait(false); + var allMediaSources = await _mediaSourceManager.GetPlaybackMediaSources(channelItem, null, true, false, CancellationToken.None).ConfigureAwait(false); var mediaStreamInfo = allMediaSources[0]; IDirectStreamProvider directStreamProvider = null; if (mediaStreamInfo.RequiresOpening) { - var liveStreamResponse = await _mediaSourceManager.OpenLiveStreamInternal(new LiveStreamRequest - { - ItemId = channelItem.Id, - OpenToken = mediaStreamInfo.OpenToken - - }, CancellationToken.None).ConfigureAwait(false); + var liveStreamResponse = await _mediaSourceManager.OpenLiveStreamInternal( + new LiveStreamRequest + { + ItemId = channelItem.Id, + OpenToken = mediaStreamInfo.OpenToken + }, + CancellationToken.None).ConfigureAwait(false); mediaStreamInfo = liveStreamResponse.Item1.MediaSource; liveStreamId = mediaStreamInfo.LiveStreamId; @@ -1412,12 +1379,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate && timer.RetryCount < 10) { - const int retryIntervalSeconds = 60; - _logger.LogInformation("Retrying recording in {0} seconds.", retryIntervalSeconds); + const int RetryIntervalSeconds = 60; + _logger.LogInformation("Retrying recording in {0} seconds.", RetryIntervalSeconds); timer.Status = RecordingStatus.New; timer.PrePaddingSeconds = 0; - timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds); + timer.StartDate = DateTime.UtcNow.AddSeconds(RetryIntervalSeconds); timer.RetryCount++; _timerProvider.AddOrUpdate(timer); } @@ -1538,6 +1505,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { return; } + if (string.IsNullOrWhiteSpace(seriesPath)) { return; @@ -1576,34 +1544,34 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV DeleteLibraryItemsForTimers(timersToDelete); var librarySeries = _libraryManager.FindByPath(seriesPath, true) as Folder; - if (librarySeries == null) { return; } - var episodesToDelete = (librarySeries.GetItemList(new InternalItemsQuery - { - OrderBy = new[] { new ValueTuple(ItemSortBy.DateCreated, SortOrder.Descending) }, - IsVirtualItem = false, - IsFolder = false, - Recursive = true, - DtoOptions = new DtoOptions(true) - - })) - .Where(i => i.IsFileProtocol && File.Exists(i.Path)) - .Skip(seriesTimer.KeepUpTo - 1) - .ToList(); + var episodesToDelete = librarySeries.GetItemList( + new InternalItemsQuery + { + OrderBy = new[] { new ValueTuple(ItemSortBy.DateCreated, SortOrder.Descending) }, + IsVirtualItem = false, + IsFolder = false, + Recursive = true, + DtoOptions = new DtoOptions(true) + }).Where(i => i.IsFileProtocol && File.Exists(i.Path)) + .Skip(seriesTimer.KeepUpTo - 1) + .ToList(); foreach (var item in episodesToDelete) { try { - _libraryManager.DeleteItem(item, new DeleteOptions - { - DeleteFileLocation = true - - }, true); + _libraryManager.DeleteItem( + item, + new DeleteOptions + { + DeleteFileLocation = true + }, + true); } catch (Exception ex) { @@ -1617,7 +1585,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1); private void DeleteLibraryItemsForTimers(List timers) { foreach (var timer in timers) @@ -1644,22 +1611,20 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (libraryItem != null) { - _libraryManager.DeleteItem(libraryItem, new DeleteOptions - { - DeleteFileLocation = true - - }, true); + _libraryManager.DeleteItem( + libraryItem, + new DeleteOptions + { + DeleteFileLocation = true + }, + true); } else { - try + if (File.Exists(timer.RecordingPath)) { _fileSystem.DeleteFile(timer.RecordingPath); } - catch (IOException) - { - - } } _timerProvider.Delete(timer); @@ -1690,16 +1655,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return true; } - var hasRecordingAtPath = _activeRecordings + return _activeRecordings .Values .ToList() .Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase)); - - if (hasRecordingAtPath) - { - return true; - } - return false; } private IRecorder GetRecorder(MediaSourceInfo mediaSource) @@ -1756,17 +1715,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private void Process_Exited(object sender, EventArgs e) { - var process = (IProcess)sender; - try + using (var process = (IProcess)sender) { _logger.LogInformation("Recording post-processing script completed with exit code {ExitCode}", process.ExitCode); - } - catch - { + process.Dispose(); } - - process.Dispose(); } private async Task SaveRecordingImage(string recordingPath, LiveTvProgram program, ItemImageInfo image) @@ -1776,44 +1730,16 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV image = await _libraryManager.ConvertImageToLocal(program, image, 0).ConfigureAwait(false); } - string imageSaveFilenameWithoutExtension = null; - - switch (image.Type) + string imageSaveFilenameWithoutExtension = image.Type switch { - case ImageType.Primary: - - if (program.IsSeries) - { - imageSaveFilenameWithoutExtension = Path.GetFileNameWithoutExtension(recordingPath) + "-thumb"; - } - else - { - imageSaveFilenameWithoutExtension = "poster"; - } - - break; - case ImageType.Logo: - imageSaveFilenameWithoutExtension = "logo"; - break; - case ImageType.Thumb: - if (program.IsSeries) - { - imageSaveFilenameWithoutExtension = Path.GetFileNameWithoutExtension(recordingPath) + "-thumb"; - } - else - { - imageSaveFilenameWithoutExtension = "landscape"; - } - - break; - case ImageType.Backdrop: - imageSaveFilenameWithoutExtension = "fanart"; - break; - default: - break; - } + ImageType.Primary => program.IsSeries ? Path.GetFileNameWithoutExtension(recordingPath) + "-thumb" : "poster", + ImageType.Logo => "logo", + ImageType.Thumb => program.IsSeries ? Path.GetFileNameWithoutExtension(recordingPath) + "-thumb" : "landscape", + ImageType.Backdrop => "fanart", + _ => null + }; - if (string.IsNullOrWhiteSpace(imageSaveFilenameWithoutExtension)) + if (imageSaveFilenameWithoutExtension == null) { return; } @@ -1897,7 +1823,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Limit = 1, ExternalId = timer.ProgramId, DtoOptions = new DtoOptions(true) - }).FirstOrDefault() as LiveTvProgram; // dummy this up @@ -1921,11 +1846,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { program.AddGenre("Sports"); } + if (timer.IsKids) { program.AddGenre("Kids"); program.AddGenre("Children"); } + if (timer.IsNews) { program.AddGenre("News"); @@ -1980,14 +1907,17 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { writer.WriteElementString("id", id); } + if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id)) { writer.WriteElementString("imdb_id", id); } + if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out id)) { writer.WriteElementString("tmdbid", id); } + if (timer.SeriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out id)) { writer.WriteElementString("zap2itid", id); @@ -2014,7 +1944,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss"; private void SaveVideoNfo(TimerInfo timer, string recordingPath, BaseItem item, bool lockData) { var nfoPath = Path.ChangeExtension(recordingPath, ".nfo"); @@ -2056,7 +1985,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { var formatString = options.ReleaseDateFormat; - writer.WriteElementString("aired", premiereDate.Value.ToLocalTime().ToString(formatString)); + writer.WriteElementString( + "aired", + premiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)); } if (item.IndexNumber.HasValue) @@ -2087,12 +2018,18 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { var formatString = options.ReleaseDateFormat; - writer.WriteElementString("premiered", item.PremiereDate.Value.ToLocalTime().ToString(formatString)); - writer.WriteElementString("releasedate", item.PremiereDate.Value.ToLocalTime().ToString(formatString)); + writer.WriteElementString( + "premiered", + item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)); + writer.WriteElementString( + "releasedate", + item.PremiereDate.Value.ToLocalTime().ToString(formatString, CultureInfo.InvariantCulture)); } } - writer.WriteElementString("dateadded", DateTime.UtcNow.ToLocalTime().ToString(DateAddedFormat)); + writer.WriteElementString( + "dateadded", + DateTime.UtcNow.ToLocalTime().ToString(DateAddedFormat, CultureInfo.InvariantCulture)); if (item.ProductionYear.HasValue) { @@ -2106,7 +2043,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var overview = (item.Overview ?? string.Empty) .StripHtml() - .Replace(""", "'"); + .Replace(""", "'", StringComparison.Ordinal); writer.WriteElementString("plot", overview); @@ -2214,17 +2151,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } private static bool IsPersonType(PersonInfo person, string type) - { - return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); - } - - private void AddGenre(List genres, string genre) - { - if (!genres.Contains(genre, StringComparer.OrdinalIgnoreCase)) - { - genres.Add(genre); - } - } + => string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) + || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); private LiveTvProgram GetProgramInfoFromCache(string programId) { @@ -2283,25 +2211,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return false; } - if (!seriesTimer.RecordAnyTime) + if (!seriesTimer.RecordAnyTime + && Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(10).Ticks) { - if (Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(10).Ticks) - { - return true; - } + return true; } - //if (!seriesTimer.Days.Contains(timer.StartDate.ToLocalTime().DayOfWeek)) - //{ - // return true; - //} - if (seriesTimer.RecordNewOnly && timer.IsRepeat) { return true; } - if (!seriesTimer.RecordAnyChannel && !string.Equals(timer.ChannelId, seriesTimer.ChannelId, StringComparison.OrdinalIgnoreCase)) + if (!seriesTimer.RecordAnyChannel + && !string.Equals(timer.ChannelId, seriesTimer.ChannelId, StringComparison.OrdinalIgnoreCase)) { return true; } @@ -2346,7 +2268,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { var allTimers = GetTimersForSeries(seriesTimer).ToList(); - var enabledTimersForSeries = new List(); foreach (var timer in allTimers) { @@ -2369,10 +2290,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { enabledTimersForSeries.Add(timer); } + _timerProvider.Add(timer); TimerCreated?.Invoke(this, new GenericEventArgs(timer)); } + // Only update if not currently active - test both new timer and existing in case Id's are different // Id's could be different if the timer was created manually prior to series timer creation else if (!_activeRecordings.TryGetValue(timer.Id, out _) && !_activeRecordings.TryGetValue(existingTimer.Id, out _)) @@ -2508,13 +2431,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { if (!tempChannelCache.TryGetValue(parent.ChannelId, out LiveTvChannel channel)) { - channel = _libraryManager.GetItemList(new InternalItemsQuery - { - IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name }, - ItemIds = new[] { parent.ChannelId }, - DtoOptions = new DtoOptions() - - }).Cast().FirstOrDefault(); + channel = _libraryManager.GetItemList( + new InternalItemsQuery + { + IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name }, + ItemIds = new[] { parent.ChannelId }, + DtoOptions = new DtoOptions() + }).FirstOrDefault() as LiveTvChannel; if (channel != null && !string.IsNullOrWhiteSpace(channel.ExternalId)) { @@ -2567,13 +2490,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { if (!tempChannelCache.TryGetValue(programInfo.ChannelId, out LiveTvChannel channel)) { - channel = _libraryManager.GetItemList(new InternalItemsQuery - { - IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name }, - ItemIds = new[] { programInfo.ChannelId }, - DtoOptions = new DtoOptions() - - }).Cast().FirstOrDefault(); + channel = _libraryManager.GetItemList( + new InternalItemsQuery + { + IncludeItemTypes = new string[] { typeof(LiveTvChannel).Name }, + ItemIds = new[] { programInfo.ChannelId }, + DtoOptions = new DtoOptions() + }).FirstOrDefault() as LiveTvChannel; if (channel != null && !string.IsNullOrWhiteSpace(channel.ExternalId)) { @@ -2618,10 +2541,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV foreach (var providerId in timerInfo.ProviderIds) { - var srch = "Series"; - if (providerId.Key.StartsWith(srch, StringComparison.OrdinalIgnoreCase)) + const string Search = "Series"; + if (providerId.Key.StartsWith(Search, StringComparison.OrdinalIgnoreCase)) { - seriesProviderIds[providerId.Key.Substring(srch.Length)] = providerId.Value; + seriesProviderIds[providerId.Key.Substring(Search.Length)] = providerId.Value; } } @@ -2632,12 +2555,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { if ((program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue) || !string.IsNullOrWhiteSpace(program.EpisodeTitle)) { - var seriesIds = _libraryManager.GetItemIds(new InternalItemsQuery - { - IncludeItemTypes = new[] { typeof(Series).Name }, - Name = program.Name - - }).ToArray(); + var seriesIds = _libraryManager.GetItemIds( + new InternalItemsQuery + { + IncludeItemTypes = new[] { typeof(Series).Name }, + Name = program.Name + }).ToArray(); if (seriesIds.Length == 0) { @@ -2666,59 +2589,70 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return false; } - private bool _disposed; + /// public void Dispose() { - _disposed = true; + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + _recordingDeleteSemaphore.Dispose(); + } + foreach (var pair in _activeRecordings.ToList()) { pair.Value.CancellationTokenSource.Cancel(); } + + _disposed = true; } - public List GetRecordingFolders() + public IEnumerable GetRecordingFolders() { - var list = new List(); - var defaultFolder = RecordingPath; var defaultName = "Recordings"; if (Directory.Exists(defaultFolder)) { - list.Add(new VirtualFolderInfo + yield return new VirtualFolderInfo { Locations = new string[] { defaultFolder }, Name = defaultName - }); + }; } var customPath = GetConfiguration().MovieRecordingPath; - if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath)) + if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath)) { - list.Add(new VirtualFolderInfo + yield return new VirtualFolderInfo { Locations = new string[] { customPath }, Name = "Recorded Movies", CollectionType = CollectionType.Movies - }); + }; } customPath = GetConfiguration().SeriesRecordingPath; - if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath)) + if (!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase) && Directory.Exists(customPath)) { - list.Add(new VirtualFolderInfo + yield return new VirtualFolderInfo { Locations = new string[] { customPath }, Name = "Recorded Shows", CollectionType = CollectionType.TvShows - }); + }; } - - return list; } - private const int TunerDiscoveryDurationMs = 3000; - public async Task> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken) { var list = new List(); @@ -2737,6 +2671,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparer.OrdinalIgnoreCase)) .ToList(); } + list.AddRange(discoveredDevices); } @@ -2773,11 +2708,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - private async Task> DiscoverDevices(ITunerHost host, int discoveryDuationMs, CancellationToken cancellationToken) + private async Task> DiscoverDevices(ITunerHost host, int discoveryDurationMs, CancellationToken cancellationToken) { try { - var discoveredDevices = await host.DiscoverDevices(discoveryDuationMs, cancellationToken).ConfigureAwait(false); + var discoveredDevices = await host.DiscoverDevices(discoveryDurationMs, cancellationToken).ConfigureAwait(false); foreach (var device in discoveredDevices) { @@ -2794,11 +2729,4 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } } - public static class ConfigurationExtension - { - public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager manager) - { - return manager.GetConfiguration("xbmcmetadata"); - } - } } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs new file mode 100644 index 000000000..498aa3c26 --- /dev/null +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EpgChannelData.cs @@ -0,0 +1,68 @@ +#pragma warning disable SA1600 +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using MediaBrowser.Controller.LiveTv; + +namespace Emby.Server.Implementations.LiveTv.EmbyTV +{ + + internal class EpgChannelData + { + public EpgChannelData(IEnumerable channels) + { + ChannelsById = new Dictionary(StringComparer.OrdinalIgnoreCase); + ChannelsByNumber = new Dictionary(StringComparer.OrdinalIgnoreCase); + ChannelsByName = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var channel in channels) + { + ChannelsById[channel.Id] = channel; + + if (!string.IsNullOrEmpty(channel.Number)) + { + ChannelsByNumber[channel.Number] = channel; + } + + var normalizedName = NormalizeName(channel.Name ?? string.Empty); + if (!string.IsNullOrWhiteSpace(normalizedName)) + { + ChannelsByName[normalizedName] = channel; + } + } + } + + private Dictionary ChannelsById { get; set; } + + private Dictionary ChannelsByNumber { get; set; } + + private Dictionary ChannelsByName { get; set; } + + public ChannelInfo GetChannelById(string id) + { + ChannelsById.TryGetValue(id, out var result); + + return result; + } + + public ChannelInfo GetChannelByNumber(string number) + { + ChannelsByNumber.TryGetValue(number, out var result); + + return result; + } + + public ChannelInfo GetChannelByName(string name) + { + ChannelsByName.TryGetValue(name, out var result); + + return result; + } + + public static string NormalizeName(string value) + { + return value.Replace(" ", string.Empty, StringComparison.Ordinal).Replace("-", string.Empty, StringComparison.Ordinal); + } + } +} diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs new file mode 100644 index 000000000..83f5e8413 --- /dev/null +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/NfoConfigurationExtensions.cs @@ -0,0 +1,19 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; + +namespace Emby.Server.Implementations.LiveTv.EmbyTV +{ + /// + /// Class containing extension methods for working with the nfo configuration. + /// + public static class NfoConfigurationExtensions + { + /// + /// Gets the nfo configuration. + /// + /// The configuration manager. + /// The nfo configuration. + public static XbmcMetadataOptions GetNfoConfiguration(this IConfigurationManager configurationManager) + => configurationManager.GetConfiguration("xbmcmetadata"); + } +} diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index d4bd598e3..d1f405679 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1,3 +1,6 @@ +#pragma warning disable SA1600 +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -35,7 +38,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv { /// - /// Class LiveTvManager + /// Class LiveTvManager. /// public class LiveTvManager : ILiveTvManager, IDisposable { diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 4bd729aac..d6f5bb0c0 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -768,7 +768,7 @@ namespace MediaBrowser.Api.Playback if (mediaSource == null) { - var mediaSources = (await MediaSourceManager.GetPlayackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList(); + var mediaSources = (await MediaSourceManager.GetPlaybackMediaSources(LibraryManager.GetItemById(request.Id), null, false, false, cancellationToken).ConfigureAwait(false)).ToList(); mediaSource = string.IsNullOrEmpty(request.MediaSourceId) ? mediaSources[0] diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index da8f99a3d..ee440f5db 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -270,7 +270,7 @@ namespace MediaBrowser.Api.Playback try { // TODO handle supportedLiveMediaTypes ? - mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false); + mediaSources = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index fbae4edb0..1bf981d79 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -39,9 +39,9 @@ namespace MediaBrowser.Controller.Library List GetMediaStreams(MediaStreamQuery query); /// - /// Gets the playack media sources. + /// Gets the playback media sources. /// - Task> GetPlayackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken); + Task> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken); /// /// Gets the static media sources. diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index d5fa76c3a..67c7fa343 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -126,7 +126,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw new ArgumentNullException(nameof(mediaSourceId)); } - var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false); + var mediaSources = await _mediaSourceManager.GetPlaybackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false); var mediaSource = mediaSources .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 75b5573b6..d3d3001ed 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -31,7 +31,9 @@ - + + + -- cgit v1.2.3 From 35151553e3fc9ddbe352744af8d832b1337491c8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 6 Dec 2019 20:40:06 +0100 Subject: Add back all old emby tests --- Emby.Naming/Common/NamingOptions.cs | 41 +- Emby.Naming/Video/CleanStringParser.cs | 2 +- Emby.Naming/Video/ExtraResolver.cs | 2 +- Emby.Naming/Video/ExtraResult.cs | 5 +- Emby.Naming/Video/ExtraRule.cs | 5 +- Emby.Naming/Video/StubResolver.cs | 4 +- Emby.Naming/Video/VideoFileInfo.cs | 6 +- Emby.Naming/Video/VideoListResolver.cs | 9 +- Emby.Naming/Video/VideoResolver.cs | 2 +- .../Library/LibraryManager.cs | 31 +- MediaBrowser.Model/Entities/MediaType.cs | 5 +- jellyfin.ruleset | 2 + .../Jellyfin.Naming.Tests/EpisodePathParserTest.cs | 55 --- .../Music/MultiDiscAlbumTests.cs | 57 +++ .../Subtitles/SubtitleParserTests.cs | 40 ++ .../TV/AbsoluteEpisodeNumberTests.cs | 61 +++ .../Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs | 69 +++ .../Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs | 421 ++++++++++++++++++ .../TV/EpisodeNumberWithoutSeasonTests.cs | 127 ++++++ .../TV/EpisodePathParserTest.cs | 55 +++ .../TV/EpisodeWithoutSeasonTests.cs | 56 +++ .../Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs | 105 +++++ .../Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs | 112 +++++ .../Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs | 305 +++++++++++++ .../Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs | 95 ++++ tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs | 15 + .../Video/CleanDateTimeTests.cs | 143 ++++++ .../Video/CleanStringTests.cs | 133 ++++++ tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs | 77 ++++ tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs | 78 ++++ .../Video/MultiVersionTests.cs | 438 +++++++++++++++++++ tests/Jellyfin.Naming.Tests/Video/StackTests.cs | 478 +++++++++++++++++++++ tests/Jellyfin.Naming.Tests/Video/StubTests.cs | 55 +++ .../Video/VideoListResolverTests.cs | 457 ++++++++++++++++++++ .../Video/VideoResolverTests.cs | 275 ++++++++++++ 35 files changed, 3705 insertions(+), 116 deletions(-) delete mode 100644 tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs create mode 100644 tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/StackTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/StubTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs create mode 100644 tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index d37be0e63..f8601c59f 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Text.RegularExpressions; using Emby.Naming.Video; +using MediaBrowser.Model.Entities; namespace Emby.Naming.Common { @@ -173,7 +174,7 @@ namespace Emby.Naming.Common CleanDateTimes = new[] { - @"(.+[^ _\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9][0-9]|20[0-1][0-9])([ _\,\.\(\)\[\]\-][^0-9]|$)" + @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19[0-9][0-9]|20[0-1][0-9])([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9][0-9]|20[0-1][0-9])*" }; CleanStrings = new[] @@ -336,7 +337,7 @@ namespace Emby.Naming.Common // *** End Kodi Standard Naming -                // [bar] Foo - 1 [baz] +                // [bar] Foo - 1 [baz] new EpisodeExpression(@".*?(\[.*?\])+.*?(?(\w+\s*?)+?)[-\s_]+(?\d+).*$") { IsNamed = true @@ -420,126 +421,126 @@ namespace Emby.Naming.Common { new ExtraRule { - ExtraType = "trailer", + ExtraType = ExtraType.Trailer, RuleType = ExtraRuleType.Filename, Token = "trailer", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "trailer", + ExtraType = ExtraType.Trailer, RuleType = ExtraRuleType.Suffix, Token = "-trailer", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "trailer", + ExtraType = ExtraType.Trailer, RuleType = ExtraRuleType.Suffix, Token = ".trailer", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "trailer", + ExtraType = ExtraType.Trailer, RuleType = ExtraRuleType.Suffix, Token = "_trailer", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "trailer", + ExtraType = ExtraType.Trailer, RuleType = ExtraRuleType.Suffix, Token = " trailer", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "sample", + ExtraType = ExtraType.Sample, RuleType = ExtraRuleType.Filename, Token = "sample", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "sample", + ExtraType = ExtraType.Sample, RuleType = ExtraRuleType.Suffix, Token = "-sample", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "sample", + ExtraType = ExtraType.Sample, RuleType = ExtraRuleType.Suffix, Token = ".sample", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "sample", + ExtraType = ExtraType.Sample, RuleType = ExtraRuleType.Suffix, Token = "_sample", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "sample", + ExtraType = ExtraType.Sample, RuleType = ExtraRuleType.Suffix, Token = " sample", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "themesong", + ExtraType = ExtraType.ThemeSong, RuleType = ExtraRuleType.Filename, Token = "theme", MediaType = MediaType.Audio }, new ExtraRule { - ExtraType = "scene", + ExtraType = ExtraType.Scene, RuleType = ExtraRuleType.Suffix, Token = "-scene", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "clip", + ExtraType = ExtraType.Clip, RuleType = ExtraRuleType.Suffix, Token = "-clip", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "interview", + ExtraType = ExtraType.Interview, RuleType = ExtraRuleType.Suffix, Token = "-interview", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "behindthescenes", + ExtraType = ExtraType.BehindTheScenes, RuleType = ExtraRuleType.Suffix, Token = "-behindthescenes", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "deletedscene", + ExtraType = ExtraType.DeletedScene, RuleType = ExtraRuleType.Suffix, Token = "-deleted", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "featurette", + ExtraType = ExtraType.Clip, RuleType = ExtraRuleType.Suffix, Token = "-featurette", MediaType = MediaType.Video }, new ExtraRule { - ExtraType = "short", + ExtraType = ExtraType.Clip, RuleType = ExtraRuleType.Suffix, Token = "-short", MediaType = MediaType.Video diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs index 02b90310d..e2da121db 100644 --- a/Emby.Naming/Video/CleanStringParser.cs +++ b/Emby.Naming/Video/CleanStringParser.cs @@ -4,7 +4,7 @@ using System.Text.RegularExpressions; namespace Emby.Naming.Video { /// - /// http://kodi.wiki/view/Advancedsettings.xml#video + /// . /// public class CleanStringParser { diff --git a/Emby.Naming/Video/ExtraResolver.cs b/Emby.Naming/Video/ExtraResolver.cs index 9f70494d0..f83da44a4 100644 --- a/Emby.Naming/Video/ExtraResolver.cs +++ b/Emby.Naming/Video/ExtraResolver.cs @@ -20,7 +20,7 @@ namespace Emby.Naming.Video { return _options.VideoExtraRules .Select(i => GetExtraInfo(path, i)) - .FirstOrDefault(i => !string.IsNullOrEmpty(i.ExtraType)) ?? new ExtraResult(); + .FirstOrDefault(i => i.ExtraType != null) ?? new ExtraResult(); } private ExtraResult GetExtraInfo(string path, ExtraRule rule) diff --git a/Emby.Naming/Video/ExtraResult.cs b/Emby.Naming/Video/ExtraResult.cs index ff6f20c47..60d6b80ec 100644 --- a/Emby.Naming/Video/ExtraResult.cs +++ b/Emby.Naming/Video/ExtraResult.cs @@ -1,3 +1,5 @@ +using MediaBrowser.Model.Entities; + namespace Emby.Naming.Video { public class ExtraResult @@ -6,7 +8,8 @@ namespace Emby.Naming.Video /// Gets or sets the type of the extra. /// /// The type of the extra. - public string ExtraType { get; set; } + public ExtraType? ExtraType { get; set; } + /// /// Gets or sets the rule. /// diff --git a/Emby.Naming/Video/ExtraRule.cs b/Emby.Naming/Video/ExtraRule.cs index b8eb8427e..62ec70163 100644 --- a/Emby.Naming/Video/ExtraRule.cs +++ b/Emby.Naming/Video/ExtraRule.cs @@ -9,16 +9,19 @@ namespace Emby.Naming.Video /// /// The token. public string Token { get; set; } + /// /// Gets or sets the type of the extra. /// /// The type of the extra. - public string ExtraType { get; set; } + public MediaBrowser.Model.Entities.ExtraType ExtraType { get; set; } + /// /// Gets or sets the type of the rule. /// /// The type of the rule. public ExtraRuleType RuleType { get; set; } + /// /// Gets or sets the type of the media. /// diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs index b78244cb3..97f3178e5 100644 --- a/Emby.Naming/Video/StubResolver.cs +++ b/Emby.Naming/Video/StubResolver.cs @@ -11,14 +11,14 @@ namespace Emby.Naming.Video { if (path == null) { - return default(StubResult); + return default; } var extension = Path.GetExtension(path); if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - return default(StubResult); + return default; } var result = new StubResult() diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs index 2f42f7784..8416821c2 100644 --- a/Emby.Naming/Video/VideoFileInfo.cs +++ b/Emby.Naming/Video/VideoFileInfo.cs @@ -1,3 +1,5 @@ +using MediaBrowser.Model.Entities; + namespace Emby.Naming.Video { /// @@ -30,10 +32,10 @@ namespace Emby.Naming.Video public int? Year { get; set; } /// - /// Gets or sets the type of the extra, e.g. trailer, theme song, behing the scenes, etc. + /// Gets or sets the type of the extra, e.g. trailer, theme song, behind the scenes, etc. /// /// The type of the extra. - public string ExtraType { get; set; } + public ExtraType? ExtraType { get; set; } /// /// Gets or sets the extra rule. diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 5fa0041e0..e43e920c4 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using Emby.Naming.Common; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; namespace Emby.Naming.Video @@ -29,7 +30,7 @@ namespace Emby.Naming.Video // Filter out all extras, otherwise they could cause stacks to not be resolved // See the unit test TestStackedWithTrailer var nonExtras = videoInfos - .Where(i => string.IsNullOrEmpty(i.ExtraType)) + .Where(i => i.ExtraType == null) .Select(i => new FileSystemMetadata { FullName = i.Path, @@ -76,7 +77,7 @@ namespace Emby.Naming.Video } var standaloneMedia = remainingFiles - .Where(i => string.IsNullOrEmpty(i.ExtraType)) + .Where(i => i.ExtraType == null) .ToList(); foreach (var media in standaloneMedia) @@ -145,7 +146,7 @@ namespace Emby.Naming.Video if (list.Count == 1) { var trailers = remainingFiles - .Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)) + .Where(i => i.ExtraType == ExtraType.Trailer) .ToList(); list[0].Extras.AddRange(trailers); @@ -226,7 +227,7 @@ namespace Emby.Naming.Video } return remainingFiles - .Where(i => !string.IsNullOrEmpty(i.ExtraType)) + .Where(i => i.ExtraType == null) .Where(i => baseNames.Any(b => i.FileNameWithoutExtension.StartsWith(b, StringComparison.OrdinalIgnoreCase))) .ToList(); } diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 91f443500..05ba0c2e5 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -91,7 +91,7 @@ namespace Emby.Naming.Video { var cleanDateTimeResult = CleanDateTime(name); - if (string.IsNullOrEmpty(extraResult.ExtraType)) + if (extraResult.ExtraType == null) { name = CleanString(cleanDateTimeResult.Name).Name; } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 528636ecd..8589b1b33 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2556,7 +2556,7 @@ namespace Emby.Server.Implementations.Library if (currentVideo != null) { - files.AddRange(currentVideo.Extras.Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path))); + files.AddRange(currentVideo.Extras.Where(i => i.ExtraType == ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path))); } var resolvers = new IItemResolver[] @@ -2606,7 +2606,7 @@ namespace Emby.Server.Implementations.Library if (currentVideo != null) { - files.AddRange(currentVideo.Extras.Where(i => !string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path))); + files.AddRange(currentVideo.Extras.Where(i => i.ExtraType != ExtraType.Trailer).Select(i => _fileSystem.GetFileInfo(i.Path))); } return ResolvePaths(files, directoryService, null, new LibraryOptions(), null) @@ -2710,7 +2710,7 @@ namespace Emby.Server.Implementations.Library if (!string.Equals(newPath, path, StringComparison.Ordinal)) { - if (to.IndexOf('/') != -1) + if (to.IndexOf('/', StringComparison.Ordinal) != -1) { newPath = newPath.Replace('\\', '/'); } @@ -2731,30 +2731,7 @@ namespace Emby.Server.Implementations.Library var result = resolver.GetExtraInfo(item.Path); - if (string.Equals(result.ExtraType, "deletedscene", StringComparison.OrdinalIgnoreCase)) - { - item.ExtraType = ExtraType.DeletedScene; - } - else if (string.Equals(result.ExtraType, "behindthescenes", StringComparison.OrdinalIgnoreCase)) - { - item.ExtraType = ExtraType.BehindTheScenes; - } - else if (string.Equals(result.ExtraType, "interview", StringComparison.OrdinalIgnoreCase)) - { - item.ExtraType = ExtraType.Interview; - } - else if (string.Equals(result.ExtraType, "scene", StringComparison.OrdinalIgnoreCase)) - { - item.ExtraType = ExtraType.Scene; - } - else if (string.Equals(result.ExtraType, "sample", StringComparison.OrdinalIgnoreCase)) - { - item.ExtraType = ExtraType.Sample; - } - else - { - item.ExtraType = ExtraType.Clip; - } + item.ExtraType = result.ExtraType; } public List GetPeople(InternalPeopleQuery query) diff --git a/MediaBrowser.Model/Entities/MediaType.cs b/MediaBrowser.Model/Entities/MediaType.cs index c56c8f8f2..d8b02c9ea 100644 --- a/MediaBrowser.Model/Entities/MediaType.cs +++ b/MediaBrowser.Model/Entities/MediaType.cs @@ -3,20 +3,23 @@ namespace MediaBrowser.Model.Entities /// /// Class MediaType /// - public class MediaType + public static class MediaType { /// /// The video /// public const string Video = "Video"; + /// /// The audio /// public const string Audio = "Audio"; + /// /// The photo /// public const string Photo = "Photo"; + /// /// The book /// diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 75b5573b6..1d424a27d 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -24,6 +24,8 @@ + + diff --git a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs deleted file mode 100644 index dd1e04215..000000000 --- a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Emby.Naming.Common; -using Emby.Naming.TV; -using Xunit; - -namespace Jellyfin.Naming.Tests -{ - public class EpisodePathParserTest - { - [Theory] - [InlineData("/media/Foo/Foo-S01E01", "Foo", 1, 1)] - [InlineData("/media/Foo - S04E011", "Foo", 4, 11)] - [InlineData("/media/Foo/Foo s01x01", "Foo", 1, 1)] - [InlineData("/media/Foo (2019)/Season 4/Foo (2019).S04E03", "Foo (2019)", 4, 3)] - public void ParseEpisodesCorrectly(string path, string name, int season, int episode) - { - NamingOptions o = new NamingOptions(); - EpisodePathParser p = new EpisodePathParser(o); - var res = p.Parse(path, false); - - Assert.True(res.Success); - Assert.Equal(name, res.SeriesName); - Assert.Equal(season, res.SeasonNumber); - Assert.Equal(episode, res.EpisodeNumber); - - // testing other paths delimeter - var res2 = p.Parse(path.Replace('/', '\\'), false); - Assert.True(res2.Success); - Assert.Equal(name, res2.SeriesName); - Assert.Equal(season, res2.SeasonNumber); - Assert.Equal(episode, res2.EpisodeNumber); - } - - [Theory] - [InlineData("/media/Foo/Foo 889", "Foo", 889)] - [InlineData("/media/Foo/[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] - public void ParseEpisodeWithoutSeason(string path, string name, int episode) - { - NamingOptions o = new NamingOptions(); - EpisodePathParser p = new EpisodePathParser(o); - var res = p.Parse(path, true, fillExtendedInfo: true); - - Assert.True(res.Success); - Assert.Equal(name, res.SeriesName); - Assert.Null(res.SeasonNumber); - Assert.Equal(episode, res.EpisodeNumber); - - // testing other paths delimeter - var res2 = p.Parse(path.Replace('/', '\\'), false, fillExtendedInfo: false); - Assert.True(res2.Success); - Assert.Equal(name, res2.SeriesName); - Assert.Null(res2.SeasonNumber); - Assert.Equal(episode, res2.EpisodeNumber); - } - } -} diff --git a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs new file mode 100644 index 000000000..eb69d915c --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs @@ -0,0 +1,57 @@ +using Emby.Naming.Audio; +using Emby.Naming.Common; +using Xunit; + +namespace Jellyfin.Naming.Tests.Music +{ + public class MultiDiscAlbumTests + { + [Fact] + public void TestMultiDiscAlbums() + { + Assert.False(IsMultiDiscAlbumFolder(@"blah blah")); + Assert.False(IsMultiDiscAlbumFolder(@"d:/music\weezer/03 Pinkerton")); + Assert.False(IsMultiDiscAlbumFolder(@"d:/music/michael jackson/Bad (2012 Remaster)")); + + Assert.True(IsMultiDiscAlbumFolder(@"cd1")); + Assert.True(IsMultiDiscAlbumFolder(@"disc1")); + Assert.True(IsMultiDiscAlbumFolder(@"disk1")); + + // Add a space + Assert.True(IsMultiDiscAlbumFolder(@"cd 1")); + Assert.True(IsMultiDiscAlbumFolder(@"disc 1")); + Assert.True(IsMultiDiscAlbumFolder(@"disk 1")); + + Assert.True(IsMultiDiscAlbumFolder(@"cd - 1")); + Assert.True(IsMultiDiscAlbumFolder(@"disc- 1")); + Assert.True(IsMultiDiscAlbumFolder(@"disk - 1")); + + Assert.True(IsMultiDiscAlbumFolder(@"Disc 01 (Hugo Wolf · 24 Lieder)")); + Assert.True(IsMultiDiscAlbumFolder(@"Disc 04 (Encores and Folk Songs)")); + Assert.True(IsMultiDiscAlbumFolder(@"Disc04 (Encores and Folk Songs)")); + Assert.True(IsMultiDiscAlbumFolder(@"Disc 04(Encores and Folk Songs)")); + Assert.True(IsMultiDiscAlbumFolder(@"Disc04(Encores and Folk Songs)")); + + Assert.True(IsMultiDiscAlbumFolder(@"D:/Video/MBTestLibrary/VideoTest/music/.38 special/anth/Disc 2")); + } + + [Fact] + public void TestMultiDiscAlbums1() + { + Assert.False(IsMultiDiscAlbumFolder(@"[1985] Oppurtunities (Let's make lots of money) (1985)")); + } + + [Fact] + public void TestMultiDiscAlbums2() + { + Assert.False(IsMultiDiscAlbumFolder(@"Blah 04(Encores and Folk Songs)")); + } + + private bool IsMultiDiscAlbumFolder(string path) + { + var parser = new AlbumParser(new NamingOptions()); + + return parser.ParseMultiPart(path).IsMultiPart; + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs new file mode 100644 index 000000000..e8f14cdc4 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs @@ -0,0 +1,40 @@ +using Emby.Naming.Common; +using Emby.Naming.Subtitles; +using Xunit; + +namespace Jellyfin.Naming.Tests.Subtitles +{ + public class SubtitleParserTests + { + private SubtitleParser GetParser() + { + var options = new NamingOptions(); + + return new SubtitleParser(options); + } + + [Fact] + public void TestSubtitles() + { + Test("The Skin I Live In (2011).srt", null, false, false); + Test("The Skin I Live In (2011).eng.srt", "eng", false, false); + Test("The Skin I Live In (2011).eng.default.srt", "eng", true, false); + Test("The Skin I Live In (2011).eng.forced.srt", "eng", false, true); + Test("The Skin I Live In (2011).eng.foreign.srt", "eng", false, true); + Test("The Skin I Live In (2011).eng.default.foreign.srt", "eng", true, true); + + Test("The Skin I Live In (2011).default.foreign.eng.srt", "eng", true, true); + } + + private void Test(string input, string language, bool isDefault, bool isForced) + { + var parser = GetParser(); + + var result = parser.ParseFile(input); + + Assert.Equal(language, result.Language, true); + Assert.Equal(isDefault, result.IsDefault); + Assert.Equal(isForced, result.IsForced); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs new file mode 100644 index 000000000..9abbcc7bf --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/AbsoluteEpisodeNumberTests.cs @@ -0,0 +1,61 @@ +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class AbsoluteEpisodeNumberTests + { + [Fact] + public void TestAbsoluteEpisodeNumber1() + { + Assert.Equal(12, GetEpisodeNumberFromFile(@"The Simpsons/12.avi")); + } + + [Fact] + public void TestAbsoluteEpisodeNumber2() + { + Assert.Equal(12, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 12.avi")); + } + + [Fact] + public void TestAbsoluteEpisodeNumber3() + { + Assert.Equal(82, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 82.avi")); + } + + [Fact] + public void TestAbsoluteEpisodeNumber4() + { + Assert.Equal(112, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 112.avi")); + } + + [Fact] + public void TestAbsoluteEpisodeNumber5() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/Foo_ep_02.avi")); + } + + [Fact] + public void TestAbsoluteEpisodeNumber6() + { + Assert.Equal(889, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 889.avi")); + } + + [Fact] + public void TestAbsoluteEpisodeNumber7() + { + Assert.Equal(101, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 101.avi")); + } + + private int? GetEpisodeNumberFromFile(string path) + { + var options = new NamingOptions(); + + var result = new EpisodeResolver(options) + .Resolve(path, false, null, null, true); + + return result.EpisodeNumber; + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs new file mode 100644 index 000000000..29daf8cc3 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/DailyEpisodeTests.cs @@ -0,0 +1,69 @@ +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class DailyEpisodeTests + { + [Fact] + public void TestDailyEpisode1() + { + Test(@"/server/anything_1996.11.14.mp4", "anything", 1996, 11, 14); + } + + [Fact] + public void TestDailyEpisode2() + { + Test(@"/server/anything_1996-11-14.mp4", "anything", 1996, 11, 14); + } + + // FIXME + // [Fact] + public void TestDailyEpisode3() + { + Test(@"/server/anything_14.11.1996.mp4", "anything", 1996, 11, 14); + } + + // FIXME + // [Fact] + public void TestDailyEpisode4() + { + Test(@"/server/A Daily Show - (2015-01-15) - Episode Name - [720p].mkv", "A Daily Show", 2015, 01, 15); + } + + [Fact] + public void TestDailyEpisode5() + { + Test(@"/server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv", "james.corden", 2017, 04, 20); + } + + [Fact] + public void TestDailyEpisode6() + { + Test(@"/server/ABC News 2018_03_24_19_00_00.mkv", "ABC News", 2018, 03, 24); + } + + // FIXME + // [Fact] + public void TestDailyEpisode7() + { + Test(@"/server/Last Man Standing_KTLADT_2018_05_25_01_28_00.wtv", "Last Man Standing", 2018, 05, 25); + } + + private void Test(string path, string seriesName, int? year, int? month, int? day) + { + var options = new NamingOptions(); + + var result = new EpisodeResolver(options) + .Resolve(path, false); + + Assert.Null(result.SeasonNumber); + Assert.Null(result.EpisodeNumber); + Assert.Equal(year, result.Year); + Assert.Equal(month, result.Month); + Assert.Equal(day, result.Day); + Assert.Equal(seriesName, result.SeriesName, true); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs new file mode 100644 index 000000000..03fae3159 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -0,0 +1,421 @@ +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class EpisodeNumberTests + { + [Fact] + public void TestEpisodeNumber1() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/S02E03 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber40() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber41() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/01x02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber42() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/S01x02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber43() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/S01E02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber44() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber45() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/S01xE02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber46() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname S01E02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber47() + { + Assert.Equal(36, GetEpisodeNumberFromFile(@"Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv")); + } + + [Fact] + public void TestEpisodeNumber50() + { + // This convention is not currently supported, just adding in case we want to look at it in the future + Assert.Equal(1, GetEpisodeNumberFromFile(@"2016/Season s2016e1.mp4")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumber51() + { + // This convention is not currently supported, just adding in case we want to look at it in the future + Assert.Equal(1, GetEpisodeNumberFromFile(@"2016/Season 2016x1.mp4")); + } + + [Fact] + public void TestEpisodeNumber52() + { + Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode - 16.avi")); + } + + [Fact] + public void TestEpisodeNumber53() + { + // This is not supported. Expected to fail, although it would be a good one to add support for. + Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode 16.avi")); + } + [Fact] + public void TestEpisodeNumber54() + { + // This is not supported. Expected to fail, although it would be a good one to add support for. + Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode 16 - Some Title.avi")); + } + [Fact] + public void TestEpisodeNumber55() + { + // This is not supported. Expected to fail, although it would be a good one to add support for. + Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Season 3 Episode 16.avi")); + } + [Fact] + public void TestEpisodeNumber56() + { + // This is not supported. Expected to fail, although it would be a good one to add support for. + Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Season 3 Episode 16 - Some Title.avi")); + } + + [Fact] + public void TestEpisodeNumber57() + { + Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/16 Some Title.avi")); + } + + [Fact] + public void TestEpisodeNumber58() + { + Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/16 - 12 Some Title.avi")); + } + + [Fact] + public void TestEpisodeNumber59() + { + Assert.Equal(7, GetEpisodeNumberFromFile(@"Season 2/7 - 12 Angry Men.avi")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumber60() + { + Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/16 12 Some Title.avi")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumber61() + { + Assert.Equal(7, GetEpisodeNumberFromFile(@"Season 2/7 12 Angry Men.avi")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumber62() + { + // This is not supported. Expected to fail, although it would be a good one to add support for. + Assert.Equal(3, GetEpisodeNumberFromFile(@"Season 4/Uchuu.Senkan.Yamato.2199.E03.avi")); + } + + [Fact] + public void TestEpisodeNumber63() + { + Assert.Equal(3, GetEpisodeNumberFromFile(@"Season 4/Uchuu.Senkan.Yamato.2199.S04E03.avi")); + } + + [Fact] + public void TestEpisodeNumber64() + { + Assert.Equal(368, GetEpisodeNumberFromFile(@"Running Man/Running Man S2017E368.mkv")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumber65() + { + // Not supported yet + Assert.Equal(7, GetEpisodeNumberFromFile(@"/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv/The.Legend.of.Condor.Heroes.2017.E07.V2.web-dl.1080p.h264.aac-hdctv.mkv")); + } + + [Fact] + public void TestEpisodeNumber30() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumber31() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname 01x02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber32() + { + Assert.Equal(9, GetEpisodeNumberFromFile(@"Season 25/The Simpsons.S25E09.Steal this episode.mp4")); + } + + [Fact] + public void TestEpisodeNumber33() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname S01x02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber34() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber35() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/seriesname S01xE02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber36() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber37() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber38() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/02x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber39() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber20() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2/02x03-04-15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber21() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/02x03-E15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber22() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber23() + { + Assert.Equal(23, GetEpisodeNumberFromFile(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4")); + } + + [Fact] + public void TestEpisodeNumber24() + { + Assert.Equal(23, GetEpisodeNumberFromFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4")); + } + + [Fact] + public void TestEpisodeNumber25() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/2009x02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber26() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/S2009x02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber27() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/S2009E02 blah.avi")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumber28() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname 2009x02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber29() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber11() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber12() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber13() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/S2009xE02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber14() + { + Assert.Equal(23, GetEpisodeNumberFromFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4")); + } + + [Fact] + public void TestEpisodeNumber15() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname S2009xE02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber16() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03-E15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber17() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname S2009E02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber18() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber19() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03 - x04 - x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber2() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2009/seriesname S2009x02 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber3() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber4() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber5() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/2009x03-04-15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber6() + { + Assert.Equal(03, GetEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4")); + } + + [Fact] + public void TestEpisodeNumber7() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/02 - blah-02 a.avi")); + } + + [Fact] + public void TestEpisodeNumber8() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 1/02 - blah.avi")); + } + + [Fact] + public void TestEpisodeNumber9() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2/02 - blah 14 blah.avi")); + } + + [Fact] + public void TestEpisodeNumber10() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2/02.avi")); + } + + [Fact] + public void TestEpisodeNumber48() + { + Assert.Equal(02, GetEpisodeNumberFromFile(@"Season 2/2. Infestation.avi")); + } + + [Fact] + public void TestEpisodeNumber49() + { + Assert.Equal(7, GetEpisodeNumberFromFile(@"The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH/The Wonder Years s04e07 Christmas Party NTSC PDTV.avi")); + } + + private int? GetEpisodeNumberFromFile(string path) + { + var options = new NamingOptions(); + + var result = new EpisodePathParser(options) + .Parse(path, false); + + return result.EpisodeNumber; + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs new file mode 100644 index 000000000..00aa9ee7c --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberWithoutSeasonTests.cs @@ -0,0 +1,127 @@ +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class EpisodeNumberWithoutSeasonTests + { + [Fact] + public void TestEpisodeNumberWithoutSeason1() + { + Assert.Equal(8, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons.S25E08.Steal this episode.mp4")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason2() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons - 02 - Ep Name.avi")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason3() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02.avi")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason4() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02 - Ep Name.avi")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason5() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02-Ep Name.avi")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason6() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/02.EpName.avi")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason7() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons - 02.avi")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason8() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons - 02 Ep Name.avi")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumberWithoutSeason9() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 5 - 02 - Ep Name.avi")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumberWithoutSeason10() + { + Assert.Equal(2, GetEpisodeNumberFromFile(@"The Simpsons/The Simpsons 5 - 02 Ep Name.avi")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumberWithoutSeason11() + { + Assert.Equal(7, GetEpisodeNumberFromFile(@"Seinfeld/Seinfeld 0807 The Checks.avi")); + Assert.Equal(8, GetSeasonNumberFromFile(@"Seinfeld/Seinfeld 0807 The Checks.avi")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason12() + { + Assert.Equal(7, GetEpisodeNumberFromFile(@"GJ Club (2013)/GJ Club - 07.mkv")); + } + + // FIXME + // [Fact] + public void TestEpisodeNumberWithoutSeason13() + { + // This is not supported anymore after removing the episode number 365+ hack from EpisodePathParser + Assert.Equal(13, GetEpisodeNumberFromFile(@"Case Closed (1996-2007)/Case Closed - 13.mkv")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason14() + { + Assert.Equal(3, GetSeasonNumberFromFile(@"Case Closed (1996-2007)/Case Closed - 317.mkv")); + Assert.Equal(17, GetEpisodeNumberFromFile(@"Case Closed (1996-2007)/Case Closed - 317.mkv")); + } + + [Fact] + public void TestEpisodeNumberWithoutSeason15() + { + Assert.Equal(2017, GetSeasonNumberFromFile(@"Running Man/Running Man S2017E368.mkv")); + } + + private int? GetEpisodeNumberFromFile(string path) + { + var options = new NamingOptions(); + + var result = new EpisodeResolver(options) + .Resolve(path, false); + + return result.EpisodeNumber; + } + + private int? GetSeasonNumberFromFile(string path) + { + var options = new NamingOptions(); + + var result = new EpisodeResolver(options) + .Resolve(path, false); + + return result.SeasonNumber; + } + + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs new file mode 100644 index 000000000..8e8adb35b --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs @@ -0,0 +1,55 @@ +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class EpisodePathParserTest + { + [Theory] + [InlineData("/media/Foo/Foo-S01E01", "Foo", 1, 1)] + [InlineData("/media/Foo - S04E011", "Foo", 4, 11)] + [InlineData("/media/Foo/Foo s01x01", "Foo", 1, 1)] + [InlineData("/media/Foo (2019)/Season 4/Foo (2019).S04E03", "Foo (2019)", 4, 3)] + public void ParseEpisodesCorrectly(string path, string name, int season, int episode) + { + NamingOptions o = new NamingOptions(); + EpisodePathParser p = new EpisodePathParser(o); + var res = p.Parse(path, false); + + Assert.True(res.Success); + Assert.Equal(name, res.SeriesName); + Assert.Equal(season, res.SeasonNumber); + Assert.Equal(episode, res.EpisodeNumber); + + // testing other paths delimeter + var res2 = p.Parse(path.Replace('/', '/'), false); + Assert.True(res2.Success); + Assert.Equal(name, res2.SeriesName); + Assert.Equal(season, res2.SeasonNumber); + Assert.Equal(episode, res2.EpisodeNumber); + } + + [Theory] + [InlineData("/media/Foo/Foo 889", "Foo", 889)] + [InlineData("/media/Foo/[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] + public void ParseEpisodeWithoutSeason(string path, string name, int episode) + { + NamingOptions o = new NamingOptions(); + EpisodePathParser p = new EpisodePathParser(o); + var res = p.Parse(path, true, fillExtendedInfo: true); + + Assert.True(res.Success); + Assert.Equal(name, res.SeriesName); + Assert.Null(res.SeasonNumber); + Assert.Equal(episode, res.EpisodeNumber); + + // testing other paths delimeter + var res2 = p.Parse(path.Replace('/', '/'), false, fillExtendedInfo: false); + Assert.True(res2.Success); + Assert.Equal(name, res2.SeriesName); + Assert.Null(res2.SeasonNumber); + Assert.Equal(episode, res2.EpisodeNumber); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs new file mode 100644 index 000000000..c2851ccdb --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs @@ -0,0 +1,56 @@ +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class EpisodeWithoutSeasonTests + { + // FIXME + // [Fact] + public void TestWithoutSeason1() + { + Test(@"/server/anything_ep02.mp4", "anything", null, 2); + } + + // FIXME + // [Fact] + public void TestWithoutSeason2() + { + Test(@"/server/anything_ep_02.mp4", "anything", null, 2); + } + + // FIXME + // [Fact] + public void TestWithoutSeason3() + { + Test(@"/server/anything_part.II.mp4", "anything", null, null); + } + + // FIXME + // [Fact] + public void TestWithoutSeason4() + { + Test(@"/server/anything_pt.II.mp4", "anything", null, null); + } + + // FIXME + // [Fact] + public void TestWithoutSeason5() + { + Test(@"/server/anything_pt_II.mp4", "anything", null, null); + } + + private void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber) + { + var options = new NamingOptions(); + + var result = new EpisodeResolver(options) + .Resolve(path, false); + + Assert.Equal(seasonNumber, result.SeasonNumber); + Assert.Equal(episodeNumber, result.EpisodeNumber); + Assert.Equal(seriesName, result.SeriesName, true); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs new file mode 100644 index 000000000..b15dd6b74 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/MultiEpisodeTests.cs @@ -0,0 +1,105 @@ +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class MultiEpisodeTests + { + [Fact] + public void TestGetEndingEpisodeNumberFromFile() + { + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/4x01 – 20 Hours in America (1).mkv")); + + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/01x02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/S01x02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/S01E02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/S01xE02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname 01x02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname S01x02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname S01E02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/seriesname S01xE02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/02x03 - 04 Ep Name.mp4")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/My show name 02x03 - 04 Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/02x03-04-15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/02x03-E15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/02x03x04x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4")); + Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4")); + Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 1/S01E23-E24-E26 - The Woman.mp4")); + + + // Four Digits seasons + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/2009x02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/S2009x02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/S2009E02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/S2009xE02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname 2009x02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname S2009x02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname S2009E02 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2009/seriesname S2009xE02 blah.avi")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03-04-15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-04-15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03-E15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03-E15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03 - x04 - x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4")); + Assert.Equal(15, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4")); + Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4")); + Assert.Equal(26, GetEndingEpisodeNumberFromFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4")); + + // Without season number + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/02 - blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/02 - blah 14 blah.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/02 - blah-02 a.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/02.avi")); + + Assert.Equal(3, GetEndingEpisodeNumberFromFile(@"Season 1/02-03 - blah.avi")); + Assert.Equal(4, GetEndingEpisodeNumberFromFile(@"Season 2/02-04 - blah 14 blah.avi")); + Assert.Equal(5, GetEndingEpisodeNumberFromFile(@"Season 1/02-05 - blah-02 a.avi")); + Assert.Equal(4, GetEndingEpisodeNumberFromFile(@"Season 2/02-04.avi")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 2/[HorribleSubs] Hunter X Hunter - 136 [720p].mkv")); + + // With format specification that must not be detected as ending episode number + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/series-s09e14-1080p.mkv")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/series-s09e14-720p.mkv")); + Assert.Null(GetEndingEpisodeNumberFromFile(@"Season 1/series-s09e14-720i.mkv")); + Assert.Equal(4, GetEndingEpisodeNumberFromFile(@"Season 1/MOONLIGHTING_s01e01-e04.mkv")); + } + + [Fact] + public void TestGetEndingEpisodeNumberFromFolder() + { + Assert.Equal(4, GetEndingEpisodeNumberFromFolder(@"Season 1/MOONLIGHTING_s01e01-e04")); + } + + private int? GetEndingEpisodeNumberFromFolder(string path) + { + var options = new NamingOptions(); + + var result = new EpisodePathParser(options) + .Parse(path, true); + + return result.EndingEpsiodeNumber; + } + + private int? GetEndingEpisodeNumberFromFile(string path) + { + var options = new NamingOptions(); + + var result = new EpisodePathParser(options) + .Parse(path, false); + + return result.EndingEpsiodeNumber; + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs new file mode 100644 index 000000000..ffa8d3483 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs @@ -0,0 +1,112 @@ +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class SeasonFolderTests + { + [Fact] + public void TestGetSeasonNumberFromPath1() + { + Assert.Equal(1, GetSeasonNumberFromPath(@"/Drive/Season 1")); + } + + [Fact] + public void TestGetSeasonNumberFromPath2() + { + Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Season 2")); + } + + [Fact] + public void TestGetSeasonNumberFromPath3() + { + Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Season 02")); + } + + [Fact] + public void TestGetSeasonNumberFromPath4() + { + Assert.Equal(1, GetSeasonNumberFromPath(@"/Drive/Season 1")); + } + + [Fact] + public void TestGetSeasonNumberFromPath5() + { + Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Seinfeld/S02")); + } + + [Fact] + public void TestGetSeasonNumberFromPath6() + { + Assert.Equal(2, GetSeasonNumberFromPath(@"/Drive/Seinfeld/2")); + } + + [Fact] + public void TestGetSeasonNumberFromPath7() + { + Assert.Equal(2009, GetSeasonNumberFromPath(@"/Drive/Season 2009")); + } + + [Fact] + public void TestGetSeasonNumberFromPath8() + { + Assert.Equal(1, GetSeasonNumberFromPath(@"/Drive/Season1")); + } + + [Fact] + public void TestGetSeasonNumberFromPath9() + { + Assert.Equal(4, GetSeasonNumberFromPath(@"The Wonder Years/The.Wonder.Years.S04.PDTV.x264-JCH")); + } + + [Fact] + public void TestGetSeasonNumberFromPath10() + { + Assert.Equal(7, GetSeasonNumberFromPath(@"/Drive/Season 7 (2016)")); + } + + [Fact] + public void TestGetSeasonNumberFromPath11() + { + Assert.Equal(7, GetSeasonNumberFromPath(@"/Drive/Staffel 7 (2016)")); + } + + [Fact] + public void TestGetSeasonNumberFromPath12() + { + Assert.Equal(7, GetSeasonNumberFromPath(@"/Drive/Stagione 7 (2016)")); + } + + [Fact] + public void TestGetSeasonNumberFromPath14() + { + Assert.Null(GetSeasonNumberFromPath(@"/Drive/Season (8)")); + } + + [Fact] + public void TestGetSeasonNumberFromPath13() + { + Assert.Equal(3, GetSeasonNumberFromPath(@"/Drive/3.Staffel")); + } + + [Fact] + public void TestGetSeasonNumberFromPath15() + { + Assert.Null(GetSeasonNumberFromPath(@"/Drive/s06e05")); + } + + [Fact] + public void TestGetSeasonNumberFromPath16() + { + Assert.Null(GetSeasonNumberFromPath(@"/Drive/The.Legend.of.Condor.Heroes.2017.V2.web-dl.1080p.h264.aac-hdctv")); + } + + private int? GetSeasonNumberFromPath(string path) + { + var result = new SeasonPathParser() + .Parse(path, true, true); + + return result.SeasonNumber; + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs new file mode 100644 index 000000000..ba3c5ecac --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/SeasonNumberTests.cs @@ -0,0 +1,305 @@ +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class SeasonNumberTests + { + private int? GetSeasonNumberFromEpisodeFile(string path) + { + var options = new NamingOptions(); + + var result = new EpisodeResolver(options) + .Resolve(path, false); + + return result.SeasonNumber; + } + + [Fact] + public void TestSeasonNumber1() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"/Show/Season 02/S02E03 blah.avi")); + } + + [Fact] + public void TestSeasonNumber2() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01x02 blah.avi")); + } + + [Fact] + public void TestSeasonNumber3() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01x02 blah.avi")); + } + + [Fact] + public void TestSeasonNumber4() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01xE02 blah.avi")); + } + + [Fact] + public void TestSeasonNumber5() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/01x02 blah.avi")); + } + + [Fact] + public void TestSeasonNumber6() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01E02 blah.avi")); + } + + [Fact] + public void TestSeasonNumber7() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01xE02 blah.avi")); + } + + // FIXME + // [Fact] + public void TestSeasonNumber8() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname 01x02 blah.avi")); + } + + [Fact] + public void TestSeasonNumber9() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01x02 blah.avi")); + } + + [Fact] + public void TestSeasonNumber10() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/seriesname S01E02 blah.avi")); + } + + [Fact] + public void TestSeasonNumber11() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/Elementary - 02x03 - 02x04 - 02x15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber12() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/02x03 - 02x04 - 02x15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber13() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/02x03-04-15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber14() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 2/Elementary - 02x03-04-15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber15() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/02x03-E15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber16() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/Elementary - 02x03-E15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber17() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/02x03 - x04 - x15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber18() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/Elementary - 02x03 - x04 - x15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber19() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/02x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber20() + { + Assert.Equal(2, GetSeasonNumberFromEpisodeFile(@"Season 02/Elementary - 02x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestSeasonNumber21() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/Elementary - S01E23-E24-E26 - The Woman.mp4")); + } + + [Fact] + public void TestSeasonNumber22() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Season 1/S01E23-E24-E26 - The Woman.mp4")); + } + + [Fact] + public void TestSeasonNumber23() + { + Assert.Equal(25, GetSeasonNumberFromEpisodeFile(@"Season 25/The Simpsons.S25E09.Steal this episode.mp4")); + } + + [Fact] + public void TestSeasonNumber24() + { + Assert.Equal(25, GetSeasonNumberFromEpisodeFile(@"The Simpsons/The Simpsons.S25E09.Steal this episode.mp4")); + } + + [Fact] + public void TestSeasonNumber25() + { + Assert.Equal(2016, GetSeasonNumberFromEpisodeFile(@"2016/Season s2016e1.mp4")); + } + + // FIXME + // [Fact] + public void TestSeasonNumber26() + { + // This convention is not currently supported, just adding in case we want to look at it in the future + Assert.Equal(2016, GetSeasonNumberFromEpisodeFile(@"2016/Season 2016x1.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber1() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x02 blah.avi")); + } + + [Fact] + public void TestFourDigitSeasonNumber2() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009x02 blah.avi")); + } + + [Fact] + public void TestFourDigitSeasonNumber3() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009E02 blah.avi")); + } + + [Fact] + public void TestFourDigitSeasonNumber4() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009xE02 blah.avi")); + } + + // FIXME + // [Fact] + public void TestFourDigitSeasonNumber5() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/seriesname 2009x02 blah.avi")); + } + + [Fact] + public void TestFourDigitSeasonNumber6() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/seriesname S2009x02 blah.avi")); + } + + [Fact] + public void TestFourDigitSeasonNumber7() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/seriesname S2009E02 blah.avi")); + } + + [Fact] + public void TestFourDigitSeasonNumber8() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber9() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03 - 2009x04 - 2009x15 - Ep Name.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber10() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03-04-15 - Ep Name.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber11() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber12() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber13() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber14() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber15() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber16() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03 - x04 - x15 - Ep Name.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber17() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/2009x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber18() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - 2009x03x04x15 - Ep Name.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber19() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/Elementary - S2009E23-E24-E26 - The Woman.mp4")); + } + + [Fact] + public void TestFourDigitSeasonNumber20() + { + Assert.Equal(2009, GetSeasonNumberFromEpisodeFile(@"Season 2009/S2009E23-E24-E26 - The Woman.mp4")); + } + + [Fact] + public void TestNoSeriesFolder() + { + Assert.Equal(1, GetSeasonNumberFromEpisodeFile(@"Series/1-12 - The Woman.mp4")); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs new file mode 100644 index 000000000..c9323c218 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/TV/SimpleEpisodeTests.cs @@ -0,0 +1,95 @@ +using Emby.Naming.Common; +using Emby.Naming.TV; +using Xunit; + +namespace Jellyfin.Naming.Tests.TV +{ + public class SimpleEpisodeTests + { + [Fact] + public void TestSimpleEpisodePath1() + { + Test(@"/server/anything_s01e02.mp4", "anything", 1, 2); + } + + [Fact] + public void TestSimpleEpisodePath2() + { + Test(@"/server/anything_s1e2.mp4", "anything", 1, 2); + } + + [Fact] + public void TestSimpleEpisodePath3() + { + Test(@"/server/anything_s01.e02.mp4", "anything", 1, 2); + } + + [Fact] + public void TestSimpleEpisodePath4() + { + Test(@"/server/anything_s01_e02.mp4", "anything", 1, 2); + } + + [Fact] + public void TestSimpleEpisodePath5() + { + Test(@"/server/anything_102.mp4", "anything", 1, 2); + } + + [Fact] + public void TestSimpleEpisodePath6() + { + Test(@"/server/anything_1x02.mp4", "anything", 1, 2); + } + + // FIXME + // [Fact] + public void TestSimpleEpisodePath7() + { + Test(@"/server/The Walking Dead 4x01.mp4", "The Walking Dead", 4, 1); + } + + [Fact] + public void TestSimpleEpisodePath8() + { + Test(@"/server/the_simpsons-s02e01_18536.mp4", "the_simpsons", 2, 1); + } + + + [Fact] + public void TestSimpleEpisodePath9() + { + Test(@"/server/Temp/S01E02 foo.mp4", string.Empty, 1, 2); + } + + [Fact] + public void TestSimpleEpisodePath10() + { + Test(@"Series/4-12 - The Woman.mp4", string.Empty, 4, 12); + } + + [Fact] + public void TestSimpleEpisodePath11() + { + Test(@"Series/4x12 - The Woman.mp4", string.Empty, 4, 12); + } + + [Fact] + public void TestSimpleEpisodePath12() + { + Test(@"Series/LA X, Pt. 1_s06e32.mp4", "LA X, Pt. 1", 6, 32); + } + + private void Test(string path, string seriesName, int? seasonNumber, int? episodeNumber) + { + var options = new NamingOptions(); + + var result = new EpisodeResolver(options) + .Resolve(path, false); + + Assert.Equal(seasonNumber, result.SeasonNumber); + Assert.Equal(episodeNumber, result.EpisodeNumber); + Assert.Equal(seriesName, result.SeriesName, true); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs b/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs new file mode 100644 index 000000000..b993e241c --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs @@ -0,0 +1,15 @@ +using Emby.Naming.Common; +using Emby.Naming.Video; + +namespace Jellyfin.Naming.Tests.Video +{ + public abstract class BaseVideoTest + { + protected VideoResolver GetParser() + { + var options = new NamingOptions(); + + return new VideoResolver(options); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs new file mode 100644 index 000000000..bba73ad91 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs @@ -0,0 +1,143 @@ +using System.IO; +using Xunit; + +namespace Jellyfin.Naming.Tests.Video +{ + public class CleanDateTimeTests : BaseVideoTest + { + // FIXME + // [Fact] + public void TestCleanDateTime() + { + Test(@"The Wolf of Wall Street (2013).mkv", "The Wolf of Wall Street", 2013); + Test(@"The Wolf of Wall Street 2 (2013).mkv", "The Wolf of Wall Street 2", 2013); + Test(@"The Wolf of Wall Street - 2 (2013).mkv", "The Wolf of Wall Street - 2", 2013); + Test(@"The Wolf of Wall Street 2001 (2013).mkv", "The Wolf of Wall Street 2001", 2013); + + Test(@"300 (2006).mkv", "300", 2006); + Test(@"d:/movies/300 (2006).mkv", "300", 2006); + Test(@"300 2 (2006).mkv", "300 2", 2006); + Test(@"300 - 2 (2006).mkv", "300 - 2", 2006); + Test(@"300 2001 (2006).mkv", "300 2001", 2006); + + Test(@"curse.of.chucky.2013.stv.unrated.multi.1080p.bluray.x264-rough", "curse.of.chucky", 2013); + Test(@"curse.of.chucky.2013.stv.unrated.multi.2160p.bluray.x264-rough", "curse.of.chucky", 2013); + + Test(@"/server/Movies/300 (2007)/300 (2006).bluray.disc", "300", 2006); + } + + // FIXME + // [Fact] + public void TestCleanDateTime1() + { + Test(@"Arrival.2016.2160p.Blu-Ray.HEVC.mkv", "Arrival", 2016); + } + + // FIXME + // [Fact] + public void TestCleanDateTimeWithoutFileExtension() + { + Test(@"The Wolf of Wall Street (2013)", "The Wolf of Wall Street", 2013); + Test(@"The Wolf of Wall Street 2 (2013)", "The Wolf of Wall Street 2", 2013); + Test(@"The Wolf of Wall Street - 2 (2013)", "The Wolf of Wall Street - 2", 2013); + Test(@"The Wolf of Wall Street 2001 (2013)", "The Wolf of Wall Street 2001", 2013); + + Test(@"300 (2006)", "300", 2006); + Test(@"d:/movies/300 (2006)", "300", 2006); + Test(@"300 2 (2006)", "300 2", 2006); + Test(@"300 - 2 (2006)", "300 - 2", 2006); + Test(@"300 2001 (2006)", "300 2001", 2006); + + Test(@"/server/Movies/300 (2007)/300 (2006)", "300", 2006); + Test(@"/server/Movies/300 (2007)/300 (2006).mkv", "300", 2006); + } + + [Fact] + public void TestCleanDateTimeWithoutDate() + { + Test(@"American.Psycho.mkv", "American.Psycho.mkv", null); + Test(@"American Psycho.mkv", "American Psycho.mkv", null); + } + + [Fact] + public void TestCleanDateTimeWithBracketedName() + { + Test(@"[rec].mkv", "[rec].mkv", null); + } + + // FIXME + // [Fact] + public void TestCleanDateTimeWithoutExtension() + { + Test(@"St. Vincent (2014)", "St. Vincent", 2014); + } + + // FIXME + // [Fact] + public void TestCleanDateTimeWithoutDate1() + { + Test("Super movie(2009).mp4", "Super movie", 2009); + } + + // FIXME + // [Fact] + public void TestCleanDateTimeWithoutParenthesis() + { + Test("Drug War 2013.mp4", "Drug War", 2013); + } + + // FIXME + // [Fact] + public void TestCleanDateTimeWithMultipleYears() + { + Test("My Movie (1997) - GreatestReleaseGroup 2019.mp4", "My Movie", 1997); + } + + // FIXME + // [Fact] + public void TestCleanDateTimeWithYearAndResolution() + { + Test("First Man 2018 1080p.mkv", "First Man", 2018); + } + + // FIXME + // [Fact] + public void TestCleanDateTimeWithYearAndResolution1() + { + Test("First Man (2018) 1080p.mkv", "First Man", 2018); + } + + // FIXME + // [Fact] + public void TestCleanDateTimeWithSceneRelease() + { + Test("Maximum Ride - 2016 - WEBDL-1080p - x264 AC3.mkv", "Maximum Ride", 2016); + } + + // FIXME + // [Fact] + public void TestYearInBrackets() + { + Test("Robin Hood [Multi-Subs] [2018].mkv", "Robin Hood", 2018); + } + + private void Test(string input, string expectedName, int? expectedYear) + { + input = Path.GetFileName(input); + + var result = GetParser().CleanDateTime(input); + + Assert.Equal(expectedName, result.Name, true); + Assert.Equal(expectedYear, result.Year); + } + + // FIXME + // [Fact] + public void TestCleanDateAndStringsSequence() + { + // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again + + Test(@"3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv", "3.Days.to.Kill", 2014); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs new file mode 100644 index 000000000..cd90ac236 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs @@ -0,0 +1,133 @@ +using System; +using System.Globalization; +using Xunit; + +namespace Jellyfin.Naming.Tests.Video +{ + public class CleanStringTests : BaseVideoTest + { + // FIXME + // [Fact] + public void TestCleanString() + { + Test("Super movie 480p.mp4", "Super movie"); + Test("Super movie 480p 2001.mp4", "Super movie"); + Test("Super movie [480p].mp4", "Super movie"); + Test("480 Super movie [tmdbid=12345].mp4", "480 Super movie"); + } + + // FIXME + // [Fact] + public void TestCleanString1() + { + Test("Super movie(2009).mp4", "Super movie(2009).mp4"); + } + + // FIXME + // [Fact] + public void TestCleanString2() + { + Test("Run lola run (lola rennt) (2009).mp4", "Run lola run (lola rennt) (2009).mp4"); + } + + // FIXME + // [Fact] + public void TestStringWithoutDate() + { + Test(@"American.Psycho.mkv", "American.Psycho.mkv"); + Test(@"American Psycho.mkv", "American Psycho.mkv"); + } + + // FIXME + // [Fact] + public void TestNameWithBrackets() + { + Test(@"[rec].mkv", "[rec].mkv"); + } + + // FIXME + // [Fact] + public void Test4k() + { + Test("Crouching.Tiger.Hidden.Dragon.4k.mkv", "Crouching.Tiger.Hidden.Dragon"); + } + + // FIXME + // [Fact] + public void TestUltraHd() + { + Test("Crouching.Tiger.Hidden.Dragon.UltraHD.mkv", "Crouching.Tiger.Hidden.Dragon"); + } + + // FIXME + // [Fact] + public void TestUHd() + { + Test("Crouching.Tiger.Hidden.Dragon.UHD.mkv", "Crouching.Tiger.Hidden.Dragon"); + } + + // FIXME + // [Fact] + public void TestHDR() + { + Test("Crouching.Tiger.Hidden.Dragon.HDR.mkv", "Crouching.Tiger.Hidden.Dragon"); + } + + // FIXME + // [Fact] + public void TestHDC() + { + Test("Crouching.Tiger.Hidden.Dragon.HDC.mkv", "Crouching.Tiger.Hidden.Dragon"); + } + + // FIXME + // [Fact] + public void TestHDC1() + { + Test("Crouching.Tiger.Hidden.Dragon-HDC.mkv", "Crouching.Tiger.Hidden.Dragon"); + } + + // FIXME + // [Fact] + public void TestBDrip() + { + Test("Crouching.Tiger.Hidden.Dragon.BDrip.mkv", "Crouching.Tiger.Hidden.Dragon"); + } + + // FIXME + // [Fact] + public void TestBDripHDC() + { + Test("Crouching.Tiger.Hidden.Dragon.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon"); + } + + // FIXME + // [Fact] + public void TestMulti() + { + Test("Crouching.Tiger.Hidden.Dragon.4K.UltraHD.HDR.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon"); + } + + // FIXME + // [Fact] + public void TestLeadingBraces() + { + // Not actually supported, just reported by a user + Test("[0004] - After The Sunset.el.mkv", "After The Sunset"); + } + + // FIXME + // [Fact] + public void TestTrailingBraces() + { + Test("After The Sunset - [0004].mkv", "After The Sunset"); + } + + private void Test(string input, string expectedName) + { + var result = GetParser().CleanString(input).ToString(); + + Assert.Equal(expectedName, result, true); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs new file mode 100644 index 000000000..1646237a0 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs @@ -0,0 +1,77 @@ +using Emby.Naming.Common; +using Emby.Naming.Video; +using MediaBrowser.Model.Entities; +using Xunit; + +namespace Jellyfin.Naming.Tests.Video +{ + public class ExtraTests : BaseVideoTest + { + // Requirements + // movie-deleted = ExtraType deletedscene + + // All of the above rules should be configurable through the options objects (ideally, even the ExtraTypes) + + [Fact] + public void TestKodiExtras() + { + var videoOptions = new NamingOptions(); + + Test("trailer.mp4", ExtraType.Trailer, videoOptions); + Test("300-trailer.mp4", ExtraType.Trailer, videoOptions); + + Test("theme.mp3", ExtraType.ThemeSong, videoOptions); + } + + [Fact] + public void TestExpandedExtras() + { + var videoOptions = new NamingOptions(); + + Test("trailer.mp4", ExtraType.Trailer, videoOptions); + Test("trailer.mp3", null, videoOptions); + Test("300-trailer.mp4", ExtraType.Trailer, videoOptions); + + Test("theme.mp3", ExtraType.ThemeSong, videoOptions); + Test("theme.mkv", null, videoOptions); + + Test("300-scene.mp4", ExtraType.Scene, videoOptions); + Test("300-scene2.mp4", ExtraType.Scene, videoOptions); + Test("300-clip.mp4", ExtraType.Clip, videoOptions); + + Test("300-deleted.mp4", ExtraType.DeletedScene, videoOptions); + Test("300-deletedscene.mp4", ExtraType.DeletedScene, videoOptions); + Test("300-interview.mp4", ExtraType.Interview, videoOptions); + Test("300-behindthescenes.mp4", ExtraType.BehindTheScenes, videoOptions); + } + + [Fact] + public void TestSample() + { + var videoOptions = new NamingOptions(); + + Test("300-sample.mp4", ExtraType.Sample, videoOptions); + } + + private void Test(string input, ExtraType? expectedType, NamingOptions videoOptions) + { + var parser = GetExtraTypeParser(videoOptions); + + var extraType = parser.GetExtraInfo(input).ExtraType; + + if (expectedType == null) + { + Assert.Null(extraType); + } + else + { + Assert.Equal(expectedType, extraType); + } + } + + private ExtraResolver GetExtraTypeParser(NamingOptions videoOptions) + { + return new ExtraResolver(videoOptions); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs new file mode 100644 index 000000000..ed3112936 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs @@ -0,0 +1,78 @@ +using Emby.Naming.Common; +using Emby.Naming.Video; +using Xunit; + +namespace Jellyfin.Naming.Tests.Video +{ + public class Format3DTests : BaseVideoTest + { + [Fact] + public void TestKodiFormat3D() + { + var options = new NamingOptions(); + + Test("Super movie.3d.mp4", false, null, options); + Test("Super movie.3d.hsbs.mp4", true, "hsbs", options); + Test("Super movie.3d.sbs.mp4", true, "sbs", options); + Test("Super movie.3d.htab.mp4", true, "htab", options); + Test("Super movie.3d.tab.mp4", true, "tab", options); + Test("Super movie 3d hsbs.mp4", true, "hsbs", options); + } + + [Fact] + public void Test3DName() + { + var result = + GetParser().ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.3d.hsbs.mkv"); + + Assert.Equal("hsbs", result.Format3D); + Assert.Equal("Oblivion", result.Name); + } + + [Fact] + public void TestExpandedFormat3D() + { + // These were introduced for Media Browser 3 + // Kodi conventions are preferred but these still need to be supported + var options = new NamingOptions(); + + Test("Super movie.3d.mp4", false, null, options); + Test("Super movie.3d.hsbs.mp4", true, "hsbs", options); + Test("Super movie.3d.sbs.mp4", true, "sbs", options); + Test("Super movie.3d.htab.mp4", true, "htab", options); + Test("Super movie.3d.tab.mp4", true, "tab", options); + + Test("Super movie.hsbs.mp4", true, "hsbs", options); + Test("Super movie.sbs.mp4", true, "sbs", options); + Test("Super movie.htab.mp4", true, "htab", options); + Test("Super movie.tab.mp4", true, "tab", options); + Test("Super movie.sbs3d.mp4", true, "sbs3d", options); + Test("Super movie.3d.mvc.mp4", true, "mvc", options); + + Test("Super movie [3d].mp4", false, null, options); + Test("Super movie [hsbs].mp4", true, "hsbs", options); + Test("Super movie [fsbs].mp4", true, "fsbs", options); + Test("Super movie [ftab].mp4", true, "ftab", options); + Test("Super movie [htab].mp4", true, "htab", options); + Test("Super movie [sbs3d].mp4", true, "sbs3d", options); + } + + private void Test(string input, bool is3D, string format3D, NamingOptions options) + { + var parser = new Format3DParser(options); + + var result = parser.Parse(input); + + Assert.Equal(is3D, result.Is3D); + + if (format3D == null) + { + Assert.Null(result.Format3D); + } + else + { + Assert.Equal(format3D, result.Format3D, true); + } + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs new file mode 100644 index 000000000..b8674ec49 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -0,0 +1,438 @@ +using System.Linq; +using Emby.Naming.Common; +using Emby.Naming.Video; +using MediaBrowser.Model.IO; +using Xunit; + +namespace Jellyfin.Naming.Tests.Video +{ + public class MultiVersionTests + { + // FIXME + // [Fact] + public void TestMultiEdition1() + { + var files = new[] + { + @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - 1080p.mkv", + @"/movies/X-Men Days of Future Past/X-Men Days of Future Past-trailer.mp4", + @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - [hsbs].mkv", + @"/movies/X-Men Days of Future Past/X-Men Days of Future Past [hsbs].mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Single(result[0].Extras); + } + + // FIXME + // [Fact] + public void TestMultiEdition2() + { + var files = new[] + { + @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - apple.mkv", + @"/movies/X-Men Days of Future Past/X-Men Days of Future Past-trailer.mp4", + @"/movies/X-Men Days of Future Past/X-Men Days of Future Past - banana.mkv", + @"/movies/X-Men Days of Future Past/X-Men Days of Future Past [banana].mp4" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Single(result[0].Extras); + Assert.Equal(2, result[0].AlternateVersions.Count); + } + + [Fact] + public void TestMultiEdition3() + { + // This is currently not supported and will fail, but we should try to figure it out + var files = new[] + { + @"/movies/The Phantom of the Opera (1925)/The Phantom of the Opera (1925) - 1925 version.mkv", + @"/movies/The Phantom of the Opera (1925)/The Phantom of the Opera (1925) - 1929 version.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Single(result[0].AlternateVersions); + } + + // FIXME + // [Fact] + public void TestLetterFolders() + { + var files = new[] + { + @"/movies/M/Movie 1.mkv", + @"/movies/M/Movie 2.mkv", + @"/movies/M/Movie 3.mkv", + @"/movies/M/Movie 4.mkv", + @"/movies/M/Movie 5.mkv", + @"/movies/M/Movie 6.mkv", + @"/movies/M/Movie 7.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(7, result.Count); + Assert.Empty(result[0].Extras); + Assert.Empty(result[0].AlternateVersions); + } + + // FIXME + // [Fact] + public void TestMultiVersionLimit() + { + var files = new[] + { + @"/movies/Movie/Movie.mkv", + @"/movies/Movie/Movie-2.mkv", + @"/movies/Movie/Movie-3.mkv", + @"/movies/Movie/Movie-4.mkv", + @"/movies/Movie/Movie-5.mkv", + @"/movies/Movie/Movie-6.mkv", + @"/movies/Movie/Movie-7.mkv", + @"/movies/Movie/Movie-8.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Empty(result[0].Extras); + Assert.Equal(7, result[0].AlternateVersions.Count); + } + + // FIXME + // [Fact] + public void TestMultiVersionLimit2() + { + var files = new[] + { + @"/movies/Mo/Movie 1.mkv", + @"/movies/Mo/Movie 2.mkv", + @"/movies/Mo/Movie 3.mkv", + @"/movies/Mo/Movie 4.mkv", + @"/movies/Mo/Movie 5.mkv", + @"/movies/Mo/Movie 6.mkv", + @"/movies/Mo/Movie 7.mkv", + @"/movies/Mo/Movie 8.mkv", + @"/movies/Mo/Movie 9.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(9, result.Count); + Assert.Empty(result[0].Extras); + Assert.Empty(result[0].AlternateVersions); + } + + // FIXME + // [Fact] + public void TestMultiVersion3() + { + var files = new[] + { + @"/movies/Movie/Movie 1.mkv", + @"/movies/Movie/Movie 2.mkv", + @"/movies/Movie/Movie 3.mkv", + @"/movies/Movie/Movie 4.mkv", + @"/movies/Movie/Movie 5.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(5, result.Count); + Assert.Empty(result[0].Extras); + Assert.Empty(result[0].AlternateVersions); + } + + // FIXME + // [Fact] + public void TestMultiVersion4() + { + // Test for false positive + + var files = new[] + { + @"/movies/Iron Man/Iron Man.mkv", + @"/movies/Iron Man/Iron Man (2008).mkv", + @"/movies/Iron Man/Iron Man (2009).mkv", + @"/movies/Iron Man/Iron Man (2010).mkv", + @"/movies/Iron Man/Iron Man (2011).mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(5, result.Count); + Assert.Empty(result[0].Extras); + Assert.Empty(result[0].AlternateVersions); + } + + // FIXME + // [Fact] + public void TestMultiVersion5() + { + var files = new[] + { + @"/movies/Iron Man/Iron Man.mkv", + @"/movies/Iron Man/Iron Man-720p.mkv", + @"/movies/Iron Man/Iron Man-test.mkv", + @"/movies/Iron Man/Iron Man-bluray.mkv", + @"/movies/Iron Man/Iron Man-3d.mkv", + @"/movies/Iron Man/Iron Man-3d-hsbs.mkv", + @"/movies/Iron Man/Iron Man-3d.hsbs.mkv", + @"/movies/Iron Man/Iron Man[test].mkv", + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Empty(result[0].Extras); + Assert.Equal(7, result[0].AlternateVersions.Count); + Assert.False(result[0].AlternateVersions[2].Is3D); + Assert.True(result[0].AlternateVersions[3].Is3D); + Assert.True(result[0].AlternateVersions[4].Is3D); + } + + // FIXME + // [Fact] + public void TestMultiVersion6() + { + var files = new[] + { + @"/movies/Iron Man/Iron Man.mkv", + @"/movies/Iron Man/Iron Man - 720p.mkv", + @"/movies/Iron Man/Iron Man - test.mkv", + @"/movies/Iron Man/Iron Man - bluray.mkv", + @"/movies/Iron Man/Iron Man - 3d.mkv", + @"/movies/Iron Man/Iron Man - 3d-hsbs.mkv", + @"/movies/Iron Man/Iron Man - 3d.hsbs.mkv", + @"/movies/Iron Man/Iron Man [test].mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Empty(result[0].Extras); + Assert.Equal(7, result[0].AlternateVersions.Count); + Assert.False(result[0].AlternateVersions[3].Is3D); + Assert.True(result[0].AlternateVersions[4].Is3D); + Assert.True(result[0].AlternateVersions[5].Is3D); + } + + // FIXME + // [Fact] + public void TestMultiVersion7() + { + var files = new[] + { + @"/movies/Iron Man/Iron Man - B (2006).mkv", + @"/movies/Iron Man/Iron Man - C (2007).mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(2, result.Count); + } + + // FIXME + // [Fact] + public void TestMultiVersion8() + { + // This is not actually supported yet + + var files = new[] + { + @"/movies/Iron Man/Iron Man.mkv", + @"/movies/Iron Man/Iron Man_720p.mkv", + @"/movies/Iron Man/Iron Man_test.mkv", + @"/movies/Iron Man/Iron Man_bluray.mkv", + @"/movies/Iron Man/Iron Man_3d.mkv", + @"/movies/Iron Man/Iron Man_3d-hsbs.mkv", + @"/movies/Iron Man/Iron Man_3d.hsbs.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Empty(result[0].Extras); + Assert.Equal(6, result[0].AlternateVersions.Count); + Assert.False(result[0].AlternateVersions[2].Is3D); + Assert.True(result[0].AlternateVersions[3].Is3D); + Assert.True(result[0].AlternateVersions[4].Is3D); + } + + // FIXME + // [Fact] + public void TestMultiVersion9() + { + // Test for false positive + + var files = new[] + { + @"/movies/Iron Man/Iron Man (2007).mkv", + @"/movies/Iron Man/Iron Man (2008).mkv", + @"/movies/Iron Man/Iron Man (2009).mkv", + @"/movies/Iron Man/Iron Man (2010).mkv", + @"/movies/Iron Man/Iron Man (2011).mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(5, result.Count); + Assert.Empty(result[0].Extras); + Assert.Empty(result[0].AlternateVersions); + } + + // FIXME + // [Fact] + public void TestMultiVersion10() + { + var files = new[] + { + @"/movies/Blade Runner (1982)/Blade Runner (1982) [Final Cut] [1080p HEVC AAC].mkv", + @"/movies/Blade Runner (1982)/Blade Runner (1982) [EE by ADM] [480p HEVC AAC,AAC,AAC].mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Empty(result[0].Extras); + Assert.Single(result[0].AlternateVersions); + } + + // FIXME + // [Fact] + public void TestMultiVersion11() + { + // Currently not supported but we should probably handle this. + + var files = new[] + { + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) [1080p] Blu-ray.x264.DTS.mkv", + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) [2160p] Blu-ray.x265.AAC.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + Assert.Empty(result[0].Extras); + Assert.Single(result[0].AlternateVersions); + } + + private VideoListResolver GetResolver() + { + var options = new NamingOptions(); + return new VideoListResolver(options); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs new file mode 100644 index 000000000..5faef0e3d --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs @@ -0,0 +1,478 @@ +using Emby.Naming.Common; +using Emby.Naming.Video; +using MediaBrowser.Model.IO; +using Xunit; + +namespace Jellyfin.Naming.Tests.Video +{ + public class StackTests : BaseVideoTest + { + [Fact] + public void TestSimpleStack() + { + var files = new[] + { + "Bad Boys (2006) part1.mkv", + "Bad Boys (2006) part2.mkv", + "Bad Boys (2006) part3.mkv", + "Bad Boys (2006) part4.mkv", + "Bad Boys (2006)-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Single(result.Stacks); + TestStackInfo(result.Stacks[0], "Bad Boys (2006)", 4); + } + + [Fact] + public void TestFalsePositives() + { + var files = new[] + { + "Bad Boys (2006).mkv", + "Bad Boys (2007).mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Empty(result.Stacks); + } + + [Fact] + public void TestFalsePositives2() + { + var files = new[] + { + "Bad Boys 2006.mkv", + "Bad Boys 2007.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Empty(result.Stacks); + } + + [Fact] + public void TestFalsePositives3() + { + var files = new[] + { + "300 (2006).mkv", + "300 (2007).mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Empty(result.Stacks); + } + + [Fact] + public void TestFalsePositives4() + { + var files = new[] + { + "300 2006.mkv", + "300 2007.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Empty(result.Stacks); + } + + [Fact] + public void TestFalsePositives5() + { + var files = new[] + { + "Star Trek 1 - The motion picture.mkv", + "Star Trek 2- The wrath of khan.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Empty(result.Stacks); + } + + [Fact] + public void TestFalsePositives6() + { + var files = new[] + { + "Red Riding in the Year of Our Lord 1983 (2009).mkv", + "Red Riding in the Year of Our Lord 1980 (2009).mkv", + "Red Riding in the Year of Our Lord 1974 (2009).mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Empty(result.Stacks); + } + + [Fact] + public void TestStackName() + { + var files = new[] + { + "d:/movies/300 2006 part1.mkv", + "d:/movies/300 2006 part2.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Single(result.Stacks); + TestStackInfo(result.Stacks[0], "300 2006", 2); + } + + [Fact] + public void TestDirtyNames() + { + var files = new[] + { + "Bad Boys (2006).part1.stv.unrated.multi.1080p.bluray.x264-rough.mkv", + "Bad Boys (2006).part2.stv.unrated.multi.1080p.bluray.x264-rough.mkv", + "Bad Boys (2006).part3.stv.unrated.multi.1080p.bluray.x264-rough.mkv", + "Bad Boys (2006).part4.stv.unrated.multi.1080p.bluray.x264-rough.mkv", + "Bad Boys (2006)-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Single(result.Stacks); + TestStackInfo(result.Stacks[0], "Bad Boys (2006).stv.unrated.multi.1080p.bluray.x264-rough", 4); + } + + [Fact] + public void TestNumberedFiles() + { + var files = new[] + { + "Bad Boys (2006).mkv", + "Bad Boys (2006) 1.mkv", + "Bad Boys (2006) 2.mkv", + "Bad Boys (2006) 3.mkv", + "Bad Boys (2006)-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Empty(result.Stacks); + } + + [Fact] + public void TestSimpleStackWithNumericName() + { + var files = new[] + { + "300 (2006) part1.mkv", + "300 (2006) part2.mkv", + "300 (2006) part3.mkv", + "300 (2006) part4.mkv", + "300 (2006)-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Single(result.Stacks); + TestStackInfo(result.Stacks[0], "300 (2006)", 4); + } + + [Fact] + public void TestMixedExpressionsNotAllowed() + { + var files = new[] + { + "Bad Boys (2006) part1.mkv", + "Bad Boys (2006) part2.mkv", + "Bad Boys (2006) part3.mkv", + "Bad Boys (2006) parta.mkv", + "Bad Boys (2006)-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Single(result.Stacks); + TestStackInfo(result.Stacks[0], "Bad Boys (2006)", 3); + } + + [Fact] + public void TestDualStacks() + { + var files = new[] + { + "Bad Boys (2006) part1.mkv", + "Bad Boys (2006) part2.mkv", + "Bad Boys (2006) part3.mkv", + "Bad Boys (2006) part4.mkv", + "Bad Boys (2006)-trailer.mkv", + "300 (2006) part1.mkv", + "300 (2006) part2.mkv", + "300 (2006) part3.mkv", + "300 (2006)-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Equal(2, result.Stacks.Count); + TestStackInfo(result.Stacks[1], "Bad Boys (2006)", 4); + TestStackInfo(result.Stacks[0], "300 (2006)", 3); + } + + [Fact] + public void TestDirectories() + { + var files = new[] + { + "blah blah - cd 1", + "blah blah - cd 2" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveDirectories(files); + + Assert.Single(result.Stacks); + TestStackInfo(result.Stacks[0], "blah blah", 2); + } + + [Fact] + public void TestFalsePositive() + { + var files = new[] + { + "300a.mkv", + "300b.mkv", + "300c.mkv", + "300-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Single(result.Stacks); + + TestStackInfo(result.Stacks[0], "300", 3); + } + + [Fact] + public void TestFailSequence() + { + var files = new[] + { + "300 part1.mkv", + "300 part2.mkv", + "Avatar", + "Avengers part1.mkv", + "Avengers part2.mkv", + "Avengers part3.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Equal(2, result.Stacks.Count); + + TestStackInfo(result.Stacks[0], "300", 2); + TestStackInfo(result.Stacks[1], "Avengers", 3); + } + + [Fact] + public void TestMixedExpressions() + { + var files = new[] + { + "Bad Boys (2006) part1.mkv", + "Bad Boys (2006) part2.mkv", + "Bad Boys (2006) part3.mkv", + "Bad Boys (2006) part4.mkv", + "Bad Boys (2006)-trailer.mkv", + "300 (2006) parta.mkv", + "300 (2006) partb.mkv", + "300 (2006) partc.mkv", + "300 (2006) partd.mkv", + "300 (2006)-trailer.mkv", + "300a.mkv", + "300b.mkv", + "300c.mkv", + "300-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Equal(3, result.Stacks.Count); + + TestStackInfo(result.Stacks[0], "300 (2006)", 4); + TestStackInfo(result.Stacks[1], "300", 3); + TestStackInfo(result.Stacks[2], "Bad Boys (2006)", 4); + } + + [Fact] + public void TestAlphaLimitOfFour() + { + var files = new[] + { + "300 (2006) parta.mkv", + "300 (2006) partb.mkv", + "300 (2006) partc.mkv", + "300 (2006) partd.mkv", + "300 (2006) parte.mkv", + "300 (2006) partf.mkv", + "300 (2006) partg.mkv", + "300 (2006)-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Single(result.Stacks); + + TestStackInfo(result.Stacks[0], "300 (2006)", 4); + } + + [Fact] + public void TestMixed() + { + var files = new[] + { + new FileSystemMetadata{FullName = "Bad Boys (2006) part1.mkv", IsDirectory = false}, + new FileSystemMetadata{FullName = "Bad Boys (2006) part2.mkv", IsDirectory = false}, + new FileSystemMetadata{FullName = "300 (2006) part2", IsDirectory = true}, + new FileSystemMetadata{FullName = "300 (2006) part3", IsDirectory = true}, + new FileSystemMetadata{FullName = "300 (2006) part1", IsDirectory = true} + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files); + + Assert.Equal(2, result.Stacks.Count); + TestStackInfo(result.Stacks[0], "300 (2006)", 3); + TestStackInfo(result.Stacks[1], "Bad Boys (2006)", 2); + } + + [Fact] + public void TestDirectories2() + { + //TestDirectory(@"blah blah", false, @"blah blah"); + //TestDirectory(@"d:/music/weezer/03 Pinkerton", false, "03 Pinkerton"); + //TestDirectory(@"d:/music/michael jackson/Bad (2012 Remaster)", false, "Bad (2012 Remaster)"); + + //TestDirectory(@"blah blah - cd1", true, "blah blah"); + //TestDirectory(@"blah blah - disc1", true, "blah blah"); + //TestDirectory(@"blah blah - disk1", true, "blah blah"); + //TestDirectory(@"blah blah - pt1", true, "blah blah"); + //TestDirectory(@"blah blah - part1", true, "blah blah"); + //TestDirectory(@"blah blah - dvd1", true, "blah blah"); + + //// Add a space + //TestDirectory(@"blah blah - cd 1", true, "blah blah"); + //TestDirectory(@"blah blah - disc 1", true, "blah blah"); + //TestDirectory(@"blah blah - disk 1", true, "blah blah"); + //TestDirectory(@"blah blah - pt 1", true, "blah blah"); + //TestDirectory(@"blah blah - part 1", true, "blah blah"); + //TestDirectory(@"blah blah - dvd 1", true, "blah blah"); + + //// Not case sensitive + //TestDirectory(@"blah blah - Disc1", true, "blah blah"); + } + + [Fact] + public void TestNamesWithoutParts() + { + // No stacking here because there is no part/disc/etc + var files = new[] + { + "Harry Potter and the Deathly Hallows.mkv", + "Harry Potter and the Deathly Hallows 1.mkv", + "Harry Potter and the Deathly Hallows 2.mkv", + "Harry Potter and the Deathly Hallows 3.mkv", + "Harry Potter and the Deathly Hallows 4.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Empty(result.Stacks); + } + + [Fact] + public void TestNumbersAppearingBeforePartNumber() + { + // No stacking here because there is no part/disc/etc + var files = new[] + { + "Neverland (2011)[720p][PG][Voted 6.5][Family-Fantasy]part1.mkv", + "Neverland (2011)[720p][PG][Voted 6.5][Family-Fantasy]part2.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveFiles(files); + + Assert.Single(result.Stacks); + Assert.Equal(2, result.Stacks[0].Files.Count); + } + + [Fact] + public void TestMultiDiscs() + { + // No stacking here because there is no part/disc/etc + var files = new[] + { + @"M:/Movies (DVD)/Movies (Musical)/The Sound of Music/The Sound of Music (1965) (Disc 01)", + @"M:/Movies (DVD)/Movies (Musical)/The Sound of Music/The Sound of Music (1965) (Disc 02)" + }; + + var resolver = GetResolver(); + + var result = resolver.ResolveDirectories(files); + + Assert.Single(result.Stacks); + Assert.Equal(2, result.Stacks[0].Files.Count); + } + + private void TestStackInfo(FileStack stack, string name, int fileCount) + { + Assert.Equal(fileCount, stack.Files.Count); + Assert.Equal(name, stack.Name); + } + + private StackResolver GetResolver() + { + return new StackResolver(new NamingOptions()); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs new file mode 100644 index 000000000..96fa8c5a5 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs @@ -0,0 +1,55 @@ +using System; +using System.Globalization; +using Emby.Naming.Common; +using Emby.Naming.Video; +using Xunit; + +namespace Jellyfin.Naming.Tests.Video +{ + public class StubTests : BaseVideoTest + { + [Fact] + public void TestStubs() + { + Test("video.mkv", false, null); + Test("video.disc", true, null); + Test("video.dvd.disc", true, "dvd"); + Test("video.hddvd.disc", true, "hddvd"); + Test("video.bluray.disc", true, "bluray"); + Test("video.brrip.disc", true, "bluray"); + Test("video.bd25.disc", true, "bluray"); + Test("video.bd50.disc", true, "bluray"); + Test("video.vhs.disc", true, "vhs"); + Test("video.hdtv.disc", true, "tv"); + Test("video.pdtv.disc", true, "tv"); + Test("video.dsr.disc", true, "tv"); + } + + [Fact] + public void TestStubName() + { + var result = + GetParser().ResolveFile(@"C:/Users/media/Desktop/Video Test/Movies/Oblivion/Oblivion.dvd.disc"); + + Assert.Equal("Oblivion", result.Name); + } + + private void Test(string path, bool isStub, string stubType) + { + var options = new NamingOptions(); + + var resultStubType = StubResolver.ResolveFile(path, options); + + Assert.Equal(isStub, resultStubType.IsStub); + + if (stubType == null) + { + Assert.Null(resultStubType.StubType); + } + else + { + Assert.Equal(stubType, resultStubType.StubType, true); + } + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs new file mode 100644 index 000000000..ef8a17898 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs @@ -0,0 +1,457 @@ +using System.Linq; +using Emby.Naming.Common; +using Emby.Naming.Video; +using MediaBrowser.Model.IO; +using Xunit; + +namespace Jellyfin.Naming.Tests.Video +{ + public class VideoListResolverTests + { + // FIXME + // [Fact] + public void TestStackAndExtras() + { + // No stacking here because there is no part/disc/etc + var files = new[] + { + "Harry Potter and the Deathly Hallows-trailer.mkv", + "Harry Potter and the Deathly Hallows.trailer.mkv", + "Harry Potter and the Deathly Hallows part1.mkv", + "Harry Potter and the Deathly Hallows part2.mkv", + "Harry Potter and the Deathly Hallows part3.mkv", + "Harry Potter and the Deathly Hallows part4.mkv", + "Batman-deleted.mkv", + "Batman-sample.mkv", + "Batman-trailer.mkv", + "Batman part1.mkv", + "Batman part2.mkv", + "Batman part3.mkv", + "Avengers.mkv", + "Avengers-trailer.mkv", + + // Despite having a keyword in the name that will return an ExtraType, there's no original video to match it to + // So this is just a standalone video + "trailer.mkv", + + // Same as above + "WillyWonka-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(5, result.Count); + + Assert.Equal(3, result[1].Files.Count); + Assert.Equal(3, result[1].Extras.Count); + Assert.Equal("Batman", result[1].Name); + + Assert.Equal(4, result[2].Files.Count); + Assert.Equal(2, result[2].Extras.Count); + Assert.Equal("Harry Potter and the Deathly Hallows", result[2].Name); + } + + [Fact] + public void TestWithMetadata() + { + var files = new[] + { + "300.mkv", + "300.nfo" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestWithExtra() + { + var files = new[] + { + "300.mkv", + "300 trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestVariationWithFolderName() + { + var files = new[] + { + "X-Men Days of Future Past - 1080p.mkv", + "X-Men Days of Future Past-trailer.mp4" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestTrailer2() + { + var files = new[] + { + "X-Men Days of Future Past - 1080p.mkv", + "X-Men Days of Future Past-trailer.mp4", + "X-Men Days of Future Past-trailer2.mp4" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestDifferentNames() + { + var files = new[] + { + "Looper (2012)-trailer.mkv", + "Looper.2012.bluray.720p.x264.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestSeparateFiles() + { + // These should be considered separate, unrelated videos + var files = new[] + { + "My video 1.mkv", + "My video 2.mkv", + "My video 3.mkv", + "My video 4.mkv", + "My video 5.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(5, result.Count); + } + + [Fact] + public void TestMultiDisc() + { + var files = new[] + { + @"M:/Movies (DVD)/Movies (Musical)/Sound of Music (1965)/Sound of Music Disc 1", + @"M:/Movies (DVD)/Movies (Musical)/Sound of Music (1965)/Sound of Music Disc 2" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = true, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestPoundSign() + { + // These should be considered separate, unrelated videos + var files = new[] + { + @"My movie #1.mp4", + @"My movie #2.mp4" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = true, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(2, result.Count); + } + + [Fact] + public void TestStackedWithTrailer() + { + var files = new[] + { + @"No (2012) part1.mp4", + @"No (2012) part2.mp4", + @"No (2012) part1-trailer.mp4" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestStackedWithTrailer2() + { + var files = new[] + { + @"No (2012) part1.mp4", + @"No (2012) part2.mp4", + @"No (2012)-trailer.mp4" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestExtrasByFolderName() + { + var files = new[] + { + @"/Movies/Top Gun (1984)/movie.mp4", + @"/Movies/Top Gun (1984)/Top Gun (1984)-trailer.mp4", + @"/Movies/Top Gun (1984)/Top Gun (1984)-trailer2.mp4", + @"trailer.mp4" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestDoubleTags() + { + var files = new[] + { + @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Counterfeit Racks (2011) Disc 1 cd1.avi", + @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Counterfeit Racks (2011) Disc 1 cd2.avi", + @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Disc 2 cd1.avi", + @"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Disc 2 cd2.avi" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(2, result.Count); + } + + [Fact] + public void TestArgumentOutOfRangeException() + { + var files = new[] + { + @"/nas-markrobbo78/Videos/INDEX HTPC/Movies/Watched/3 - ACTION/Argo (2012)/movie.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestColony() + { + var files = new[] + { + @"The Colony.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestFourSisters() + { + var files = new[] + { + @"Four Sisters and a Wedding - A.avi", + @"Four Sisters and a Wedding - B.avi" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestMovieTrailer() + { + var files = new[] + { + @"/Server/Despicable Me/Despicable Me (2010).mkv", + @"/Server/Despicable Me/movie-trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + [Fact] + public void TestTrailerFalsePositives() + { + var files = new[] + { + @"/Server/Despicable Me/Skyscraper (2018) - Big Game Spot.mkv", + @"/Server/Despicable Me/Skyscraper (2018) - Trailer.mkv", + @"/Server/Despicable Me/Baywatch (2017) - Big Game Spot.mkv", + @"/Server/Despicable Me/Baywatch (2017) - Trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Equal(4, result.Count); + } + + [Fact] + public void TestSubfolders() + { + var files = new[] + { + @"/Movies/Despicable Me/Despicable Me.mkv", + @"/Movies/Despicable Me/trailers/trailer.mkv" + }; + + var resolver = GetResolver(); + + var result = resolver.Resolve(files.Select(i => new FileSystemMetadata + { + IsDirectory = false, + FullName = i + + }).ToList()).ToList(); + + Assert.Single(result); + } + + private VideoListResolver GetResolver() + { + var options = new NamingOptions(); + return new VideoListResolver(options); + } + } +} diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs new file mode 100644 index 000000000..5a3ce8886 --- /dev/null +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -0,0 +1,275 @@ +using MediaBrowser.Model.Entities; +using Xunit; + +namespace Jellyfin.Naming.Tests.Video +{ + public class VideoResolverTests : BaseVideoTest + { + // FIXME + // [Fact] + public void TestSimpleFile() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006).mkv"); + + Assert.Equal(2006, result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Equal("Brave", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void TestSimpleFile2() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/Bad Boys (1995)/Bad Boys (1995).mkv"); + + Assert.Equal(1995, result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Equal("Bad Boys", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void TestSimpleFileWithNumericName() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).mkv"); + + Assert.Equal(2006, result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Equal("300", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void TestExtra() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006)-trailer.mkv"); + + Assert.Equal(2006, result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Equal(ExtraType.Trailer, result.ExtraType); + Assert.Equal("Brave (2006)-trailer", result.Name); + } + + // FIXME + // [Fact] + public void TestExtraWithNumericName() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006)-trailer.mkv"); + + Assert.Equal(2006, result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Equal("300 (2006)-trailer", result.Name); + Assert.Equal(ExtraType.Trailer, result.ExtraType); + } + + // FIXME + // [Fact] + public void TestStubFileWithNumericName() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).bluray.disc"); + + Assert.Equal(2006, result.Year); + Assert.True(result.IsStub); + Assert.Equal("bluray", result.StubType); + Assert.False(result.Is3D); + Assert.Equal("300", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void TestStubFile() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/Brave (2007)/Brave (2006).bluray.disc"); + + Assert.Equal(2006, result.Year); + Assert.True(result.IsStub); + Assert.Equal("bluray", result.StubType); + Assert.False(result.Is3D); + Assert.Equal("Brave", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void TestExtraStubWithNumericNameNotSupported() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006)-trailer.bluray.disc"); + + Assert.Equal(2006, result.Year); + Assert.True(result.IsStub); + Assert.Equal("bluray", result.StubType); + Assert.False(result.Is3D); + Assert.Equal("300", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void TestExtraStubNotSupported() + { + // Using a stub for an extra is currently not supported + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/brave (2007)/brave (2006)-trailer.bluray.disc"); + + Assert.Equal(2006, result.Year); + Assert.True(result.IsStub); + Assert.Equal("bluray", result.StubType); + Assert.False(result.Is3D); + Assert.Equal("brave", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void Test3DFileWithNumericName() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).3d.sbs.mkv"); + + Assert.Equal(2006, result.Year); + Assert.False(result.IsStub); + Assert.True(result.Is3D); + Assert.Equal("sbs", result.Format3D); + Assert.Equal("300", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void TestBad3DFileWithNumericName() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/300 (2007)/300 (2006).3d1.sbas.mkv"); + + Assert.Equal(2006, result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Equal("300", result.Name); + Assert.Null(result.ExtraType); + Assert.Null(result.Format3D); + } + + // FIXME + // [Fact] + public void Test3DFile() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/brave (2007)/brave (2006).3d.sbs.mkv"); + + Assert.Equal(2006, result.Year); + Assert.False(result.IsStub); + Assert.True(result.Is3D); + Assert.Equal("sbs", result.Format3D); + Assert.Equal("brave", result.Name); + Assert.Null(result.ExtraType); + } + + [Fact] + public void TestNameWithoutDate() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/American Psycho/American.Psycho.mkv"); + + Assert.Null(result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Null(result.Format3D); + Assert.Equal("American.Psycho", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void TestCleanDateAndStringsSequence() + { + var parser = GetParser(); + + // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again + var result = + parser.ResolveFile(@"/server/Movies/3.Days.to.Kill/3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv"); + + Assert.Equal(2014, result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Null(result.Format3D); + Assert.Equal("3.Days.to.Kill", result.Name); + Assert.Null(result.ExtraType); + } + + // FIXME + // [Fact] + public void TestCleanDateAndStringsSequence1() + { + var parser = GetParser(); + + // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again + var result = + parser.ResolveFile(@"/server/Movies/3 days to kill (2005)/3 days to kill (2005).mkv"); + + Assert.Equal(2005, result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Null(result.Format3D); + Assert.Equal("3 days to kill", result.Name); + Assert.Null(result.ExtraType); + } + + [Fact] + public void TestFolderNameWithExtension() + { + var parser = GetParser(); + + var result = + parser.ResolveFile(@"/server/Movies/7 Psychos.mkv/7 Psychos.mkv"); + + Assert.Null(result.Year); + Assert.False(result.IsStub); + Assert.False(result.Is3D); + Assert.Equal("7 Psychos", result.Name); + Assert.Null(result.ExtraType); + } + } +} -- cgit v1.2.3 From 2ef4ffd698fb4ae95754f76238e52cfb6162db24 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 11 Dec 2019 00:13:57 +0100 Subject: More warnings (removed) --- Emby.Naming/Emby.Naming.csproj | 2 +- Emby.Photos/Emby.Photos.csproj | 7 +- .../Activity/ActivityLogEntryPoint.cs | 1 + .../Activity/ActivityManager.cs | 1 + .../Activity/ActivityRepository.cs | 1 + Emby.Server.Implementations/ApplicationHost.cs | 13 +- .../Branding/BrandingConfigurationFactory.cs | 1 + .../Channels/ChannelDynamicMediaSourceProvider.cs | 1 + .../Channels/ChannelImageProvider.cs | 1 + .../Channels/ChannelManager.cs | 1 + .../Channels/ChannelPostScanTask.cs | 1 + .../Channels/RefreshChannelsScheduledTask.cs | 1 + .../Collections/CollectionImageProvider.cs | 1 + .../Collections/CollectionManager.cs | 1 + .../Cryptography/CryptographyProvider.cs | 2 +- .../Data/BaseSqliteRepository.cs | 1 + .../Data/CleanDatabaseScheduledTask.cs | 1 + .../Data/ManagedConnection.cs | 1 + .../Data/SqliteDisplayPreferencesRepository.cs | 1 + .../Data/SqliteExtensions.cs | 1 + .../Data/SqliteUserDataRepository.cs | 1 + .../Data/SqliteUserRepository.cs | 1 + Emby.Server.Implementations/Devices/DeviceId.cs | 1 + .../Devices/DeviceManager.cs | 1 + .../Diagnostics/CommonProcess.cs | 1 + .../Diagnostics/ProcessFactory.cs | 1 + Emby.Server.Implementations/Dto/DtoService.cs | 1 + .../Emby.Server.Implementations.csproj | 16 +-- .../EntryPoints/AutomaticRestartEntryPoint.cs | 1 + .../EntryPoints/ExternalPortForwarding.cs | 1 + .../EntryPoints/LibraryChangedNotifier.cs | 1 + .../EntryPoints/RecordingNotifier.cs | 1 + .../EntryPoints/UserDataChangeNotifier.cs | 1 + .../HttpServer/FileWriter.cs | 1 + .../HttpServer/HttpListenerHost.cs | 2 +- .../HttpServer/HttpResultFactory.cs | 1 + .../HttpServer/IHttpListener.cs | 1 + .../HttpServer/RangeRequestWriter.cs | 1 + .../HttpServer/Security/AuthService.cs | 1 + .../HttpServer/Security/AuthorizationContext.cs | 1 + .../HttpServer/Security/SessionContext.cs | 1 + .../IO/ExtendedFileSystemInfo.cs | 1 + Emby.Server.Implementations/IO/FileRefresher.cs | 1 + Emby.Server.Implementations/IO/LibraryMonitor.cs | 1 + .../IO/ManagedFileSystem.cs | 1 + .../IO/MbLinkShortcutHandler.cs | 1 + Emby.Server.Implementations/IO/StreamHelper.cs | 1 + .../Images/BaseDynamicImageProvider.cs | 1 + .../Library/DefaultAuthenticationProvider.cs | 13 +- .../Library/ExclusiveLiveStream.cs | 1 + .../Library/LibraryManager.cs | 1 + .../Library/LiveStreamHelper.cs | 1 + .../Library/MediaSourceManager.cs | 1 + .../Library/MediaStreamSelector.cs | 1 + .../Library/MusicManager.cs | 1 + .../Library/Resolvers/Audio/AudioResolver.cs | 1 + .../Library/Resolvers/BaseVideoResolver.cs | 1 + .../Library/Resolvers/Books/BookResolver.cs | 1 + .../Library/Resolvers/PhotoResolver.cs | 1 + .../Library/Resolvers/PlaylistResolver.cs | 1 + .../Library/Resolvers/SpecialFolderResolver.cs | 1 + .../Library/Resolvers/TV/SeriesResolver.cs | 1 + .../Library/Resolvers/VideoResolver.cs | 1 + .../Library/SearchEngine.cs | 1 + .../Library/UserDataManager.cs | 1 + Emby.Server.Implementations/Library/UserManager.cs | 3 +- .../Library/UserViewManager.cs | 1 + Jellyfin.Api/Jellyfin.Api.csproj | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 14 +- .../Configuration/ConfigurationUpdateEventArgs.cs | 1 + .../Configuration/IConfigurationFactory.cs | 1 + .../Configuration/IConfigurationManager.cs | 1 + MediaBrowser.Common/Cryptography/PasswordHash.cs | 25 ++-- MediaBrowser.Common/Events/EventHelper.cs | 8 +- MediaBrowser.Common/Extensions/BaseExtensions.cs | 7 +- MediaBrowser.Common/Extensions/CopyToExtensions.cs | 4 +- .../Extensions/MethodNotAllowedException.cs | 26 ++++ .../Extensions/RateLimitExceededException.cs | 26 ++++ .../Extensions/ResourceNotFoundException.cs | 65 +-------- MediaBrowser.Common/Hex.cs | 12 +- MediaBrowser.Common/IApplicationHost.cs | 55 ++++---- MediaBrowser.Common/MediaBrowser.Common.csproj | 17 ++- MediaBrowser.Common/Net/CustomHeaderNames.cs | 1 + MediaBrowser.Common/Net/HttpRequestOptions.cs | 28 ++-- MediaBrowser.Common/Net/HttpResponseInfo.cs | 32 ++--- MediaBrowser.Common/Net/IHttpClient.cs | 4 +- MediaBrowser.Common/Net/INetworkManager.cs | 11 +- MediaBrowser.Common/Plugins/BasePlugin.cs | 150 ++++++++++----------- MediaBrowser.Common/Plugins/IPlugin.cs | 29 ++-- MediaBrowser.Common/Plugins/IPluginAssembly.cs | 14 ++ MediaBrowser.Common/Progress/ActionableProgress.cs | 1 + .../Providers/SubtitleConfigurationFactory.cs | 1 + MediaBrowser.Common/System/OperatingSystem.cs | 1 + .../Updates/IInstallationManager.cs | 10 +- .../Updates/InstallationEventArgs.cs | 1 + .../Updates/InstallationFailedEventArgs.cs | 1 + .../Authentication/AuthenticationResult.cs | 7 +- MediaBrowser.Controller/Entities/BaseItem.cs | 1 - jellyfin.ruleset | 12 +- 99 files changed, 394 insertions(+), 291 deletions(-) create mode 100644 MediaBrowser.Common/Extensions/MethodNotAllowedException.cs create mode 100644 MediaBrowser.Common/Extensions/RateLimitExceededException.cs create mode 100644 MediaBrowser.Common/Plugins/IPluginAssembly.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 7258beaf4..3be5bbbaf 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -23,7 +23,7 @@ - + diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 64692c370..d3c87edff 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -22,9 +22,10 @@ - - - + + + + diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index b622a3167..ac8af66a2 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index a30e93912..b03c4d182 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Linq; diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 7be72319e..633343bb6 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 8c625539a..ae979682e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Concurrent; @@ -180,11 +181,7 @@ namespace Emby.Server.Implementations /// Gets the plugins. /// /// The plugins. - public IPlugin[] Plugins - { - get => _plugins; - protected set => _plugins = value; - } + public IReadOnlyList Plugins => _plugins; /// /// Gets or sets the logger factory. @@ -1051,7 +1048,7 @@ namespace Emby.Server.Implementations } ConfigurationManager.AddParts(GetExports()); - Plugins = GetExports() + _plugins = GetExports() .Select(LoadPlugin) .Where(i => i != null) .ToArray(); @@ -1683,9 +1680,9 @@ namespace Emby.Server.Implementations /// The plugin. public void RemovePlugin(IPlugin plugin) { - var list = Plugins.ToList(); + var list = _plugins.ToList(); list.Remove(plugin); - Plugins = list.ToArray(); + _plugins = list.ToArray(); } /// diff --git a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs index 93000ae12..15aee63a0 100644 --- a/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs +++ b/Emby.Server.Implementations/Branding/BrandingConfigurationFactory.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System.Collections.Generic; using MediaBrowser.Common.Configuration; diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs index 6016fed07..aae416b37 100644 --- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs index 62aeb9bcb..fe64f1b15 100644 --- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System.Collections.Generic; using System.Linq; diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 6e1baddfe..de2e123af 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Concurrent; diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs index 2712fc8c5..36e0e5e26 100644 --- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Linq; diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index 5774c0415..039e2c138 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs index 1fa556ec9..8006b8694 100644 --- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs +++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 2b8a5bdc5..5a25b5d3e 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs index 776074b72..de83b023d 100644 --- a/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs +++ b/Emby.Server.Implementations/Cryptography/CryptographyProvider.cs @@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.Cryptography /// /// Releases unmanaged and - optionally - managed resources. /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 0654132f4..b7f643819 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs index 2a8f2d6b3..8a5387e9b 100644 --- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs +++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Threading; diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs index 5c094ddd2..2c2f19cd3 100644 --- a/Emby.Server.Implementations/Data/ManagedConnection.cs +++ b/Emby.Server.Implementations/Data/ManagedConnection.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index d474f1c6b..8087419ce 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index c87793072..55c24ccc0 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 22955850a..f6c37e4e5 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Data/SqliteUserRepository.cs b/Emby.Server.Implementations/Data/SqliteUserRepository.cs index a042320c9..c82c93ffc 100644 --- a/Emby.Server.Implementations/Data/SqliteUserRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserRepository.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs index f0d43e665..ff75efa59 100644 --- a/Emby.Server.Implementations/Devices/DeviceId.cs +++ b/Emby.Server.Implementations/Devices/DeviceId.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Globalization; diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 2393f1f45..ef7317050 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs index bfa49ac5f..f8b754151 100644 --- a/Emby.Server.Implementations/Diagnostics/CommonProcess.cs +++ b/Emby.Server.Implementations/Diagnostics/CommonProcess.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Diagnostics; diff --git a/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs b/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs index 02ad3c1a8..219f73c78 100644 --- a/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs +++ b/Emby.Server.Implementations/Diagnostics/ProcessFactory.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using MediaBrowser.Model.Diagnostics; diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 3d622b3fc..fcf0360c7 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index fde4d7059..5ca508776 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,10 +29,10 @@ - - - - + + + + @@ -52,10 +52,10 @@ - - - - + + + + diff --git a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs index d69b0909d..a6eb1152f 100644 --- a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Linq; diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index e290c62e1..4e4ef3be0 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 5f938e59a..f85d52dbc 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs index dbb3503c4..e0aa18e89 100644 --- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Linq; diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index e431da148..3e22080fc 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index c1c8c3eb3..1795651fd 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 2aefc9fe5..b0126f7fa 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -218,7 +219,6 @@ namespace Emby.Server.Implementations.HttpServer case FileNotFoundException _: case ResourceNotFoundException _: return 404; case MethodNotAllowedException _: return 405; - case RemoteServiceUnavailableException _: return 502; default: return 500; } } diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index a62b4e7af..cefcaa835 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/HttpServer/IHttpListener.cs b/Emby.Server.Implementations/HttpServer/IHttpListener.cs index 501593725..1c3496e5d 100644 --- a/Emby.Server.Implementations/HttpServer/IHttpListener.cs +++ b/Emby.Server.Implementations/HttpServer/IHttpListener.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Threading; diff --git a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs index 8b9028f6b..7cb113a58 100644 --- a/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ b/Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs index 58421aaf1..03b5b748d 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Linq; diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index 129faeaab..e8884bca0 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs index 166952c64..a6a0f5b03 100644 --- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using MediaBrowser.Controller.Entities; diff --git a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs index 3150f3367..5be144452 100644 --- a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs +++ b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 namespace Emby.Server.Implementations.IO { diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index 4b5b11f01..cf92ddbcd 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index b1fb8cc63..7777efc3b 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Concurrent; diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 442fbabd1..d8da0888e 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs index e6696b8c4..574b63ae6 100644 --- a/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs +++ b/Emby.Server.Implementations/IO/MbLinkShortcutHandler.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.IO; diff --git a/Emby.Server.Implementations/IO/StreamHelper.cs b/Emby.Server.Implementations/IO/StreamHelper.cs index 40b397edc..c99018e40 100644 --- a/Emby.Server.Implementations/IO/StreamHelper.cs +++ b/Emby.Server.Implementations/IO/StreamHelper.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Buffers; diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index fd50f156a..acf3a3b23 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs index 94f60ea62..ab036eca7 100644 --- a/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs +++ b/Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs @@ -70,9 +70,9 @@ namespace Emby.Server.Implementations.Library byte[] calculatedHash = _cryptographyProvider.ComputeHash( readyHash.Id, passwordbytes, - readyHash.Salt); + readyHash.Salt.ToArray()); - if (calculatedHash.SequenceEqual(readyHash.Hash)) + if (readyHash.Hash.SequenceEqual(calculatedHash)) { success = true; } @@ -148,17 +148,18 @@ namespace Emby.Server.Implementations.Library // TODO: make use of iterations parameter? PasswordHash passwordHash = PasswordHash.Parse(user.Password); + var salt = passwordHash.Salt.ToArray(); return new PasswordHash( passwordHash.Id, _cryptographyProvider.ComputeHash( passwordHash.Id, Encoding.UTF8.GetBytes(str), - passwordHash.Salt), - passwordHash.Salt, + salt), + salt, passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString(); } - public byte[] GetHashed(User user, string str) + public ReadOnlySpan GetHashed(User user, string str) { if (string.IsNullOrEmpty(user.Password)) { @@ -170,7 +171,7 @@ namespace Emby.Server.Implementations.Library return _cryptographyProvider.ComputeHash( passwordHash.Id, Encoding.UTF8.GetBytes(str), - passwordHash.Salt); + passwordHash.Salt.ToArray()); } } } diff --git a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs index 9a7186898..3eb64c29c 100644 --- a/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs +++ b/Emby.Server.Implementations/Library/ExclusiveLiveStream.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Globalization; diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6942088fe..d8ef8192a 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Concurrent; diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index ed7d8aa40..f28f4a538 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 22193c997..0899832a7 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs index 6b9f4d052..1652ad974 100644 --- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs +++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/MusicManager.cs b/Emby.Server.Implementations/Library/MusicManager.cs index 1ec578371..29af6670b 100644 --- a/Emby.Server.Implementations/Library/MusicManager.cs +++ b/Emby.Server.Implementations/Library/MusicManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 9d4bd9e59..7e3b27a12 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index c4bb861b8..43302bb3f 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.IO; diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 0b93ebeb8..1e2e0704c 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.IO; diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index a71ae8250..e1eb23652 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs index a68562fc2..5e672f221 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.IO; diff --git a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs index 1030ed39d..eca60b133 100644 --- a/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.IO; diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 7cc9eabc8..e39d85bc9 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs index 62268fce9..6404d6476 100644 --- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index 11d6c737a..76ae14720 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index 48d33c26c..5d4f17861 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Concurrent; diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 85bfa154a..c7f6de24f 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Concurrent; @@ -480,7 +481,7 @@ namespace Emby.Server.Implementations.Library var hash = _cryptoProvider.ComputeHash( passwordHash.Id, Encoding.UTF8.GetBytes(password), - passwordHash.Salt); + passwordHash.Salt.ToArray()); success = passwordHash.Hash.SequenceEqual(hash); } diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index 322819b05..935deb71c 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index a2818b45d..f4e5337de 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -19,7 +19,7 @@ - + diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 110028176..63410f606 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -24,13 +24,13 @@ - + - - - - + + + + @@ -39,8 +39,8 @@ - - + + diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs index 344aecf53..828415c18 100644 --- a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs +++ b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; diff --git a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs index 4c4060096..9b4ed772d 100644 --- a/MediaBrowser.Common/Configuration/IConfigurationFactory.cs +++ b/MediaBrowser.Common/Configuration/IConfigurationFactory.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/MediaBrowser.Common/Configuration/IConfigurationManager.cs b/MediaBrowser.Common/Configuration/IConfigurationManager.cs index caf2edd83..7773596af 100644 --- a/MediaBrowser.Common/Configuration/IConfigurationManager.cs +++ b/MediaBrowser.Common/Configuration/IConfigurationManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; diff --git a/MediaBrowser.Common/Cryptography/PasswordHash.cs b/MediaBrowser.Common/Cryptography/PasswordHash.cs index 19b8be47a..3477c1c04 100644 --- a/MediaBrowser.Common/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Common/Cryptography/PasswordHash.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -15,24 +16,24 @@ namespace MediaBrowser.Common.Cryptography public class PasswordHash { private readonly Dictionary _parameters; + private readonly byte[] _salt; + private readonly byte[] _hash; public PasswordHash(string id, byte[] hash) : this(id, hash, Array.Empty()) { - } public PasswordHash(string id, byte[] hash, byte[] salt) : this(id, hash, salt, new Dictionary()) { - } public PasswordHash(string id, byte[] hash, byte[] salt, Dictionary parameters) { Id = id; - Hash = hash; - Salt = salt; + _hash = hash; + _salt = salt; _parameters = parameters; } @@ -45,25 +46,24 @@ namespace MediaBrowser.Common.Cryptography /// /// Gets the additional parameters used by the hash function. /// - /// public IReadOnlyDictionary Parameters => _parameters; /// /// Gets the salt used for hashing the password. /// /// Returns the salt used for hashing the password. - public byte[] Salt { get; } + public ReadOnlySpan Salt => _salt; /// /// Gets the hashed password. /// /// Return the hashed password. - public byte[] Hash { get; } + public ReadOnlySpan Hash => _hash; public static PasswordHash Parse(string hashString) { - string[] splitted = hashString.Split('$'); // The string should at least contain the hash function and the hash itself + string[] splitted = hashString.Split('$'); if (splitted.Length < 3) { throw new ArgumentException("String doesn't contain enough segments", nameof(hashString)); @@ -77,7 +77,7 @@ namespace MediaBrowser.Common.Cryptography // Optional parameters Dictionary parameters = new Dictionary(); - if (splitted[index].IndexOf('=') != -1) + if (splitted[index].IndexOf('=', StringComparison.Ordinal) != -1) { foreach (string paramset in splitted[index++].Split(',')) { @@ -98,6 +98,7 @@ namespace MediaBrowser.Common.Cryptography byte[] hash; byte[] salt; + // Check if the string also contains a salt if (splitted.Length - index == 2) { @@ -141,14 +142,14 @@ namespace MediaBrowser.Common.Cryptography .Append(Id); SerializeParameters(str); - if (Salt.Length != 0) + if (_salt.Length != 0) { str.Append('$') - .Append(Hex.Encode(Salt, false)); + .Append(Hex.Encode(_salt, false)); } return str.Append('$') - .Append(Hex.Encode(Hash, false)).ToString(); + .Append(Hex.Encode(_hash, false)).ToString(); } } } diff --git a/MediaBrowser.Common/Events/EventHelper.cs b/MediaBrowser.Common/Events/EventHelper.cs index b67315df6..c9d3226ac 100644 --- a/MediaBrowser.Common/Events/EventHelper.cs +++ b/MediaBrowser.Common/Events/EventHelper.cs @@ -1,15 +1,13 @@ -#pragma warning disable CS1591 - using System; using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace MediaBrowser.Common.Events { - // TODO: @bond Remove /// - /// Class EventHelper + /// Class EventHelper. /// + // TODO: @bond Remove public static class EventHelper { /// @@ -40,7 +38,7 @@ namespace MediaBrowser.Common.Events /// /// Queues the event. /// - /// + /// Argument type for the handler. /// The handler. /// The sender. /// The args. diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs index 33473c2be..08964420e 100644 --- a/MediaBrowser.Common/Extensions/BaseExtensions.cs +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -1,12 +1,12 @@ using System; +using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; -using System.Security.Cryptography; namespace MediaBrowser.Common.Extensions { /// - /// Class BaseExtensions + /// Class BaseExtensions. /// public static class BaseExtensions { @@ -30,10 +30,13 @@ namespace MediaBrowser.Common.Extensions /// . public static Guid GetMD5(this string str) { +#pragma warning disable CA5351 using (var provider = MD5.Create()) { return new Guid(provider.ComputeHash(Encoding.Unicode.GetBytes(str))); } + +#pragma warning restore CA5351 } } } diff --git a/MediaBrowser.Common/Extensions/CopyToExtensions.cs b/MediaBrowser.Common/Extensions/CopyToExtensions.cs index 78a73f07e..2ecbc6539 100644 --- a/MediaBrowser.Common/Extensions/CopyToExtensions.cs +++ b/MediaBrowser.Common/Extensions/CopyToExtensions.cs @@ -5,7 +5,7 @@ namespace MediaBrowser.Common.Extensions /// /// Provides CopyTo extensions methods for . /// - public static class CollectionExtensions + public static class CopyToExtensions { /// /// Copies all the elements of the current collection to the specified list @@ -14,7 +14,7 @@ namespace MediaBrowser.Common.Extensions /// The current collection that is the source of the elements. /// The list that is the destination of the elements copied from the current collection. /// A 32-bit integer that represents the index in destination at which copying begins. - /// + /// The type of the array. public static void CopyTo(this IReadOnlyList source, IList destination, int index = 0) { for (int i = 0; i < source.Count; i++) diff --git a/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs new file mode 100644 index 000000000..48e758ee4 --- /dev/null +++ b/MediaBrowser.Common/Extensions/MethodNotAllowedException.cs @@ -0,0 +1,26 @@ +using System; + +namespace MediaBrowser.Common.Extensions +{ + /// + /// Class MethodNotAllowedException. + /// + public class MethodNotAllowedException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public MethodNotAllowedException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public MethodNotAllowedException(string message) + : base(message) + { + } + } +} diff --git a/MediaBrowser.Common/Extensions/RateLimitExceededException.cs b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs new file mode 100644 index 000000000..4e5d4e9ca --- /dev/null +++ b/MediaBrowser.Common/Extensions/RateLimitExceededException.cs @@ -0,0 +1,26 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + +using System; + +namespace MediaBrowser.Common.Extensions +{ + public class RateLimitExceededException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public RateLimitExceededException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public RateLimitExceededException(string message) + : base(message) + { + } + } +} diff --git a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs index 9b064a40d..22130c5a1 100644 --- a/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs +++ b/MediaBrowser.Common/Extensions/ResourceNotFoundException.cs @@ -1,11 +1,9 @@ -#pragma warning disable CS1591 - using System; namespace MediaBrowser.Common.Extensions { /// - /// Class ResourceNotFoundException + /// Class ResourceNotFoundException. /// public class ResourceNotFoundException : Exception { @@ -14,7 +12,6 @@ namespace MediaBrowser.Common.Extensions /// public ResourceNotFoundException() { - } /// @@ -24,66 +21,6 @@ namespace MediaBrowser.Common.Extensions public ResourceNotFoundException(string message) : base(message) { - - } - } - - /// - /// Class MethodNotAllowedException - /// - public class MethodNotAllowedException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public MethodNotAllowedException() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - public MethodNotAllowedException(string message) - : base(message) - { - - } - } - - public class RemoteServiceUnavailableException : Exception - { - public RemoteServiceUnavailableException() - { - - } - - public RemoteServiceUnavailableException(string message) - : base(message) - { - - } - } - - public class RateLimitExceededException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public RateLimitExceededException() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - public RateLimitExceededException(string message) - : base(message) - { - } } } diff --git a/MediaBrowser.Common/Hex.cs b/MediaBrowser.Common/Hex.cs index b2d9aea3a..863192809 100644 --- a/MediaBrowser.Common/Hex.cs +++ b/MediaBrowser.Common/Hex.cs @@ -14,11 +14,11 @@ namespace MediaBrowser.Common internal const int LastHexSymbol = 0x66; // 102: f /// - /// Map from an ASCII char to its hex value shifted, + /// Gets an map from an ASCII char to its hex value shifted, /// e.g. b -> 11. 0xFF means it's not a hex symbol. /// - /// - internal static ReadOnlySpan HexLookup => new byte[] { + internal static ReadOnlySpan HexLookup => new byte[] + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -29,10 +29,10 @@ namespace MediaBrowser.Common }; /// - /// Encodes bytes as a hex string. + /// Encodes each element of the specified bytes as its hexadecimal string representation. /// - /// - /// + /// An array of bytes. + /// true to use lowercase hexadecimal characters; otherwise false. /// bytes as a hex string. public static string Encode(ReadOnlySpan bytes, bool lowercase = true) { diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 6668e98aa..68a24aaba 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -8,10 +8,15 @@ using Microsoft.Extensions.DependencyInjection; namespace MediaBrowser.Common { /// - /// An interface to be implemented by the applications hosting a kernel + /// An interface to be implemented by the applications hosting a kernel. /// public interface IApplicationHost { + /// + /// Occurs when [has pending restart changed]. + /// + event EventHandler HasPendingRestartChanged; + /// /// Gets the name. /// @@ -25,13 +30,13 @@ namespace MediaBrowser.Common string SystemId { get; } /// - /// Gets or sets a value indicating whether this instance has pending kernel reload. + /// Gets a value indicating whether this instance has pending kernel reload. /// /// true if this instance has pending kernel reload; otherwise, false. bool HasPendingRestart { get; } /// - /// Gets or sets a value indicating whether this instance is currently shutting down. + /// Gets a value indicating whether this instance is currently shutting down. /// /// true if this instance is shutting down; otherwise, false. bool IsShuttingDown { get; } @@ -43,26 +48,11 @@ namespace MediaBrowser.Common bool CanSelfRestart { get; } /// - /// Get the version class of the system. + /// Gets the version class of the system. /// /// or . PackageVersionClass SystemUpdateLevel { get; } - /// - /// Occurs when [has pending restart changed]. - /// - event EventHandler HasPendingRestartChanged; - - /// - /// Notifies the pending restart. - /// - void NotifyPendingRestart(); - - /// - /// Restarts this instance. - /// - void Restart(); - /// /// Gets the application version. /// @@ -87,6 +77,22 @@ namespace MediaBrowser.Common /// string ApplicationUserAgentAddress { get; } + /// + /// Gets the plugins. + /// + /// The plugins. + IReadOnlyList Plugins { get; } + + /// + /// Notifies the pending restart. + /// + void NotifyPendingRestart(); + + /// + /// Restarts this instance. + /// + void Restart(); + /// /// Gets the exports. /// @@ -98,21 +104,16 @@ namespace MediaBrowser.Common /// /// Resolves this instance. /// - /// + /// The Type. /// ``0. T Resolve(); /// /// Shuts down. /// + /// A task. Task Shutdown(); - /// - /// Gets the plugins. - /// - /// The plugins. - IPlugin[] Plugins { get; } - /// /// Removes the plugin. /// @@ -122,6 +123,8 @@ namespace MediaBrowser.Common /// /// Inits this instance. /// + /// The service collection. + /// A task. Task InitAsync(IServiceCollection serviceCollection); /// diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 889fbfa5a..567fcdda1 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -12,8 +12,8 @@ - - + + @@ -27,9 +27,16 @@ true - - - latest + + + + + + + + + + ../jellyfin.ruleset diff --git a/MediaBrowser.Common/Net/CustomHeaderNames.cs b/MediaBrowser.Common/Net/CustomHeaderNames.cs index 5ca9897eb..8cc48c55f 100644 --- a/MediaBrowser.Common/Net/CustomHeaderNames.cs +++ b/MediaBrowser.Common/Net/CustomHeaderNames.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 namespace MediaBrowser.Common.Net { diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 18c4b181f..8207a45f3 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -8,10 +9,21 @@ using Microsoft.Net.Http.Headers; namespace MediaBrowser.Common.Net { /// - /// Class HttpRequestOptions + /// Class HttpRequestOptions. /// public class HttpRequestOptions { + /// + /// Initializes a new instance of the class. + /// + public HttpRequestOptions() + { + RequestHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + + CacheMode = CacheMode.None; + DecompressionMethod = CompressionMethod.Deflate; + } + /// /// Gets or sets the URL. /// @@ -71,14 +83,17 @@ namespace MediaBrowser.Common.Net public string RequestContentType { get; set; } public string RequestContent { get; set; } + public byte[] RequestContentBytes { get; set; } public bool BufferContent { get; set; } public bool LogErrorResponseBody { get; set; } + public bool EnableKeepAlive { get; set; } public CacheMode CacheMode { get; set; } + public TimeSpan CacheLength { get; set; } public bool EnableDefaultUserAgent { get; set; } @@ -89,17 +104,6 @@ namespace MediaBrowser.Common.Net return value; } - - /// - /// Initializes a new instance of the class. - /// - public HttpRequestOptions() - { - RequestHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - - CacheMode = CacheMode.None; - DecompressionMethod = CompressionMethod.Deflate; - } } public enum CacheMode diff --git a/MediaBrowser.Common/Net/HttpResponseInfo.cs b/MediaBrowser.Common/Net/HttpResponseInfo.cs index 0de034b0e..d711ad64a 100644 --- a/MediaBrowser.Common/Net/HttpResponseInfo.cs +++ b/MediaBrowser.Common/Net/HttpResponseInfo.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.IO; using System.Net; @@ -8,10 +6,25 @@ using System.Net.Http.Headers; namespace MediaBrowser.Common.Net { /// - /// Class HttpResponseInfo + /// Class HttpResponseInfo. /// public class HttpResponseInfo : IDisposable { +#pragma warning disable CS1591 +#pragma warning disable SA1600 + public HttpResponseInfo() + { + } + + public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader) + { + Headers = headers; + ContentHeaders = contentHeader; + } + +#pragma warning restore CS1591 +#pragma warning restore SA1600 + /// /// Gets or sets the type of the content. /// @@ -60,21 +73,10 @@ namespace MediaBrowser.Common.Net /// The content headers. public HttpContentHeaders ContentHeaders { get; set; } - public HttpResponseInfo() - { - - } - - public HttpResponseInfo(HttpResponseHeaders headers, HttpContentHeaders contentHeader) - { - Headers = headers; - ContentHeaders = contentHeader; - } - /// public void Dispose() { - // Only IDisposable for backwards compatibility + // backwards compatibility } } } diff --git a/MediaBrowser.Common/Net/IHttpClient.cs b/MediaBrowser.Common/Net/IHttpClient.cs index 23ba34173..534e22edd 100644 --- a/MediaBrowser.Common/Net/IHttpClient.cs +++ b/MediaBrowser.Common/Net/IHttpClient.cs @@ -1,12 +1,12 @@ using System; using System.IO; -using System.Threading.Tasks; using System.Net.Http; +using System.Threading.Tasks; namespace MediaBrowser.Common.Net { /// - /// Interface IHttpClient + /// Interface IHttpClient. /// public interface IHttpClient { diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 0b99dc910..6bd7dd1d6 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -11,20 +12,20 @@ namespace MediaBrowser.Common.Net { event EventHandler NetworkChanged; + Func LocalSubnetsFn { get; set; } + /// - /// Gets a random port number that is currently available + /// Gets a random port number that is currently available. /// /// System.Int32. int GetRandomUnusedTcpPort(); int GetRandomUnusedUdpPort(); - Func LocalSubnetsFn { get; set; } - /// - /// Returns MAC Address from first Network Card in Computer + /// Returns the MAC Address from first Network Card in Computer. /// - /// [string] MAC Address + /// The MAC Address. List GetMacAddresses(); /// diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 6ef891d66..25519ccc0 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable SA1402 using System; using System.IO; @@ -8,10 +8,13 @@ using MediaBrowser.Model.Serialization; namespace MediaBrowser.Common.Plugins { + /// + /// Provides a common base class for all plugins. + /// public abstract class BasePlugin : IPlugin, IPluginAssembly { /// - /// Gets the name of the plugin + /// Gets the name of the plugin. /// /// The name. public abstract string Name { get; } @@ -29,17 +32,23 @@ namespace MediaBrowser.Common.Plugins public virtual Guid Id { get; private set; } /// - /// Gets the plugin version + /// Gets the plugin version. /// /// The version. public Version Version { get; private set; } /// - /// Gets the path to the assembly file + /// Gets the path to the assembly file. /// /// The assembly file path. public string AssemblyFilePath { get; private set; } + /// + /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed. + /// + /// The data folder path. + public string DataFolderPath { get; private set; } + /// /// Gets the plugin info. /// @@ -62,9 +71,9 @@ namespace MediaBrowser.Common.Plugins /// public virtual void OnUninstalling() { - } + /// public void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion) { AssemblyFilePath = assemblyFilePath; @@ -72,25 +81,48 @@ namespace MediaBrowser.Common.Plugins Version = assemblyVersion; } + /// public void SetId(Guid assemblyId) { Id = assemblyId; } - - /// - /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed - /// - /// The data folder path. - public string DataFolderPath { get; private set; } } /// - /// Provides a common base class for all plugins + /// Provides a common base class for all plugins. /// /// The type of the T configuration type. public abstract class BasePlugin : BasePlugin, IHasPluginConfiguration where TConfigurationType : BasePluginConfiguration { + /// + /// The _configuration sync lock. + /// + private readonly object _configurationSyncLock = new object(); + + /// + /// The _save lock. + /// + private readonly object _configurationSaveLock = new object(); + + private Action _directoryCreateFn; + + /// + /// The _configuration. + /// + private TConfigurationType _configuration; + + /// + /// Initializes a new instance of the class. + /// + /// The application paths. + /// The XML serializer. + protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + { + ApplicationPaths = applicationPaths; + XmlSerializer = xmlSerializer; + } + /// /// Gets the application paths. /// @@ -104,34 +136,19 @@ namespace MediaBrowser.Common.Plugins protected IXmlSerializer XmlSerializer { get; private set; } /// - /// Gets the type of configuration this plugin uses + /// Gets the type of configuration this plugin uses. /// /// The type of the configuration. public Type ConfigurationType => typeof(TConfigurationType); - private Action _directoryCreateFn; - public void SetStartupInfo(Action directoryCreateFn) - { - // hack alert, until the .net core transition is complete - _directoryCreateFn = directoryCreateFn; - } - /// - /// Gets the name the assembly file + /// Gets the name the assembly file. /// /// The name of the assembly file. protected string AssemblyFileName => Path.GetFileName(AssemblyFilePath); /// - /// The _configuration sync lock - /// - private readonly object _configurationSyncLock = new object(); - /// - /// The _configuration - /// - private TConfigurationType _configuration; - /// - /// Gets the plugin's configuration + /// Gets or sets the plugin's configuration. /// /// The configuration. public TConfigurationType Configuration @@ -149,55 +166,54 @@ namespace MediaBrowser.Common.Plugins } } } + return _configuration; } - protected set => _configuration = value; - } - private TConfigurationType LoadConfiguration() - { - var path = ConfigurationFilePath; - - try - { - return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path); - } - catch - { - return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType)); - } + protected set => _configuration = value; } /// - /// Gets the name of the configuration file. Subclasses should override + /// Gets the name of the configuration file. Subclasses should override. /// /// The name of the configuration file. public virtual string ConfigurationFileName => Path.ChangeExtension(AssemblyFileName, ".xml"); /// - /// Gets the full path to the configuration file + /// Gets the full path to the configuration file. /// /// The configuration file path. public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName); /// - /// Initializes a new instance of the class. + /// Gets the plugin's configuration. /// - /// The application paths. - /// The XML serializer. - protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + /// The configuration. + BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration; + + /// + public void SetStartupInfo(Action directoryCreateFn) { - ApplicationPaths = applicationPaths; - XmlSerializer = xmlSerializer; + // hack alert, until the .net core transition is complete + _directoryCreateFn = directoryCreateFn; } - /// - /// The _save lock - /// - private readonly object _configurationSaveLock = new object(); + private TConfigurationType LoadConfiguration() + { + var path = ConfigurationFilePath; + + try + { + return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path); + } + catch + { + return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType)); + } + } /// - /// Saves the current configuration to the file system + /// Saves the current configuration to the file system. /// public virtual void SaveConfiguration() { @@ -209,12 +225,7 @@ namespace MediaBrowser.Common.Plugins } } - /// - /// Completely overwrites the current configuration with a new copy - /// Returns true or false indicating success or failure - /// - /// The configuration. - /// configuration + /// public virtual void UpdateConfiguration(BasePluginConfiguration configuration) { if (configuration == null) @@ -227,12 +238,7 @@ namespace MediaBrowser.Common.Plugins SaveConfiguration(); } - /// - /// Gets the plugin's configuration - /// - /// The configuration. - BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration; - + /// public override PluginInfo GetPluginInfo() { var info = base.GetPluginInfo(); @@ -242,10 +248,4 @@ namespace MediaBrowser.Common.Plugins return info; } } - - public interface IPluginAssembly - { - void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion); - void SetId(Guid assemblyId); - } } diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs index 7bd90c964..001ca8be8 100644 --- a/MediaBrowser.Common/Plugins/IPlugin.cs +++ b/MediaBrowser.Common/Plugins/IPlugin.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using MediaBrowser.Model.Plugins; @@ -6,12 +7,12 @@ using MediaBrowser.Model.Plugins; namespace MediaBrowser.Common.Plugins { /// - /// Interface IPlugin + /// Interface IPlugin. /// public interface IPlugin { /// - /// Gets the name of the plugin + /// Gets the name of the plugin. /// /// The name. string Name { get; } @@ -29,19 +30,19 @@ namespace MediaBrowser.Common.Plugins Guid Id { get; } /// - /// Gets the plugin version + /// Gets the plugin version. /// /// The version. Version Version { get; } /// - /// Gets the path to the assembly file + /// Gets the path to the assembly file. /// /// The assembly file path. string AssemblyFilePath { get; } /// - /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed + /// Gets the full path to the data folder, where the plugin can store any miscellaneous files needed. /// /// The data folder path. string DataFolderPath { get; } @@ -61,24 +62,24 @@ namespace MediaBrowser.Common.Plugins public interface IHasPluginConfiguration { /// - /// Gets the type of configuration this plugin uses + /// Gets the type of configuration this plugin uses. /// /// The type of the configuration. Type ConfigurationType { get; } /// - /// Completely overwrites the current configuration with a new copy - /// Returns true or false indicating success or failure + /// Gets the plugin's configuration. /// - /// The configuration. - /// configuration - void UpdateConfiguration(BasePluginConfiguration configuration); + /// The configuration. + BasePluginConfiguration Configuration { get; } /// - /// Gets the plugin's configuration + /// Completely overwrites the current configuration with a new copy + /// Returns true or false indicating success or failure. /// - /// The configuration. - BasePluginConfiguration Configuration { get; } + /// The configuration. + /// configuration is null. + void UpdateConfiguration(BasePluginConfiguration configuration); void SetStartupInfo(Action directoryCreateFn); } diff --git a/MediaBrowser.Common/Plugins/IPluginAssembly.cs b/MediaBrowser.Common/Plugins/IPluginAssembly.cs new file mode 100644 index 000000000..388ac61ab --- /dev/null +++ b/MediaBrowser.Common/Plugins/IPluginAssembly.cs @@ -0,0 +1,14 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + +using System; + +namespace MediaBrowser.Common.Plugins +{ + public interface IPluginAssembly + { + void SetAttributes(string assemblyFilePath, string dataFolderPath, Version assemblyVersion); + + void SetId(Guid assemblyId); + } +} diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs index af69055aa..92141ba52 100644 --- a/MediaBrowser.Common/Progress/ActionableProgress.cs +++ b/MediaBrowser.Common/Progress/ActionableProgress.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; diff --git a/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs b/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs index 0445397ad..a6422e2c8 100644 --- a/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs +++ b/MediaBrowser.Common/Providers/SubtitleConfigurationFactory.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System.Collections.Generic; using MediaBrowser.Common.Configuration; diff --git a/MediaBrowser.Common/System/OperatingSystem.cs b/MediaBrowser.Common/System/OperatingSystem.cs index 7d38ddb6e..f23af4799 100644 --- a/MediaBrowser.Common/System/OperatingSystem.cs +++ b/MediaBrowser.Common/System/OperatingSystem.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Runtime.InteropServices; diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index e49812f15..a09c1916c 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; using System.Collections.Generic; @@ -103,17 +104,16 @@ namespace MediaBrowser.Common.Updates Task InstallPackage(PackageVersionInfo package, CancellationToken cancellationToken = default); /// - /// Uninstalls a plugin + /// Uninstalls a plugin. /// /// The plugin. - /// void UninstallPlugin(IPlugin plugin); /// - /// Cancels the installation + /// Cancels the installation. /// - /// The id of the package that is being installed - /// Returns true if the install was cancelled + /// The id of the package that is being installed. + /// Returns true if the install was cancelled. bool CancelInstallation(Guid id); } } diff --git a/MediaBrowser.Common/Updates/InstallationEventArgs.cs b/MediaBrowser.Common/Updates/InstallationEventArgs.cs index 36e124ddf..8bbb231ce 100644 --- a/MediaBrowser.Common/Updates/InstallationEventArgs.cs +++ b/MediaBrowser.Common/Updates/InstallationEventArgs.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using MediaBrowser.Model.Updates; diff --git a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs index 46f10c84f..c8967f9db 100644 --- a/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs +++ b/MediaBrowser.Common/Updates/InstallationFailedEventArgs.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1600 using System; diff --git a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs index 3c65156e3..5248ea4c1 100644 --- a/MediaBrowser.Controller/Authentication/AuthenticationResult.cs +++ b/MediaBrowser.Controller/Authentication/AuthenticationResult.cs @@ -1,14 +1,19 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; - namespace MediaBrowser.Controller.Authentication { public class AuthenticationResult { public UserDto User { get; set; } + public SessionInfo SessionInfo { get; set; } + public string AccessToken { get; set; } + public string ServerId { get; set; } } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 1fd706857..cf8f03002 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -19,7 +19,6 @@ using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Library; diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 75b5573b6..b6f5cf01b 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -31,8 +31,12 @@ + + + + + + + + + + - - -- cgit v1.2.3 From 599432890311c9255cb376bbeb6f5f4f673fbf1b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 2 Jan 2020 21:22:10 +0100 Subject: Fix baseurl (again) --- Emby.Server.Implementations/ApplicationHost.cs | 40 +++++++++++++++-------- MediaBrowser.Controller/IServerApplicationHost.cs | 8 +++-- 2 files changed, 32 insertions(+), 16 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 67bc0cd2b..9b853c6d1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -103,14 +103,11 @@ using MediaBrowser.Providers.Subtitles; using MediaBrowser.Providers.TV.TheTVDB; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Server.Implementations @@ -1478,7 +1475,7 @@ namespace Emby.Server.Implementations /// /// The IPv6 address. /// The IPv6 address without the scope id. - private string RemoveScopeId(string address) + private ReadOnlySpan RemoveScopeId(ReadOnlySpan address) { var index = address.IndexOf('%'); if (index == -1) @@ -1486,33 +1483,50 @@ namespace Emby.Server.Implementations return address; } - return address.Substring(0, index); + return address.Slice(0, index); } + /// public string GetLocalApiUrl(IPAddress ipAddress) { if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) { var str = RemoveScopeId(ipAddress.ToString()); + Span span = new char[str.Length + 2]; + span[0] = '['; + str.CopyTo(span.Slice(1)); + span[^1] = ']'; - return GetLocalApiUrl("[" + str + "]"); + return GetLocalApiUrl(span); } return GetLocalApiUrl(ipAddress.ToString()); } - public string GetLocalApiUrl(string host) + /// + public string GetLocalApiUrl(ReadOnlySpan host) { + var url = new StringBuilder(64); if (EnableHttps) { - return string.Format("https://{0}:{1}", - host, - HttpsPort.ToString(CultureInfo.InvariantCulture)); + url.Append("https://"); + } + else + { + url.Append("http://"); + } + + url.Append(host) + .Append(':') + .Append(HttpPort); + + string baseUrl = ServerConfigurationManager.Configuration.BaseUrl; + if (baseUrl.Length != 0) + { + url.Append('/').Append(baseUrl); } - return string.Format("http://{0}:{1}", - host, - HttpPort.ToString(CultureInfo.InvariantCulture)); + return url.ToString(); } public Task> GetLocalIpAddresses(CancellationToken cancellationToken) diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index b3c56bdd5..25f0905eb 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -71,13 +71,15 @@ namespace MediaBrowser.Controller /// /// Gets the local API URL. /// - /// The host. - /// System.String. - string GetLocalApiUrl(string host); + /// The hostname. + /// The local API URL. + string GetLocalApiUrl(ReadOnlySpan hostname); /// /// Gets the local API URL. /// + /// The IP address. + /// The local API URL. string GetLocalApiUrl(IPAddress address); void LaunchUrl(string url); -- cgit v1.2.3 From a253fa616da3fd982ca2190b69d25853893665f1 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 26 Dec 2019 23:09:00 +0100 Subject: Fix build and address comments --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- .../Data/SqliteItemRepository.cs | 34 ++++---- .../Library/MediaSourceManager.cs | 9 -- Jellyfin.Api/Controllers/StartupController.cs | 1 - MediaBrowser.Api/Attachments/AttachmentService.cs | 19 ++--- .../Library/IMediaSourceManager.cs | 11 +-- .../MediaEncoding/IAttachmentExtractor.cs | 4 +- .../Attachments/AttachmentExtractor.cs | 97 +++++++++++----------- .../Encoder/EncodingUtils.cs | 2 +- 9 files changed, 82 insertions(+), 97 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7c3f59af2..c5ac27ed4 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -878,7 +878,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(typeof(IResourceFileManager), typeof(ResourceFileManager)); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(typeof(MediaBrowser.Controller.MediaEncoding.IAttachmentExtractor),typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor)); + serviceCollection.AddSingleton(typeof(IAttachmentExtractor), typeof(MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor)); _displayPreferencesRepository.Initialize(); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2206816a5..91ca8477d 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -55,8 +55,8 @@ namespace Emby.Server.Implementations.Data queryPrefixText.Append("insert into mediaattachments ("); foreach (var column in _mediaAttachmentSaveColumns) { - queryPrefixText.Append(column); - queryPrefixText.Append(','); + queryPrefixText.Append(column) + .Append(','); } queryPrefixText.Length -= 1; @@ -449,6 +449,7 @@ namespace Emby.Server.Implementations.Data "Filename", "MIMEType" }; + private static readonly string _mediaAttachmentInsertPrefix; private static string GetSaveItemCommandText() @@ -6208,7 +6209,10 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type return list; } - public void SaveMediaAttachments(Guid id, List attachments, CancellationToken cancellationToken) + public void SaveMediaAttachments( + Guid id, + List attachments, + CancellationToken cancellationToken) { CheckDisposed(); if (id == Guid.Empty) @@ -6237,24 +6241,22 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type } } - private void InsertMediaAttachments(byte[] idBlob, List attachments, IDatabaseConnection db, CancellationToken cancellationToken) + private void InsertMediaAttachments( + byte[] idBlob, + List attachments, + IDatabaseConnection db, + CancellationToken cancellationToken) { - var startIndex = 0; - var insertAtOnce = 10; + const int InsertAtOnce = 10; - while (startIndex < attachments.Count) + for (var startIndex = 0; startIndex < attachments.Count; startIndex += InsertAtOnce) { var insertText = new StringBuilder(_mediaAttachmentInsertPrefix); - var endIndex = Math.Min(attachments.Count, startIndex + insertAtOnce); + var endIndex = Math.Min(attachments.Count, startIndex + InsertAtOnce); for (var i = startIndex; i < endIndex; i++) { - if (i != startIndex) - { - insertText.Append(','); - } - var index = i.ToString(CultureInfo.InvariantCulture); insertText.Append("(@ItemId, "); @@ -6265,9 +6267,11 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type insertText.Length -= 1; - insertText.Append(")"); + insertText.Append("),"); } + insertText.Length--; + cancellationToken.ThrowIfCancellationRequested(); using (var statement = PrepareStatement(db, insertText.ToString())) @@ -6291,8 +6295,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statement.Reset(); statement.MoveNext(); } - - startIndex += insertAtOnce; } } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 4eff2807c..ba1564d1f 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -136,15 +136,6 @@ namespace Emby.Server.Implementations.Library return _itemRepo.GetMediaAttachments(query); } - /// - public List GetMediaAttachments(string mediaSourceId) - { - return GetMediaAttachments(new MediaAttachmentQuery - { - ItemId = new Guid(mediaSourceId) - }); - } - /// public List GetMediaAttachments(Guid itemId) { diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index 1014c8c56..afc9b8f3d 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -96,7 +96,6 @@ namespace Jellyfin.Api.Controllers public StartupUserDto GetFirstUser() { var user = _userManager.Users.First(); - return new StartupUserDto { Name = user.Name, diff --git a/MediaBrowser.Api/Attachments/AttachmentService.cs b/MediaBrowser.Api/Attachments/AttachmentService.cs index 1ebfaa14b..ef09951b6 100644 --- a/MediaBrowser.Api/Attachments/AttachmentService.cs +++ b/MediaBrowser.Api/Attachments/AttachmentService.cs @@ -1,22 +1,14 @@ using System; -using System.Collections.Generic; -using System.Globalization; using System.IO; -using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Providers; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; -using MimeTypes = MediaBrowser.Model.Net.MimeTypes; namespace MediaBrowser.Api.Attachments { @@ -38,7 +30,13 @@ namespace MediaBrowser.Api.Attachments private readonly ILibraryManager _libraryManager; private readonly IAttachmentExtractor _attachmentExtractor; - public AttachmentService(ILibraryManager libraryManager, IAttachmentExtractor attachmentExtractor) + public AttachmentService( + ILogger logger, + IServerConfigurationManager serverConfigurationManager, + IHttpResultFactory httpResultFactory, + ILibraryManager libraryManager, + IAttachmentExtractor attachmentExtractor) + : base(logger, serverConfigurationManager, httpResultFactory) { _libraryManager = libraryManager; _attachmentExtractor = attachmentExtractor; @@ -46,7 +44,6 @@ namespace MediaBrowser.Api.Attachments public async Task Get(GetAttachment request) { - var item = (Video)_libraryManager.GetItemById(request.Id); var (attachment, attachmentStream) = await GetAttachment(request).ConfigureAwait(false); var mime = string.IsNullOrWhiteSpace(attachment.MIMEType) ? "application/octet-stream" : attachment.MIMEType; diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index dda8d397a..09e6fda88 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -41,21 +41,14 @@ namespace MediaBrowser.Controller.Library /// /// Gets the media attachments. /// - /// The item identifier. + /// The item identifier. /// IEnumerable<MediaAttachment>. List GetMediaAttachments(Guid itemId); /// /// Gets the media attachments. /// - /// The The media source identifier. - /// IEnumerable<MediaAttachment>. - - List GetMediaAttachments(string mediaSourceId); - /// - /// Gets the media attachments. - /// - /// The query. + /// The query. /// IEnumerable<MediaAttachment>. List GetMediaAttachments(MediaAttachmentQuery query); diff --git a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs index 59c0a3f21..7c7e84de6 100644 --- a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs +++ b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs @@ -2,14 +2,14 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.MediaEncoding { public interface IAttachmentExtractor { - Task<(MediaAttachment attachment, Stream stream)> GetAttachment(BaseItem item, + Task<(MediaAttachment attachment, Stream stream)> GetAttachment( + BaseItem item, string mediaSourceId, int attachmentStreamIndex, CancellationToken cancellationToken); diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index cb22343c4..c371e8b94 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -4,44 +4,41 @@ using System.Collections.Concurrent; using System.Globalization; using System.IO; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; -using UtfUnknown; namespace MediaBrowser.MediaEncoding.Attachments { - public class AttachmentExtractor : IAttachmentExtractor + public class AttachmentExtractor : IAttachmentExtractor, IDisposable { - private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; private readonly IMediaEncoder _mediaEncoder; private readonly IMediaSourceManager _mediaSourceManager; + private readonly ConcurrentDictionary _semaphoreLocks = + new ConcurrentDictionary(); + + private bool _disposed = false; + public AttachmentExtractor( - ILibraryManager libraryManager, ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IMediaSourceManager mediaSourceManager) { - _libraryManager = libraryManager; _logger = logger; _appPaths = appPaths; _fileSystem = fileSystem; @@ -49,8 +46,7 @@ namespace MediaBrowser.MediaEncoding.Attachments _mediaSourceManager = mediaSourceManager; } - private string AttachmentCachePath => Path.Combine(_appPaths.DataPath, "attachments"); - + /// public async Task<(MediaAttachment attachment, Stream stream)> GetAttachment(BaseItem item, string mediaSourceId, int attachmentStreamIndex, CancellationToken cancellationToken) { if (item == null) @@ -70,12 +66,14 @@ namespace MediaBrowser.MediaEncoding.Attachments { throw new ResourceNotFoundException($"MediaSource {mediaSourceId} not found"); } + var mediaAttachment = mediaSource.MediaAttachments .FirstOrDefault(i => i.Index == attachmentStreamIndex); if (mediaAttachment == null) { throw new ResourceNotFoundException($"MediaSource {mediaSourceId} has no attachment with stream index {attachmentStreamIndex}"); } + var attachmentStream = await GetAttachmentStream(mediaSource, mediaAttachment, cancellationToken) .ConfigureAwait(false); @@ -87,49 +85,32 @@ namespace MediaBrowser.MediaEncoding.Attachments MediaAttachment mediaAttachment, CancellationToken cancellationToken) { - var inputFiles = new[] { mediaSource.Path }; - var attachmentPath = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, mediaAttachment, cancellationToken).ConfigureAwait(false); - var stream = await GetAttachmentStream(attachmentPath, cancellationToken).ConfigureAwait(false); - return stream; + var attachmentPath = await GetReadableFile(mediaSource.Path, mediaSource.Path, mediaSource.Protocol, mediaAttachment, cancellationToken).ConfigureAwait(false); + return File.OpenRead(attachmentPath); } - private async Task GetAttachmentStream( - string path, - CancellationToken cancellationToken) - { - return File.OpenRead(path); - } - - private async Task GetReadableFile( + private async Task GetReadableFile( string mediaPath, - string[] inputFiles, + string inputFile, MediaProtocol protocol, MediaAttachment mediaAttachment, CancellationToken cancellationToken) { var outputPath = GetAttachmentCachePath(mediaPath, protocol, mediaAttachment.Index); - await ExtractAttachment(inputFiles, protocol, mediaAttachment.Index, outputPath, cancellationToken) + await ExtractAttachment(inputFile, protocol, mediaAttachment.Index, outputPath, cancellationToken) .ConfigureAwait(false); return outputPath; } - private readonly ConcurrentDictionary _semaphoreLocks = - new ConcurrentDictionary(); - - private SemaphoreSlim GetLock(string filename) - { - return _semaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); - } - private async Task ExtractAttachment( - string[] inputFiles, + string inputFile, MediaProtocol protocol, int attachmentStreamIndex, string outputPath, CancellationToken cancellationToken) { - var semaphore = GetLock(outputPath); + var semaphore = _semaphoreLocks.GetOrAdd(outputPath, key => new SemaphoreSlim(1, 1)); await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -137,7 +118,11 @@ namespace MediaBrowser.MediaEncoding.Attachments { if (!File.Exists(outputPath)) { - await ExtractAttachmentInternal(_mediaEncoder.GetInputArgument(inputFiles, protocol), attachmentStreamIndex, outputPath, cancellationToken).ConfigureAwait(false); + await ExtractAttachmentInternal( + _mediaEncoder.GetInputArgument(new[] { inputFile }, protocol), + attachmentStreamIndex, + outputPath, + cancellationToken).ConfigureAwait(false); } } finally @@ -186,16 +171,7 @@ namespace MediaBrowser.MediaEncoding.Attachments _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments); - try - { - process.Start(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error starting ffmpeg"); - - throw; - } + process.Start(); var processTcs = new TaskCompletionSource(); process.EnableRaisingEvents = true; @@ -216,6 +192,7 @@ namespace MediaBrowser.MediaEncoding.Attachments _logger.LogError(ex, "Error killing attachment extraction process"); } } + var exitCode = ranToCompletion ? process.ExitCode : -1; process.Dispose(); @@ -270,9 +247,35 @@ namespace MediaBrowser.MediaEncoding.Attachments { filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D"); } + var prefix = filename.Substring(0, 1); - return Path.Combine(AttachmentCachePath, prefix, filename); + return Path.Combine(_appPaths.DataPath, "attachments", prefix, filename); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); } + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + if (disposing) + { + + } + + _disposed = true; + } } } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index d4aede572..c5da42089 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -43,7 +43,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// The path. /// System.String. - private static string GetFileInputArgument(string path) + public static string GetFileInputArgument(string path) { if (path.IndexOf("://") != -1) { -- cgit v1.2.3 From 8a0ef4103632b2888249af2f385e1e7bfc06fbfe Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 26 Dec 2019 23:20:31 +0100 Subject: Minor improvements --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 4 ++-- MediaBrowser.Controller/Persistence/IItemRepository.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs | 2 +- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 2 +- MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 10 +++++----- 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 91ca8477d..2ff19a639 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6211,7 +6211,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type public void SaveMediaAttachments( Guid id, - List attachments, + IReadOnlyList attachments, CancellationToken cancellationToken) { CheckDisposed(); @@ -6243,7 +6243,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type private void InsertMediaAttachments( byte[] idBlob, - List attachments, + IReadOnlyList attachments, IDatabaseConnection db, CancellationToken cancellationToken) { diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 68df20c3a..5a5b7f58f 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -91,7 +91,7 @@ namespace MediaBrowser.Controller.Persistence /// The identifier. /// The attachments. /// The cancellation token. - void SaveMediaAttachments(Guid id, List attachments, CancellationToken cancellationToken); + void SaveMediaAttachments(Guid id, IReadOnlyList attachments, CancellationToken cancellationToken); /// /// Gets the item ids. diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs index c5da42089..d4aede572 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs @@ -43,7 +43,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// The path. /// System.String. - public static string GetFileInputArgument(string path) + private static string GetFileInputArgument(string path) { if (path.IndexOf("://") != -1) { diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 8a1aa55b6..5cb056566 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Model.Dto public List MediaStreams { get; set; } - public List MediaAttachments { get; set; } + public IReadOnlyList MediaAttachments { get; set; } public string[] Formats { get; set; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index ae3e584d4..2b178d4d4 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -158,7 +158,7 @@ namespace MediaBrowser.Providers.MediaInfo MetadataRefreshOptions options) { List mediaStreams; - List mediaAttachments; + IReadOnlyList mediaAttachments; List chapters; if (mediaInfo != null) @@ -200,7 +200,7 @@ namespace MediaBrowser.Providers.MediaInfo else { mediaStreams = new List(); - mediaAttachments = new List(); + mediaAttachments = Array.Empty(); chapters = new List(); } @@ -213,13 +213,13 @@ namespace MediaBrowser.Providers.MediaInfo FetchEmbeddedInfo(video, mediaInfo, options, libraryOptions); FetchPeople(video, mediaInfo, options); video.Timestamp = mediaInfo.Timestamp; - video.Video3DFormat = video.Video3DFormat ?? mediaInfo.Video3DFormat; + video.Video3DFormat ??= mediaInfo.Video3DFormat; } var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); - video.Height = videoStream == null ? 0 : videoStream.Height ?? 0; - video.Width = videoStream == null ? 0 : videoStream.Width ?? 0; + video.Height = videoStream?.Height ?? 0; + video.Width = videoStream?.Width ?? 0; video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index; -- cgit v1.2.3 From 73fac50e57982ac46aea2b487e9906826c3dc3b2 Mon Sep 17 00:00:00 2001 From: dkanada Date: Wed, 8 Jan 2020 10:52:48 +0900 Subject: rename two properties based on code suggestions --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 8 ++++---- MediaBrowser.Api/Attachments/AttachmentService.cs | 2 +- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 4 ++-- MediaBrowser.Model/Entities/MediaAttachment.cs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2ff19a639..c514846e5 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -6288,8 +6288,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type statement.TryBind("@Codec" + index, attachment.Codec); statement.TryBind("@CodecTag" + index, attachment.CodecTag); statement.TryBind("@Comment" + index, attachment.Comment); - statement.TryBind("@Filename" + index, attachment.Filename); - statement.TryBind("@MIMEType" + index, attachment.MIMEType); + statement.TryBind("@FileName" + index, attachment.FileName); + statement.TryBind("@MimeType" + index, attachment.MimeType); } statement.Reset(); @@ -6327,12 +6327,12 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type if (reader[6].SQLiteType != SQLiteType.Null) { - item.Filename = reader[5].ToString(); + item.FileName = reader[5].ToString(); } if (reader[6].SQLiteType != SQLiteType.Null) { - item.MIMEType = reader[6].ToString(); + item.MimeType = reader[6].ToString(); } return item; diff --git a/MediaBrowser.Api/Attachments/AttachmentService.cs b/MediaBrowser.Api/Attachments/AttachmentService.cs index ef09951b6..1632ca1b0 100644 --- a/MediaBrowser.Api/Attachments/AttachmentService.cs +++ b/MediaBrowser.Api/Attachments/AttachmentService.cs @@ -45,7 +45,7 @@ namespace MediaBrowser.Api.Attachments public async Task Get(GetAttachment request) { var (attachment, attachmentStream) = await GetAttachment(request).ConfigureAwait(false); - var mime = string.IsNullOrWhiteSpace(attachment.MIMEType) ? "application/octet-stream" : attachment.MIMEType; + var mime = string.IsNullOrWhiteSpace(attachment.MimeType) ? "application/octet-stream" : attachment.MimeType; return ResultFactory.GetResult(Request, attachmentStream, mime); } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index f2056c566..6664b34a5 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -542,8 +542,8 @@ namespace MediaBrowser.MediaEncoding.Probing if (streamInfo.tags != null) { - attachment.Filename = GetDictionaryValue(streamInfo.tags, "filename"); - attachment.MIMEType = GetDictionaryValue(streamInfo.tags, "mimetype"); + attachment.FileName = GetDictionaryValue(streamInfo.tags, "filename"); + attachment.MimeType = GetDictionaryValue(streamInfo.tags, "mimetype"); attachment.Comment = GetDictionaryValue(streamInfo.tags, "comment"); } diff --git a/MediaBrowser.Model/Entities/MediaAttachment.cs b/MediaBrowser.Model/Entities/MediaAttachment.cs index 26279b72b..8f8c3efd2 100644 --- a/MediaBrowser.Model/Entities/MediaAttachment.cs +++ b/MediaBrowser.Model/Entities/MediaAttachment.cs @@ -33,13 +33,13 @@ namespace MediaBrowser.Model.Entities /// Gets or sets the filename. /// /// The filename. - public string Filename { get; set; } + public string FileName { get; set; } /// /// Gets or sets the MIME type. /// /// The MIME type. - public string MIMEType { get; set; } + public string MimeType { get; set; } /// /// Gets or sets the delivery URL. -- cgit v1.2.3 From 75f19a762cf4cf769df7545612e68e95bb6905f2 Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Wed, 8 Jan 2020 04:05:07 -0500 Subject: Re-order the path statement to avoid file issues Fixes #31874. --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 442fbabd1..9568f62df 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -109,7 +109,7 @@ namespace Emby.Server.Implementations.IO } try { - return Path.Combine(Path.GetFullPath(folderPath), filePath); + return Path.GetFullPath(Path.Combine(folderPath, filePath)); } catch (ArgumentException) { -- cgit v1.2.3 From fdbb32911835b5ed8e39f518ccc20b0a26bd8bab Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 9 Jan 2020 17:07:13 +0100 Subject: Remove StringHelper functions --- DvdLib/DvdLib.csproj | 2 +- Emby.Dlna/Didl/Filter.cs | 2 +- .../LiveTv/EmbyTV/EmbyTV.cs | 2 +- MediaBrowser.Model/Dlna/ConditionProcessor.cs | 4 +- MediaBrowser.Model/Dlna/DeviceProfile.cs | 6 +- .../Dlna/MediaFormatProfileResolver.cs | 144 ++++++++++----------- MediaBrowser.Model/Dlna/ResolutionNormalizer.cs | 6 +- MediaBrowser.Model/Dlna/SearchCriteria.cs | 12 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 10 +- MediaBrowser.Model/Dlna/StreamInfo.cs | 28 ++-- MediaBrowser.Model/Entities/MediaStream.cs | 16 +-- MediaBrowser.Model/Extensions/ListHelper.cs | 1 + MediaBrowser.Model/Extensions/StringHelper.cs | 63 ++++----- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- MediaBrowser.Model/Net/MimeTypes.cs | 10 +- .../Notifications/NotificationOptions.cs | 2 +- 16 files changed, 146 insertions(+), 164 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj index 9dbaa9e2f..f4df6a9f5 100644 --- a/DvdLib/DvdLib.csproj +++ b/DvdLib/DvdLib.csproj @@ -9,7 +9,7 @@ - netstandard2.0 + netstandard2.1 false true diff --git a/Emby.Dlna/Didl/Filter.cs b/Emby.Dlna/Didl/Filter.cs index a0e67870e..e7f9577ee 100644 --- a/Emby.Dlna/Didl/Filter.cs +++ b/Emby.Dlna/Didl/Filter.cs @@ -16,7 +16,7 @@ namespace Emby.Dlna.Didl public Filter(string filter) { - _all = StringHelper.EqualsIgnoreCase(filter, "*"); + _all = string.Equals(filter, "*", StringComparison.OrdinalIgnoreCase); _fields = (filter ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 3b6bfce6a..4e09f90b4 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -458,7 +458,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { foreach (NameValuePair mapping in mappings) { - if (StringHelper.EqualsIgnoreCase(mapping.Name, channelId)) + if (string.Equals(mapping.Name, channelId, StringComparison.OrdinalIgnoreCase)) { return mapping.Value; } diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index 3629d1547..caaceda1b 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -169,9 +169,9 @@ namespace MediaBrowser.Model.Dlna return ListHelper.ContainsIgnoreCase(expected.Split('|'), currentValue); } case ProfileConditionType.Equals: - return StringHelper.EqualsIgnoreCase(currentValue, expected); + return string.Equals(currentValue, expected, StringComparison.OrdinalIgnoreCase); case ProfileConditionType.NotEquals: - return !StringHelper.EqualsIgnoreCase(currentValue, expected); + return !string.Equals(currentValue, expected, StringComparison.OrdinalIgnoreCase); default: throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition); } diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 8d8fe9eb5..f152ee880 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -122,7 +122,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (!StringHelper.EqualsIgnoreCase(container, i.Container)) + if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) { continue; } @@ -148,7 +148,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (!StringHelper.EqualsIgnoreCase(container, i.Container)) + if (!string.Equals(container, i.Container, StringComparison.OrdinalIgnoreCase)) { continue; } @@ -158,7 +158,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (!StringHelper.EqualsIgnoreCase(videoCodec, i.VideoCodec ?? string.Empty)) + if (!string.Equals(videoCodec, i.VideoCodec ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; } diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs index 672784589..333cab60d 100644 --- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs +++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs @@ -17,53 +17,53 @@ namespace MediaBrowser.Model.Dlna private MediaFormatProfile[] ResolveVideoFormatInternal(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType) { - if (StringHelper.EqualsIgnoreCase(container, "asf")) + if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase)) { MediaFormatProfile? val = ResolveVideoASFFormat(videoCodec, audioCodec, width, height); return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { }; } - if (StringHelper.EqualsIgnoreCase(container, "mp4")) + if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase)) { MediaFormatProfile? val = ResolveVideoMP4Format(videoCodec, audioCodec, width, height); return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { }; } - if (StringHelper.EqualsIgnoreCase(container, "avi")) + if (string.Equals(container, "avi", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { MediaFormatProfile.AVI }; - if (StringHelper.EqualsIgnoreCase(container, "mkv")) + if (string.Equals(container, "mkv", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { MediaFormatProfile.MATROSKA }; - if (StringHelper.EqualsIgnoreCase(container, "mpeg2ps") || - StringHelper.EqualsIgnoreCase(container, "ts")) + if (string.Equals(container, "mpeg2ps", StringComparison.OrdinalIgnoreCase) || + string.Equals(container, "ts", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { MediaFormatProfile.MPEG_PS_NTSC, MediaFormatProfile.MPEG_PS_PAL }; - if (StringHelper.EqualsIgnoreCase(container, "mpeg1video")) + if (string.Equals(container, "mpeg1video", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { MediaFormatProfile.MPEG1 }; - if (StringHelper.EqualsIgnoreCase(container, "mpeg2ts") || - StringHelper.EqualsIgnoreCase(container, "mpegts") || - StringHelper.EqualsIgnoreCase(container, "m2ts")) + if (string.Equals(container, "mpeg2ts", StringComparison.OrdinalIgnoreCase) || + string.Equals(container, "mpegts", StringComparison.OrdinalIgnoreCase) || + string.Equals(container, "m2ts", StringComparison.OrdinalIgnoreCase)) { return ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, timestampType); } - if (StringHelper.EqualsIgnoreCase(container, "flv")) + if (string.Equals(container, "flv", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { MediaFormatProfile.FLV }; - if (StringHelper.EqualsIgnoreCase(container, "wtv")) + if (string.Equals(container, "wtv", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { MediaFormatProfile.WTV }; - if (StringHelper.EqualsIgnoreCase(container, "3gp")) + if (string.Equals(container, "3gp", StringComparison.OrdinalIgnoreCase)) { MediaFormatProfile? val = ResolveVideo3GPFormat(videoCodec, audioCodec); return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { }; } - if (StringHelper.EqualsIgnoreCase(container, "ogv") || StringHelper.EqualsIgnoreCase(container, "ogg")) + if (string.Equals(container, "ogv", StringComparison.OrdinalIgnoreCase) || string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { MediaFormatProfile.OGV }; return new MediaFormatProfile[] { }; @@ -89,7 +89,7 @@ namespace MediaBrowser.Model.Dlna resolution = "H"; } - if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video")) + if (string.Equals(videoCodec, "mpeg2video", StringComparison.OrdinalIgnoreCase)) { var list = new List(); @@ -97,18 +97,18 @@ namespace MediaBrowser.Model.Dlna list.Add(ValueOf("MPEG_TS_SD_EU" + suffix)); list.Add(ValueOf("MPEG_TS_SD_KO" + suffix)); - if ((timestampType == TransportStreamTimestamp.Valid) && StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + if ((timestampType == TransportStreamTimestamp.Valid) && string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) { list.Add(MediaFormatProfile.MPEG_TS_JP_T); } return list.ToArray(); } - if (StringHelper.EqualsIgnoreCase(videoCodec, "h264")) + if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) { - if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm")) + if (string.Equals(audioCodec, "lpcm", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_50_LPCM_T }; - if (StringHelper.EqualsIgnoreCase(audioCodec, "dts")) + if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase)) { if (timestampType == TransportStreamTimestamp.None) { @@ -117,7 +117,7 @@ namespace MediaBrowser.Model.Dlna return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_T }; } - if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2")) + if (string.Equals(audioCodec, "mp2", StringComparison.OrdinalIgnoreCase)) { if (timestampType == TransportStreamTimestamp.None) { @@ -127,19 +127,19 @@ namespace MediaBrowser.Model.Dlna return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) }; } - if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) }; - if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3")) + if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) }; if (string.IsNullOrEmpty(audioCodec) || - StringHelper.EqualsIgnoreCase(audioCodec, "ac3")) + string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) }; } - else if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1")) + else if (string.Equals(videoCodec, "vc1", StringComparison.OrdinalIgnoreCase)) { - if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "ac3")) + if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) { if ((width.HasValue && width.Value > 720) || (height.HasValue && height.Value > 576)) { @@ -147,23 +147,23 @@ namespace MediaBrowser.Model.Dlna } return new MediaFormatProfile[] { MediaFormatProfile.VC1_TS_AP_L1_AC3_ISO }; } - if (StringHelper.EqualsIgnoreCase(audioCodec, "dts")) + if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase)) { - suffix = StringHelper.EqualsIgnoreCase(suffix, "_ISO") ? suffix : "_T"; + suffix = string.Equals(suffix, "_ISO", StringComparison.OrdinalIgnoreCase) ? suffix : "_T"; return new MediaFormatProfile[] { ValueOf(string.Format("VC1_TS_HD_DTS{0}", suffix)) }; } } - else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") || StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4")) + else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) { - if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AAC{0}", suffix)) }; - if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3")) + if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) }; - if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2")) + if (string.Equals(audioCodec, "mp2", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) }; - if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3")) + if (string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AC3{0}", suffix)) }; } @@ -177,16 +177,16 @@ namespace MediaBrowser.Model.Dlna private MediaFormatProfile? ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height) { - if (StringHelper.EqualsIgnoreCase(videoCodec, "h264")) + if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) { - if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm")) + if (string.Equals(audioCodec, "lpcm", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.AVC_MP4_LPCM; if (string.IsNullOrEmpty(audioCodec) || - StringHelper.EqualsIgnoreCase(audioCodec, "ac3")) + string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.AVC_MP4_MP_SD_AC3; } - if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3")) + if (string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.AVC_MP4_MP_SD_MPEG1_L3; } @@ -194,41 +194,41 @@ namespace MediaBrowser.Model.Dlna { if ((width.Value <= 720) && (height.Value <= 576)) { - if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.AVC_MP4_MP_SD_AAC_MULT5; } else if ((width.Value <= 1280) && (height.Value <= 720)) { - if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.AVC_MP4_MP_HD_720p_AAC; } else if ((width.Value <= 1920) && (height.Value <= 1080)) { - if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.AVC_MP4_MP_HD_1080i_AAC; } } } } - else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") || - StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4")) + else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) || + string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) { if (width.HasValue && height.HasValue && width.Value <= 720 && height.Value <= 576) { - if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.MPEG4_P2_MP4_ASP_AAC; - if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3") || StringHelper.EqualsIgnoreCase(audioCodec, "mp3")) + if (string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase) || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.MPEG4_P2_MP4_NDSD; } } - else if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + else if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.MPEG4_P2_MP4_SP_L6_AAC; } } - else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + else if (string.Equals(videoCodec, "h263", StringComparison.OrdinalIgnoreCase) && string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.MPEG4_H263_MP4_P0_L10_AAC; } @@ -238,20 +238,20 @@ namespace MediaBrowser.Model.Dlna private MediaFormatProfile? ResolveVideo3GPFormat(string videoCodec, string audioCodec) { - if (StringHelper.EqualsIgnoreCase(videoCodec, "h264")) + if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase)) { - if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.AVC_3GPP_BL_QCIF15_AAC; } - else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") || - StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4")) + else if (string.Equals(videoCodec, "mpeg4", StringComparison.OrdinalIgnoreCase) || + string.Equals(videoCodec, "msmpeg4", StringComparison.OrdinalIgnoreCase)) { - if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma")) + if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AAC; - if (StringHelper.EqualsIgnoreCase(audioCodec, "amrnb")) + if (string.Equals(audioCodec, "amrnb", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AMR; } - else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "amrnb")) + else if (string.Equals(videoCodec, "h263", StringComparison.OrdinalIgnoreCase) && string.Equals(audioCodec, "amrnb", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.MPEG4_H263_3GPP_P0_L10_AMR; } @@ -261,15 +261,15 @@ namespace MediaBrowser.Model.Dlna private MediaFormatProfile? ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height) { - if (StringHelper.EqualsIgnoreCase(videoCodec, "wmv") && - (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma") || StringHelper.EqualsIgnoreCase(videoCodec, "wmapro"))) + if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase) && + (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "wmapro", StringComparison.OrdinalIgnoreCase))) { if (width.HasValue && height.HasValue) { if ((width.Value <= 720) && (height.Value <= 576)) { - if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma")) + if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.WMVMED_FULL; } @@ -277,14 +277,14 @@ namespace MediaBrowser.Model.Dlna } } - if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma")) + if (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.WMVHIGH_FULL; } return MediaFormatProfile.WMVHIGH_PRO; } - if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1")) + if (string.Equals(videoCodec, "vc1", StringComparison.OrdinalIgnoreCase)) { if (width.HasValue && height.HasValue) { @@ -296,7 +296,7 @@ namespace MediaBrowser.Model.Dlna return MediaFormatProfile.VC1_ASF_AP_L3_WMA; } } - else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video")) + else if (string.Equals(videoCodec, "mpeg2video", StringComparison.OrdinalIgnoreCase)) { return MediaFormatProfile.DVR_MS; } @@ -306,27 +306,27 @@ namespace MediaBrowser.Model.Dlna public MediaFormatProfile? ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels) { - if (StringHelper.EqualsIgnoreCase(container, "asf")) + if (string.Equals(container, "asf", StringComparison.OrdinalIgnoreCase)) return ResolveAudioASFFormat(bitrate); - if (StringHelper.EqualsIgnoreCase(container, "mp3")) + if (string.Equals(container, "mp3", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.MP3; - if (StringHelper.EqualsIgnoreCase(container, "lpcm")) + if (string.Equals(container, "lpcm", StringComparison.OrdinalIgnoreCase)) return ResolveAudioLPCMFormat(frequency, channels); - if (StringHelper.EqualsIgnoreCase(container, "mp4") || - StringHelper.EqualsIgnoreCase(container, "aac")) + if (string.Equals(container, "mp4", StringComparison.OrdinalIgnoreCase) || + string.Equals(container, "aac", StringComparison.OrdinalIgnoreCase)) return ResolveAudioMP4Format(bitrate); - if (StringHelper.EqualsIgnoreCase(container, "adts")) + if (string.Equals(container, "adts", StringComparison.OrdinalIgnoreCase)) return ResolveAudioADTSFormat(bitrate); - if (StringHelper.EqualsIgnoreCase(container, "flac")) + if (string.Equals(container, "flac", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.FLAC; - if (StringHelper.EqualsIgnoreCase(container, "oga") || - StringHelper.EqualsIgnoreCase(container, "ogg")) + if (string.Equals(container, "oga", StringComparison.OrdinalIgnoreCase) || + string.Equals(container, "ogg", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.OGG; return null; @@ -388,17 +388,17 @@ namespace MediaBrowser.Model.Dlna public MediaFormatProfile? ResolveImageFormat(string container, int? width, int? height) { - if (StringHelper.EqualsIgnoreCase(container, "jpeg") || - StringHelper.EqualsIgnoreCase(container, "jpg")) + if (string.Equals(container, "jpeg", StringComparison.OrdinalIgnoreCase) || + string.Equals(container, "jpg", StringComparison.OrdinalIgnoreCase)) return ResolveImageJPGFormat(width, height); - if (StringHelper.EqualsIgnoreCase(container, "png")) + if (string.Equals(container, "png", StringComparison.OrdinalIgnoreCase)) return ResolveImagePNGFormat(width, height); - if (StringHelper.EqualsIgnoreCase(container, "gif")) + if (string.Equals(container, "gif", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.GIF_LRG; - if (StringHelper.EqualsIgnoreCase(container, "raw")) + if (string.Equals(container, "raw", StringComparison.OrdinalIgnoreCase)) return MediaFormatProfile.RAW; return null; diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs index cf92633c3..8a00f4ae4 100644 --- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs +++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs @@ -76,9 +76,9 @@ namespace MediaBrowser.Model.Dlna private static double GetVideoBitrateScaleFactor(string codec) { - if (StringHelper.EqualsIgnoreCase(codec, "h265") || - StringHelper.EqualsIgnoreCase(codec, "hevc") || - StringHelper.EqualsIgnoreCase(codec, "vp9")) + if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || + string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) || + string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)) { return .5; } diff --git a/MediaBrowser.Model/Dlna/SearchCriteria.cs b/MediaBrowser.Model/Dlna/SearchCriteria.cs index 4f47c2821..891993881 100644 --- a/MediaBrowser.Model/Dlna/SearchCriteria.cs +++ b/MediaBrowser.Model/Dlna/SearchCriteria.cs @@ -48,22 +48,22 @@ namespace MediaBrowser.Model.Dlna if (subFactors.Length == 3) { - if (StringHelper.EqualsIgnoreCase("upnp:class", subFactors[0]) && - (StringHelper.EqualsIgnoreCase("=", subFactors[1]) || StringHelper.EqualsIgnoreCase("derivedfrom", subFactors[1]))) + if (string.Equals("upnp:class", subFactors[0], StringComparison.OrdinalIgnoreCase) && + (string.Equals("=", subFactors[1]) || string.Equals("derivedfrom", subFactors[1], StringComparison.OrdinalIgnoreCase))) { - if (StringHelper.EqualsIgnoreCase("\"object.item.imageItem\"", subFactors[2]) || StringHelper.EqualsIgnoreCase("\"object.item.imageItem.photo\"", subFactors[2])) + if (string.Equals("\"object.item.imageItem\"", subFactors[2]) || string.Equals("\"object.item.imageItem.photo\"", subFactors[2], StringComparison.OrdinalIgnoreCase)) { SearchType = SearchType.Image; } - else if (StringHelper.EqualsIgnoreCase("\"object.item.videoItem\"", subFactors[2])) + else if (string.Equals("\"object.item.videoItem\"", subFactors[2], StringComparison.OrdinalIgnoreCase)) { SearchType = SearchType.Video; } - else if (StringHelper.EqualsIgnoreCase("\"object.container.playlistContainer\"", subFactors[2])) + else if (string.Equals("\"object.container.playlistContainer\"", subFactors[2], StringComparison.OrdinalIgnoreCase)) { SearchType = SearchType.Playlist; } - else if (StringHelper.EqualsIgnoreCase("\"object.container.album.musicAlbum\"", subFactors[2])) + else if (string.Equals("\"object.container.album.musicAlbum\"", subFactors[2], StringComparison.OrdinalIgnoreCase)) { SearchType = SearchType.MusicAlbum; } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 586e322e4..e039ac5d6 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.Model.Dlna foreach (MediaSourceInfo i in options.MediaSources) { if (string.IsNullOrEmpty(options.MediaSourceId) || - StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId)) + string.Equals(i.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase)) { mediaSources.Add(i); } @@ -68,7 +68,7 @@ namespace MediaBrowser.Model.Dlna foreach (MediaSourceInfo i in options.MediaSources) { if (string.IsNullOrEmpty(options.MediaSourceId) || - StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId)) + string.Equals(i.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase)) { mediaSources.Add(i); } @@ -582,7 +582,7 @@ namespace MediaBrowser.Model.Dlna { foreach (var profile in subtitleProfiles) { - if (profile.Method == SubtitleDeliveryMethod.External && StringHelper.EqualsIgnoreCase(profile.Format, stream.Codec)) + if (profile.Method == SubtitleDeliveryMethod.External && string.Equals(profile.Format, stream.Codec, StringComparison.OrdinalIgnoreCase)) { return stream.Index; } @@ -1198,7 +1198,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format) && StringHelper.EqualsIgnoreCase(profile.Format, subtitleStream.Codec)) + if (subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format) && string.Equals(profile.Format, subtitleStream.Codec, StringComparison.OrdinalIgnoreCase)) { return profile; } @@ -1292,7 +1292,7 @@ namespace MediaBrowser.Model.Dlna if ((profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) || (profile.Method == SubtitleDeliveryMethod.Hls && subtitleStream.IsTextSubtitleStream)) { - bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format); + bool requiresConversion = !string.Equals(subtitleStream.Codec, profile.Format, StringComparison.OrdinalIgnoreCase); if (!requiresConversion) { diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 10efb9b38..7962b51ce 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -153,18 +153,18 @@ namespace MediaBrowser.Model.Dlna } // Try to keep the url clean by omitting defaults - if (StringHelper.EqualsIgnoreCase(pair.Name, "StartTimeTicks") && - StringHelper.EqualsIgnoreCase(pair.Value, "0")) + if (string.Equals(pair.Name, "StartTimeTicks", StringComparison.OrdinalIgnoreCase) && + string.Equals(pair.Value, "0", StringComparison.OrdinalIgnoreCase)) { continue; } - if (StringHelper.EqualsIgnoreCase(pair.Name, "SubtitleStreamIndex") && - StringHelper.EqualsIgnoreCase(pair.Value, "-1")) + if (string.Equals(pair.Name, "SubtitleStreamIndex", StringComparison.OrdinalIgnoreCase) && + string.Equals(pair.Value, "-1", StringComparison.OrdinalIgnoreCase)) { continue; } - if (StringHelper.EqualsIgnoreCase(pair.Name, "Static") && - StringHelper.EqualsIgnoreCase(pair.Value, "false")) + if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) && + string.Equals(pair.Value, "false", StringComparison.OrdinalIgnoreCase)) { continue; } @@ -192,7 +192,7 @@ namespace MediaBrowser.Model.Dlna if (MediaType == DlnaProfileType.Audio) { - if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls")) + if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) { return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); } @@ -200,7 +200,7 @@ namespace MediaBrowser.Model.Dlna return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); } - if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls")) + if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)) { return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); } @@ -237,7 +237,7 @@ namespace MediaBrowser.Model.Dlna long startPositionTicks = item.StartPositionTicks; - var isHls = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls"); + var isHls = string.Equals(item.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase); if (isHls) { @@ -370,7 +370,7 @@ namespace MediaBrowser.Model.Dlna var list = new List(); // HLS will preserve timestamps so we can just grab the full subtitle stream - long startPositionTicks = StringHelper.EqualsIgnoreCase(SubProtocol, "hls") + long startPositionTicks = string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase) ? 0 : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0); @@ -435,7 +435,7 @@ namespace MediaBrowser.Model.Dlna if (info.DeliveryMethod == SubtitleDeliveryMethod.External) { - if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format) || !stream.IsExternal) + if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal) { info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", baseUrl, @@ -802,7 +802,7 @@ namespace MediaBrowser.Model.Dlna foreach (string codec in AudioCodecs) { - if (StringHelper.EqualsIgnoreCase(codec, inputCodec)) + if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) { return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec }; } @@ -827,7 +827,7 @@ namespace MediaBrowser.Model.Dlna foreach (string codec in VideoCodecs) { - if (StringHelper.EqualsIgnoreCase(codec, inputCodec)) + if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase)) { return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec }; } @@ -884,7 +884,7 @@ namespace MediaBrowser.Model.Dlna { get { - var defaultValue = StringHelper.EqualsIgnoreCase(Container, "m2ts") + var defaultValue = string.Equals(Container, "m2ts", StringComparison.OrdinalIgnoreCase) ? TransportStreamTimestamp.Valid : TransportStreamTimestamp.None; diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 5652962da..5122f42b8 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -88,11 +88,11 @@ namespace MediaBrowser.Model.Entities { attributes.Add(StringHelper.FirstToUpper(Language)); } - if (!string.IsNullOrEmpty(Codec) && !StringHelper.EqualsIgnoreCase(Codec, "dca")) + if (!string.IsNullOrEmpty(Codec) && !string.Equals(Codec, "dca", StringComparison.OrdinalIgnoreCase)) { attributes.Add(AudioCodec.GetFriendlyName(Codec)); } - else if (!string.IsNullOrEmpty(Profile) && !StringHelper.EqualsIgnoreCase(Profile, "lc")) + else if (!string.IsNullOrEmpty(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase)) { attributes.Add(Profile); } @@ -394,8 +394,8 @@ namespace MediaBrowser.Model.Entities return codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) == -1 && codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) == -1 && codec.IndexOf("dvbsub", StringComparison.OrdinalIgnoreCase) == -1 && - !StringHelper.EqualsIgnoreCase(codec, "sub") && - !StringHelper.EqualsIgnoreCase(codec, "dvb_subtitle"); + !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase) && + !string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase); } public bool SupportsSubtitleConversionTo(string toCodec) @@ -408,21 +408,21 @@ namespace MediaBrowser.Model.Entities var fromCodec = Codec; // Can't convert from this - if (StringHelper.EqualsIgnoreCase(fromCodec, "ass")) + if (string.Equals(fromCodec, "ass", StringComparison.OrdinalIgnoreCase)) { return false; } - if (StringHelper.EqualsIgnoreCase(fromCodec, "ssa")) + if (string.Equals(fromCodec, "ssa", StringComparison.OrdinalIgnoreCase)) { return false; } // Can't convert to this - if (StringHelper.EqualsIgnoreCase(toCodec, "ass")) + if (string.Equals(toCodec, "ass", StringComparison.OrdinalIgnoreCase)) { return false; } - if (StringHelper.EqualsIgnoreCase(toCodec, "ssa")) + if (string.Equals(toCodec, "ssa", StringComparison.OrdinalIgnoreCase)) { return false; } diff --git a/MediaBrowser.Model/Extensions/ListHelper.cs b/MediaBrowser.Model/Extensions/ListHelper.cs index b5bd07702..d751f77a7 100644 --- a/MediaBrowser.Model/Extensions/ListHelper.cs +++ b/MediaBrowser.Model/Extensions/ListHelper.cs @@ -2,6 +2,7 @@ using System; namespace MediaBrowser.Model.Extensions { + // TODO: @bond remove public static class ListHelper { public static bool ContainsIgnoreCase(string[] list, string value) diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs index 75ba12a17..9ce1cf79f 100644 --- a/MediaBrowser.Model/Extensions/StringHelper.cs +++ b/MediaBrowser.Model/Extensions/StringHelper.cs @@ -1,57 +1,38 @@ -using System; -using System.Text; - namespace MediaBrowser.Model.Extensions { /// - /// Isolating these helpers allow this entire project to be easily converted to Java + /// Helper methods for manipulating strings. /// public static class StringHelper { /// - /// Equalses the ignore case. + /// Returns the string with the first character inheritdoc uppercase. /// - /// The STR1. - /// The STR2. - /// true if XXXX, false otherwise. - public static bool EqualsIgnoreCase(string str1, string str2) + /// The input string. + /// The string with the first character inheritdoc uppercase. + public static string FirstToUpper(string str) { - return string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase); - } - - /// - /// Replaces the specified STR. - /// - /// The STR. - /// The old value. - /// The new value. - /// The comparison. - /// System.String. - public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) - { - var sb = new StringBuilder(); - - var previousIndex = 0; - var index = str.IndexOf(oldValue, comparison); - - while (index != -1) + if (string.IsNullOrEmpty(str)) { - sb.Append(str.Substring(previousIndex, index - previousIndex)); - sb.Append(newValue); - index += oldValue.Length; - - previousIndex = index; - index = str.IndexOf(oldValue, index, comparison); + return string.Empty; } - sb.Append(str.Substring(previousIndex)); - - return sb.ToString(); - } + if (char.IsUpper(str[0])) + { + return str; + } - public static string FirstToUpper(this string str) - { - return string.IsNullOrEmpty(str) ? string.Empty : str.Substring(0, 1).ToUpperInvariant() + str.Substring(1); + return string.Create( + str.Length, + str, + (chars, buf) => + { + chars[0] = char.ToUpperInvariant(buf[0]); + for (int i = 1; i - netstandard2.0 + netstandard2.1 false true diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index de5e58d22..42fff3775 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -165,20 +165,20 @@ namespace MediaBrowser.Model.Net } // Type text - if (StringHelper.EqualsIgnoreCase(ext, ".html") - || StringHelper.EqualsIgnoreCase(ext, ".htm")) + if (string.Equals(ext, ".html", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".htm", StringComparison.OrdinalIgnoreCase)) { return "text/html; charset=UTF-8"; } - if (StringHelper.EqualsIgnoreCase(ext, ".log") - || StringHelper.EqualsIgnoreCase(ext, ".srt")) + if (string.Equals(ext, ".log", StringComparison.OrdinalIgnoreCase) + || string.Equals(ext, ".srt", StringComparison.OrdinalIgnoreCase)) { return "text/plain"; } // Misc - if (StringHelper.EqualsIgnoreCase(ext, ".dll")) + if (string.Equals(ext, ".dll", StringComparison.OrdinalIgnoreCase)) { return "application/octet-stream"; } diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs index f48b5ee7f..38600b9c8 100644 --- a/MediaBrowser.Model/Notifications/NotificationOptions.cs +++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs @@ -79,7 +79,7 @@ namespace MediaBrowser.Model.Notifications { foreach (NotificationOption i in Options) { - if (StringHelper.EqualsIgnoreCase(type, i.Type)) return i; + if (string.Equals(type, i.Type, StringComparison.OrdinalIgnoreCase)) return i; } return null; } -- cgit v1.2.3 From 0a92200c6fe0adc6c28294e0c7dca7029973d1b7 Mon Sep 17 00:00:00 2001 From: keitaro62 Date: Thu, 9 Jan 2020 10:54:06 +0000 Subject: Translated using Weblate (French) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/ --- Emby.Server.Implementations/Localization/Core/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index e1dce82ff..d6d5bba72 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -5,7 +5,7 @@ "Artists": "Artistes", "AuthenticationSucceededWithUserName": "{0} s'est authentifié avec succès", "Books": "Livres", - "CameraImageUploadedFrom": "Une nouvelle image de caméra a été chargée depuis {0}", + "CameraImageUploadedFrom": "Une nouvelle image d'appareil photo a été chargée depuis {0}", "Channels": "Chaînes", "ChapterNameValue": "Chapitre {0}", "Collections": "Collections", -- cgit v1.2.3 From 81ae6d8afda06a77f5e3a8647c9432959bd191a6 Mon Sep 17 00:00:00 2001 From: Philmo67 Date: Fri, 10 Jan 2020 15:17:02 +0000 Subject: Translated using Weblate (French) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/ --- Emby.Server.Implementations/Localization/Core/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index d6d5bba72..6b25e3df0 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -5,7 +5,7 @@ "Artists": "Artistes", "AuthenticationSucceededWithUserName": "{0} s'est authentifié avec succès", "Books": "Livres", - "CameraImageUploadedFrom": "Une nouvelle image d'appareil photo a été chargée depuis {0}", + "CameraImageUploadedFrom": "Une nouvelle photo a été chargée depuis {0}", "Channels": "Chaînes", "ChapterNameValue": "Chapitre {0}", "Collections": "Collections", -- cgit v1.2.3 From 0a39e54984ae2eb7ab9ec3382a9122a6de9e56b1 Mon Sep 17 00:00:00 2001 From: archon eleven Date: Fri, 10 Jan 2020 06:34:37 +0000 Subject: Translated using Weblate (Malay) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ms/ --- .../Localization/Core/ms.json | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index c10fbe58f..b91c98ca0 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -1,23 +1,23 @@ { "Albums": "Album-album", "AppDeviceValues": "App: {0}, Device: {1}", - "Application": "Application", + "Application": "Aplikasi", "Artists": "Artis-artis", "AuthenticationSucceededWithUserName": "{0} successfully authenticated", "Books": "Buku-buku", "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", - "Channels": "Channels", + "Channels": "Saluran", "ChapterNameValue": "Chapter {0}", - "Collections": "Collections", + "Collections": "Koleksi", "DeviceOfflineWithName": "{0} has disconnected", "DeviceOnlineWithName": "{0} is connected", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", + "FailedLoginAttemptWithUserName": "Cubaan log masuk gagal dari {0}", "Favorites": "Favorites", "Folders": "Folders", - "Genres": "Genres", + "Genres": "Genre-genre", "HeaderAlbumArtists": "Album Artists", - "HeaderCameraUploads": "Camera Uploads", - "HeaderContinueWatching": "Continue Watching", + "HeaderCameraUploads": "Muatnaik Kamera", + "HeaderContinueWatching": "Terus Menonton", "HeaderFavoriteAlbums": "Favorite Albums", "HeaderFavoriteArtists": "Favorite Artists", "HeaderFavoriteEpisodes": "Favorite Episodes", @@ -39,8 +39,8 @@ "MessageServerConfigurationUpdated": "Server configuration has been updated", "MixedContent": "Mixed content", "Movies": "Movies", - "Music": "Music", - "MusicVideos": "Music videos", + "Music": "Muzik", + "MusicVideos": "Video muzik", "NameInstallFailed": "{0} installation failed", "NameSeasonNumber": "Season {0}", "NameSeasonUnknown": "Season Unknown", @@ -68,8 +68,8 @@ "PluginUninstalledWithName": "{0} was uninstalled", "PluginUpdatedWithName": "{0} was updated", "ProviderValue": "Provider: {0}", - "ScheduledTaskFailedWithName": "{0} failed", - "ScheduledTaskStartedWithName": "{0} started", + "ScheduledTaskFailedWithName": "{0} gagal", + "ScheduledTaskStartedWithName": "{0} bermula", "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", "Shows": "Series", "Songs": "Songs", @@ -78,7 +78,7 @@ "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", "Sync": "Sync", - "System": "System", + "System": "Sistem", "TvShows": "TV Shows", "User": "User", "UserCreatedWithName": "User {0} has been created", @@ -92,6 +92,6 @@ "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", - "ValueSpecialEpisodeName": "Special - {0}", - "VersionNumber": "Version {0}" + "ValueSpecialEpisodeName": "Khas - {0}", + "VersionNumber": "Versi {0}" } -- cgit v1.2.3 From fa1aeeb18a766fd7b2265dc0352ee100d8377163 Mon Sep 17 00:00:00 2001 From: Slobodan Simić Date: Fri, 10 Jan 2020 20:27:17 +0000 Subject: Added translation using Weblate (Serbian) --- Emby.Server.Implementations/Localization/Core/sr.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/sr.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/sr.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From abf03f7d3ad14f026fd439f522403353a7ecec86 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 21:31:35 +0100 Subject: Clean up some more --- Emby.Naming/Video/CleanDateTimeParser.cs | 19 +++++-------------- Emby.Naming/Video/VideoResolver.cs | 2 +- .../Library/LibraryManager.cs | 21 ++++++--------------- 3 files changed, 12 insertions(+), 30 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index 84a14a54f..6c74c07d5 100644 --- a/Emby.Naming/Video/CleanDateTimeParser.cs +++ b/Emby.Naming/Video/CleanDateTimeParser.cs @@ -2,33 +2,24 @@ #pragma warning disable SA1600 #nullable enable +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; -using Emby.Naming.Common; namespace Emby.Naming.Video { /// /// . /// - public class CleanDateTimeParser + public static class CleanDateTimeParser { - private readonly NamingOptions _options; - - public CleanDateTimeParser(NamingOptions options) + public static CleanDateTimeResult Clean(string name, IReadOnlyList cleanDateTimeRegexes) { - _options = options; - } - - public CleanDateTimeResult Clean(string name) - { - var regexes = _options.CleanDateTimeRegexes; - var len = regexes.Length; - CleanDateTimeResult result = new CleanDateTimeResult(name); + var len = cleanDateTimeRegexes.Count; for (int i = 0; i < len; i++) { - if (TryClean(name, regexes[i], ref result)) + if (TryClean(name, cleanDateTimeRegexes[i], ref result)) { return result; } diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 76e0a5a0e..f93db2486 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -138,7 +138,7 @@ namespace Emby.Naming.Video public CleanDateTimeResult CleanDateTime(string name) { - return new CleanDateTimeParser(_options).Clean(name); + return CleanDateTimeParser.Clean(name, _options.CleanDateTimeRegexes); } } } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index ae3cdece9..6fb623554 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -36,7 +36,6 @@ using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Library; using MediaBrowser.Model.Net; @@ -54,6 +53,9 @@ namespace Emby.Server.Implementations.Library /// public class LibraryManager : ILibraryManager { + private NamingOptions _namingOptions; + private string[] _videoFileExtensions; + /// /// Gets or sets the postscan tasks. /// @@ -2508,21 +2510,11 @@ namespace Emby.Server.Implementations.Library } public NamingOptions GetNamingOptions() - { - return GetNamingOptionsInternal(); - } - - private NamingOptions _namingOptions; - private string[] _videoFileExtensions; - - private NamingOptions GetNamingOptionsInternal() { if (_namingOptions == null) { - var options = new NamingOptions(); - - _namingOptions = options; - _videoFileExtensions = _namingOptions.VideoFileExtensions.ToArray(); + _namingOptions = new NamingOptions(); + _videoFileExtensions = _namingOptions.VideoFileExtensions; } return _namingOptions; @@ -2533,11 +2525,10 @@ namespace Emby.Server.Implementations.Library var resolver = new VideoResolver(GetNamingOptions()); var result = resolver.CleanDateTime(name); - var cleanName = resolver.CleanString(result.Name); return new ItemLookupInfo { - Name = cleanName.Name, + Name = resolver.TryCleanString(result.Name, out var newName) ? newName.ToString() : result.Name, Year = result.Year }; } -- cgit v1.2.3 From ded9857f45388ce5499d09b537c56157c48ca759 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 12 Jan 2020 18:59:10 +0100 Subject: Clean up server discovery code --- .../EntryPoints/UdpServerEntryPoint.cs | 58 ++--- Emby.Server.Implementations/Net/SocketFactory.cs | 29 --- Emby.Server.Implementations/Net/UdpSocket.cs | 9 - Emby.Server.Implementations/Udp/UdpServer.cs | 265 +++++---------------- MediaBrowser.Model/Net/ISocket.cs | 2 - MediaBrowser.Model/Net/ISocketFactory.cs | 9 - 6 files changed, 74 insertions(+), 298 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs index 9ee219854..a83817cb9 100644 --- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Udp; using MediaBrowser.Controller; @@ -12,7 +13,7 @@ namespace Emby.Server.Implementations.EntryPoints /// /// Class UdpServerEntryPoint. /// - public class UdpServerEntryPoint : IServerEntryPoint + public sealed class UdpServerEntryPoint : IServerEntryPoint { /// /// The port of the UDP server. @@ -31,61 +32,44 @@ namespace Emby.Server.Implementations.EntryPoints /// The UDP server. /// private UdpServer _udpServer; + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private bool _disposed = false; /// /// Initializes a new instance of the class. /// public UdpServerEntryPoint( - ILogger logger, - IServerApplicationHost appHost, - IJsonSerializer json, - ISocketFactory socketFactory) + ILogger logger, + IServerApplicationHost appHost) { _logger = logger; _appHost = appHost; - _json = json; - _socketFactory = socketFactory; - } - /// - public Task RunAsync() - { - var udpServer = new UdpServer(_logger, _appHost, _json, _socketFactory); - - try - { - udpServer.Start(PortNumber); - - _udpServer = udpServer; - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to start UDP Server"); - } - return Task.CompletedTask; } /// - public void Dispose() + public async Task RunAsync() { - Dispose(true); - GC.SuppressFinalize(this); + _udpServer = new UdpServer(_logger, _appHost); + _udpServer.Start(PortNumber, _cancellationTokenSource.Token); } - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) + /// + public void Dispose() { - if (dispose) + if (_disposed) { - if (_udpServer != null) - { - _udpServer.Dispose(); - } + return; } + + _cancellationTokenSource.Cancel(); + _udpServer.Dispose(); + + _cancellationTokenSource = null; + _udpServer = null; + + _disposed = true; } } } diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index 0870db003..4e04cde78 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -8,32 +8,6 @@ namespace Emby.Server.Implementations.Net { public class SocketFactory : ISocketFactory { - /// - /// Creates a new UDP acceptSocket and binds it to the specified local port. - /// - /// An integer specifying the local port to bind the acceptSocket to. - public ISocket CreateUdpSocket(int localPort) - { - if (localPort < 0) - { - throw new ArgumentException("localPort cannot be less than zero.", nameof(localPort)); - } - - var retVal = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - - try - { - retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - return new UdpSocket(retVal, localPort, IPAddress.Any); - } - catch - { - retVal?.Dispose(); - - throw; - } - } - public ISocket CreateUdpBroadcastSocket(int localPort) { if (localPort < 0) @@ -156,8 +130,5 @@ namespace Emby.Server.Implementations.Net throw; } } - - public Stream CreateNetworkStream(ISocket socket, bool ownsSocket) - => new NetworkStream(((UdpSocket)socket).Socket, ownsSocket); } } diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs index dde4a2a34..211ca6784 100644 --- a/Emby.Server.Implementations/Net/UdpSocket.cs +++ b/Emby.Server.Implementations/Net/UdpSocket.cs @@ -181,15 +181,6 @@ namespace Emby.Server.Implementations.Net return taskCompletion.Task; } - public Task ReceiveAsync(CancellationToken cancellationToken) - { - ThrowIfDisposed(); - - var buffer = new byte[8192]; - - return ReceiveAsync(buffer, 0, buffer.Length, cancellationToken); - } - public Task SendToAsync(byte[] buffer, int offset, int size, IPEndPoint endPoint, CancellationToken cancellationToken) { ThrowIfDisposed(); diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index 185a282ac..19fc1e316 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -1,112 +1,47 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Net; +using System.Net.Sockets; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller; using MediaBrowser.Model.ApiClient; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Udp { /// - /// Provides a Udp Server + /// Provides a Udp Server. /// - public class UdpServer : IDisposable + public sealed class UdpServer : IDisposable { /// /// The _logger /// private readonly ILogger _logger; - - private bool _isDisposed; - - private readonly List>> _responders = new List>>(); - private readonly IServerApplicationHost _appHost; - private readonly IJsonSerializer _json; /// - /// Initializes a new instance of the class. + /// The _udp client. /// - public UdpServer(ILogger logger, IServerApplicationHost appHost, IJsonSerializer json, ISocketFactory socketFactory) - { - _logger = logger; - _appHost = appHost; - _json = json; - _socketFactory = socketFactory; - - AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message); - } + private Socket _udpSocket; + private IPEndPoint _endpoint; + private readonly byte[] _receiveBuffer = new byte[8192]; - private void AddMessageResponder(string message, bool isSubstring, Func responder) - { - _responders.Add(new Tuple>(message, isSubstring, responder)); - } + private bool _disposed = false; /// - /// Raises the event. + /// Initializes a new instance of the class. /// - private async void OnMessageReceived(GenericEventArgs e) - { - var message = e.Argument; - - var encoding = Encoding.UTF8; - var responder = GetResponder(message.Buffer, message.ReceivedBytes, encoding); - - if (responder == null) - { - encoding = Encoding.Unicode; - responder = GetResponder(message.Buffer, message.ReceivedBytes, encoding); - } - - if (responder != null) - { - var cancellationToken = CancellationToken.None; - - try - { - await responder.Item2.Item3(responder.Item1, message.RemoteEndPoint, encoding, cancellationToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in OnMessageReceived"); - } - } - } - - private Tuple>> GetResponder(byte[] buffer, int bytesReceived, Encoding encoding) + public UdpServer(ILogger logger, IServerApplicationHost appHost) { - var text = encoding.GetString(buffer, 0, bytesReceived); - var responder = _responders.FirstOrDefault(i => - { - if (i.Item2) - { - return text.IndexOf(i.Item1, StringComparison.OrdinalIgnoreCase) != -1; - } - return string.Equals(i.Item1, text, StringComparison.OrdinalIgnoreCase); - }); - - if (responder == null) - { - return null; - } - return new Tuple>>(text, responder); + _logger = logger; + _appHost = appHost; } - private async Task RespondToV2Message(string messageText, IPEndPoint endpoint, Encoding encoding, CancellationToken cancellationToken) + private async Task RespondToV2Message(string messageText, EndPoint endpoint, CancellationToken cancellationToken) { - var parts = messageText.Split('|'); - var localUrl = await _appHost.GetLocalApiUrl(cancellationToken).ConfigureAwait(false); if (!string.IsNullOrEmpty(localUrl)) @@ -118,8 +53,16 @@ namespace Emby.Server.Implementations.Udp Name = _appHost.FriendlyName }; - await SendAsync(encoding.GetBytes(_json.SerializeToString(response)), endpoint, cancellationToken).ConfigureAwait(false); + try + { + await _udpSocket.SendToAsync(JsonSerializer.SerializeToUtf8Bytes(response), SocketFlags.None, endpoint).ConfigureAwait(false); + } + catch (SocketException ex) + { + _logger.LogError(ex, "Error sending response message"); + } + var parts = messageText.Split('|'); if (parts.Length > 1) { _appHost.EnableLoopback(parts[1]); @@ -131,162 +74,60 @@ namespace Emby.Server.Implementations.Udp } } - /// - /// The _udp client - /// - private ISocket _udpClient; - private readonly ISocketFactory _socketFactory; - /// /// Starts the specified port. /// /// The port. - public void Start(int port) - { - _udpClient = _socketFactory.CreateUdpSocket(port); - - Task.Run(() => BeginReceive()); - } - - private readonly byte[] _receiveBuffer = new byte[8192]; - - private void BeginReceive() + /// + public void Start(int port, CancellationToken cancellationToken) { - if (_isDisposed) - { - return; - } + _endpoint = new IPEndPoint(IPAddress.Any, port); - try - { - var result = _udpClient.BeginReceive(_receiveBuffer, 0, _receiveBuffer.Length, OnReceiveResult); + _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + _udpSocket.Bind(_endpoint); - if (result.CompletedSynchronously) - { - OnReceiveResult(result); - } - } - catch (ObjectDisposedException) - { - //TODO Investigate and properly fix. - } - catch (Exception ex) - { - _logger.LogError(ex, "Error receiving udp message"); - } - } - - private void OnReceiveResult(IAsyncResult result) - { - if (_isDisposed) - { - return; - } - - try - { - var socketResult = _udpClient.EndReceive(result); - - OnMessageReceived(socketResult); - } - catch (ObjectDisposedException) - { - //TODO Investigate and properly fix. - } - catch (Exception ex) - { - _logger.LogError(ex, "Error receiving udp message"); - } - - BeginReceive(); + _ = Task.Run(async () => await BeginReceiveAsync(cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); } - /// - /// Called when [message received]. - /// - /// The message. - private void OnMessageReceived(SocketReceiveResult message) + private async Task BeginReceiveAsync(CancellationToken cancellationToken) { - if (_isDisposed) - { - return; - } - - if (message.RemoteEndPoint.Port == 0) + while (!cancellationToken.IsCancellationRequested) { - return; - } - - try - { - OnMessageReceived(new GenericEventArgs + try { - Argument = message - }); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error handling UDP message"); - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - } + var result = await _udpSocket.ReceiveFromAsync(_receiveBuffer, SocketFlags.None, _endpoint).ConfigureAwait(false); - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - _isDisposed = true; + cancellationToken.ThrowIfCancellationRequested(); - if (_udpClient != null) + var text = Encoding.UTF8.GetString(_receiveBuffer, 0, result.ReceivedBytes); + if (text.Contains("who is JellyfinServer?", StringComparison.OrdinalIgnoreCase)) + { + await RespondToV2Message(text, result.RemoteEndPoint, cancellationToken).ConfigureAwait(false); + } + } + catch (SocketException ex) { - _udpClient.Dispose(); + _logger.LogError(ex, "Failed to receive data drom socket"); + } + catch (OperationCanceledException) + { + // Don't throw } } } - public async Task SendAsync(byte[] bytes, IPEndPoint remoteEndPoint, CancellationToken cancellationToken) + /// + public void Dispose() { - if (_isDisposed) + if (_disposed) { - throw new ObjectDisposedException(GetType().Name); - } - - if (bytes == null) - { - throw new ArgumentNullException(nameof(bytes)); - } - - if (remoteEndPoint == null) - { - throw new ArgumentNullException(nameof(remoteEndPoint)); + return; } - try - { - await _udpClient.SendToAsync(bytes, 0, bytes.Length, remoteEndPoint, cancellationToken).ConfigureAwait(false); + _udpSocket?.Dispose(); - _logger.LogInformation("Udp message sent to {remoteEndPoint}", remoteEndPoint); - } - catch (OperationCanceledException) - { - - } - catch (Exception ex) - { - _logger.LogError(ex, "Error sending message to {remoteEndPoint}", remoteEndPoint); - } + GC.SuppressFinalize(this); } } - } diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs index f80de5524..3fdc40bbe 100644 --- a/MediaBrowser.Model/Net/ISocket.cs +++ b/MediaBrowser.Model/Net/ISocket.cs @@ -14,8 +14,6 @@ namespace MediaBrowser.Model.Net Task ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken); - int Receive(byte[] buffer, int offset, int count); - IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback); SocketReceiveResult EndReceive(IAsyncResult result); diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index 2f857f1af..dc69b1fb2 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -8,13 +8,6 @@ namespace MediaBrowser.Model.Net /// public interface ISocketFactory { - /// - /// Creates a new unicast socket using the specified local port number. - /// - /// The local port to bind to. - /// A implementation. - ISocket CreateUdpSocket(int localPort); - ISocket CreateUdpBroadcastSocket(int localPort); /// @@ -30,7 +23,5 @@ namespace MediaBrowser.Model.Net /// The local port to bind to. /// A implementation. ISocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort); - - Stream CreateNetworkStream(ISocket socket, bool ownsSocket); } } -- cgit v1.2.3 From a8cd963d468ba4fb4caa003d9eb927117a3afa77 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 13 Jan 2020 15:25:54 +0900 Subject: fix tests for absolute paths --- .ci/azure-pipelines.yml | 2 +- .../IO/ManagedFileSystem.cs | 35 +++++++++++----------- 2 files changed, 18 insertions(+), 19 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 7bcaed70c..d69cc4943 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -112,7 +112,7 @@ jobs: - job: main_test displayName: Main Test pool: - vmImage: windows-latest + vmImage: ubuntu-latest steps: - checkout: self clean: true diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index e27081c45..bf2173d79 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -17,7 +17,7 @@ using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Server.Implementations.IO { /// - /// Class ManagedFileSystem + /// Class ManagedFileSystem. /// public class ManagedFileSystem : IFileSystem { @@ -80,20 +80,20 @@ namespace Emby.Server.Implementations.IO public virtual string MakeAbsolutePath(string folderPath, string filePath) { - if (string.IsNullOrWhiteSpace(filePath) - // stream - || filePath.Contains("://")) + // path is actually a stream + if (string.IsNullOrWhiteSpace(filePath) || filePath.Contains("://", StringComparison.InvariantCulture)) { return filePath; } if (filePath.Length > 3 && filePath[1] == ':' && filePath[2] == '/') { - return filePath; // absolute local path + // absolute local path + return filePath; } // unc path - if (filePath.StartsWith("\\\\")) + if (filePath.StartsWith("\\\\", StringComparison.InvariantCulture)) { return filePath; } @@ -101,13 +101,16 @@ namespace Emby.Server.Implementations.IO var firstChar = filePath[0]; if (firstChar == '/') { - // For this we don't really know. + // for this we don't really know return filePath; } - if (firstChar == '\\') //relative path + + // relative path + if (firstChar == '\\') { filePath = filePath.Substring(1); } + try { return Path.GetFullPath(Path.Combine(folderPath, filePath)); @@ -131,11 +134,7 @@ namespace Emby.Server.Implementations.IO /// /// The shortcut path. /// The target. - /// - /// shortcutPath - /// or - /// target - /// + /// The shortcutPath or target is null. public virtual void CreateShortcut(string shortcutPath, string target) { if (string.IsNullOrEmpty(shortcutPath)) @@ -281,11 +280,11 @@ namespace Emby.Server.Implementations.IO } /// - /// Takes a filename and removes invalid characters + /// Takes a filename and removes invalid characters. /// /// The filename. /// System.String. - /// filename + /// The filename is null. public virtual string GetValidFilename(string filename) { var builder = new StringBuilder(filename); @@ -473,7 +472,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetReadOnly(string path, bool isReadOnly) { - if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows) + if (OperatingSystem.Id != OperatingSystemId.Windows) { return; } @@ -497,7 +496,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly) { - if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows) + if (OperatingSystem.Id != OperatingSystemId.Windows) { return; } -- cgit v1.2.3 From 65e9a705d303ebfadbc58035f9763966141ee437 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 13 Jan 2020 15:34:50 +0900 Subject: check operating system for absolute path test --- .ci/azure-pipelines.yml | 2 +- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 4 ++-- .../IO/ManagedFileSystemTests.cs | 13 ++++++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index d69cc4943..7bcaed70c 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -112,7 +112,7 @@ jobs: - job: main_test displayName: Main Test pool: - vmImage: ubuntu-latest + vmImage: windows-latest steps: - checkout: self clean: true diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index bf2173d79..1fd8ddc4d 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -448,7 +448,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetHidden(string path, bool isHidden) { - if (OperatingSystem.Id != MediaBrowser.Model.System.OperatingSystemId.Windows) + if (OperatingSystem.Id != OperatingSystemId.Windows) { return; } @@ -779,7 +779,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetExecutable(string path) { - if (OperatingSystem.Id == MediaBrowser.Model.System.OperatingSystemId.Darwin) + if (OperatingSystem.Id == OperatingSystemId.Darwin) { RunProcess("chmod", "+x \"" + path + "\"", Path.GetDirectoryName(path)); } diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index f2a8e447f..b2aa01e65 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -1,6 +1,8 @@ +using System; using AutoFixture; using AutoFixture.AutoMoq; using Emby.Server.Implementations.IO; +using MediaBrowser.Model.System; using Xunit; namespace Jellyfin.Server.Implementations.Tests.IO @@ -26,7 +28,16 @@ namespace Jellyfin.Server.Implementations.Tests.IO string expectedAbsolutePath) { var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath); - Assert.Equal(expectedAbsolutePath, generatedPath); + + if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) + { + var windowsPath = "d:" + generatedPath.Replace('/', '\\'); + Assert.Equal(expectedAbsolutePath, windowsPath); + } + else + { + Assert.Equal(expectedAbsolutePath, generatedPath); + } } } } -- cgit v1.2.3 From 665ca5cf1fcd6c757f214998d121e3f6a50fb8f3 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 13 Jan 2020 09:00:30 +0100 Subject: Update UdpServer.cs --- Emby.Server.Implementations/Udp/UdpServer.cs | 3 --- 1 file changed, 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index 19fc1e316..c91d137a7 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -22,9 +22,6 @@ namespace Emby.Server.Implementations.Udp private readonly ILogger _logger; private readonly IServerApplicationHost _appHost; - /// - /// The _udp client. - /// private Socket _udpSocket; private IPEndPoint _endpoint; private readonly byte[] _receiveBuffer = new byte[8192]; -- cgit v1.2.3 From 9eac19c75ad9c69c0c30ae90c0772856e7b0ce9e Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 13 Jan 2020 17:09:22 +0900 Subject: change invariant culture to ordinal --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 1fd8ddc4d..68417876c 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -81,7 +81,7 @@ namespace Emby.Server.Implementations.IO public virtual string MakeAbsolutePath(string folderPath, string filePath) { // path is actually a stream - if (string.IsNullOrWhiteSpace(filePath) || filePath.Contains("://", StringComparison.InvariantCulture)) + if (string.IsNullOrWhiteSpace(filePath) || filePath.Contains("://", StringComparison.Ordinal)) { return filePath; } @@ -93,7 +93,7 @@ namespace Emby.Server.Implementations.IO } // unc path - if (filePath.StartsWith("\\\\", StringComparison.InvariantCulture)) + if (filePath.StartsWith("\\\\", StringComparison.Ordinal)) { return filePath; } -- cgit v1.2.3 From 9c2c31b0c63b12ff303790f1294eeccb6adea252 Mon Sep 17 00:00:00 2001 From: punkch Date: Fri, 10 Jan 2020 23:42:06 +0000 Subject: Translated using Weblate (Bulgarian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bg/ --- Emby.Server.Implementations/Localization/Core/bg-BG.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json index 46c10d912..9441da591 100644 --- a/Emby.Server.Implementations/Localization/Core/bg-BG.json +++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json @@ -16,7 +16,7 @@ "Folders": "Папки", "Genres": "Жанрове", "HeaderAlbumArtists": "Изпълнители на албуми", - "HeaderCameraUploads": "", + "HeaderCameraUploads": "Качени от камера", "HeaderContinueWatching": "Продължаване на гледането", "HeaderFavoriteAlbums": "Любими албуми", "HeaderFavoriteArtists": "Любими изпълнители", @@ -25,7 +25,7 @@ "HeaderFavoriteSongs": "Любими песни", "HeaderLiveTV": "Телевизия на живо", "HeaderNextUp": "Следва", - "HeaderRecordingGroups": "", + "HeaderRecordingGroups": "Запис групи", "HomeVideos": "Домашни клипове", "Inherit": "Наследяване", "ItemAddedWithName": "{0} е добавено към библиотеката", @@ -34,7 +34,7 @@ "LabelRunningTimeValue": "", "Latest": "Последни", "MessageApplicationUpdated": "Сървърът е обновен", - "MessageApplicationUpdatedTo": "", + "MessageApplicationUpdatedTo": "Сървърът е обновен до {0}", "MessageNamedServerConfigurationUpdatedWithValue": "", "MessageServerConfigurationUpdated": "", "MixedContent": "Смесено съдържание", -- cgit v1.2.3 From 31347de792cec994ac9796628e161eaac4bbaf4a Mon Sep 17 00:00:00 2001 From: Philmo67 Date: Fri, 10 Jan 2020 15:17:30 +0000 Subject: Translated using Weblate (French) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/ --- Emby.Server.Implementations/Localization/Core/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 6b25e3df0..ff7073335 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -11,11 +11,11 @@ "Collections": "Collections", "DeviceOfflineWithName": "{0} s'est déconnecté", "DeviceOnlineWithName": "{0} est connecté", - "FailedLoginAttemptWithUserName": "Échec d'une tentative de connexion de {0}", + "FailedLoginAttemptWithUserName": "Échec de connexion de {0}", "Favorites": "Favoris", "Folders": "Dossiers", "Genres": "Genres", - "HeaderAlbumArtists": "Artistes de l'album", + "HeaderAlbumArtists": "Artistes d'album", "HeaderCameraUploads": "Photos transférées", "HeaderContinueWatching": "Continuer à regarder", "HeaderFavoriteAlbums": "Albums favoris", -- cgit v1.2.3 From faf898ea0d8fc5d0dcd6d98a0d4cfc95d4126a6d Mon Sep 17 00:00:00 2001 From: hbo Date: Fri, 10 Jan 2020 15:18:30 +0000 Subject: Translated using Weblate (French) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/ --- Emby.Server.Implementations/Localization/Core/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index ff7073335..90754e415 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -34,7 +34,7 @@ "LabelRunningTimeValue": "Durée : {0}", "Latest": "Derniers", "MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour", - "MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour vers {0}", + "MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour en version {0}", "MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour", "MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour", "MixedContent": "Contenu mixte", -- cgit v1.2.3 From 7b91df199401e299463264b3b6bbae729bf51499 Mon Sep 17 00:00:00 2001 From: Alexander Brissman Date: Fri, 10 Jan 2020 21:46:39 +0000 Subject: Translated using Weblate (Norwegian Bokmål) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nb_NO/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Emby.Server.Implementations/Localization/Core/nb.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index 7d4b2bdac..f9fa1b68c 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -62,7 +62,7 @@ "NotificationOptionVideoPlayback": "Videoavspilling startet", "NotificationOptionVideoPlaybackStopped": "Videoavspilling stoppet", "Photos": "Bilder", - "Playlists": "Spliielister", + "Playlists": "Spillelister", "Plugin": "Plugin", "PluginInstalledWithName": "{0} ble installert", "PluginUninstalledWithName": "{0} ble avinstallert", -- cgit v1.2.3 From ab0130e9e288c92d8e11abc8067c69c6bb13e468 Mon Sep 17 00:00:00 2001 From: Z Yang Date: Sat, 11 Jan 2020 13:18:35 +0000 Subject: Translated using Weblate (Chinese (Simplified)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index a4d53c57e..a567e6e2d 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -3,9 +3,9 @@ "AppDeviceValues": "应用: {0}, 设备: {1}", "Application": "应用程序", "Artists": "艺术家", - "AuthenticationSucceededWithUserName": "{0} 认证成功", + "AuthenticationSucceededWithUserName": "{0} 验证成功", "Books": "书籍", - "CameraImageUploadedFrom": "已从 {0} 上传了一张新的相片", + "CameraImageUploadedFrom": "新的相机图像已从 {0} 上传", "Channels": "频道", "ChapterNameValue": "章节 {0}", "Collections": "合集", @@ -18,7 +18,7 @@ "HeaderAlbumArtists": "专辑作家", "HeaderCameraUploads": "相机上传", "HeaderContinueWatching": "继续观看", - "HeaderFavoriteAlbums": "最爱的专辑", + "HeaderFavoriteAlbums": "收藏的专辑", "HeaderFavoriteArtists": "最爱的艺术家", "HeaderFavoriteEpisodes": "最爱的剧集", "HeaderFavoriteShows": "最爱的节目", -- cgit v1.2.3 From 138bff43270279f46358c39cc03666fb3ee331b2 Mon Sep 17 00:00:00 2001 From: Slobodan Simić Date: Fri, 10 Jan 2020 20:27:47 +0000 Subject: Translated using Weblate (Serbian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sr/ --- .../Localization/Core/sr.json | 97 +++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json index 0967ef424..da0088991 100644 --- a/Emby.Server.Implementations/Localization/Core/sr.json +++ b/Emby.Server.Implementations/Localization/Core/sr.json @@ -1 +1,96 @@ -{} +{ + "UserPolicyUpdatedWithName": "Корисничке смернице ажуриране за {0}", + "NotificationOptionUserLockedOut": "Корисник закључан", + "VersionNumber": "Верзија {0}", + "ValueSpecialEpisodeName": "Специјал - {0}", + "ValueHasBeenAddedToLibrary": "{0} је додато у вашу медијску библиотеку", + "UserStoppedPlayingItemWithValues": "{0} заврши пуштање {1} на {2}", + "UserStartedPlayingItemWithValues": "{0} пушта {1} на {2}", + "UserPasswordChangedWithName": "Лозинка је промењена за корисника {0}", + "UserOnlineFromDevice": "{0} је на вези од {1}", + "UserOfflineFromDevice": "{0} се одвезао са {1}", + "UserLockedOutWithName": "Корисник {0} је закључан", + "UserDownloadingItemWithValues": "{0} преузима {1}", + "UserDeletedWithName": "Корисник {0} је обрисан", + "UserCreatedWithName": "Корисник {0} је направљен", + "User": "Корисник", + "TvShows": "ТВ серије", + "System": "Систем", + "Sync": "Усклади", + "SubtitlesDownloadedForItem": "Титлови преузети за {0}", + "SubtitleDownloadFailureFromForItem": "Неуспело преузимање титлова за {1} са {0}", + "StartupEmbyServerIsLoading": "Џелифин сервер се подиже. Покушајте поново убрзо.", + "Songs": "Песме", + "Shows": "Серије", + "ServerNameNeedsToBeRestarted": "{0} треба поново покренути", + "ScheduledTaskStartedWithName": "{0} покренуто", + "ScheduledTaskFailedWithName": "{0} неуспело", + "ProviderValue": "Пружалац: {0}", + "PluginUpdatedWithName": "{0} ажуриран", + "PluginUninstalledWithName": "{0} деинсталиран", + "PluginInstalledWithName": "{0} инсталиран", + "Plugin": "Прикључак", + "Playlists": "Листе", + "Photos": "Фотографије", + "NotificationOptionVideoPlaybackStopped": "Заустављено пуштање видеа", + "NotificationOptionVideoPlayback": "Покренуто пуштање видеа", + "NotificationOptionTaskFailed": "Неуспешан заказани посао", + "NotificationOptionServerRestartRequired": "Потребан је рестарт сервера", + "NotificationOptionPluginUpdateInstalled": "Ажурирање прикључка инсталирано", + "NotificationOptionPluginUninstalled": "Прикључак уклоњен", + "NotificationOptionPluginInstalled": "Прикључак инсталиран", + "NotificationOptionPluginError": "Грешка прикључка", + "NotificationOptionNewLibraryContent": "Додат нови садржај", + "NotificationOptionInstallationFailed": "Неуспела инсталација", + "NotificationOptionCameraImageUploaded": "Слика са камере послата", + "NotificationOptionAudioPlaybackStopped": "Заустављено пуштање звука", + "NotificationOptionAudioPlayback": "Покренуто пуштање звука", + "NotificationOptionApplicationUpdateInstalled": "Ажурирање инсталирано", + "NotificationOptionApplicationUpdateAvailable": "Доступно је ажурирање за апликацију", + "NewVersionIsAvailable": "Нова верзија Џелифин сервера је доступна за преузимање.", + "NameSeasonUnknown": "Непозната сезона", + "NameSeasonNumber": "Сезона {0}", + "NameInstallFailed": "Инсталација {0} није успела", + "MusicVideos": "Музички спотови", + "Music": "Музика", + "Movies": "Филмови", + "MixedContent": "Мешовит садржај", + "MessageServerConfigurationUpdated": "Серверска поставка је ажурирана", + "MessageNamedServerConfigurationUpdatedWithValue": "Одељак серверске поставке {0} је ажуриран", + "MessageApplicationUpdatedTo": "Џелифин сервер је ажуриран на {0}", + "MessageApplicationUpdated": "Џелифин сервер је ажуриран", + "Latest": "Последње", + "LabelRunningTimeValue": "Време рада: {0}", + "LabelIpAddressValue": "ИП адреса: {0}", + "ItemRemovedWithName": "{0} уклоњено из библиотеке", + "ItemAddedWithName": "{0} додато у библиотеку", + "Inherit": "Наследи", + "HomeVideos": "Кућни видео", + "HeaderRecordingGroups": "Групе снимања", + "HeaderNextUp": "Следеће горе", + "HeaderLiveTV": "ТВ уживо", + "HeaderFavoriteSongs": "Омиљене песме", + "HeaderFavoriteShows": "Омиљене серије", + "HeaderFavoriteEpisodes": "Омиљене епизоде", + "HeaderFavoriteArtists": "Омиљени извођачи", + "HeaderFavoriteAlbums": "Омиљени албуми", + "HeaderContinueWatching": "Настави гледање", + "HeaderCameraUploads": "Слања са камере", + "HeaderAlbumArtists": "Извођачи албума", + "Genres": "Жанрови", + "Folders": "Фасцикле", + "Favorites": "Омиљено", + "FailedLoginAttemptWithUserName": "Неуспела пријава са {0}", + "DeviceOnlineWithName": "{0} се повезао", + "DeviceOfflineWithName": "{0} се одвезао", + "Collections": "Колекције", + "ChapterNameValue": "Поглавље {0}", + "Channels": "Канали", + "CameraImageUploadedFrom": "Нова фотографија је послата са {0}", + "Books": "Књиге", + "AuthenticationSucceededWithUserName": "{0} успешно проверено", + "Artists": "Извођач", + "Application": "Апликација", + "AppDeviceValues": "Апл: {0}, уређај: {1}", + "Albums": "Албуми" +} -- cgit v1.2.3 From d4745957b3dc0b1cc5f5baac299883a89df4cfb6 Mon Sep 17 00:00:00 2001 From: nextlooper42 Date: Tue, 14 Jan 2020 10:29:23 +0000 Subject: Translated using Weblate (Slovak) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sk/ --- .../Localization/Core/sk.json | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 9bac305a2..1988bda52 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -15,7 +15,7 @@ "Favorites": "Obľúbené", "Folders": "Priečinky", "Genres": "Žánre", - "HeaderAlbumArtists": "Albumy umelcov", + "HeaderAlbumArtists": "Umelci albumu", "HeaderCameraUploads": "Nahrané fotografie", "HeaderContinueWatching": "Pokračovať v pozeraní", "HeaderFavoriteAlbums": "Obľúbené albumy", @@ -45,25 +45,25 @@ "NameSeasonNumber": "Séria {0}", "NameSeasonUnknown": "Neznáma séria", "NewVersionIsAvailable": "Nová verzia Jellyfin Serveru je dostupná na stiahnutie.", - "NotificationOptionApplicationUpdateAvailable": "Aktualizácia aplikácie je dostupná", - "NotificationOptionApplicationUpdateInstalled": "Aktualizácia aplikácie nainštalovaná", - "NotificationOptionAudioPlayback": "Prehrávanie audia bolo spustené", - "NotificationOptionAudioPlaybackStopped": "Prehrávanie audia bolo zastavené", + "NotificationOptionApplicationUpdateAvailable": "Je dostupná aktualizácia aplikácie", + "NotificationOptionApplicationUpdateInstalled": "Aktualizácia aplikácie bola nainštalovaná", + "NotificationOptionAudioPlayback": "Prehrávanie zvuku bolo spustené", + "NotificationOptionAudioPlaybackStopped": "Prehrávanie zvuku bolo zastavené", "NotificationOptionCameraImageUploaded": "Obrázok z fotoaparátu bol nahraný", "NotificationOptionInstallationFailed": "Chyba inštalácie", - "NotificationOptionNewLibraryContent": "Nový obsah bol pridaný", - "NotificationOptionPluginError": "Chyba rozšírenia", - "NotificationOptionPluginInstalled": "Rozšírenie nainštalované", - "NotificationOptionPluginUninstalled": "Rozšírenie odinštalované", - "NotificationOptionPluginUpdateInstalled": "Aktualizácia rozšírenia nainštalovaná", + "NotificationOptionNewLibraryContent": "Bol pridaný nový obsah", + "NotificationOptionPluginError": "Chyba zásuvného modulu", + "NotificationOptionPluginInstalled": "Bol nainštalovaný zásuvný modul", + "NotificationOptionPluginUninstalled": "Zásuvný modul bol odinštalovaný", + "NotificationOptionPluginUpdateInstalled": "Bola nainštalovaná aktualizácia zásuvného modulu", "NotificationOptionServerRestartRequired": "Vyžaduje sa reštart servera", "NotificationOptionTaskFailed": "Naplánovaná úloha zlyhala", "NotificationOptionUserLockedOut": "Používateľ je uzamknutý", "NotificationOptionVideoPlayback": "Spustené prehrávanie videa", "NotificationOptionVideoPlaybackStopped": "Zastavené prehrávanie videa", "Photos": "Fotky", - "Playlists": "Zoznamy skladieb", - "Plugin": "Rozšírenie", + "Playlists": "Playlisty", + "Plugin": "Zásuvný modul", "PluginInstalledWithName": "{0} bol nainštalovaný", "PluginUninstalledWithName": "{0} bol odinštalovaný", "PluginUpdatedWithName": "{0} bol aktualizovaný", @@ -72,8 +72,8 @@ "ScheduledTaskStartedWithName": "{0} zahájených", "ServerNameNeedsToBeRestarted": "{0} vyžaduje reštart", "Shows": "Seriály", - "Songs": "Skladby", - "StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Skúste to prosím o chvíľu znova.", + "Songs": "Piesne", + "StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Prosím, skúste to o chvíľu znova.", "SubtitleDownloadFailureForItem": "Sťahovanie titulkov pre {0} zlyhalo", "SubtitleDownloadFailureFromForItem": "Sťahovanie titulkov z {0} pre {1} zlyhalo", "SubtitlesDownloadedForItem": "Titulky pre {0} stiahnuté", @@ -87,11 +87,11 @@ "UserLockedOutWithName": "Používateľ {0} bol vymknutý", "UserOfflineFromDevice": "{0} sa odpojil od {1}", "UserOnlineFromDevice": "{0} je online z {1}", - "UserPasswordChangedWithName": "Heslo používateľa {0} zmenené", + "UserPasswordChangedWithName": "Heslo používateľa {0} bolo zmenené", "UserPolicyUpdatedWithName": "Používateľské zásady pre {0} boli aktualizované", - "UserStartedPlayingItemWithValues": "{0} spustil prehrávanie {1}", - "UserStoppedPlayingItemWithValues": "{0} zastavil prehrávanie {1}", - "ValueHasBeenAddedToLibrary": "{0} bolo pridané do vašej knižnice médií", + "UserStartedPlayingItemWithValues": "{0} spustil prehrávanie {1} na {2}", + "UserStoppedPlayingItemWithValues": "{0} ukončil prehrávanie {1} na {2}", + "ValueHasBeenAddedToLibrary": "{0} bol pridané do vašej knižnice médií", "ValueSpecialEpisodeName": "Špeciál - {0}", "VersionNumber": "Verzia {0}" } -- cgit v1.2.3 From 42213d94e5126a9966c5a555d4d9cce87451c481 Mon Sep 17 00:00:00 2001 From: 4d1m Date: Wed, 15 Jan 2020 06:58:17 +0000 Subject: Translated using Weblate (Romanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ro/ --- Emby.Server.Implementations/Localization/Core/ro.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index b871626f0..1a4756e3d 100644 --- a/Emby.Server.Implementations/Localization/Core/ro.json +++ b/Emby.Server.Implementations/Localization/Core/ro.json @@ -65,7 +65,7 @@ "LabelIpAddressValue": "Adresa IP: {0}", "ItemRemovedWithName": "{0} a fost eliminat din bibliotecă", "ItemAddedWithName": "{0} a fost adăugat în bibliotecă", - "Inherit": "moștenit", + "Inherit": "Moștenit", "HomeVideos": "Videoclipuri personale", "HeaderRecordingGroups": "Grupuri de înregistrare", "HeaderLiveTV": "TV în Direct", -- cgit v1.2.3 From dcd6ceb9a2a971ec10fe22c94880d643910a21ab Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Thu, 16 Jan 2020 09:31:20 -0500 Subject: Update SQLitePCL to new version --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 03cbe00b7..77333a03d 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -35,7 +35,7 @@ - + -- cgit v1.2.3 From c601def4847cb0ada323a9da8d152c8c8d7b834b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 17 Jan 2020 00:19:58 +0100 Subject: Fix warnings in SessionManager --- Emby.Server.Implementations/ApplicationHost.cs | 13 +- .../Session/SessionManager.cs | 274 +++++++++++---------- 2 files changed, 156 insertions(+), 131 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0bb1d832f..e93337c2f 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -808,7 +808,18 @@ namespace Emby.Server.Implementations ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LoggerFactory, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, ProviderManager); serviceCollection.AddSingleton(ChannelManager); - SessionManager = new SessionManager(UserDataManager, LoggerFactory, LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, this, AuthenticationRepository, DeviceManager, MediaSourceManager); + SessionManager = new SessionManager( + LoggerFactory.CreateLogger(), + UserDataManager, + LibraryManager, + UserManager, + musicManager, + DtoService, + ImageProcessor, + this, + AuthenticationRepository, + DeviceManager, + MediaSourceManager); serviceCollection.AddSingleton(SessionManager); serviceCollection.AddSingleton( diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index b1d513dd4..bb0836614 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -30,17 +30,17 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Session { /// - /// Class SessionManager + /// Class SessionManager. /// public class SessionManager : ISessionManager, IDisposable { /// - /// The _user data repository + /// The _user data repository. /// private readonly IUserDataManager _userDataManager; /// - /// The _logger + /// The _logger. /// private readonly ILogger _logger; @@ -57,36 +57,19 @@ namespace Emby.Server.Implementations.Session private readonly IDeviceManager _deviceManager; /// - /// The _active connections + /// The _active connections. /// private readonly ConcurrentDictionary _activeConnections = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - public event EventHandler> AuthenticationFailed; - - public event EventHandler> AuthenticationSucceeded; - - /// - /// Occurs when [playback start]. - /// - public event EventHandler PlaybackStart; - /// - /// Occurs when [playback progress]. - /// - public event EventHandler PlaybackProgress; - /// - /// Occurs when [playback stopped]. - /// - public event EventHandler PlaybackStopped; + private Timer _idleTimer; - public event EventHandler SessionStarted; - public event EventHandler CapabilitiesChanged; - public event EventHandler SessionEnded; - public event EventHandler SessionActivity; + private DtoOptions _itemInfoDtoOptions; + private bool _disposed = false; public SessionManager( + ILogger logger, IUserDataManager userDataManager, - ILoggerFactory loggerFactory, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, @@ -97,8 +80,8 @@ namespace Emby.Server.Implementations.Session IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager) { + _logger = logger; _userDataManager = userDataManager; - _logger = loggerFactory.CreateLogger(nameof(SessionManager)); _libraryManager = libraryManager; _userManager = userManager; _musicManager = musicManager; @@ -108,9 +91,49 @@ namespace Emby.Server.Implementations.Session _authRepo = authRepo; _deviceManager = deviceManager; _mediaSourceManager = mediaSourceManager; + _deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated; } + /// + public event EventHandler> AuthenticationFailed; + + /// + public event EventHandler> AuthenticationSucceeded; + + /// + /// Occurs when [playback start]. + /// + public event EventHandler PlaybackStart; + + /// + /// Occurs when [playback progress]. + /// + public event EventHandler PlaybackProgress; + + /// + /// Occurs when [playback stopped]. + /// + public event EventHandler PlaybackStopped; + + /// + public event EventHandler SessionStarted; + + /// + public event EventHandler CapabilitiesChanged; + + /// + public event EventHandler SessionEnded; + + /// + public event EventHandler SessionActivity; + + /// + /// Gets all connections. + /// + /// All connections. + public IEnumerable Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate); + private void OnDeviceManagerDeviceOptionsUpdated(object sender, GenericEventArgs> e) { foreach (var session in Sessions) @@ -130,14 +153,17 @@ namespace Emby.Server.Implementations.Session } } - private bool _disposed = false; - + /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (_disposed) @@ -147,15 +173,17 @@ namespace Emby.Server.Implementations.Session if (disposing) { - // TODO: dispose stuff + _idleTimer?.Dispose(); } + _idleTimer = null; + _deviceManager.DeviceOptionsUpdated -= OnDeviceManagerDeviceOptionsUpdated; _disposed = true; } - public void CheckDisposed() + private void CheckDisposed() { if (_disposed) { @@ -163,12 +191,6 @@ namespace Emby.Server.Implementations.Session } } - /// - /// Gets all connections. - /// - /// All connections. - public IEnumerable Sessions => _activeConnections.Values.OrderByDescending(c => c.LastActivityDate); - private void OnSessionStarted(SessionInfo info) { if (!string.IsNullOrEmpty(info.DeviceId)) @@ -199,13 +221,13 @@ namespace Emby.Server.Implementations.Session new SessionEventArgs { SessionInfo = info - }, _logger); info.Dispose(); } + /// public void UpdateDeviceName(string sessionId, string deviceName) { var session = GetSession(sessionId); @@ -225,7 +247,6 @@ namespace Emby.Server.Implementations.Session /// The remote end point. /// The user. /// SessionInfo. - /// user public SessionInfo LogSessionActivity( string appName, string appVersion, @@ -263,14 +284,7 @@ namespace Emby.Server.Implementations.Session if ((activityDate - userLastActivityDate).TotalSeconds > 60) { - try - { - _userManager.UpdateUser(user); - } - catch (Exception ex) - { - _logger.LogError("Error updating user", ex); - } + _userManager.UpdateUser(user); } } @@ -287,18 +301,20 @@ namespace Emby.Server.Implementations.Session return session; } + /// public void CloseIfNeeded(SessionInfo session) { if (!session.SessionControllers.Any(i => i.IsSessionActive)) { var key = GetSessionKey(session.Client, session.DeviceId); - _activeConnections.TryRemove(key, out var removed); + _activeConnections.TryRemove(key, out _); OnSessionEnded(session); } } + /// public void ReportSessionEnded(string sessionId) { CheckDisposed(); @@ -308,7 +324,7 @@ namespace Emby.Server.Implementations.Session { var key = GetSessionKey(session.Client, session.DeviceId); - _activeConnections.TryRemove(key, out var removed); + _activeConnections.TryRemove(key, out _); OnSessionEnded(session); } @@ -339,7 +355,7 @@ namespace Emby.Server.Implementations.Session var runtimeTicks = libraryItem.RunTimeTicks; MediaSourceInfo mediaSource = null; - if (libraryItem is IHasMediaSources hasMediaSources) + if (libraryItem is IHasMediaSources) { mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); @@ -391,7 +407,6 @@ namespace Emby.Server.Implementations.Session /// Removes the now playing item id. /// /// The session. - /// item private void RemoveNowPlayingItem(SessionInfo session) { session.NowPlayingItem = null; @@ -404,9 +419,7 @@ namespace Emby.Server.Implementations.Session } private static string GetSessionKey(string appName, string deviceId) - { - return appName + deviceId; - } + => appName + deviceId; /// /// Gets the connection. @@ -426,6 +439,7 @@ namespace Emby.Server.Implementations.Session { throw new ArgumentNullException(nameof(deviceId)); } + var key = GetSessionKey(appName, deviceId); CheckDisposed(); @@ -498,7 +512,7 @@ namespace Emby.Server.Implementations.Session { var users = new List(); - if (!session.UserId.Equals(Guid.Empty)) + if (session.UserId != Guid.Empty) { var user = _userManager.GetUserById(session.UserId); @@ -517,8 +531,6 @@ namespace Emby.Server.Implementations.Session return users; } - private Timer _idleTimer; - private void StartIdleCheckTimer() { if (_idleTimer == null) @@ -594,11 +606,11 @@ namespace Emby.Server.Implementations.Session } /// - /// Used to report that playback has started for an item + /// Used to report that playback has started for an item. /// /// The info. /// Task. - /// info + /// info is null. public async Task OnPlaybackStart(PlaybackStartInfo info) { CheckDisposed(); @@ -610,7 +622,7 @@ namespace Emby.Server.Implementations.Session var session = GetSession(info.SessionId); - var libraryItem = info.ItemId.Equals(Guid.Empty) + var libraryItem = info.ItemId == Guid.Empty ? null : GetNowPlayingItem(session, info.ItemId); @@ -648,7 +660,6 @@ namespace Emby.Server.Implementations.Session ClientName = session.Client, DeviceId = session.DeviceId, Session = session - }, _logger); @@ -679,13 +690,14 @@ namespace Emby.Server.Implementations.Session _userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None); } + /// public Task OnPlaybackProgress(PlaybackProgressInfo info) { return OnPlaybackProgress(info, false); } /// - /// Used to report playback progress for an item + /// Used to report playback progress for an item. /// /// Task. public async Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated) @@ -852,7 +864,7 @@ namespace Emby.Server.Implementations.Session { MediaSourceInfo mediaSource = null; - if (libraryItem is IHasMediaSources hasMediaSources) + if (libraryItem is IHasMediaSources) { mediaSource = await GetMediaSource(libraryItem, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false); } @@ -924,7 +936,6 @@ namespace Emby.Server.Implementations.Session ClientName = session.Client, DeviceId = session.DeviceId, Session = session - }, _logger); } @@ -962,13 +973,17 @@ namespace Emby.Server.Implementations.Session /// The session identifier. /// if set to true [throw on missing]. /// SessionInfo. - /// sessionId + /// + /// No session with an Id equal to sessionId was found + /// and throwOnMissing is true. + /// private SessionInfo GetSession(string sessionId, bool throwOnMissing = true) { var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId, StringComparison.Ordinal)); if (session == null && throwOnMissing) { - throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId)); + throw new ResourceNotFoundException( + string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId)); } return session; @@ -981,12 +996,14 @@ namespace Emby.Server.Implementations.Session if (session == null) { - throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId)); + throw new ResourceNotFoundException( + string.Format(CultureInfo.InvariantCulture, "Session {0} not found.", sessionId)); } return session; } + /// public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1007,6 +1024,7 @@ namespace Emby.Server.Implementations.Session return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken); } + /// public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1051,6 +1069,7 @@ namespace Emby.Server.Implementations.Session return Task.WhenAll(GetTasks()); } + /// public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1092,7 +1111,8 @@ namespace Emby.Server.Implementations.Session { if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full)) { - throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name)); + throw new ArgumentException( + string.Format(CultureInfo.InvariantCulture, "{0} is not allowed to play media.", user.Name)); } } @@ -1200,6 +1220,7 @@ namespace Emby.Server.Implementations.Session return _musicManager.GetInstantMixFromItem(item, user, new DtoOptions(false) { EnableImages = false }); } + /// public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken) { var generalCommand = new GeneralCommand @@ -1216,6 +1237,7 @@ namespace Emby.Server.Implementations.Session return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken); } + /// public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken) { CheckDisposed(); @@ -1299,12 +1321,12 @@ namespace Emby.Server.Implementations.Session var session = GetSession(sessionId); - if (session.UserId.Equals(userId)) + if (session.UserId == userId) { throw new ArgumentException("The requested user is already the primary user of the session."); } - if (session.AdditionalUsers.All(i => !i.UserId.Equals(userId))) + if (session.AdditionalUsers.All(i => i.UserId != userId)) { var user = _userManager.GetUserById(userId); @@ -1430,19 +1452,19 @@ namespace Emby.Server.Implementations.Session private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName) { - var existing = _authRepo.Get(new AuthenticationInfoQuery - { - DeviceId = deviceId, - UserId = user.Id, - Limit = 1 - - }).Items.FirstOrDefault(); - - var allExistingForDevice = _authRepo.Get(new AuthenticationInfoQuery - { - DeviceId = deviceId + var existing = _authRepo.Get( + new AuthenticationInfoQuery + { + DeviceId = deviceId, + UserId = user.Id, + Limit = 1 + }).Items.FirstOrDefault(); - }).Items; + var allExistingForDevice = _authRepo.Get( + new AuthenticationInfoQuery + { + DeviceId = deviceId + }).Items; foreach (var auth in allExistingForDevice) { @@ -1461,7 +1483,7 @@ namespace Emby.Server.Implementations.Session if (existing != null) { - _logger.LogInformation("Reissuing access token: " + existing.AccessToken); + _logger.LogInformation("Reissuing access token: {Token}", existing.AccessToken); return existing.AccessToken; } @@ -1486,6 +1508,7 @@ namespace Emby.Server.Implementations.Session return newToken.AccessToken; } + /// public void Logout(string accessToken) { CheckDisposed(); @@ -1495,19 +1518,20 @@ namespace Emby.Server.Implementations.Session throw new ArgumentNullException(nameof(accessToken)); } - var existing = _authRepo.Get(new AuthenticationInfoQuery - { - Limit = 1, - AccessToken = accessToken - - }).Items.FirstOrDefault(); + var existing = _authRepo.Get( + new AuthenticationInfoQuery + { + Limit = 1, + AccessToken = accessToken + }).Items; - if (existing != null) + if (existing.Count > 0) { - Logout(existing); + Logout(existing[0]); } } + /// public void Logout(AuthenticationInfo existing) { CheckDisposed(); @@ -1533,6 +1557,7 @@ namespace Emby.Server.Implementations.Session } } + /// public void RevokeUserTokens(Guid userId, string currentAccessToken) { CheckDisposed(); @@ -1551,6 +1576,7 @@ namespace Emby.Server.Implementations.Session } } + /// public void RevokeToken(string token) { Logout(token); @@ -1607,10 +1633,8 @@ namespace Emby.Server.Implementations.Session _deviceManager.SaveCapabilities(deviceId, capabilities); } - private DtoOptions _itemInfoDtoOptions; - /// - /// Converts a BaseItem to a BaseItemInfo + /// Converts a BaseItem to a BaseItemInfo. /// private BaseItemDto GetItemInfo(BaseItem item, MediaSourceInfo mediaSource) { @@ -1685,6 +1709,7 @@ namespace Emby.Server.Implementations.Session } } + /// public void ReportNowViewingItem(string sessionId, string itemId) { if (string.IsNullOrEmpty(itemId)) @@ -1692,23 +1717,26 @@ namespace Emby.Server.Implementations.Session throw new ArgumentNullException(nameof(itemId)); } - //var item = _libraryManager.GetItemById(new Guid(itemId)); + var item = _libraryManager.GetItemById(new Guid(itemId)); - //var info = GetItemInfo(item, null, null); + var info = GetItemInfo(item, null); - //ReportNowViewingItem(sessionId, info); + ReportNowViewingItem(sessionId, info); } + /// public void ReportNowViewingItem(string sessionId, BaseItemDto item) { - //var session = GetSession(sessionId); + throw new NotImplementedException(); - //session.NowViewingItem = item; + // var session = GetSession(sessionId); + // session.NowViewingItem = item; } + /// public void ReportTranscodingInfo(string deviceId, TranscodingInfo info) { - var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId)); + var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId, StringComparison.Ordinal)); if (session != null) { @@ -1716,17 +1744,18 @@ namespace Emby.Server.Implementations.Session } } + /// public void ClearTranscodingInfo(string deviceId) { ReportTranscodingInfo(deviceId, null); } + /// public SessionInfo GetSession(string deviceId, string client, string version) - { - return Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) && - string.Equals(i.Client, client)); - } + => Sessions.FirstOrDefault( + i => string.Equals(i.DeviceId, deviceId, StringComparison.Ordinal) && string.Equals(i.Client, client, StringComparison.Ordinal)); + /// public SessionInfo GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion) { if (info == null) @@ -1759,23 +1788,24 @@ namespace Emby.Server.Implementations.Session return LogSessionActivity(appName, appVersion, deviceId, deviceName, remoteEndpoint, user); } + /// public SessionInfo GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint) { - var result = _authRepo.Get(new AuthenticationInfoQuery + var items = _authRepo.Get(new AuthenticationInfoQuery { - AccessToken = token - }); - - var info = result.Items.FirstOrDefault(); + AccessToken = token, + Limit = 1 + }).Items; - if (info == null) + if (items.Count == 0) { return null; } - return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null); + return GetSessionByAuthenticationToken(items[0], deviceId, remoteEndpoint, null); } + /// public Task SendMessageToAdminSessions(string name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1785,6 +1815,7 @@ namespace Emby.Server.Implementations.Session return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken); } + /// public Task SendMessageToUserSessions(List userIds, string name, Func dataFn, CancellationToken cancellationToken) { CheckDisposed(); @@ -1796,11 +1827,10 @@ namespace Emby.Server.Implementations.Session return Task.CompletedTask; } - var data = dataFn(); - - return SendMessageToSessions(sessions, name, data, cancellationToken); + return SendMessageToSessions(sessions, name, dataFn(), cancellationToken); } + /// public Task SendMessageToUserSessions(List userIds, string name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1809,6 +1839,7 @@ namespace Emby.Server.Implementations.Session return SendMessageToSessions(sessions, name, data, cancellationToken); } + /// public Task SendMessageToUserDeviceSessions(string deviceId, string name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1817,22 +1848,5 @@ namespace Emby.Server.Implementations.Session return SendMessageToSessions(sessions, name, data, cancellationToken); } - - public Task SendMessageToUserDeviceAndAdminSessions(string deviceId, string name, T data, CancellationToken cancellationToken) - { - CheckDisposed(); - - var sessions = Sessions - .Where(i => string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase) || IsAdminSession(i)); - - return SendMessageToSessions(sessions, name, data, cancellationToken); - } - - private bool IsAdminSession(SessionInfo s) - { - var user = _userManager.GetUserById(s.UserId); - - return user != null && user.Policy.IsAdministrator; - } } } -- cgit v1.2.3 From 8e71046a3c7b2e39bbdbfda8518730df21120ef8 Mon Sep 17 00:00:00 2001 From: 4d1m Date: Thu, 16 Jan 2020 08:34:40 +0000 Subject: Translated using Weblate (Romanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ro/ --- Emby.Server.Implementations/Localization/Core/ro.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index 1a4756e3d..71bffffc6 100644 --- a/Emby.Server.Implementations/Localization/Core/ro.json +++ b/Emby.Server.Implementations/Localization/Core/ro.json @@ -66,7 +66,7 @@ "ItemRemovedWithName": "{0} a fost eliminat din bibliotecă", "ItemAddedWithName": "{0} a fost adăugat în bibliotecă", "Inherit": "Moștenit", - "HomeVideos": "Videoclipuri personale", + "HomeVideos": "Filme personale", "HeaderRecordingGroups": "Grupuri de înregistrare", "HeaderLiveTV": "TV în Direct", "HeaderFavoriteSongs": "Melodii Favorite", -- cgit v1.2.3 From ddf9b38799fa4a0e227ae3c2516a72ec48f13854 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 21 Jan 2020 20:26:30 +0100 Subject: Simplify image processing by removing image enhancers --- Emby.Drawing/Emby.Drawing.csproj | 12 + Emby.Drawing/ImageProcessor.cs | 349 ++------------------- Emby.Server.Implementations/ApplicationHost.cs | 2 - Emby.Server.Implementations/Dto/DtoService.cs | 47 +-- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 20 +- Jellyfin.Server/Program.cs | 11 +- MediaBrowser.Api/Images/ImageService.cs | 26 +- MediaBrowser.Controller/Drawing/IImageProcessor.cs | 35 --- MediaBrowser.Controller/Drawing/ImageHelper.cs | 2 - .../Drawing/ImageProcessingOptions.cs | 3 - .../MediaEncoding/EncodingJobInfo.cs | 12 +- .../Providers/IImageEnhancer.cs | 61 ---- MediaBrowser.Model/Drawing/ImageDimensions.cs | 31 +- 13 files changed, 96 insertions(+), 515 deletions(-) delete mode 100644 MediaBrowser.Controller/Providers/IImageEnhancer.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 85cecdc44..b7090b262 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -17,4 +17,16 @@ + + + + + + + + + + ../jellyfin.ruleset + + diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index ce8089e59..8f6850936 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; @@ -11,10 +10,8 @@ using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; @@ -24,7 +21,7 @@ namespace Emby.Drawing /// /// Class ImageProcessor. /// - public class ImageProcessor : IImageProcessor, IDisposable + public sealed class ImageProcessor : IImageProcessor, IDisposable { // Increment this when there's a change requiring caches to be invalidated private const string Version = "3"; @@ -32,28 +29,24 @@ namespace Emby.Drawing private static readonly HashSet _transparentImageTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" }; - /// - /// The _logger - /// private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly IServerApplicationPaths _appPaths; - private IImageEncoder _imageEncoder; + private readonly IImageEncoder _imageEncoder; private readonly Func _libraryManager; private readonly Func _mediaEncoder; - private readonly Dictionary _locks = new Dictionary(); private bool _disposed = false; /// - /// + /// Initializes a new instance of the class. /// - /// - /// - /// - /// - /// - /// + /// The logger. + /// The server application paths. + /// The filesystem. + /// The image encoder. + /// The library manager. + /// The media encoder. public ImageProcessor( ILogger logger, IServerApplicationPaths appPaths, @@ -68,16 +61,10 @@ namespace Emby.Drawing _libraryManager = libraryManager; _mediaEncoder = mediaEncoder; _appPaths = appPaths; - - ImageEnhancers = Array.Empty(); - - ImageHelper.ImageProcessor = this; } private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images"); - private string EnhancedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "enhanced-images"); - /// public IReadOnlyCollection SupportedInputFormats => new HashSet(StringComparer.OrdinalIgnoreCase) @@ -90,9 +77,7 @@ namespace Emby.Drawing "aiff", "cr2", "crw", - - // Remove until supported - //"nef", + "nef", "orf", "pef", "arw", @@ -111,19 +96,9 @@ namespace Emby.Drawing "wbmp" }; - /// - public IReadOnlyCollection ImageEnhancers { get; set; } - /// public bool SupportsImageCollageCreation => _imageEncoder.SupportsImageCollageCreation; - /// - public IImageEncoder ImageEncoder - { - get => _imageEncoder; - set => _imageEncoder = value ?? throw new ArgumentNullException(nameof(value)); - } - /// public async Task ProcessImage(ImageProcessingOptions options, Stream toStream) { @@ -151,6 +126,8 @@ namespace Emby.Drawing throw new ArgumentNullException(nameof(options)); } + var libraryManager = _libraryManager(); + ItemImageInfo originalImage = options.Image; BaseItem item = options.Item; @@ -158,9 +135,10 @@ namespace Emby.Drawing { if (item == null) { - item = _libraryManager().GetItemById(options.ItemId); + item = libraryManager.GetItemById(options.ItemId); } - originalImage = await _libraryManager().ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false); + + originalImage = await libraryManager.ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false); } string originalImagePath = originalImage.Path; @@ -187,27 +165,6 @@ namespace Emby.Drawing dateModified = supportedImageInfo.dateModified; bool requiresTransparency = _transparentImageTypes.Contains(Path.GetExtension(originalImagePath)); - if (options.Enhancers.Count > 0) - { - if (item == null) - { - item = _libraryManager().GetItemById(options.ItemId); - } - - var tuple = await GetEnhancedImage(new ItemImageInfo - { - DateModified = dateModified, - Type = originalImage.Type, - Path = originalImagePath - }, requiresTransparency, item, options.ImageIndex, options.Enhancers, CancellationToken.None).ConfigureAwait(false); - - originalImagePath = tuple.path; - dateModified = tuple.dateModified; - requiresTransparency = tuple.transparent; - // TODO: Get this info - originalImageSize = null; - } - bool autoOrient = false; ImageOrientation? orientation = null; if (item is Photo photo) @@ -240,12 +197,6 @@ namespace Emby.Drawing ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency); string cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer); - CheckDisposed(); - - LockInfo lockInfo = GetLock(cacheFilePath); - - await lockInfo.Lock.WaitAsync().ConfigureAwait(false); - try { if (!File.Exists(cacheFilePath)) @@ -271,10 +222,6 @@ namespace Emby.Drawing _logger.LogError(ex, "Error encoding image"); return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } - finally - { - ReleaseLock(cacheFilePath, lockInfo); - } } private ImageFormat GetOutputFormat(IReadOnlyCollection clientSupportedFormats, bool requiresTransparency) @@ -306,20 +253,18 @@ namespace Emby.Drawing } private string GetMimeType(ImageFormat format, string path) - { - switch(format) - { - case ImageFormat.Bmp: return MimeTypes.GetMimeType("i.bmp"); - case ImageFormat.Gif: return MimeTypes.GetMimeType("i.gif"); - case ImageFormat.Jpg: return MimeTypes.GetMimeType("i.jpg"); - case ImageFormat.Png: return MimeTypes.GetMimeType("i.png"); - case ImageFormat.Webp: return MimeTypes.GetMimeType("i.webp"); - default: return MimeTypes.GetMimeType(path); - } - } + => format switch + { + ImageFormat.Bmp => MimeTypes.GetMimeType("i.bmp"), + ImageFormat.Gif => MimeTypes.GetMimeType("i.gif"), + ImageFormat.Jpg => MimeTypes.GetMimeType("i.jpg"), + ImageFormat.Png => MimeTypes.GetMimeType("i.png"), + ImageFormat.Webp => MimeTypes.GetMimeType("i.webp"), + _ => MimeTypes.GetMimeType(path) + }; /// - /// Gets the cache file path based on a set of parameters + /// Gets the cache file path based on a set of parameters. /// private string GetCacheFilePath(string originalPath, ImageDimensions outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer) { @@ -401,11 +346,7 @@ namespace Emby.Drawing /// public string GetImageCacheTag(BaseItem item, ItemImageInfo image) - { - var supportedEnhancers = GetSupportedEnhancers(item, image.Type).ToArray(); - - return GetImageCacheTag(item, image, supportedEnhancers); - } + => (item.Path + image.DateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture); /// public string GetImageCacheTag(BaseItem item, ChapterInfo chapter) @@ -425,26 +366,6 @@ namespace Emby.Drawing } } - /// - public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection imageEnhancers) - { - string originalImagePath = image.Path; - DateTime dateModified = image.DateModified; - ImageType imageType = image.Type; - - // Optimization - if (imageEnhancers.Count == 0) - { - return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture); - } - - // Cache name is created with supported enhancers combined with the last config change so we pick up new config changes - var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList(); - cacheKeys.Add(originalImagePath + dateModified.Ticks); - - return string.Join("|", cacheKeys).GetMD5().ToString("N", CultureInfo.InvariantCulture); - } - private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) { var inputFormat = Path.GetExtension(originalImagePath) @@ -488,154 +409,6 @@ namespace Emby.Drawing return (originalImagePath, dateModified); } - /// - public async Task GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex) - { - var enhancers = GetSupportedEnhancers(item, imageType).ToArray(); - - ItemImageInfo imageInfo = item.GetImageInfo(imageType, imageIndex); - - bool inputImageSupportsTransparency = SupportsTransparency(imageInfo.Path); - - var result = await GetEnhancedImage(imageInfo, inputImageSupportsTransparency, item, imageIndex, enhancers, CancellationToken.None); - - return result.path; - } - - private async Task<(string path, DateTime dateModified, bool transparent)> GetEnhancedImage( - ItemImageInfo image, - bool inputImageSupportsTransparency, - BaseItem item, - int imageIndex, - IReadOnlyCollection enhancers, - CancellationToken cancellationToken) - { - var originalImagePath = image.Path; - var dateModified = image.DateModified; - var imageType = image.Type; - - try - { - var cacheGuid = GetImageCacheTag(item, image, enhancers); - - // Enhance if we have enhancers - var enhancedImageInfo = await GetEnhancedImageInternal(originalImagePath, item, imageType, imageIndex, enhancers, cacheGuid, cancellationToken).ConfigureAwait(false); - - string enhancedImagePath = enhancedImageInfo.path; - - // If the path changed update dateModified - if (!string.Equals(enhancedImagePath, originalImagePath, StringComparison.OrdinalIgnoreCase)) - { - var treatmentRequiresTransparency = enhancedImageInfo.transparent; - - return (enhancedImagePath, _fileSystem.GetLastWriteTimeUtc(enhancedImagePath), treatmentRequiresTransparency); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error enhancing image"); - } - - return (originalImagePath, dateModified, inputImageSupportsTransparency); - } - - /// - /// Gets the enhanced image internal. - /// - /// The original image path. - /// The item. - /// Type of the image. - /// Index of the image. - /// The supported enhancers. - /// The cache unique identifier. - /// The cancellation token. - /// Task<System.String>. - /// - /// originalImagePath - /// or - /// item - /// - private async Task<(string path, bool transparent)> GetEnhancedImageInternal( - string originalImagePath, - BaseItem item, - ImageType imageType, - int imageIndex, - IReadOnlyCollection supportedEnhancers, - string cacheGuid, - CancellationToken cancellationToken = default) - { - if (string.IsNullOrEmpty(originalImagePath)) - { - throw new ArgumentNullException(nameof(originalImagePath)); - } - - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - var treatmentRequiresTransparency = false; - foreach (var enhancer in supportedEnhancers) - { - if (!treatmentRequiresTransparency) - { - treatmentRequiresTransparency = enhancer.GetEnhancedImageInfo(item, originalImagePath, imageType, imageIndex).RequiresTransparency; - } - } - - // All enhanced images are saved as png to allow transparency - string cacheExtension = _imageEncoder.SupportedOutputFormats.Contains(ImageFormat.Webp) ? - ".webp" : - (treatmentRequiresTransparency ? ".png" : ".jpg"); - - string enhancedImagePath = GetCachePath(EnhancedImageCachePath, cacheGuid + cacheExtension); - - LockInfo lockInfo = GetLock(enhancedImagePath); - - await lockInfo.Lock.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - // Check again in case of contention - if (File.Exists(enhancedImagePath)) - { - return (enhancedImagePath, treatmentRequiresTransparency); - } - - Directory.CreateDirectory(Path.GetDirectoryName(enhancedImagePath)); - - await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false); - - return (enhancedImagePath, treatmentRequiresTransparency); - } - finally - { - ReleaseLock(enhancedImagePath, lockInfo); - } - } - - /// - /// Executes the image enhancers. - /// - /// The image enhancers. - /// The input path. - /// The output path. - /// The item. - /// Type of the image. - /// Index of the image. - /// Task{EnhancedImage}. - private static async Task ExecuteImageEnhancers(IEnumerable imageEnhancers, string inputPath, string outputPath, BaseItem item, ImageType imageType, int imageIndex) - { - // Run the enhancers sequentially in order of priority - foreach (var enhancer in imageEnhancers) - { - await enhancer.EnhanceImageAsync(item, inputPath, outputPath, imageType, imageIndex).ConfigureAwait(false); - - // Feed the output into the next enhancer as input - inputPath = outputPath; - } - } - /// /// Gets the cache path. /// @@ -648,7 +421,7 @@ namespace Emby.Drawing /// or /// uniqueName /// or - /// fileExtension + /// fileExtension. /// public string GetCachePath(string path, string uniqueName, string fileExtension) { @@ -681,7 +454,7 @@ namespace Emby.Drawing /// /// path /// or - /// filename + /// filename. /// public string GetCachePath(string path, string filename) { @@ -689,6 +462,7 @@ namespace Emby.Drawing { throw new ArgumentNullException(nameof(path)); } + if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException(nameof(filename)); @@ -710,74 +484,19 @@ namespace Emby.Drawing } /// - public IEnumerable GetSupportedEnhancers(BaseItem item, ImageType imageType) - { - foreach (var i in ImageEnhancers) - { - if (i.Supports(item, imageType)) - { - yield return i; - } - } - } - - - private class LockInfo - { - public SemaphoreSlim Lock = new SemaphoreSlim(1, 1); - public int Count = 1; - } - - private LockInfo GetLock(string key) - { - lock (_locks) - { - if (_locks.TryGetValue(key, out LockInfo info)) - { - info.Count++; - } - else - { - info = new LockInfo(); - _locks[key] = info; - } - return info; - } - } - - private void ReleaseLock(string key, LockInfo info) + public void Dispose() { - info.Lock.Release(); - - lock (_locks) + if (_disposed) { - info.Count--; - if (info.Count <= 0) - { - _locks.Remove(key); - info.Lock.Dispose(); - } + return; } - } - - /// - public void Dispose() - { - _disposed = true; - var disposable = _imageEncoder as IDisposable; - if (disposable != null) + if (_imageEncoder is IDisposable disposable) { disposable.Dispose(); } - } - private void CheckDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(GetType().Name); - } + _disposed = true; } } } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0bb1d832f..4f4787443 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1074,8 +1074,6 @@ namespace Emby.Server.Implementations GetExports(), GetExports()); - ImageProcessor.ImageEnhancers = GetExports(); - LiveTvManager.AddParts(GetExports(), GetExports(), GetExports()); SubtitleManager.AddParts(GetExports()); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index fcf0360c7..960f3f2d6 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -1362,56 +1362,33 @@ namespace Emby.Server.Implementations.Dto return null; } - var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToArray(); - ImageDimensions size; var defaultAspectRatio = item.GetDefaultPrimaryImageAspectRatio(); if (defaultAspectRatio > 0) { - if (supportedEnhancers.Length == 0) - { - return defaultAspectRatio; - } + return defaultAspectRatio; + } - int dummyWidth = 200; - int dummyHeight = Convert.ToInt32(dummyWidth / defaultAspectRatio); - size = new ImageDimensions(dummyWidth, dummyHeight); + if (!imageInfo.IsLocalFile) + { + return null; } - else + + try { - if (!imageInfo.IsLocalFile) - { - return null; - } + size = _imageProcessor.GetImageDimensions(item, imageInfo); - try + if (size.Width <= 0 || size.Height <= 0) { - size = _imageProcessor.GetImageDimensions(item, imageInfo); - - if (size.Width <= 0 || size.Height <= 0) - { - return null; - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Failed to determine primary image aspect ratio for {0}", imageInfo.Path); return null; } } - - foreach (var enhancer in supportedEnhancers) + catch (Exception ex) { - try - { - size = enhancer.GetEnhancedImageSize(item, ImageType.Primary, 0, size); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in image enhancer: {0}", enhancer.GetType().Name); - } + _logger.LogError(ex, "Failed to determine primary image aspect ratio for {0}", imageInfo.Path); + return null; } var width = size.Width; diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index b080b3e6a..2ea690650 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -6,7 +6,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Extensions; using MediaBrowser.Model.Drawing; -using MediaBrowser.Model.Globalization; using Microsoft.Extensions.Logging; using SkiaSharp; using static Jellyfin.Drawing.Skia.SkiaHelper; @@ -18,27 +17,23 @@ namespace Jellyfin.Drawing.Skia /// public class SkiaEncoder : IImageEncoder { - private readonly ILogger _logger; - private readonly IApplicationPaths _appPaths; - private readonly ILocalizationManager _localizationManager; - private static readonly HashSet _transparentImageTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".gif", ".webp" }; + private readonly ILogger _logger; + private readonly IApplicationPaths _appPaths; + /// /// Initializes a new instance of the class. /// /// The application logger. /// The application paths. - /// The application localization manager. public SkiaEncoder( ILogger logger, - IApplicationPaths appPaths, - ILocalizationManager localizationManager) + IApplicationPaths appPaths) { _logger = logger; _appPaths = appPaths; - _localizationManager = localizationManager; } /// @@ -235,9 +230,12 @@ namespace Jellyfin.Drawing.Skia private bool RequiresSpecialCharacterHack(string path) { - if (_localizationManager.HasUnicodeCategory(path, UnicodeCategory.OtherLetter)) + for (int i = 0; i < path.Length; i++) { - return true; + if (char.GetUnicodeCategory(path[i]) == UnicodeCategory.OtherLetter) + { + return true; + } } if (HasDiacritics(path)) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index fa9b62674..c0621ba14 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -169,7 +169,7 @@ namespace Jellyfin.Server _loggerFactory, options, new ManagedFileSystem(_loggerFactory.CreateLogger(), appPaths), - new NullImageEncoder(), + GetImageEncoder(appPaths), new NetworkManager(_loggerFactory.CreateLogger()), appConfig); try @@ -193,8 +193,6 @@ namespace Jellyfin.Server throw; } - appHost.ImageProcessor.ImageEncoder = GetImageEncoder(appPaths, appHost.LocalizationManager); - await appHost.RunStartupTasksAsync().ConfigureAwait(false); stopWatch.Stop(); @@ -494,9 +492,7 @@ namespace Jellyfin.Server } } - private static IImageEncoder GetImageEncoder( - IApplicationPaths appPaths, - ILocalizationManager localizationManager) + private static IImageEncoder GetImageEncoder(IApplicationPaths appPaths) { try { @@ -505,8 +501,7 @@ namespace Jellyfin.Server return new SkiaEncoder( _loggerFactory.CreateLogger(), - appPaths, - localizationManager); + appPaths); } catch (Exception ex) { diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index e94c1321f..4060bf4cc 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -24,7 +24,7 @@ using Microsoft.Net.Http.Headers; namespace MediaBrowser.Api.Images { /// - /// Class GetItemImage + /// Class GetItemImage. /// [Route("/Items/{Id}/Images", "GET", Summary = "Gets information about an item's images")] [Authenticated] @@ -558,21 +558,6 @@ namespace MediaBrowser.Api.Images throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", displayText, request.Type)); } - IImageEnhancer[] supportedImageEnhancers; - if (_imageProcessor.ImageEnhancers.Count > 0) - { - if (item == null) - { - item = _libraryManager.GetItemById(itemId); - } - - supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type).ToArray() : Array.Empty(); - } - else - { - supportedImageEnhancers = Array.Empty(); - } - bool cropwhitespace; if (request.CropWhitespace.HasValue) { @@ -598,25 +583,25 @@ namespace MediaBrowser.Api.Images {"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"} }; - return GetImageResult(item, + return GetImageResult( + item, itemId, request, imageInfo, cropwhitespace, outputFormats, - supportedImageEnhancers, cacheDuration, responseHeaders, isHeadRequest); } - private async Task GetImageResult(BaseItem item, + private async Task GetImageResult( + BaseItem item, Guid itemId, ImageRequest request, ItemImageInfo image, bool cropwhitespace, IReadOnlyCollection supportedFormats, - IReadOnlyCollection enhancers, TimeSpan? cacheDuration, IDictionary headers, bool isHeadRequest) @@ -624,7 +609,6 @@ namespace MediaBrowser.Api.Images var options = new ImageProcessingOptions { CropWhiteSpace = cropwhitespace, - Enhancers = enhancers, Height = request.Height, ImageIndex = request.Index ?? 0, Image = image, diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index a58a11bd1..79399807f 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -20,20 +19,12 @@ namespace MediaBrowser.Controller.Drawing /// The supported input formats. IReadOnlyCollection SupportedInputFormats { get; } - /// - /// Gets the image enhancers. - /// - /// The image enhancers. - IReadOnlyCollection ImageEnhancers { get; set; } - /// /// Gets a value indicating whether [supports image collage creation]. /// /// true if [supports image collage creation]; otherwise, false. bool SupportsImageCollageCreation { get; } - IImageEncoder ImageEncoder { get; set; } - /// /// Gets the dimensions of the image. /// @@ -58,14 +49,6 @@ namespace MediaBrowser.Controller.Drawing /// ImageDimensions ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem); - /// - /// Gets the supported enhancers. - /// - /// The item. - /// Type of the image. - /// IEnumerable{IImageEnhancer}. - IEnumerable GetSupportedEnhancers(BaseItem item, ImageType imageType); - /// /// Gets the image cache tag. /// @@ -75,15 +58,6 @@ namespace MediaBrowser.Controller.Drawing string GetImageCacheTag(BaseItem item, ItemImageInfo image); string GetImageCacheTag(BaseItem item, ChapterInfo info); - /// - /// Gets the image cache tag. - /// - /// The item. - /// The image. - /// The image enhancers. - /// Guid. - string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection imageEnhancers); - /// /// Processes the image. /// @@ -99,15 +73,6 @@ namespace MediaBrowser.Controller.Drawing /// Task. Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options); - /// - /// Gets the enhanced image. - /// - /// The item. - /// Type of the image. - /// Index of the image. - /// Task{System.String}. - Task GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex); - /// /// Gets the supported image output formats. /// diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs index 432cf8042..d5a5f547e 100644 --- a/MediaBrowser.Controller/Drawing/ImageHelper.cs +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -19,8 +19,6 @@ namespace MediaBrowser.Controller.Drawing return GetSizeEstimate(options); } - public static IImageProcessor ImageProcessor { get; set; } - private static ImageDimensions GetSizeEstimate(ImageProcessingOptions options) { if (options.Width.HasValue && options.Height.HasValue) diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 29addf6e6..870e0278e 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; namespace MediaBrowser.Controller.Drawing @@ -34,8 +33,6 @@ namespace MediaBrowser.Controller.Drawing public int Quality { get; set; } - public IReadOnlyCollection Enhancers { get; set; } - public IReadOnlyCollection SupportedOutputFormats { get; set; } public bool AddPlayedIndicator { get; set; } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 34af3b156..6b3fb4144 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -316,11 +316,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue) { - var size = new ImageDimensions - { - Width = VideoStream.Width.Value, - Height = VideoStream.Height.Value - }; + var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value); var newSize = DrawingUtils.Resize(size, BaseRequest.Width ?? 0, @@ -346,11 +342,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (VideoStream != null && VideoStream.Width.HasValue && VideoStream.Height.HasValue) { - var size = new ImageDimensions - { - Width = VideoStream.Width.Value, - Height = VideoStream.Height.Value - }; + var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value); var newSize = DrawingUtils.Resize(size, BaseRequest.Width ?? 0, diff --git a/MediaBrowser.Controller/Providers/IImageEnhancer.cs b/MediaBrowser.Controller/Providers/IImageEnhancer.cs deleted file mode 100644 index c27c00ca2..000000000 --- a/MediaBrowser.Controller/Providers/IImageEnhancer.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Drawing; -using MediaBrowser.Model.Entities; - -namespace MediaBrowser.Controller.Providers -{ - public interface IImageEnhancer - { - /// - /// Return true only if the given image for the given item will be enhanced by this enhancer. - /// - /// The item. - /// Type of the image. - /// true if this enhancer will enhance the supplied image for the supplied item, false otherwise - bool Supports(BaseItem item, ImageType imageType); - - /// - /// Gets the priority or order in which this enhancer should be run. - /// - /// The priority. - MetadataProviderPriority Priority { get; } - - /// - /// Return a key incorporating all configuration information related to this item - /// - /// The item. - /// Type of the image. - /// Cache key relating to the current state of this item and configuration - string GetConfigurationCacheKey(BaseItem item, ImageType imageType); - - /// - /// Gets the size of the enhanced image. - /// - /// The item. - /// Type of the image. - /// Index of the image. - /// Size of the original image. - /// ImageSize. - ImageDimensions GetEnhancedImageSize(BaseItem item, ImageType imageType, int imageIndex, ImageDimensions originalImageSize); - - EnhancedImageInfo GetEnhancedImageInfo(BaseItem item, string inputFile, ImageType imageType, int imageIndex); - - /// - /// Enhances the image async. - /// - /// The item. - /// The input file. - /// The output file. - /// Type of the image. - /// Index of the image. - /// Task{Image}. - /// - Task EnhanceImageAsync(BaseItem item, string inputFile, string outputFile, ImageType imageType, int imageIndex); - } - - public class EnhancedImageInfo - { - public bool RequiresTransparency { get; set; } - } -} diff --git a/MediaBrowser.Model/Drawing/ImageDimensions.cs b/MediaBrowser.Model/Drawing/ImageDimensions.cs index e7805ac49..4bd8d85d4 100644 --- a/MediaBrowser.Model/Drawing/ImageDimensions.cs +++ b/MediaBrowser.Model/Drawing/ImageDimensions.cs @@ -1,36 +1,43 @@ +using System.Globalization; + namespace MediaBrowser.Model.Drawing { /// /// Struct ImageDimensions /// - public struct ImageDimensions + public readonly struct ImageDimensions { + public ImageDimensions(int width, int height) + { + Width = width; + Height = height; + } + /// - /// Gets or sets the height. + /// Gets the height. /// /// The height. - public int Height { get; set; } + public int Height { get; } /// - /// Gets or sets the width. + /// Gets the width. /// /// The width. - public int Width { get; set; } + public int Width { get; } public bool Equals(ImageDimensions size) { return Width.Equals(size.Width) && Height.Equals(size.Height); } + /// public override string ToString() { - return string.Format("{0}-{1}", Width, Height); - } - - public ImageDimensions(int width, int height) - { - Width = width; - Height = height; + return string.Format( + CultureInfo.InvariantCulture, + "{0}-{1}", + Width, + Height); } } } -- cgit v1.2.3 From 6eac7f0fa7e753731e3d65c0b6c0323eb730ccd8 Mon Sep 17 00:00:00 2001 From: Marco Date: Tue, 21 Jan 2020 16:40:09 +0000 Subject: Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 8f91effb9..c1c40c18e 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -9,7 +9,7 @@ "Channels": "Canali", "ChapterNameValue": "Capitolo {0}", "Collections": "Collezioni", - "DeviceOfflineWithName": "{0} ha disconnesso", + "DeviceOfflineWithName": "{0} si è disconnesso", "DeviceOnlineWithName": "{0} è connesso", "FailedLoginAttemptWithUserName": "Tentativo di accesso fallito da {0}", "Favorites": "Preferiti", -- cgit v1.2.3 From dc62e436c43362f2193415f4c81280c6c1a8560c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 22 Jan 2020 22:18:56 +0100 Subject: Clean up Emby.Naming --- Emby.Naming/Audio/AlbumParser.cs | 11 +- Emby.Naming/Audio/MultiPartResult.cs | 26 ----- Emby.Naming/AudioBook/AudioBookFileInfo.cs | 2 +- Emby.Naming/AudioBook/AudioBookListResolver.cs | 19 +-- Emby.Naming/Common/EpisodeExpression.cs | 36 +++--- Emby.Naming/Common/NamingOptions.cs | 83 ++++++------- Emby.Naming/Emby.Naming.csproj | 5 +- Emby.Naming/TV/EpisodePathParser.cs | 5 +- Emby.Naming/TV/EpisodeResolver.cs | 20 +--- Emby.Naming/TV/SeasonPathParser.cs | 39 ++++--- Emby.Naming/Video/StackResolver.cs | 21 ++-- Emby.Naming/Video/StackResult.cs | 17 --- Emby.Naming/Video/StubResolver.cs | 20 ++-- Emby.Naming/Video/VideoFileInfo.cs | 2 +- Emby.Naming/Video/VideoInfo.cs | 18 +-- Emby.Naming/Video/VideoListResolver.cs | 43 ++++--- Emby.Naming/Video/VideoResolver.cs | 21 ++-- .../Library/LibraryManager.cs | 2 +- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 20 +--- .../Library/Resolvers/Movies/MovieResolver.cs | 73 ++++++------ .../Library/Resolvers/TV/SeasonResolver.cs | 21 ++-- .../Library/Resolvers/TV/SeriesResolver.cs | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 3 - .../Music/MultiDiscAlbumTests.cs | 2 +- .../Jellyfin.Naming.Tests/TV/SeasonFolderTests.cs | 3 +- tests/Jellyfin.Naming.Tests/Video/StackTests.cs | 130 ++++++++++----------- tests/Jellyfin.Naming.Tests/Video/StubTests.cs | 10 +- 27 files changed, 287 insertions(+), 367 deletions(-) delete mode 100644 Emby.Naming/Audio/MultiPartResult.cs delete mode 100644 Emby.Naming/Video/StackResult.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs index 4975b8e19..b807816eb 100644 --- a/Emby.Naming/Audio/AlbumParser.cs +++ b/Emby.Naming/Audio/AlbumParser.cs @@ -19,15 +19,13 @@ namespace Emby.Naming.Audio _options = options; } - public MultiPartResult ParseMultiPart(string path) + public bool IsMultiPart(string path) { - var result = new MultiPartResult(); - var filename = Path.GetFileName(path); if (string.IsNullOrEmpty(filename)) { - return result; + return false; } // TODO: Move this logic into options object @@ -57,12 +55,11 @@ namespace Emby.Naming.Audio if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _)) { - result.IsMultiPart = true; - break; + return true; } } - return result; + return false; } } } diff --git a/Emby.Naming/Audio/MultiPartResult.cs b/Emby.Naming/Audio/MultiPartResult.cs deleted file mode 100644 index 8f68d97fa..000000000 --- a/Emby.Naming/Audio/MultiPartResult.cs +++ /dev/null @@ -1,26 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - -namespace Emby.Naming.Audio -{ - public class MultiPartResult - { - /// - /// Gets or sets the name. - /// - /// The name. - public string Name { get; set; } - - /// - /// Gets or sets the part. - /// - /// The part. - public string Part { get; set; } - - /// - /// Gets or sets a value indicating whether this instance is multi part. - /// - /// true if this instance is multi part; otherwise, false. - public bool IsMultiPart { get; set; } - } -} diff --git a/Emby.Naming/AudioBook/AudioBookFileInfo.cs b/Emby.Naming/AudioBook/AudioBookFileInfo.cs index 769e3d7fa..0bc6ec7e4 100644 --- a/Emby.Naming/AudioBook/AudioBookFileInfo.cs +++ b/Emby.Naming/AudioBook/AudioBookFileInfo.cs @@ -32,7 +32,7 @@ namespace Emby.Naming.AudioBook public int? ChapterNumber { get; set; } /// - /// Gets or sets the type. + /// Gets or sets a value indicating whether this instance is a directory. /// /// The type. public bool IsDirectory { get; set; } diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index 97f359285..835e83a08 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -39,9 +39,7 @@ namespace Emby.Naming.AudioBook var stackResult = new StackResolver(_options) .ResolveAudioBooks(metadata); - var list = new List(); - - foreach (var stack in stackResult.Stacks) + foreach (var stack in stackResult) { var stackFiles = stack.Files.Select(i => audioBookResolver.Resolve(i, stack.IsDirectoryStack)).ToList(); stackFiles.Sort(); @@ -50,20 +48,9 @@ namespace Emby.Naming.AudioBook Files = stackFiles, Name = stack.Name }; - list.Add(info); - } - - // Whatever files are left, just add them - /*list.AddRange(remainingFiles.Select(i => new AudioBookInfo - { - Files = new List { i }, - Name = i., - Year = i.Year - }));*/ - - var orderedList = list.OrderBy(i => i.Name); - return orderedList; + yield return info; + } } } } diff --git a/Emby.Naming/Common/EpisodeExpression.cs b/Emby.Naming/Common/EpisodeExpression.cs index 30a74fb65..f60f7e84b 100644 --- a/Emby.Naming/Common/EpisodeExpression.cs +++ b/Emby.Naming/Common/EpisodeExpression.cs @@ -11,6 +11,24 @@ namespace Emby.Naming.Common private string _expression; private Regex _regex; + public EpisodeExpression(string expression, bool byDate) + { + Expression = expression; + IsByDate = byDate; + DateTimeFormats = Array.Empty(); + SupportsAbsoluteEpisodeNumbers = true; + } + + public EpisodeExpression(string expression) + : this(expression, false) + { + } + + public EpisodeExpression() + : this(null) + { + } + public string Expression { get => _expression; @@ -32,23 +50,5 @@ namespace Emby.Naming.Common public string[] DateTimeFormats { get; set; } public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled)); - - public EpisodeExpression(string expression, bool byDate) - { - Expression = expression; - IsByDate = byDate; - DateTimeFormats = Array.Empty(); - SupportsAbsoluteEpisodeNumbers = true; - } - - public EpisodeExpression(string expression) - : this(expression, false) - { - } - - public EpisodeExpression() - : this(null) - { - } } } diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 9ce503b8e..1554e61d8 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -11,46 +11,6 @@ namespace Emby.Naming.Common { public class NamingOptions { - public string[] AudioFileExtensions { get; set; } - - public string[] AlbumStackingPrefixes { get; set; } - - public string[] SubtitleFileExtensions { get; set; } - - public char[] SubtitleFlagDelimiters { get; set; } - - public string[] SubtitleForcedFlags { get; set; } - - public string[] SubtitleDefaultFlags { get; set; } - - public EpisodeExpression[] EpisodeExpressions { get; set; } - - public string[] EpisodeWithoutSeasonExpressions { get; set; } - - public string[] EpisodeMultiPartExpressions { get; set; } - - public string[] VideoFileExtensions { get; set; } - - public string[] StubFileExtensions { get; set; } - - public string[] AudioBookPartsExpressions { get; set; } - - public StubTypeRule[] StubTypes { get; set; } - - public char[] VideoFlagDelimiters { get; set; } - - public Format3DRule[] Format3DRules { get; set; } - - public string[] VideoFileStackingExpressions { get; set; } - - public string[] CleanDateTimes { get; set; } - - public string[] CleanStrings { get; set; } - - public EpisodeExpression[] MultipleEpisodeExpressions { get; set; } - - public ExtraRule[] VideoExtraRules { get; set; } - public NamingOptions() { VideoFileExtensions = new[] @@ -681,11 +641,54 @@ namespace Emby.Naming.Common Compile(); } + public string[] AudioFileExtensions { get; set; } + + public string[] AlbumStackingPrefixes { get; set; } + + public string[] SubtitleFileExtensions { get; set; } + + public char[] SubtitleFlagDelimiters { get; set; } + + public string[] SubtitleForcedFlags { get; set; } + + public string[] SubtitleDefaultFlags { get; set; } + + public EpisodeExpression[] EpisodeExpressions { get; set; } + + public string[] EpisodeWithoutSeasonExpressions { get; set; } + + public string[] EpisodeMultiPartExpressions { get; set; } + + public string[] VideoFileExtensions { get; set; } + + public string[] StubFileExtensions { get; set; } + + public string[] AudioBookPartsExpressions { get; set; } + + public StubTypeRule[] StubTypes { get; set; } + + public char[] VideoFlagDelimiters { get; set; } + + public Format3DRule[] Format3DRules { get; set; } + + public string[] VideoFileStackingExpressions { get; set; } + + public string[] CleanDateTimes { get; set; } + + public string[] CleanStrings { get; set; } + + public EpisodeExpression[] MultipleEpisodeExpressions { get; set; } + + public ExtraRule[] VideoExtraRules { get; set; } + public Regex[] VideoFileStackingRegexes { get; private set; } + public Regex[] CleanDateTimeRegexes { get; private set; } + public Regex[] CleanStringRegexes { get; private set; } public Regex[] EpisodeWithoutSeasonRegexes { get; private set; } + public Regex[] EpisodeMultiPartRegexes { get; private set; } public void Compile() diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 900b9694c..4e08170a4 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -4,9 +4,6 @@ netstandard2.1 false true - - - true @@ -27,7 +24,7 @@ - + diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs index 6b557d2e1..b97b3137b 100644 --- a/Emby.Naming/TV/EpisodePathParser.cs +++ b/Emby.Naming/TV/EpisodePathParser.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable using System; using System.Collections.Generic; @@ -28,7 +29,7 @@ namespace Emby.Naming.TV path += ".mp4"; } - EpisodePathParserResult result = null; + EpisodePathParserResult? result = null; foreach (var expression in _options.EpisodeExpressions) { @@ -136,7 +137,7 @@ namespace Emby.Naming.TV // It avoids erroneous parsing of something like "series-s09e14-1080p.mkv" as a multi-episode from E14 to E108 int nextIndex = endingNumberGroup.Index + endingNumberGroup.Length; if (nextIndex >= name.Length - || "0123456789iIpP".IndexOf(name[nextIndex]) == -1) + || !"0123456789iIpP".Contains(name[nextIndex], StringComparison.Ordinal)) { if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) { diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs index 5e115fc75..57659ee13 100644 --- a/Emby.Naming/TV/EpisodeResolver.cs +++ b/Emby.Naming/TV/EpisodeResolver.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable using System; using System.IO; @@ -18,7 +19,7 @@ namespace Emby.Naming.TV _options = options; } - public EpisodeInfo Resolve( + public EpisodeInfo? Resolve( string path, bool isDirectory, bool? isNamed = null, @@ -26,14 +27,9 @@ namespace Emby.Naming.TV bool? supportsAbsoluteNumbers = null, bool fillExtendedInfo = true) { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(nameof(path)); - } - bool isStub = false; - string container = null; - string stubType = null; + string? container = null; + string? stubType = null; if (!isDirectory) { @@ -41,17 +37,13 @@ namespace Emby.Naming.TV // Check supported extensions if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - var stubResult = StubResolver.ResolveFile(path, _options); - - isStub = stubResult.IsStub; - // It's not supported. Check stub extensions - if (!isStub) + if (!StubResolver.TryResolveFile(path, _options, out stubType)) { return null; } - stubType = stubResult.StubType; + isStub = true; } container = extension.TrimStart('.'); diff --git a/Emby.Naming/TV/SeasonPathParser.cs b/Emby.Naming/TV/SeasonPathParser.cs index e5f90e966..7715a16a4 100644 --- a/Emby.Naming/TV/SeasonPathParser.cs +++ b/Emby.Naming/TV/SeasonPathParser.cs @@ -8,9 +8,24 @@ using System.Linq; namespace Emby.Naming.TV { - public class SeasonPathParser + public static class SeasonPathParser { - public SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders) + /// + /// A season folder must contain one of these somewhere in the name. + /// + private static readonly string[] _seasonFolderNames = + { + "season", + "sæson", + "temporada", + "saison", + "staffel", + "series", + "сезон", + "stagione" + }; + + public static SeasonPathParserResult Parse(string path, bool supportSpecialAliases, bool supportNumericSeasonFolders) { var result = new SeasonPathParserResult(); @@ -27,21 +42,6 @@ namespace Emby.Naming.TV return result; } - /// - /// A season folder must contain one of these somewhere in the name. - /// - private static readonly string[] _seasonFolderNames = - { - "season", - "sæson", - "temporada", - "saison", - "staffel", - "series", - "сезон", - "stagione" - }; - /// /// Gets the season number from path. /// @@ -150,6 +150,7 @@ namespace Emby.Naming.TV { numericStart = i; } + length++; } } @@ -161,11 +162,11 @@ namespace Emby.Naming.TV } var currentChar = path[i]; - if (currentChar.Equals('(')) + if (currentChar == '(') { hasOpenParenth = true; } - else if (currentChar.Equals(')')) + else if (currentChar == ')') { hasOpenParenth = false; } diff --git a/Emby.Naming/Video/StackResolver.cs b/Emby.Naming/Video/StackResolver.cs index e7a769ae6..8f210fa45 100644 --- a/Emby.Naming/Video/StackResolver.cs +++ b/Emby.Naming/Video/StackResolver.cs @@ -20,7 +20,7 @@ namespace Emby.Naming.Video _options = options; } - public StackResult ResolveDirectories(IEnumerable files) + public IEnumerable ResolveDirectories(IEnumerable files) { return Resolve(files.Select(i => new FileSystemMetadata { @@ -29,7 +29,7 @@ namespace Emby.Naming.Video })); } - public StackResult ResolveFiles(IEnumerable files) + public IEnumerable ResolveFiles(IEnumerable files) { return Resolve(files.Select(i => new FileSystemMetadata { @@ -38,9 +38,8 @@ namespace Emby.Naming.Video })); } - public StackResult ResolveAudioBooks(IEnumerable files) + public IEnumerable ResolveAudioBooks(IEnumerable files) { - var result = new StackResult(); foreach (var directory in files.GroupBy(file => file.IsDirectory ? file.FullName : Path.GetDirectoryName(file.FullName))) { var stack = new FileStack() @@ -58,20 +57,16 @@ namespace Emby.Naming.Video stack.Files.Add(file.FullName); } - result.Stacks.Add(stack); + yield return stack; } - - return result; } - public StackResult Resolve(IEnumerable files) + public IEnumerable Resolve(IEnumerable files) { - var result = new StackResult(); - var resolver = new VideoResolver(_options); var list = files - .Where(i => i.IsDirectory || (resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName))) + .Where(i => i.IsDirectory || resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName)) .OrderBy(i => i.FullName) .ToList(); @@ -191,14 +186,12 @@ namespace Emby.Naming.Video if (stack.Files.Count > 1) { - result.Stacks.Add(stack); + yield return stack; i += stack.Files.Count - 1; break; } } } - - return result; } private string GetRegexInput(FileSystemMetadata file) diff --git a/Emby.Naming/Video/StackResult.cs b/Emby.Naming/Video/StackResult.cs deleted file mode 100644 index 31ef2d69c..000000000 --- a/Emby.Naming/Video/StackResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - -using System.Collections.Generic; - -namespace Emby.Naming.Video -{ - public class StackResult - { - public List Stacks { get; set; } - - public StackResult() - { - Stacks = new List(); - } - } -} diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs index 95868e89d..4024d6d59 100644 --- a/Emby.Naming/Video/StubResolver.cs +++ b/Emby.Naming/Video/StubResolver.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable using System; using System.IO; @@ -10,25 +11,22 @@ namespace Emby.Naming.Video { public static class StubResolver { - public static StubResult ResolveFile(string path, NamingOptions options) + public static bool TryResolveFile(string path, NamingOptions options, out string? stubType) { + stubType = default; + if (path == null) { - return default; + return false; } var extension = Path.GetExtension(path); if (!options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - return default; + return false; } - var result = new StubResult() - { - IsStub = true - }; - path = Path.GetFileNameWithoutExtension(path); var token = Path.GetExtension(path).TrimStart('.'); @@ -36,12 +34,12 @@ namespace Emby.Naming.Video { if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase)) { - result.StubType = rule.StubType; - break; + stubType = rule.StubType; + return true; } } - return result; + return true; } } } diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs index 90c798da1..aa4f3a35c 100644 --- a/Emby.Naming/Video/VideoFileInfo.cs +++ b/Emby.Naming/Video/VideoFileInfo.cs @@ -68,7 +68,7 @@ namespace Emby.Naming.Video public string StubType { get; set; } /// - /// Gets or sets the type. + /// Gets or sets a value indicating whether this instance is a directory. /// /// The type. public bool IsDirectory { get; set; } diff --git a/Emby.Naming/Video/VideoInfo.cs b/Emby.Naming/Video/VideoInfo.cs index a585bb99a..ea74c40e2 100644 --- a/Emby.Naming/Video/VideoInfo.cs +++ b/Emby.Naming/Video/VideoInfo.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace Emby.Naming.Video @@ -10,11 +11,14 @@ namespace Emby.Naming.Video /// /// Initializes a new instance of the class. /// - public VideoInfo() + /// The name. + public VideoInfo(string name) { - Files = new List(); - Extras = new List(); - AlternateVersions = new List(); + Name = name; + + Files = Array.Empty(); + Extras = Array.Empty(); + AlternateVersions = Array.Empty(); } /// @@ -33,18 +37,18 @@ namespace Emby.Naming.Video /// Gets or sets the files. /// /// The files. - public List Files { get; set; } + public IReadOnlyList Files { get; set; } /// /// Gets or sets the extras. /// /// The extras. - public List Extras { get; set; } + public IReadOnlyList Extras { get; set; } /// /// Gets or sets the alternate versions. /// /// The alternate versions. - public List AlternateVersions { get; set; } + public IReadOnlyList AlternateVersions { get; set; } } } diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 87498000c..136658353 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -41,20 +41,19 @@ namespace Emby.Naming.Video }); var stackResult = new StackResolver(_options) - .Resolve(nonExtras); + .Resolve(nonExtras).ToList(); var remainingFiles = videoInfos - .Where(i => !stackResult.Stacks.Any(s => s.ContainsFile(i.Path, i.IsDirectory))) + .Where(i => !stackResult.Any(s => s.ContainsFile(i.Path, i.IsDirectory))) .ToList(); var list = new List(); - foreach (var stack in stackResult.Stacks) + foreach (var stack in stackResult) { - var info = new VideoInfo + var info = new VideoInfo(stack.Name) { - Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)).ToList(), - Name = stack.Name + Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)).ToList() }; info.Year = info.Files[0].Year; @@ -85,10 +84,9 @@ namespace Emby.Naming.Video foreach (var media in standaloneMedia) { - var info = new VideoInfo + var info = new VideoInfo(media.Name) { - Files = new List { media }, - Name = media.Name + Files = new List { media } }; info.Year = info.Files[0].Year; @@ -128,7 +126,8 @@ namespace Emby.Naming.Video .Except(extras) .ToList(); - info.Extras.AddRange(extras); + extras.AddRange(info.Extras); + info.Extras = extras; } } @@ -141,7 +140,8 @@ namespace Emby.Naming.Video .Except(extrasByFileName) .ToList(); - info.Extras.AddRange(extrasByFileName); + extrasByFileName.AddRange(info.Extras); + info.Extras = extrasByFileName; } // If there's only one video, accept all trailers @@ -152,7 +152,8 @@ namespace Emby.Naming.Video .Where(i => i.ExtraType == ExtraType.Trailer) .ToList(); - list[0].Extras.AddRange(trailers); + trailers.AddRange(list[0].Extras); + list[0].Extras = trailers; remainingFiles = remainingFiles .Except(trailers) @@ -160,14 +161,13 @@ namespace Emby.Naming.Video } // Whatever files are left, just add them - list.AddRange(remainingFiles.Select(i => new VideoInfo + list.AddRange(remainingFiles.Select(i => new VideoInfo(i.Name) { Files = new List { i }, - Name = i.Name, Year = i.Year })); - return list.OrderBy(i => i.Name); + return list; } private IEnumerable GetVideosGroupedByVersion(List videos) @@ -191,9 +191,18 @@ namespace Emby.Naming.Video list.Add(ordered[0]); - list[0].AlternateVersions = ordered.Skip(1).Select(i => i.Files[0]).ToList(); + var alternateVersionsLen = ordered.Count - 1; + var alternateVersions = new VideoFileInfo[alternateVersionsLen]; + for (int i = 0; i < alternateVersionsLen; i++) + { + alternateVersions[i] = ordered[i + 1].Files[0]; + } + + list[0].AlternateVersions = alternateVersions; list[0].Name = folderName; - list[0].Extras.AddRange(ordered.Skip(1).SelectMany(i => i.Extras)); + var extras = ordered.Skip(1).SelectMany(i => i.Extras).ToList(); + extras.AddRange(list[0].Extras); + list[0].Extras = extras; return list; } diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index f93db2486..699bbe40a 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable using System; using System.IO; @@ -22,7 +23,7 @@ namespace Emby.Naming.Video /// /// The path. /// VideoFileInfo. - public VideoFileInfo ResolveDirectory(string path) + public VideoFileInfo? ResolveDirectory(string path) { return Resolve(path, true); } @@ -32,7 +33,7 @@ namespace Emby.Naming.Video /// /// The path. /// VideoFileInfo. - public VideoFileInfo ResolveFile(string path) + public VideoFileInfo? ResolveFile(string path) { return Resolve(path, false); } @@ -42,10 +43,10 @@ namespace Emby.Naming.Video /// /// The path. /// if set to true [is folder]. - /// Whether or not the name should be parsed for info + /// Whether or not the name should be parsed for info. /// VideoFileInfo. /// path is null. - public VideoFileInfo Resolve(string path, bool isDirectory, bool parseName = true) + public VideoFileInfo? Resolve(string path, bool isDirectory, bool parseName = true) { if (string.IsNullOrEmpty(path)) { @@ -53,8 +54,8 @@ namespace Emby.Naming.Video } bool isStub = false; - string container = null; - string stubType = null; + string? container = null; + string? stubType = null; if (!isDirectory) { @@ -63,17 +64,13 @@ namespace Emby.Naming.Video // Check supported extensions if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { - var stubResult = StubResolver.ResolveFile(path, _options); - - isStub = stubResult.IsStub; - // It's not supported. Check stub extensions - if (!isStub) + if (!StubResolver.TryResolveFile(path, _options, out stubType)) { return null; } - stubType = stubResult.StubType; + isStub = true; } container = extension.TrimStart('.'); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6fb623554..5c04631da 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2384,7 +2384,7 @@ namespace Emby.Server.Implementations.Library public int? GetSeasonNumberFromPath(string path) { - return new SeasonPathParser().Parse(path, true, true).SeasonNumber; + return SeasonPathParser.Parse(path, true, true).SeasonNumber; } public bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh) diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 4a2d210d5..9f858f98d 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -76,7 +76,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } /// - /// Determine if the supplied file data points to a music album + /// Determine if the supplied file data points to a music album. /// public bool IsMusicAlbum(string path, IDirectoryService directoryService, LibraryOptions libraryOptions) { @@ -84,7 +84,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } /// - /// Determine if the supplied resolve args should be considered a music album + /// Determine if the supplied resolve args should be considered a music album. /// /// The args. /// true if [is music album] [the specified args]; otherwise, false. @@ -104,7 +104,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } /// - /// Determine if the supplied list contains what we should consider music + /// Determine if the supplied list contains what we should consider music. /// private bool ContainsMusic( IEnumerable list, @@ -118,6 +118,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var discSubfolderCount = 0; var notMultiDisc = false; + var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); + var parser = new AlbumParser(namingOptions); foreach (var fileSystemInfo in list) { if (fileSystemInfo.IsDirectory) @@ -134,7 +136,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio if (hasMusic) { - if (IsMultiDiscFolder(path, libraryOptions)) + if (parser.IsMultiPart(path)) { logger.LogDebug("Found multi-disc folder: " + path); discSubfolderCount++; @@ -165,15 +167,5 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return discSubfolderCount > 0; } - - private bool IsMultiDiscFolder(string path, LibraryOptions libraryOptions) - { - var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); - - var parser = new AlbumParser(namingOptions); - var result = parser.ParseMultiPart(path); - - return result.IsMultiPart; - } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 6c7690055..08db168bc 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -21,6 +21,28 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies /// public class MovieResolver : BaseVideoResolver