From 321e5cba60825d5791730da75cd12905e97b527e Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 3 Oct 2019 11:51:28 -0400 Subject: [PATCH 001/252] Add new MediaAttachment to store attachments found during media probing. --- .../Probing/ProbeResultNormalizer.cs | 38 ++++++++++++++++ MediaBrowser.Model/Dto/MediaSourceInfo.cs | 2 + .../Entities/MediaAttachment.cs | 44 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 MediaBrowser.Model/Entities/MediaAttachment.cs diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 54d02fc9f7..2c001d775a 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -49,6 +49,10 @@ namespace MediaBrowser.MediaEncoding.Probing .Where(i => i.Type != MediaStreamType.Subtitle || !string.IsNullOrWhiteSpace(i.Codec)) .ToList(); + info.MediaAttachments = internalStreams.Select(s => GetMediaAttachment(s)) + .Where(i => i != null) + .ToList(); + if (data.format != null) { info.Container = NormalizeFormat(data.format.format_name); @@ -513,6 +517,40 @@ namespace MediaBrowser.MediaEncoding.Probing return codec; } + /// + /// Converts ffprobe stream info to our MediaAttachment class + /// + /// The stream info. + /// MediaAttachments. + private MediaAttachment GetMediaAttachment(MediaStreamInfo streamInfo) + { + if (!string.Equals(streamInfo.codec_type, "attachment", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + var attachment = new MediaAttachment + { + Codec = streamInfo.codec_name, + Index = streamInfo.index + }; + + // Filter out junk + if (!string.IsNullOrWhiteSpace(streamInfo.codec_tag_string) && streamInfo.codec_tag_string.IndexOf("[0]", StringComparison.OrdinalIgnoreCase) == -1) + { + attachment.CodecTag = streamInfo.codec_tag_string; + } + + if (streamInfo.tags != null) + { + attachment.Filename = GetDictionaryValue(streamInfo.tags, "filename"); + attachment.MIMEType = GetDictionaryValue(streamInfo.tags, "mimetype"); + attachment.Comment = GetDictionaryValue(streamInfo.tags, "comment"); + } + + return attachment; + } + /// /// Converts ffprobe stream info to our MediaStream class /// diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 5bdc4809a9..8a1aa55b69 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -57,6 +57,8 @@ namespace MediaBrowser.Model.Dto public List MediaStreams { get; set; } + public List MediaAttachments { get; set; } + public string[] Formats { get; set; } public int? Bitrate { get; set; } diff --git a/MediaBrowser.Model/Entities/MediaAttachment.cs b/MediaBrowser.Model/Entities/MediaAttachment.cs new file mode 100644 index 0000000000..daabbe91df --- /dev/null +++ b/MediaBrowser.Model/Entities/MediaAttachment.cs @@ -0,0 +1,44 @@ +namespace MediaBrowser.Model.Entities +{ + /// + /// Class MediaAttachment + /// + public class MediaAttachment + { + /// + /// Gets or sets the codec. + /// + /// The codec. + public string Codec { get; set; } + + /// + /// Gets or sets the codec tag. + /// + /// The codec tag. + public string CodecTag { get; set; } + + /// + /// Gets or sets the comment. + /// + /// The comment. + public string Comment { get; set; } + + /// + /// Gets or sets the index. + /// + /// The index. + public int Index { get; set; } + + /// + /// Gets or sets the filename. + /// + /// The filename. + public string Filename { get; set; } + + /// + /// Gets or sets the MIME type. + /// + /// The MIME type. + public string MIMEType { get; set; } + } +} From 03ecf57548b3bd6058b9427ff48f06050725ad4a Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Fri, 4 Oct 2019 13:23:08 -0400 Subject: [PATCH 002/252] Store MediaAttachments in DB. --- .../Data/SqliteItemRepository.cs | 179 ++++++++++++++++++ .../Persistence/IItemRepository.cs | 15 ++ .../Persistence/MediaAttachmentQuery.cs | 20 ++ .../MediaInfo/FFProbeVideoInfo.cs | 4 + 4 files changed, 218 insertions(+) create mode 100644 MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 31a661c5d9..58f04e4fcf 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; + } } } diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 47e0f34532..68df20c3af 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -78,6 +78,21 @@ namespace MediaBrowser.Controller.Persistence /// The cancellation token. void SaveMediaStreams(Guid id, List streams, CancellationToken cancellationToken); + /// + /// Gets the media attachments. + /// + /// The query. + /// IEnumerable{MediaAttachment}. + List GetMediaAttachments(MediaAttachmentQuery query); + + /// + /// Saves the media attachments. + /// + /// The identifier. + /// The attachments. + /// The cancellation token. + void SaveMediaAttachments(Guid id, List attachments, CancellationToken cancellationToken); + /// /// Gets the item ids. /// diff --git a/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs new file mode 100644 index 0000000000..91ab34aab6 --- /dev/null +++ b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs @@ -0,0 +1,20 @@ +using System; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Controller.Persistence +{ + public class MediaAttachmentQuery + { + /// + /// Gets or sets the index. + /// + /// The index. + public int? Index { get; set; } + + /// + /// Gets or sets the item identifier. + /// + /// The item identifier. + public Guid ItemId { get; set; } + } +} diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index d2abd2a639..ae3e584d4b 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -158,11 +158,13 @@ namespace MediaBrowser.Providers.MediaInfo MetadataRefreshOptions options) { List mediaStreams; + List mediaAttachments; List chapters; if (mediaInfo != null) { mediaStreams = mediaInfo.MediaStreams; + mediaAttachments = mediaInfo.MediaAttachments; video.TotalBitrate = mediaInfo.Bitrate; //video.FormatName = (mediaInfo.Container ?? string.Empty) @@ -198,6 +200,7 @@ namespace MediaBrowser.Providers.MediaInfo else { mediaStreams = new List(); + mediaAttachments = new List(); chapters = new List(); } @@ -223,6 +226,7 @@ namespace MediaBrowser.Providers.MediaInfo video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken); + _itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken); if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || options.MetadataRefreshMode == MetadataRefreshMode.Default) From 1513c76a3e0b8f84def98df3fe99432370076497 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 7 Oct 2019 07:53:28 -0400 Subject: [PATCH 003/252] Support MediaAttachment retrieval in MediaSourceManager. --- .../Library/MediaSourceManager.cs | 26 +++++++++++++++++++ .../Library/IMediaSourceManager.cs | 19 ++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index d83e1fc021..e069ebd17c 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 fbae4edb09..961028b046 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. /// From f22ca6dd9eeac73cfc5e24dceb67a7f817f77b9a Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 7 Oct 2019 07:54:11 -0400 Subject: [PATCH 004/252] Retrieve media attachments in BaseItem. --- MediaBrowser.Controller/Entities/BaseItem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 599d41bb27..2e2b465a07 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1098,6 +1098,7 @@ namespace MediaBrowser.Controller.Entities Id = item.Id.ToString("N", CultureInfo.InvariantCulture), Protocol = protocol ?? MediaProtocol.File, MediaStreams = MediaSourceManager.GetMediaStreams(item.Id), + MediaAttachments = MediaSourceManager.GetMediaAttachments(item.Id), Name = GetMediaSourceName(item), Path = enablePathSubstitution ? GetMappedPath(item, item.Path, protocol) : item.Path, RunTimeTicks = item.RunTimeTicks, From 12f752d8b1bbc9d6d480bfc34283dd15a08d1f8a Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Fri, 18 Oct 2019 07:52:32 -0400 Subject: [PATCH 005/252] FFMPEG extractor for attachments. --- .../MediaEncoding/IAttachmentExtractor.cs | 17 ++ .../Attachments/AttachmentExtractor.cs | 277 ++++++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs create mode 100644 MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs diff --git a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs new file mode 100644 index 0000000000..59c0a3f214 --- /dev/null +++ b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs @@ -0,0 +1,17 @@ +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, + string mediaSourceId, + int attachmentStreamIndex, + CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs new file mode 100644 index 0000000000..d6106df29d --- /dev/null +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -0,0 +1,277 @@ +using System; +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 + { + 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 IProcessFactory _processFactory; + + public AttachmentExtractor( + ILibraryManager libraryManager, + ILoggerFactory loggerFactory, + IApplicationPaths appPaths, + IFileSystem fileSystem, + IMediaEncoder mediaEncoder, + IMediaSourceManager mediaSourceManager, + IProcessFactory processFactory) + { + _libraryManager = libraryManager; + _logger = loggerFactory.CreateLogger(nameof(AttachmentExtractor)); + _appPaths = appPaths; + _fileSystem = fileSystem; + _mediaEncoder = mediaEncoder; + _mediaSourceManager = mediaSourceManager; + _processFactory = processFactory; + } + + 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) + { + throw new ArgumentNullException(nameof(item)); + } + if (string.IsNullOrWhiteSpace(mediaSourceId)) + { + throw new ArgumentNullException(nameof(mediaSourceId)); + } + + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false); + var mediaSource = mediaSources + .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + var mediaAttachment = mediaSource.MediaAttachments + .First(i => i.Index == attachmentStreamIndex); + var attachmentStream = await GetAttachmentStream(mediaSource, mediaAttachment, cancellationToken) + .ConfigureAwait(false); + + return (mediaAttachment, attachmentStream); + } + + private async Task GetAttachmentStream( + MediaSourceInfo mediaSource, + MediaAttachment mediaAttachment, + CancellationToken cancellationToken) + { + var inputFiles = new[] {mediaSource.Path}; + var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, mediaAttachment, cancellationToken).ConfigureAwait(false); + var stream = await GetAttachmentStream(fileInfo.Path, fileInfo.Protocol, cancellationToken).ConfigureAwait(false); + return stream; + } + + private async Task GetAttachmentStream( + string path, + MediaProtocol protocol, + CancellationToken cancellationToken) + { + return File.OpenRead(path); + } + + private async Task GetReadableFile( + string mediaPath, + string[] inputFiles, + MediaProtocol protocol, + MediaAttachment mediaAttachment, + CancellationToken cancellationToken) + { + var outputPath = GetAttachmentCachePath(mediaPath, protocol, mediaAttachment.Index); + await ExtractAttachment(inputFiles, protocol, mediaAttachment.Index, outputPath, cancellationToken) + .ConfigureAwait(false); + + return new AttachmentInfo(outputPath, MediaProtocol.File); + } + + private struct AttachmentInfo + { + public AttachmentInfo(string path, MediaProtocol protocol) + { + Path = path; + Protocol = protocol; + } + public string Path { get; set; } + public MediaProtocol Protocol { get; set; } + } + + 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, + MediaProtocol protocol, + int attachmentStreamIndex, + string outputPath, + CancellationToken cancellationToken) + { + var semaphore = GetLock(outputPath); + + await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + if (!File.Exists(outputPath)) + { + await ExtractAttachmentInternal(_mediaEncoder.GetInputArgument(inputFiles, protocol), attachmentStreamIndex, outputPath, cancellationToken).ConfigureAwait(false); + } + } + finally + { + semaphore.Release(); + } + } + + private async Task ExtractAttachmentInternal( + string inputPath, + int attachmentStreamIndex, + string outputPath, + CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(inputPath)) + { + throw new ArgumentNullException(nameof(inputPath)); + } + + if (string.IsNullOrEmpty(outputPath)) + { + throw new ArgumentNullException(nameof(outputPath)); + } + + Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + + var processArgs = string.Format("-dump_attachment:{1} {2} -i {0} -t 0 -f null null", inputPath, attachmentStreamIndex, outputPath); + var process = _processFactory.Create(new ProcessOptions + { + CreateNoWindow = true, + UseShellExecute = false, + EnableRaisingEvents = true, + FileName = _mediaEncoder.EncoderPath, + Arguments = processArgs, + IsHidden = true, + ErrorDialog = false + }); + + _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments); + + try + { + process.Start(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error starting ffmpeg"); + + throw; + } + + var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false); + + if (!ranToCompletion) + { + try + { + _logger.LogWarning("Killing ffmpeg attachment extraction process"); + process.Kill(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error killing attachment extraction process"); + } + + } + + var exitCode = ranToCompletion ? process.ExitCode : -1; + + process.Dispose(); + + var failed = false; + + if (exitCode == -1) + { + failed = true; + + try + { + _logger.LogWarning("Deleting extracted attachment due to failure: {Path}", outputPath); + _fileSystem.DeleteFile(outputPath); + } + catch (FileNotFoundException) + { + + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting extracted attachment {Path}", outputPath); + } + + } + else if (!File.Exists(outputPath)) + { + failed = true; + } + + if (failed) + { + var msg = $"ffmpeg attachment extraction failed for {inputPath} to {outputPath}"; + + _logger.LogError(msg); + + throw new Exception(msg); + } + else + { + var msg = $"ffmpeg attachment extraction completed for {inputPath} to {outputPath}"; + + _logger.LogInformation(msg); + } + } + + private string GetAttachmentCachePath(string mediaPath, MediaProtocol protocol, int attachmentStreamIndex) + { + if (protocol == MediaProtocol.File) + { + var date = _fileSystem.GetLastWriteTimeUtc(mediaPath); + var filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D"); + var prefix = filename.Substring(0, 1); + return Path.Combine(AttachmentCachePath, prefix, filename); + } + else + { + var filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D"); + var prefix = filename.Substring(0, 1); + return Path.Combine(AttachmentCachePath, prefix, filename); + } + } + + } +} From a9a85f251e77cf1670e69733e1d7b5ab9116aaaf Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Fri, 18 Oct 2019 07:56:53 -0400 Subject: [PATCH 006/252] Instantiate AttachmentExtractor in ApplicationHost. --- Emby.Server.Implementations/ApplicationHost.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 04904fc4a5..bcd99ffe4f 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); From 8c89d8993269033931fb53b66212e4f87c6edbb5 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Fri, 18 Oct 2019 07:57:40 -0400 Subject: [PATCH 007/252] Attachment service. --- .../Attachments/AttachmentService.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 MediaBrowser.Api/Attachments/AttachmentService.cs diff --git a/MediaBrowser.Api/Attachments/AttachmentService.cs b/MediaBrowser.Api/Attachments/AttachmentService.cs new file mode 100644 index 0000000000..4c7d5eccc0 --- /dev/null +++ b/MediaBrowser.Api/Attachments/AttachmentService.cs @@ -0,0 +1,67 @@ +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.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 +{ + [Route("/Videos/{Id}/{MediaSourceId}/Attachments/{Index}/Attachment", "GET", Summary = "Gets specified attachment.")] + public class GetAttachment + { + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public Guid Id { get; set; } + + [ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string MediaSourceId { get; set; } + + [ApiMember(Name = "Index", Description = "The attachment stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] + public int Index { get; set; } + } + + public class AttachmentService : BaseApiService + { + private readonly ILibraryManager _libraryManager; + private readonly IAttachmentExtractor _attachmentExtractor; + + public AttachmentService(ILibraryManager libraryManager, IAttachmentExtractor attachmentExtractor) + { + _libraryManager = libraryManager; + _attachmentExtractor = attachmentExtractor; + } + + 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; + + return ResultFactory.GetResult(Request, attachmentStream, mime); + } + + private Task<(MediaAttachment, Stream)> GetAttachment(GetAttachment request) + { + var item = _libraryManager.GetItemById(request.Id); + + return _attachmentExtractor.GetAttachment(item, + request.MediaSourceId, + request.Index, + CancellationToken.None); + } + + } +} From 01b1c847e95c28ec780dfe3f2c7d1b33ffd08bd0 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Fri, 18 Oct 2019 07:59:03 -0400 Subject: [PATCH 008/252] Provide delivery URLs for attachments in PlaybackInfo. --- MediaBrowser.Api/Playback/MediaInfoService.cs | 8 ++++++++ MediaBrowser.Model/Entities/MediaAttachment.cs | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index da8f99a3dd..6c57e37a1a 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -524,6 +524,14 @@ namespace MediaBrowser.Api.Playback SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); } } + + foreach (var attachment in mediaSource.MediaAttachments) + { + attachment.DeliveryUrl = string.Format("/Videos/{0}/{1}/Attachments/{2}/Attachment", + item.Id, + mediaSource.Id, + attachment.Index); + } } private long? GetMaxBitrate(long? clientMaxBitrate, User user) diff --git a/MediaBrowser.Model/Entities/MediaAttachment.cs b/MediaBrowser.Model/Entities/MediaAttachment.cs index daabbe91df..26279b72b8 100644 --- a/MediaBrowser.Model/Entities/MediaAttachment.cs +++ b/MediaBrowser.Model/Entities/MediaAttachment.cs @@ -40,5 +40,11 @@ namespace MediaBrowser.Model.Entities /// /// The MIME type. public string MIMEType { get; set; } + + /// + /// Gets or sets the delivery URL. + /// + /// The delivery URL. + public string DeliveryUrl { get; set; } } } From 20727906c85c0d519eda86194b081a22f60dcf61 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 22 Oct 2019 10:57:15 -0400 Subject: [PATCH 009/252] Use attachment filename if available. --- MediaBrowser.Api/Attachments/AttachmentService.cs | 5 ++++- MediaBrowser.Api/Playback/MediaInfoService.cs | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Api/Attachments/AttachmentService.cs b/MediaBrowser.Api/Attachments/AttachmentService.cs index 4c7d5eccc0..d8771f8c0e 100644 --- a/MediaBrowser.Api/Attachments/AttachmentService.cs +++ b/MediaBrowser.Api/Attachments/AttachmentService.cs @@ -20,7 +20,7 @@ using MimeTypes = MediaBrowser.Model.Net.MimeTypes; namespace MediaBrowser.Api.Attachments { - [Route("/Videos/{Id}/{MediaSourceId}/Attachments/{Index}/Attachment", "GET", Summary = "Gets specified attachment.")] + [Route("/Videos/{Id}/{MediaSourceId}/Attachments/{Index}/{Filename}", "GET", Summary = "Gets specified attachment.")] public class GetAttachment { [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] @@ -31,6 +31,9 @@ namespace MediaBrowser.Api.Attachments [ApiMember(Name = "Index", Description = "The attachment stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] public int Index { get; set; } + + [ApiMember(Name = "Filename", Description = "The attachment filename", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Filename { get; set; } } public class AttachmentService : BaseApiService diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 6c57e37a1a..41b3249751 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -527,10 +527,12 @@ namespace MediaBrowser.Api.Playback foreach (var attachment in mediaSource.MediaAttachments) { - attachment.DeliveryUrl = string.Format("/Videos/{0}/{1}/Attachments/{2}/Attachment", + var filename = string.IsNullOrWhiteSpace(attachment.Filename) ? "Attachment" : attachment.Filename; + attachment.DeliveryUrl = string.Format("/Videos/{0}/{1}/Attachments/{2}/{3}", item.Id, mediaSource.Id, - attachment.Index); + attachment.Index, + filename); } } From 90dfe729bb9f8929bf584b93f93aabd84abbef3d Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 08:48:27 -0500 Subject: [PATCH 010/252] 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(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 58f04e4fcf..d02aa9b1f2 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"; From 4573fb5301b02e8b57a15a57f909b1b63d6b3900 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:19:53 -0500 Subject: [PATCH 011/252] Use ToByteArray instead of ToGuidBlob. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 08f53f0f5d..2b07844e71 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); From 0dde5e46df13f63158288ce73b98b25a6440de43 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:28:33 -0500 Subject: [PATCH 012/252] Flatten using connection in GetMediaAttachments/SaveMediaAttachments --- .../Data/SqliteItemRepository.cs | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2b07844e71..62837933c0 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) From c2d8f210b1c8daa86a37b530a7da05370d3b209a Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:31:32 -0500 Subject: [PATCH 013/252] Check for cancellation in SaveMediaAttachments. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 62837933c0..8d39cdb8f4 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 => { From ad2101ce52999fccf51e52510729fd36e3495369 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:33:50 -0500 Subject: [PATCH 014/252] Rename "limit" to "insertAtOnce" in InsertMediaAttachments. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 8d39cdb8f4..7c0a585961 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; } } From 7110069b399496979aa8fdc701d0e1af536425f7 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:37:19 -0500 Subject: [PATCH 015/252] Return list result directly for MediaAttachments. --- .../Library/MediaSourceManager.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 2e24b847b6..27eb395309 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) From bd545891c0327eb155198c4e835f874156f416a6 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:38:53 -0500 Subject: [PATCH 016/252] Indentation fix. --- MediaBrowser.Api/Attachments/AttachmentService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Api/Attachments/AttachmentService.cs b/MediaBrowser.Api/Attachments/AttachmentService.cs index d8771f8c0e..9fceebd3b4 100644 --- a/MediaBrowser.Api/Attachments/AttachmentService.cs +++ b/MediaBrowser.Api/Attachments/AttachmentService.cs @@ -50,8 +50,8 @@ 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; + var (attachment, attachmentStream) = await GetAttachment(request).ConfigureAwait(false); + var mime = string.IsNullOrWhiteSpace(attachment.MIMEType) ? "application/octet-stream" : attachment.MIMEType; return ResultFactory.GetResult(Request, attachmentStream, mime); } From e9c893f07eeb0ddb59f136cedfdf7e5a3f06d36d Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 10:58:56 -0500 Subject: [PATCH 017/252] Fail attachment extraction on non-zero exit code. --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index d6106df29d..246023582d 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -216,7 +216,7 @@ namespace MediaBrowser.MediaEncoding.Attachments var failed = false; - if (exitCode == -1) + if (exitCode != 0) { failed = true; From 9eef5f860d29eaa32d6b06f76d74f6c9bbe1e3cd Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 11:16:57 -0500 Subject: [PATCH 018/252] AttachmentExtractor logging cleanup. --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 246023582d..57b80d0604 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -220,9 +220,9 @@ namespace MediaBrowser.MediaEncoding.Attachments { failed = true; + _logger.LogWarning("Deleting extracted attachment {Path} due to failure: {ExitCode}", outputPath, exitCode); try { - _logger.LogWarning("Deleting extracted attachment due to failure: {Path}", outputPath); _fileSystem.DeleteFile(outputPath); } catch (FileNotFoundException) From 262a8f47afb4905be4eac815dd92849c7616ce89 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 11:31:41 -0500 Subject: [PATCH 019/252] Remove attachment filenames from attachment URLs. --- MediaBrowser.Api/Attachments/AttachmentService.cs | 5 +---- MediaBrowser.Api/Playback/MediaInfoService.cs | 6 ++---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/MediaBrowser.Api/Attachments/AttachmentService.cs b/MediaBrowser.Api/Attachments/AttachmentService.cs index 9fceebd3b4..c52ca24810 100644 --- a/MediaBrowser.Api/Attachments/AttachmentService.cs +++ b/MediaBrowser.Api/Attachments/AttachmentService.cs @@ -20,7 +20,7 @@ using MimeTypes = MediaBrowser.Model.Net.MimeTypes; namespace MediaBrowser.Api.Attachments { - [Route("/Videos/{Id}/{MediaSourceId}/Attachments/{Index}/{Filename}", "GET", Summary = "Gets specified attachment.")] + [Route("/Videos/{Id}/{MediaSourceId}/Attachments/{Index}", "GET", Summary = "Gets specified attachment.")] public class GetAttachment { [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] @@ -31,9 +31,6 @@ namespace MediaBrowser.Api.Attachments [ApiMember(Name = "Index", Description = "The attachment stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")] public int Index { get; set; } - - [ApiMember(Name = "Filename", Description = "The attachment filename", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Filename { get; set; } } public class AttachmentService : BaseApiService diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 41b3249751..de5c4ddb72 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -527,12 +527,10 @@ namespace MediaBrowser.Api.Playback foreach (var attachment in mediaSource.MediaAttachments) { - var filename = string.IsNullOrWhiteSpace(attachment.Filename) ? "Attachment" : attachment.Filename; - attachment.DeliveryUrl = string.Format("/Videos/{0}/{1}/Attachments/{2}/{3}", + attachment.DeliveryUrl = string.Format("/Videos/{0}/{1}/Attachments/{2}", item.Id, mediaSource.Id, - attachment.Index, - filename); + attachment.Index); } } From c7d303a6ae9e0d5489273fa5a15262987c735bd0 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 11:39:40 -0500 Subject: [PATCH 020/252] MediaExtractor logging cleanup. --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 57b80d0604..b369e45153 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -250,9 +250,7 @@ namespace MediaBrowser.MediaEncoding.Attachments } else { - var msg = $"ffmpeg attachment extraction completed for {inputPath} to {outputPath}"; - - _logger.LogInformation(msg); + _logger.LogInformation("ffmpeg attachment extraction completed for {Path} to {Path}", inputPath, outputPath); } } From 154fb1fe9b45892807a9bea9abb282380bfd9383 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 11:45:31 -0500 Subject: [PATCH 021/252] AttachmentExtractor code cleanup. --- .../Attachments/AttachmentExtractor.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index b369e45153..1c214d5d3b 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -256,19 +256,18 @@ namespace MediaBrowser.MediaEncoding.Attachments private string GetAttachmentCachePath(string mediaPath, MediaProtocol protocol, int attachmentStreamIndex) { + String filename; if (protocol == MediaProtocol.File) { var date = _fileSystem.GetLastWriteTimeUtc(mediaPath); - var filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D"); - var prefix = filename.Substring(0, 1); - return Path.Combine(AttachmentCachePath, prefix, filename); + filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D"); } else { - var filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D"); - var prefix = filename.Substring(0, 1); - return Path.Combine(AttachmentCachePath, prefix, filename); + filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D"); } + var prefix = filename.Substring(0, 1); + return Path.Combine(AttachmentCachePath, prefix, filename); } } From 04a96788f9f3ea20b05d3c57d16d222ba7a53101 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 14:34:21 -0500 Subject: [PATCH 022/252] Convert exceptions for missing MediaSource or MediaAttachment to ResourceNotFoundException with appropriate message. --- .../Attachments/AttachmentExtractor.cs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 1c214d5d3b..be5908ccef 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -65,10 +65,26 @@ namespace MediaBrowser.MediaEncoding.Attachments } var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false); - var mediaSource = mediaSources - .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); - var mediaAttachment = mediaSource.MediaAttachments - .First(i => i.Index == attachmentStreamIndex); + MediaSourceInfo mediaSource; + MediaAttachment mediaAttachment; + try + { + mediaSource = mediaSources + .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + } + catch (Exception ex) when (ex is ArgumentNullException || ex is InvalidOperationException) + { + throw new ResourceNotFoundException($"MediaSource {mediaSourceId} not found"); + } + try + { + mediaAttachment = mediaSource.MediaAttachments + .First(i => i.Index == attachmentStreamIndex); + } + catch (Exception ex) when (ex is ArgumentNullException || ex is InvalidOperationException) + { + throw new ResourceNotFoundException($"MediaSource {mediaSourceId} has no attachment with stream index {attachmentStreamIndex}"); + } var attachmentStream = await GetAttachmentStream(mediaSource, mediaAttachment, cancellationToken) .ConfigureAwait(false); From 28a6718d8e77527c3949118676dc6e165e3608f5 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Mon, 4 Nov 2019 14:48:28 -0500 Subject: [PATCH 023/252] Return path of extracted attachment, which is always a file, instead of AttachmentInfo with path and protocol. --- .../Attachments/AttachmentExtractor.cs | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index be5908ccef..6bef1cd5a9 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -97,20 +97,19 @@ namespace MediaBrowser.MediaEncoding.Attachments CancellationToken cancellationToken) { var inputFiles = new[] {mediaSource.Path}; - var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, mediaAttachment, cancellationToken).ConfigureAwait(false); - var stream = await GetAttachmentStream(fileInfo.Path, fileInfo.Protocol, cancellationToken).ConfigureAwait(false); + var attachmentPath = await GetReadableFile(mediaSource.Path, inputFiles, mediaSource.Protocol, mediaAttachment, cancellationToken).ConfigureAwait(false); + var stream = await GetAttachmentStream(attachmentPath, cancellationToken).ConfigureAwait(false); return stream; } private async Task GetAttachmentStream( string path, - MediaProtocol protocol, CancellationToken cancellationToken) { return File.OpenRead(path); } - private async Task GetReadableFile( + private async Task GetReadableFile( string mediaPath, string[] inputFiles, MediaProtocol protocol, @@ -121,18 +120,7 @@ namespace MediaBrowser.MediaEncoding.Attachments await ExtractAttachment(inputFiles, protocol, mediaAttachment.Index, outputPath, cancellationToken) .ConfigureAwait(false); - return new AttachmentInfo(outputPath, MediaProtocol.File); - } - - private struct AttachmentInfo - { - public AttachmentInfo(string path, MediaProtocol protocol) - { - Path = path; - Protocol = protocol; - } - public string Path { get; set; } - public MediaProtocol Protocol { get; set; } + return outputPath; } private readonly ConcurrentDictionary _semaphoreLocks = From 6ca252ba5c651e41ad8a18c5555f5b60d81f0952 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 07:09:55 -0500 Subject: [PATCH 024/252] Remove check for "[0]" in codec_tag. --- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 2c001d775a..f2056c5663 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -535,8 +535,7 @@ namespace MediaBrowser.MediaEncoding.Probing Index = streamInfo.index }; - // Filter out junk - if (!string.IsNullOrWhiteSpace(streamInfo.codec_tag_string) && streamInfo.codec_tag_string.IndexOf("[0]", StringComparison.OrdinalIgnoreCase) == -1) + if (!string.IsNullOrWhiteSpace(streamInfo.codec_tag_string)) { attachment.CodecTag = streamInfo.codec_tag_string; } From bd4da93d1e871f7d58075b6780c4c1c30fc34fd7 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 07:48:32 -0500 Subject: [PATCH 025/252] Throw ArgumentException instead of ArgumentNullException on empty Guid. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 7c0a585961..e7e93d0db0 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) From 24a460dc939b8dab2aa7e3e4db7ce47373a50d1d Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:13:22 -0500 Subject: [PATCH 026/252] 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(+) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 7c0a585961..27a29318bd 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(")"); From 25bc7b81c37b635109e5c14baabf64954a517512 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:13:35 -0500 Subject: [PATCH 027/252] 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(+) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 27a29318bd..de95adfa1c 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; } } From 349310787cbf49964a0e9bd1548adb5a3a3a3c1c Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:16:46 -0500 Subject: [PATCH 028/252] Update MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs formatting Co-Authored-By: Bond-009 --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 6bef1cd5a9..0c8d9ae9c4 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -59,6 +59,7 @@ namespace MediaBrowser.MediaEncoding.Attachments { throw new ArgumentNullException(nameof(item)); } + if (string.IsNullOrWhiteSpace(mediaSourceId)) { throw new ArgumentNullException(nameof(mediaSourceId)); From d33e0a4e2c59783c785c992ea0c3a31596ae3058 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:17:34 -0500 Subject: [PATCH 029/252] Simplify AttachmentExtractor instantiation. Co-Authored-By: Bond-009 --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ac37cfe079..9330d57198 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(); From e5b65ed034c6ca0fcee9f73cc991a0962a44278f Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:19:07 -0500 Subject: [PATCH 030/252] 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(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index de95adfa1c..a7e7792517 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)); } } From a78aec56e357742c2906edbb1fcce47cb9a3c6dd Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:21:06 -0500 Subject: [PATCH 031/252] Format attachment DeliveryURL with CultureInfo.InvariantCulture. Co-Authored-By: Bond-009 --- MediaBrowser.Api/Playback/MediaInfoService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index de5c4ddb72..296d565b92 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -527,7 +527,9 @@ namespace MediaBrowser.Api.Playback foreach (var attachment in mediaSource.MediaAttachments) { - attachment.DeliveryUrl = string.Format("/Videos/{0}/{1}/Attachments/{2}", + attachment.DeliveryUrl = string.Format( + CultureInfo.InvariantCulture, + "/Videos/{0}/{1}/Attachments/{2}", item.Id, mediaSource.Id, attachment.Index); From 3602251cf53c636e3006605e2b48a77d7952d029 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 08:36:17 -0500 Subject: [PATCH 032/252] 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(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index a7e7792517..2152ed6055 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); From 8505ee9d6c0b2f073fd39c538cbdb0ee9ee95354 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Tue, 5 Nov 2019 14:53:46 -0500 Subject: [PATCH 033/252] Extract the prefix for MediaAttachment insertions to a static member instead of generating it per-query. --- .../Data/SqliteItemRepository.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2b9c6a52f6..dd8620f9f4 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); From 79bbf09ecba6d3c2ddb0955bdbd51164a6c7a236 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Wed, 6 Nov 2019 08:43:49 -0500 Subject: [PATCH 034/252] Revert "Simplify AttachmentExtractor instantiation." This reverts commit d33e0a4e2c59783c785c992ea0c3a31596ae3058. --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9330d57198..ac37cfe079 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(); From 4f3b8831552db1d376198384cfe42894883286dd Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Wed, 6 Nov 2019 09:46:31 -0500 Subject: [PATCH 035/252] Clean up handling of missing source/attachment in AttachmentExtractor. --- .../Attachments/AttachmentExtractor.cs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 0c8d9ae9c4..bda5c2f372 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -66,23 +66,15 @@ namespace MediaBrowser.MediaEncoding.Attachments } var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(item, null, true, false, cancellationToken).ConfigureAwait(false); - MediaSourceInfo mediaSource; - MediaAttachment mediaAttachment; - try - { - mediaSource = mediaSources - .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); - } - catch (Exception ex) when (ex is ArgumentNullException || ex is InvalidOperationException) + var mediaSource = mediaSources + .FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); + if (mediaSource == null) { throw new ResourceNotFoundException($"MediaSource {mediaSourceId} not found"); } - try - { - mediaAttachment = mediaSource.MediaAttachments - .First(i => i.Index == attachmentStreamIndex); - } - catch (Exception ex) when (ex is ArgumentNullException || ex is InvalidOperationException) + var mediaAttachment = mediaSource.MediaAttachments + .FirstOrDefault(i => i.Index == attachmentStreamIndex); + if (mediaAttachment == null) { throw new ResourceNotFoundException($"MediaSource {mediaSourceId} has no attachment with stream index {attachmentStreamIndex}"); } From 74fb63a8989fe19c5e985a09f9cb45e42022320f Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Wed, 6 Nov 2019 10:43:14 -0500 Subject: [PATCH 036/252] Use block rather than local using statement. --- .../Data/SqliteItemRepository.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index dd8620f9f4..f7774000cd 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) From 6defe80b62483350111dc6ba2f18cfd83542c4b1 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 08:38:36 -0500 Subject: [PATCH 037/252] Check for cancellation between each batch of MediaAttachment inserts. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index f7774000cd..1956711682 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); From c09eb3470815c8f868fd6eda6b542169d29f2098 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 08:56:12 -0500 Subject: [PATCH 038/252] Check for attachment file before trying to remove it during cleanup. --- .../Attachments/AttachmentExtractor.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index bda5c2f372..501dd3fed9 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -220,11 +220,10 @@ namespace MediaBrowser.MediaEncoding.Attachments _logger.LogWarning("Deleting extracted attachment {Path} due to failure: {ExitCode}", outputPath, exitCode); try { - _fileSystem.DeleteFile(outputPath); - } - catch (FileNotFoundException) - { - + if (File.Exists(outputPath)) + { + _fileSystem.DeleteFile(outputPath); + } } catch (IOException ex) { From f60e9b0b62388f6e758870b53f3fb59b842fecd2 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:20:29 -0500 Subject: [PATCH 039/252] formatting Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 1956711682..ba75488fb7 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) From 4b75e6518e707096e4749c82d6fb7cdd303d3a43 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:21:40 -0500 Subject: [PATCH 040/252] Update MediaBrowser.Api/Attachments/AttachmentService.cs formatting Co-Authored-By: Bond-009 --- MediaBrowser.Api/Attachments/AttachmentService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Api/Attachments/AttachmentService.cs b/MediaBrowser.Api/Attachments/AttachmentService.cs index c52ca24810..1ebfaa14bc 100644 --- a/MediaBrowser.Api/Attachments/AttachmentService.cs +++ b/MediaBrowser.Api/Attachments/AttachmentService.cs @@ -62,6 +62,5 @@ namespace MediaBrowser.Api.Attachments request.Index, CancellationToken.None); } - } } From d6aa02ff09e262bc57a4dcf6dbbc11e6d1c963dd Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:22:07 -0500 Subject: [PATCH 041/252] 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(+) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index ba75488fb7..a63052a68c 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(); From 193a1fa474202ec0933c1a937c56ccecf58c4410 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:23:28 -0500 Subject: [PATCH 042/252] 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(+) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 27eb395309..833a5c88fa 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); From 8b2d7062c43ff6618d5bf4a5e26d0ba5bb70bd07 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:24:30 -0500 Subject: [PATCH 043/252] 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(+) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 833a5c88fa..df6f0e77f6 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 From 92aae268a352c20964ca10348078fdaf4fbf8263 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:24:53 -0500 Subject: [PATCH 044/252] Update MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs formatting Co-Authored-By: Bond-009 --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 501dd3fed9..4218b180d9 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -204,7 +204,6 @@ namespace MediaBrowser.MediaEncoding.Attachments { _logger.LogError(ex, "Error killing attachment extraction process"); } - } var exitCode = ranToCompletion ? process.ExitCode : -1; From 492bbc9e13f177eea62b0b22a921106def90309c Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:25:44 -0500 Subject: [PATCH 045/252] Update MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs String -> string Co-Authored-By: Bond-009 --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 4218b180d9..7c3a0fbd3e 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -251,7 +251,7 @@ namespace MediaBrowser.MediaEncoding.Attachments private string GetAttachmentCachePath(string mediaPath, MediaProtocol protocol, int attachmentStreamIndex) { - String filename; + string filename; if (protocol == MediaProtocol.File) { var date = _fileSystem.GetLastWriteTimeUtc(mediaPath); From d73f46dcda8db877edae4918a6053cad924808fa Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:27:55 -0500 Subject: [PATCH 046/252] 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(+) diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index df6f0e77f6..9574a28e9e 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 From 0fd7886a12bee56d72f00a066c3e475fa8040060 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:28:11 -0500 Subject: [PATCH 047/252] Update MediaBrowser.Controller/Library/IMediaSourceManager.cs formatting Co-Authored-By: Bond-009 --- MediaBrowser.Controller/Library/IMediaSourceManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 961028b046..8bd55b29f2 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -44,6 +44,7 @@ namespace MediaBrowser.Controller.Library /// The item identifier. /// IEnumerable<MediaAttachment>. List GetMediaAttachments(Guid itemId); + /// /// Gets the media attachments. /// From 743685110c31f5da0f3e96e537c717e251909f38 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:29:34 -0500 Subject: [PATCH 048/252] Update MediaBrowser.Controller/Library/IMediaSourceManager.cs formatting Co-Authored-By: Bond-009 --- MediaBrowser.Controller/Library/IMediaSourceManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Controller/Library/IMediaSourceManager.cs b/MediaBrowser.Controller/Library/IMediaSourceManager.cs index 8bd55b29f2..dda8d397a1 100644 --- a/MediaBrowser.Controller/Library/IMediaSourceManager.cs +++ b/MediaBrowser.Controller/Library/IMediaSourceManager.cs @@ -50,6 +50,7 @@ namespace MediaBrowser.Controller.Library /// /// The The media source identifier. /// IEnumerable<MediaAttachment>. + List GetMediaAttachments(string mediaSourceId); /// /// Gets the media attachments. From c6855e6a2a347db17f2b8b2f18dd89d5b47a8816 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:53:39 -0500 Subject: [PATCH 049/252] Simplify AttachmentExtractor instantiation. --- Emby.Server.Implementations/ApplicationHost.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ac37cfe079..97da5b437f 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(); From 2338a53229431070b1c128436e990a486011205b Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:55:39 -0500 Subject: [PATCH 050/252] Don't user ILoggerFactory. --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 7c3a0fbd3e..d18463cb5a 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.MediaEncoding.Attachments public AttachmentExtractor( ILibraryManager libraryManager, - ILoggerFactory loggerFactory, + ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, @@ -43,7 +43,7 @@ namespace MediaBrowser.MediaEncoding.Attachments IProcessFactory processFactory) { _libraryManager = libraryManager; - _logger = loggerFactory.CreateLogger(nameof(AttachmentExtractor)); + _logger = logger; _appPaths = appPaths; _fileSystem = fileSystem; _mediaEncoder = mediaEncoder; From 2f728fd2a1be2e0abde3b8d7f8269b849e1555c6 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:58:26 -0500 Subject: [PATCH 051/252] Update MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs formatting Co-Authored-By: Bond-009 --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 7c3a0fbd3e..2cecdf1cd0 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -89,7 +89,7 @@ namespace MediaBrowser.MediaEncoding.Attachments MediaAttachment mediaAttachment, CancellationToken cancellationToken) { - var inputFiles = new[] {mediaSource.Path}; + 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; From 1eb3df1d6ce21641612ce2f7ce9f25cd757a0e0d Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 11:59:54 -0500 Subject: [PATCH 052/252] Update MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs formatting / style Co-Authored-By: Bond-009 --- .../Attachments/AttachmentExtractor.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index f5f5d213f9..e5b82d075a 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -166,7 +166,12 @@ namespace MediaBrowser.MediaEncoding.Attachments Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); - var processArgs = string.Format("-dump_attachment:{1} {2} -i {0} -t 0 -f null null", inputPath, attachmentStreamIndex, outputPath); + var processArgs = string.Format( + CultureInfo.InvariantCulture, + "-dump_attachment:{1} {2} -i {0} -t 0 -f null null", + inputPath, + attachmentStreamIndex, + outputPath); var process = _processFactory.Create(new ProcessOptions { CreateNoWindow = true, From 3a9bf84e3b1b4d6c3cd1eaf979a418087ce41e0d Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 12:00:47 -0500 Subject: [PATCH 053/252] Update MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs formatting Co-Authored-By: Bond-009 --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index e5b82d075a..0780df7735 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -233,7 +233,6 @@ namespace MediaBrowser.MediaEncoding.Attachments { _logger.LogError(ex, "Error deleting extracted attachment {Path}", outputPath); } - } else if (!File.Exists(outputPath)) { From 79858eb26c60aa7ad8b6e63ee90fbd2e0727d594 Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Thu, 7 Nov 2019 14:24:49 -0500 Subject: [PATCH 054/252] Remove use of ProcessFactory, as well as arbitrary timeout in AttachmentExtractor. --- .../Attachments/AttachmentExtractor.cs | 31 +++++++++++-------- MediaBrowser.MediaEncoding/packages.config | 3 -- 2 files changed, 18 insertions(+), 16 deletions(-) delete mode 100644 MediaBrowser.MediaEncoding/packages.config diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index f5f5d213f9..369e597ea2 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Collections.Concurrent; using System.Globalization; using System.IO; @@ -31,7 +32,6 @@ namespace MediaBrowser.MediaEncoding.Attachments private readonly IFileSystem _fileSystem; private readonly IMediaEncoder _mediaEncoder; private readonly IMediaSourceManager _mediaSourceManager; - private readonly IProcessFactory _processFactory; public AttachmentExtractor( ILibraryManager libraryManager, @@ -39,8 +39,7 @@ namespace MediaBrowser.MediaEncoding.Attachments IApplicationPaths appPaths, IFileSystem fileSystem, IMediaEncoder mediaEncoder, - IMediaSourceManager mediaSourceManager, - IProcessFactory processFactory) + IMediaSourceManager mediaSourceManager) { _libraryManager = libraryManager; _logger = logger; @@ -48,7 +47,6 @@ namespace MediaBrowser.MediaEncoding.Attachments _fileSystem = fileSystem; _mediaEncoder = mediaEncoder; _mediaSourceManager = mediaSourceManager; - _processFactory = processFactory; } private string AttachmentCachePath => Path.Combine(_appPaths.DataPath, "attachments"); @@ -167,16 +165,19 @@ namespace MediaBrowser.MediaEncoding.Attachments Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); var processArgs = string.Format("-dump_attachment:{1} {2} -i {0} -t 0 -f null null", inputPath, attachmentStreamIndex, outputPath); - var process = _processFactory.Create(new ProcessOptions + var startInfo = new ProcessStartInfo { - CreateNoWindow = true, - UseShellExecute = false, - EnableRaisingEvents = true, - FileName = _mediaEncoder.EncoderPath, Arguments = processArgs, - IsHidden = true, + FileName = _mediaEncoder.EncoderPath, + UseShellExecute = false, + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, ErrorDialog = false - }); + }; + var process = new Process + { + StartInfo = startInfo + }; _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments); @@ -191,7 +192,12 @@ namespace MediaBrowser.MediaEncoding.Attachments throw; } - var ranToCompletion = await process.WaitForExitAsync(300000).ConfigureAwait(false); + var processTcs = new TaskCompletionSource(); + process.EnableRaisingEvents = true; + process.Exited += (sender, args) => processTcs.TrySetResult(true); + var unregister = cancellationToken.Register(() => processTcs.TrySetResult(process.HasExited)); + var ranToCompletion = await processTcs.Task.ConfigureAwait(false); + unregister.Dispose(); if (!ranToCompletion) { @@ -205,7 +211,6 @@ namespace MediaBrowser.MediaEncoding.Attachments _logger.LogError(ex, "Error killing attachment extraction process"); } } - var exitCode = ranToCompletion ? process.ExitCode : -1; process.Dispose(); diff --git a/MediaBrowser.MediaEncoding/packages.config b/MediaBrowser.MediaEncoding/packages.config deleted file mode 100644 index bbeaf5f002..0000000000 --- a/MediaBrowser.MediaEncoding/packages.config +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file From dee247453e7b5cab1badb6a844af690cdf80aacd Mon Sep 17 00:00:00 2001 From: Andrew Mahone Date: Wed, 13 Nov 2019 08:52:37 -0500 Subject: [PATCH 055/252] Throw InvalidOperationException when attachment extraction exits abnormally or doesn't produce output. --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 4e277826a9..cb22343c48 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -250,7 +250,7 @@ namespace MediaBrowser.MediaEncoding.Attachments _logger.LogError(msg); - throw new Exception(msg); + throw new InvalidOperationException(msg); } else { From 9989b7b68fb6d87d3a2bf7f271e8fe0c8c3e41ab Mon Sep 17 00:00:00 2001 From: Stanislav Ionascu Date: Sun, 13 Oct 2019 11:21:20 +0200 Subject: [PATCH 056/252] Replace BDInfo plugin with nupkg and UHD/Atmos/DTS:X support --- BDInfo/BDInfo.csproj | 17 - BDInfo/BDInfoSettings.cs | 33 - BDInfo/BDROM.cs | 449 ----- BDInfo/LanguageCodes.cs | 493 ------ BDInfo/Properties/AssemblyInfo.cs | 21 - BDInfo/ReadMe.txt | 5 - BDInfo/TSCodecAC3.cs | 309 ---- BDInfo/TSCodecAVC.cs | 148 -- BDInfo/TSCodecDTS.cs | 159 -- BDInfo/TSCodecDTSHD.cs | 246 --- BDInfo/TSCodecLPCM.cs | 123 -- BDInfo/TSCodecMPEG2.cs | 208 --- BDInfo/TSCodecMVC.cs | 36 - BDInfo/TSCodecTrueHD.cs | 186 -- BDInfo/TSCodecVC1.cs | 131 -- BDInfo/TSInterleavedFile.cs | 37 - BDInfo/TSPlaylistFile.cs | 1282 -------------- BDInfo/TSStream.cs | 780 --------- BDInfo/TSStreamBuffer.cs | 130 -- BDInfo/TSStreamClip.cs | 107 -- BDInfo/TSStreamClipFile.cs | 244 --- BDInfo/TSStreamFile.cs | 1555 ----------------- .../BdInfo/BdInfoDirectoryInfo.cs | 74 + .../BdInfo/BdInfoExaminer.cs | 4 +- .../BdInfo/BdInfoFileInfo.cs | 40 + .../MediaBrowser.MediaEncoding.csproj | 2 +- MediaBrowser.sln | 2 - 27 files changed, 117 insertions(+), 6704 deletions(-) delete mode 100644 BDInfo/BDInfo.csproj delete mode 100644 BDInfo/BDInfoSettings.cs delete mode 100644 BDInfo/BDROM.cs delete mode 100644 BDInfo/LanguageCodes.cs delete mode 100644 BDInfo/Properties/AssemblyInfo.cs delete mode 100644 BDInfo/ReadMe.txt delete mode 100644 BDInfo/TSCodecAC3.cs delete mode 100644 BDInfo/TSCodecAVC.cs delete mode 100644 BDInfo/TSCodecDTS.cs delete mode 100644 BDInfo/TSCodecDTSHD.cs delete mode 100644 BDInfo/TSCodecLPCM.cs delete mode 100644 BDInfo/TSCodecMPEG2.cs delete mode 100644 BDInfo/TSCodecMVC.cs delete mode 100644 BDInfo/TSCodecTrueHD.cs delete mode 100644 BDInfo/TSCodecVC1.cs delete mode 100644 BDInfo/TSInterleavedFile.cs delete mode 100644 BDInfo/TSPlaylistFile.cs delete mode 100644 BDInfo/TSStream.cs delete mode 100644 BDInfo/TSStreamBuffer.cs delete mode 100644 BDInfo/TSStreamClip.cs delete mode 100644 BDInfo/TSStreamClipFile.cs delete mode 100644 BDInfo/TSStreamFile.cs create mode 100644 MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs create mode 100644 MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs diff --git a/BDInfo/BDInfo.csproj b/BDInfo/BDInfo.csproj deleted file mode 100644 index 9dbaa9e2f0..0000000000 --- a/BDInfo/BDInfo.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - netstandard2.0 - false - true - - - diff --git a/BDInfo/BDInfoSettings.cs b/BDInfo/BDInfoSettings.cs deleted file mode 100644 index f4cb300166..0000000000 --- a/BDInfo/BDInfoSettings.cs +++ /dev/null @@ -1,33 +0,0 @@ - -namespace BDInfo -{ - class BDInfoSettings - { - public static bool GenerateStreamDiagnostics => true; - - public static bool EnableSSIF => true; - - public static bool AutosaveReport => false; - - public static bool GenerateFrameDataFile => false; - - public static bool FilterLoopingPlaylists => true; - - public static bool FilterShortPlaylists => false; - - public static int FilterShortPlaylistsValue => 0; - - public static bool UseImagePrefix => false; - - public static string UseImagePrefixValue => null; - - /// - /// Setting this to false throws an IComparer error on some discs. - /// - public static bool KeepStreamOrder => true; - - public static bool GenerateTextSummary => false; - - public static string LastPath => string.Empty; - } -} diff --git a/BDInfo/BDROM.cs b/BDInfo/BDROM.cs deleted file mode 100644 index 3a0c14ffda..0000000000 --- a/BDInfo/BDROM.cs +++ /dev/null @@ -1,449 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using MediaBrowser.Model.IO; - -namespace BDInfo -{ - public class BDROM - { - public FileSystemMetadata DirectoryRoot = null; - public FileSystemMetadata DirectoryBDMV = null; - public FileSystemMetadata DirectoryBDJO = null; - public FileSystemMetadata DirectoryCLIPINF = null; - public FileSystemMetadata DirectoryPLAYLIST = null; - public FileSystemMetadata DirectorySNP = null; - public FileSystemMetadata DirectorySSIF = null; - public FileSystemMetadata DirectorySTREAM = null; - - public string VolumeLabel = null; - public ulong Size = 0; - public bool IsBDPlus = false; - public bool IsBDJava = false; - public bool IsDBOX = false; - public bool IsPSP = false; - public bool Is3D = false; - public bool Is50Hz = false; - - private readonly IFileSystem _fileSystem; - - public Dictionary PlaylistFiles = - new Dictionary(); - public Dictionary StreamClipFiles = - new Dictionary(); - public Dictionary StreamFiles = - new Dictionary(); - public Dictionary InterleavedFiles = - new Dictionary(); - - public delegate bool OnStreamClipFileScanError( - TSStreamClipFile streamClipFile, Exception ex); - - public event OnStreamClipFileScanError StreamClipFileScanError; - - public delegate bool OnStreamFileScanError( - TSStreamFile streamClipFile, Exception ex); - - public event OnStreamFileScanError StreamFileScanError; - - public delegate bool OnPlaylistFileScanError( - TSPlaylistFile playlistFile, Exception ex); - - public event OnPlaylistFileScanError PlaylistFileScanError; - - public BDROM(string path, IFileSystem fileSystem) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(nameof(path)); - } - - _fileSystem = fileSystem; - // - // Locate BDMV directories. - // - - DirectoryBDMV = - GetDirectoryBDMV(path); - - if (DirectoryBDMV == null) - { - throw new Exception("Unable to locate BD structure."); - } - - DirectoryRoot = - _fileSystem.GetDirectoryInfo(Path.GetDirectoryName(DirectoryBDMV.FullName)); - DirectoryBDJO = - GetDirectory("BDJO", DirectoryBDMV, 0); - DirectoryCLIPINF = - GetDirectory("CLIPINF", DirectoryBDMV, 0); - DirectoryPLAYLIST = - GetDirectory("PLAYLIST", DirectoryBDMV, 0); - DirectorySNP = - GetDirectory("SNP", DirectoryRoot, 0); - DirectorySTREAM = - GetDirectory("STREAM", DirectoryBDMV, 0); - DirectorySSIF = - GetDirectory("SSIF", DirectorySTREAM, 0); - - if (DirectoryCLIPINF == null - || DirectoryPLAYLIST == null) - { - throw new Exception("Unable to locate BD structure."); - } - - // - // Initialize basic disc properties. - // - - VolumeLabel = GetVolumeLabel(DirectoryRoot); - Size = (ulong)GetDirectorySize(DirectoryRoot); - - if (null != GetDirectory("BDSVM", DirectoryRoot, 0)) - { - IsBDPlus = true; - } - if (null != GetDirectory("SLYVM", DirectoryRoot, 0)) - { - IsBDPlus = true; - } - if (null != GetDirectory("ANYVM", DirectoryRoot, 0)) - { - IsBDPlus = true; - } - - if (DirectoryBDJO != null && - _fileSystem.GetFilePaths(DirectoryBDJO.FullName).Any()) - { - IsBDJava = true; - } - - if (DirectorySNP != null && - GetFilePaths(DirectorySNP.FullName, ".mnv").Any()) - { - IsPSP = true; - } - - if (DirectorySSIF != null && - _fileSystem.GetFilePaths(DirectorySSIF.FullName).Any()) - { - Is3D = true; - } - - if (File.Exists(Path.Combine(DirectoryRoot.FullName, "FilmIndex.xml"))) - { - IsDBOX = true; - } - - // - // Initialize file lists. - // - - if (DirectoryPLAYLIST != null) - { - FileSystemMetadata[] files = GetFiles(DirectoryPLAYLIST.FullName, ".mpls").ToArray(); - foreach (var file in files) - { - PlaylistFiles.Add( - file.Name.ToUpper(), new TSPlaylistFile(this, file)); - } - } - - if (DirectorySTREAM != null) - { - FileSystemMetadata[] files = GetFiles(DirectorySTREAM.FullName, ".m2ts").ToArray(); - foreach (var file in files) - { - StreamFiles.Add( - file.Name.ToUpper(), new TSStreamFile(file, _fileSystem)); - } - } - - if (DirectoryCLIPINF != null) - { - FileSystemMetadata[] files = GetFiles(DirectoryCLIPINF.FullName, ".clpi").ToArray(); - foreach (var file in files) - { - StreamClipFiles.Add( - file.Name.ToUpper(), new TSStreamClipFile(file)); - } - } - - if (DirectorySSIF != null) - { - FileSystemMetadata[] files = GetFiles(DirectorySSIF.FullName, ".ssif").ToArray(); - foreach (var file in files) - { - InterleavedFiles.Add( - file.Name.ToUpper(), new TSInterleavedFile(file)); - } - } - } - - private IEnumerable GetFiles(string path, string extension) - { - return _fileSystem.GetFiles(path, new[] { extension }, false, false); - } - - private IEnumerable GetFilePaths(string path, string extension) - { - return _fileSystem.GetFilePaths(path, new[] { extension }, false, false); - } - - public void Scan() - { - foreach (var streamClipFile in StreamClipFiles.Values) - { - try - { - streamClipFile.Scan(); - } - catch (Exception ex) - { - if (StreamClipFileScanError != null) - { - if (StreamClipFileScanError(streamClipFile, ex)) - { - continue; - } - else - { - break; - } - } - else throw; - } - } - - foreach (var streamFile in StreamFiles.Values) - { - string ssifName = Path.GetFileNameWithoutExtension(streamFile.Name) + ".SSIF"; - if (InterleavedFiles.ContainsKey(ssifName)) - { - streamFile.InterleavedFile = InterleavedFiles[ssifName]; - } - } - - TSStreamFile[] streamFiles = new TSStreamFile[StreamFiles.Count]; - StreamFiles.Values.CopyTo(streamFiles, 0); - Array.Sort(streamFiles, CompareStreamFiles); - - foreach (var playlistFile in PlaylistFiles.Values) - { - try - { - playlistFile.Scan(StreamFiles, StreamClipFiles); - } - catch (Exception ex) - { - if (PlaylistFileScanError != null) - { - if (PlaylistFileScanError(playlistFile, ex)) - { - continue; - } - else - { - break; - } - } - else throw; - } - } - - foreach (var streamFile in streamFiles) - { - try - { - var playlists = new List(); - foreach (var playlist in PlaylistFiles.Values) - { - foreach (var streamClip in playlist.StreamClips) - { - if (streamClip.Name == streamFile.Name) - { - playlists.Add(playlist); - break; - } - } - } - streamFile.Scan(playlists, false); - } - catch (Exception ex) - { - if (StreamFileScanError != null) - { - if (StreamFileScanError(streamFile, ex)) - { - continue; - } - else - { - break; - } - } - else throw; - } - } - - foreach (var playlistFile in PlaylistFiles.Values) - { - playlistFile.Initialize(); - if (!Is50Hz) - { - foreach (var videoStream in playlistFile.VideoStreams) - { - if (videoStream.FrameRate == TSFrameRate.FRAMERATE_25 || - videoStream.FrameRate == TSFrameRate.FRAMERATE_50) - { - Is50Hz = true; - } - } - } - } - } - - private FileSystemMetadata GetDirectoryBDMV( - string path) - { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(nameof(path)); - } - - FileSystemMetadata dir = _fileSystem.GetDirectoryInfo(path); - - while (dir != null) - { - if (string.Equals(dir.Name, "BDMV", StringComparison.OrdinalIgnoreCase)) - { - return dir; - } - var parentFolder = Path.GetDirectoryName(dir.FullName); - if (string.IsNullOrEmpty(parentFolder)) - { - dir = null; - } - else - { - dir = _fileSystem.GetDirectoryInfo(parentFolder); - } - } - - return GetDirectory("BDMV", _fileSystem.GetDirectoryInfo(path), 0); - } - - private FileSystemMetadata GetDirectory( - string name, - FileSystemMetadata dir, - int searchDepth) - { - if (dir != null) - { - FileSystemMetadata[] children = _fileSystem.GetDirectories(dir.FullName).ToArray(); - foreach (var child in children) - { - if (string.Equals(child.Name, name, StringComparison.OrdinalIgnoreCase)) - { - return child; - } - } - if (searchDepth > 0) - { - foreach (var child in children) - { - GetDirectory( - name, child, searchDepth - 1); - } - } - } - return null; - } - - private long GetDirectorySize(FileSystemMetadata directoryInfo) - { - long size = 0; - - //if (!ExcludeDirs.Contains(directoryInfo.Name.ToUpper())) // TODO: Keep? - { - FileSystemMetadata[] pathFiles = _fileSystem.GetFiles(directoryInfo.FullName).ToArray(); - foreach (var pathFile in pathFiles) - { - if (pathFile.Extension.ToUpper() == ".SSIF") - { - continue; - } - size += pathFile.Length; - } - - FileSystemMetadata[] pathChildren = _fileSystem.GetDirectories(directoryInfo.FullName).ToArray(); - foreach (var pathChild in pathChildren) - { - size += GetDirectorySize(pathChild); - } - } - - return size; - } - - private string GetVolumeLabel(FileSystemMetadata dir) - { - return dir.Name; - } - - public int CompareStreamFiles( - TSStreamFile x, - TSStreamFile y) - { - // TODO: Use interleaved file sizes - - if ((x == null || x.FileInfo == null) && (y == null || y.FileInfo == null)) - { - return 0; - } - else if ((x == null || x.FileInfo == null) && (y != null && y.FileInfo != null)) - { - return 1; - } - else if ((x != null && x.FileInfo != null) && (y == null || y.FileInfo == null)) - { - return -1; - } - else - { - if (x.FileInfo.Length > y.FileInfo.Length) - { - return 1; - } - else if (y.FileInfo.Length > x.FileInfo.Length) - { - return -1; - } - else - { - return 0; - } - } - } - } -} diff --git a/BDInfo/LanguageCodes.cs b/BDInfo/LanguageCodes.cs deleted file mode 100644 index ab2693ffbc..0000000000 --- a/BDInfo/LanguageCodes.cs +++ /dev/null @@ -1,493 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - - -namespace BDInfo -{ - public abstract class LanguageCodes - { - public static string GetName(string code) - { - switch (code) - { - case "abk": return "Abkhazian"; - case "ace": return "Achinese"; - case "ach": return "Acoli"; - case "ada": return "Adangme"; - case "aar": return "Afar"; - case "afh": return "Afrihili"; - case "afr": return "Afrikaans"; - case "afa": return "Afro-Asiatic (Other)"; - case "aka": return "Akan"; - case "akk": return "Akkadian"; - case "alb": return "Albanian"; - case "sqi": return "Albanian"; - case "ale": return "Aleut"; - case "alg": return "Algonquian languages"; - case "tut": return "Altaic (Other)"; - case "amh": return "Amharic"; - case "apa": return "Apache languages"; - case "ara": return "Arabic"; - case "arc": return "Aramaic"; - case "arp": return "Arapaho"; - case "arn": return "Araucanian"; - case "arw": return "Arawak"; - case "arm": return "Armenian"; - case "hye": return "Armenian"; - case "art": return "Artificial (Other)"; - case "asm": return "Assamese"; - case "ath": return "Athapascan languages"; - case "aus": return "Australian languages"; - case "map": return "Austronesian (Other)"; - case "ava": return "Avaric"; - case "ave": return "Avestan"; - case "awa": return "Awadhi"; - case "aym": return "Aymara"; - case "aze": return "Azerbaijani"; - case "ban": return "Balinese"; - case "bat": return "Baltic (Other)"; - case "bal": return "Baluchi"; - case "bam": return "Bambara"; - case "bai": return "Bamileke languages"; - case "bad": return "Banda"; - case "bnt": return "Bantu (Other)"; - case "bas": return "Basa"; - case "bak": return "Bashkir"; - case "baq": return "Basque"; - case "eus": return "Basque"; - case "btk": return "Batak (Indonesia)"; - case "bej": return "Beja"; - case "bel": return "Belarusian"; - case "bem": return "Bemba"; - case "ben": return "Bengali"; - case "ber": return "Berber (Other)"; - case "bho": return "Bhojpuri"; - case "bih": return "Bihari"; - case "bik": return "Bikol"; - case "bin": return "Bini"; - case "bis": return "Bislama"; - case "bos": return "Bosnian"; - case "bra": return "Braj"; - case "bre": return "Breton"; - case "bug": return "Buginese"; - case "bul": return "Bulgarian"; - case "bua": return "Buriat"; - case "bur": return "Burmese"; - case "mya": return "Burmese"; - case "cad": return "Caddo"; - case "car": return "Carib"; - case "cat": return "Catalan"; - case "cau": return "Caucasian (Other)"; - case "ceb": return "Cebuano"; - case "cel": return "Celtic (Other)"; - case "cai": return "Central American Indian (Other)"; - case "chg": return "Chagatai"; - case "cmc": return "Chamic languages"; - case "cha": return "Chamorro"; - case "che": return "Chechen"; - case "chr": return "Cherokee"; - case "chy": return "Cheyenne"; - case "chb": return "Chibcha"; - case "chi": return "Chinese"; - case "zho": return "Chinese"; - case "chn": return "Chinook jargon"; - case "chp": return "Chipewyan"; - case "cho": return "Choctaw"; - case "chu": return "Church Slavic"; - case "chk": return "Chuukese"; - case "chv": return "Chuvash"; - case "cop": return "Coptic"; - case "cor": return "Cornish"; - case "cos": return "Corsican"; - case "cre": return "Cree"; - case "mus": return "Creek"; - case "crp": return "Creoles and pidgins (Other)"; - case "cpe": return "Creoles and pidgins,"; - case "cpf": return "Creoles and pidgins,"; - case "cpp": return "Creoles and pidgins,"; - case "scr": return "Croatian"; - case "hrv": return "Croatian"; - case "cus": return "Cushitic (Other)"; - case "cze": return "Czech"; - case "ces": return "Czech"; - case "dak": return "Dakota"; - case "dan": return "Danish"; - case "day": return "Dayak"; - case "del": return "Delaware"; - case "din": return "Dinka"; - case "div": return "Divehi"; - case "doi": return "Dogri"; - case "dgr": return "Dogrib"; - case "dra": return "Dravidian (Other)"; - case "dua": return "Duala"; - case "dut": return "Dutch"; - case "nld": return "Dutch"; - case "dum": return "Dutch, Middle (ca. 1050-1350)"; - case "dyu": return "Dyula"; - case "dzo": return "Dzongkha"; - case "efi": return "Efik"; - case "egy": return "Egyptian (Ancient)"; - case "eka": return "Ekajuk"; - case "elx": return "Elamite"; - case "eng": return "English"; - case "enm": return "English, Middle (1100-1500)"; - case "ang": return "English, Old (ca.450-1100)"; - case "epo": return "Esperanto"; - case "est": return "Estonian"; - case "ewe": return "Ewe"; - case "ewo": return "Ewondo"; - case "fan": return "Fang"; - case "fat": return "Fanti"; - case "fao": return "Faroese"; - case "fij": return "Fijian"; - case "fin": return "Finnish"; - case "fiu": return "Finno-Ugrian (Other)"; - case "fon": return "Fon"; - case "fre": return "French"; - case "fra": return "French"; - case "frm": return "French, Middle (ca.1400-1600)"; - case "fro": return "French, Old (842-ca.1400)"; - case "fry": return "Frisian"; - case "fur": return "Friulian"; - case "ful": return "Fulah"; - case "gaa": return "Ga"; - case "glg": return "Gallegan"; - case "lug": return "Ganda"; - case "gay": return "Gayo"; - case "gba": return "Gbaya"; - case "gez": return "Geez"; - case "geo": return "Georgian"; - case "kat": return "Georgian"; - case "ger": return "German"; - case "deu": return "German"; - case "nds": return "Saxon"; - case "gmh": return "German, Middle High (ca.1050-1500)"; - case "goh": return "German, Old High (ca.750-1050)"; - case "gem": return "Germanic (Other)"; - case "gil": return "Gilbertese"; - case "gon": return "Gondi"; - case "gor": return "Gorontalo"; - case "got": return "Gothic"; - case "grb": return "Grebo"; - case "grc": return "Greek, Ancient (to 1453)"; - case "gre": return "Greek"; - case "ell": return "Greek"; - case "grn": return "Guarani"; - case "guj": return "Gujarati"; - case "gwi": return "Gwich´in"; - case "hai": return "Haida"; - case "hau": return "Hausa"; - case "haw": return "Hawaiian"; - case "heb": return "Hebrew"; - case "her": return "Herero"; - case "hil": return "Hiligaynon"; - case "him": return "Himachali"; - case "hin": return "Hindi"; - case "hmo": return "Hiri Motu"; - case "hit": return "Hittite"; - case "hmn": return "Hmong"; - case "hun": return "Hungarian"; - case "hup": return "Hupa"; - case "iba": return "Iban"; - case "ice": return "Icelandic"; - case "isl": return "Icelandic"; - case "ibo": return "Igbo"; - case "ijo": return "Ijo"; - case "ilo": return "Iloko"; - case "inc": return "Indic (Other)"; - case "ine": return "Indo-European (Other)"; - case "ind": return "Indonesian"; - case "ina": return "Interlingua (International"; - case "ile": return "Interlingue"; - case "iku": return "Inuktitut"; - case "ipk": return "Inupiaq"; - case "ira": return "Iranian (Other)"; - case "gle": return "Irish"; - case "mga": return "Irish, Middle (900-1200)"; - case "sga": return "Irish, Old (to 900)"; - case "iro": return "Iroquoian languages"; - case "ita": return "Italian"; - case "jpn": return "Japanese"; - case "jav": return "Javanese"; - case "jrb": return "Judeo-Arabic"; - case "jpr": return "Judeo-Persian"; - case "kab": return "Kabyle"; - case "kac": return "Kachin"; - case "kal": return "Kalaallisut"; - case "kam": return "Kamba"; - case "kan": return "Kannada"; - case "kau": return "Kanuri"; - case "kaa": return "Kara-Kalpak"; - case "kar": return "Karen"; - case "kas": return "Kashmiri"; - case "kaw": return "Kawi"; - case "kaz": return "Kazakh"; - case "kha": return "Khasi"; - case "khm": return "Khmer"; - case "khi": return "Khoisan (Other)"; - case "kho": return "Khotanese"; - case "kik": return "Kikuyu"; - case "kmb": return "Kimbundu"; - case "kin": return "Kinyarwanda"; - case "kir": return "Kirghiz"; - case "kom": return "Komi"; - case "kon": return "Kongo"; - case "kok": return "Konkani"; - case "kor": return "Korean"; - case "kos": return "Kosraean"; - case "kpe": return "Kpelle"; - case "kro": return "Kru"; - case "kua": return "Kuanyama"; - case "kum": return "Kumyk"; - case "kur": return "Kurdish"; - case "kru": return "Kurukh"; - case "kut": return "Kutenai"; - case "lad": return "Ladino"; - case "lah": return "Lahnda"; - case "lam": return "Lamba"; - case "lao": return "Lao"; - case "lat": return "Latin"; - case "lav": return "Latvian"; - case "ltz": return "Letzeburgesch"; - case "lez": return "Lezghian"; - case "lin": return "Lingala"; - case "lit": return "Lithuanian"; - case "loz": return "Lozi"; - case "lub": return "Luba-Katanga"; - case "lua": return "Luba-Lulua"; - case "lui": return "Luiseno"; - case "lun": return "Lunda"; - case "luo": return "Luo (Kenya and Tanzania)"; - case "lus": return "Lushai"; - case "mac": return "Macedonian"; - case "mkd": return "Macedonian"; - case "mad": return "Madurese"; - case "mag": return "Magahi"; - case "mai": return "Maithili"; - case "mak": return "Makasar"; - case "mlg": return "Malagasy"; - case "may": return "Malay"; - case "msa": return "Malay"; - case "mal": return "Malayalam"; - case "mlt": return "Maltese"; - case "mnc": return "Manchu"; - case "mdr": return "Mandar"; - case "man": return "Mandingo"; - case "mni": return "Manipuri"; - case "mno": return "Manobo languages"; - case "glv": return "Manx"; - case "mao": return "Maori"; - case "mri": return "Maori"; - case "mar": return "Marathi"; - case "chm": return "Mari"; - case "mah": return "Marshall"; - case "mwr": return "Marwari"; - case "mas": return "Masai"; - case "myn": return "Mayan languages"; - case "men": return "Mende"; - case "mic": return "Micmac"; - case "min": return "Minangkabau"; - case "mis": return "Miscellaneous languages"; - case "moh": return "Mohawk"; - case "mol": return "Moldavian"; - case "mkh": return "Mon-Khmer (Other)"; - case "lol": return "Mongo"; - case "mon": return "Mongolian"; - case "mos": return "Mossi"; - case "mul": return "Multiple languages"; - case "mun": return "Munda languages"; - case "nah": return "Nahuatl"; - case "nau": return "Nauru"; - case "nav": return "Navajo"; - case "nde": return "Ndebele, North"; - case "nbl": return "Ndebele, South"; - case "ndo": return "Ndonga"; - case "nep": return "Nepali"; - case "new": return "Newari"; - case "nia": return "Nias"; - case "nic": return "Niger-Kordofanian (Other)"; - case "ssa": return "Nilo-Saharan (Other)"; - case "niu": return "Niuean"; - case "non": return "Norse, Old"; - case "nai": return "North American Indian (Other)"; - case "sme": return "Northern Sami"; - case "nor": return "Norwegian"; - case "nob": return "Norwegian Bokmål"; - case "nno": return "Norwegian Nynorsk"; - case "nub": return "Nubian languages"; - case "nym": return "Nyamwezi"; - case "nya": return "Nyanja"; - case "nyn": return "Nyankole"; - case "nyo": return "Nyoro"; - case "nzi": return "Nzima"; - case "oci": return "Occitan"; - case "oji": return "Ojibwa"; - case "ori": return "Oriya"; - case "orm": return "Oromo"; - case "osa": return "Osage"; - case "oss": return "Ossetian"; - case "oto": return "Otomian languages"; - case "pal": return "Pahlavi"; - case "pau": return "Palauan"; - case "pli": return "Pali"; - case "pam": return "Pampanga"; - case "pag": return "Pangasinan"; - case "pan": return "Panjabi"; - case "pap": return "Papiamento"; - case "paa": return "Papuan (Other)"; - case "per": return "Persian"; - case "fas": return "Persian"; - case "peo": return "Persian, Old (ca.600-400 B.C.)"; - case "phi": return "Philippine (Other)"; - case "phn": return "Phoenician"; - case "pon": return "Pohnpeian"; - case "pol": return "Polish"; - case "por": return "Portuguese"; - case "pra": return "Prakrit languages"; - case "pro": return "Provençal"; - case "pus": return "Pushto"; - case "que": return "Quechua"; - case "roh": return "Raeto-Romance"; - case "raj": return "Rajasthani"; - case "rap": return "Rapanui"; - case "rar": return "Rarotongan"; - case "roa": return "Romance (Other)"; - case "rum": return "Romanian"; - case "ron": return "Romanian"; - case "rom": return "Romany"; - case "run": return "Rundi"; - case "rus": return "Russian"; - case "sal": return "Salishan languages"; - case "sam": return "Samaritan Aramaic"; - case "smi": return "Sami languages (Other)"; - case "smo": return "Samoan"; - case "sad": return "Sandawe"; - case "sag": return "Sango"; - case "san": return "Sanskrit"; - case "sat": return "Santali"; - case "srd": return "Sardinian"; - case "sas": return "Sasak"; - case "sco": return "Scots"; - case "gla": return "Gaelic"; - case "sel": return "Selkup"; - case "sem": return "Semitic (Other)"; - case "scc": return "Serbian"; - case "srp": return "Serbian"; - case "srr": return "Serer"; - case "shn": return "Shan"; - case "sna": return "Shona"; - case "sid": return "Sidamo"; - case "sgn": return "Sign languages"; - case "bla": return "Siksika"; - case "snd": return "Sindhi"; - case "sin": return "Sinhalese"; - case "sit": return "Sino-Tibetan (Other)"; - case "sio": return "Siouan languages"; - case "den": return "Slave (Athapascan)"; - case "sla": return "Slavic (Other)"; - case "slo": return "Slovak"; - case "slk": return "Slovak"; - case "slv": return "Slovenian"; - case "sog": return "Sogdian"; - case "som": return "Somali"; - case "son": return "Songhai"; - case "snk": return "Soninke"; - case "wen": return "Sorbian languages"; - case "nso": return "Sotho, Northern"; - case "sot": return "Sotho, Southern"; - case "sai": return "South American Indian (Other)"; - case "spa": return "Spanish"; - case "suk": return "Sukuma"; - case "sux": return "Sumerian"; - case "sun": return "Sundanese"; - case "sus": return "Susu"; - case "swa": return "Swahili"; - case "ssw": return "Swati"; - case "swe": return "Swedish"; - case "syr": return "Syriac"; - case "tgl": return "Tagalog"; - case "tah": return "Tahitian"; - case "tai": return "Tai (Other)"; - case "tgk": return "Tajik"; - case "tmh": return "Tamashek"; - case "tam": return "Tamil"; - case "tat": return "Tatar"; - case "tel": return "Telugu"; - case "ter": return "Tereno"; - case "tet": return "Tetum"; - case "tha": return "Thai"; - case "tib": return "Tibetan"; - case "bod": return "Tibetan"; - case "tig": return "Tigre"; - case "tir": return "Tigrinya"; - case "tem": return "Timne"; - case "tiv": return "Tiv"; - case "tli": return "Tlingit"; - case "tpi": return "Tok Pisin"; - case "tkl": return "Tokelau"; - case "tog": return "Tonga (Nyasa)"; - case "ton": return "Tonga (Tonga Islands)"; - case "tsi": return "Tsimshian"; - case "tso": return "Tsonga"; - case "tsn": return "Tswana"; - case "tum": return "Tumbuka"; - case "tur": return "Turkish"; - case "ota": return "Turkish, Ottoman (1500-1928)"; - case "tuk": return "Turkmen"; - case "tvl": return "Tuvalu"; - case "tyv": return "Tuvinian"; - case "twi": return "Twi"; - case "uga": return "Ugaritic"; - case "uig": return "Uighur"; - case "ukr": return "Ukrainian"; - case "umb": return "Umbundu"; - case "und": return "Undetermined"; - case "urd": return "Urdu"; - case "uzb": return "Uzbek"; - case "vai": return "Vai"; - case "ven": return "Venda"; - case "vie": return "Vietnamese"; - case "vol": return "Volapük"; - case "vot": return "Votic"; - case "wak": return "Wakashan languages"; - case "wal": return "Walamo"; - case "war": return "Waray"; - case "was": return "Washo"; - case "wel": return "Welsh"; - case "cym": return "Welsh"; - case "wol": return "Wolof"; - case "xho": return "Xhosa"; - case "sah": return "Yakut"; - case "yao": return "Yao"; - case "yap": return "Yapese"; - case "yid": return "Yiddish"; - case "yor": return "Yoruba"; - case "ypk": return "Yupik languages"; - case "znd": return "Zande"; - case "zap": return "Zapotec"; - case "zen": return "Zenaga"; - case "zha": return "Zhuang"; - case "zul": return "Zulu"; - case "zun": return "Zuni"; - - default: return code; - } - } - } -} diff --git a/BDInfo/Properties/AssemblyInfo.cs b/BDInfo/Properties/AssemblyInfo.cs deleted file mode 100644 index f65c7036a4..0000000000 --- a/BDInfo/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("BDInfo")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin Server")] -[assembly: AssemblyCopyright("Copyright © 2016 CinemaSquid. Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: NeutralResourcesLanguage("en")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] diff --git a/BDInfo/ReadMe.txt b/BDInfo/ReadMe.txt deleted file mode 100644 index e70b0b66cd..0000000000 --- a/BDInfo/ReadMe.txt +++ /dev/null @@ -1,5 +0,0 @@ -The source is taken from the BDRom folder of this project: - -http://www.cinemasquid.com/blu-ray/tools/bdinfo - -BDInfoSettings was taken from the FormSettings class, and changed so that the settings all return defaults. diff --git a/BDInfo/TSCodecAC3.cs b/BDInfo/TSCodecAC3.cs deleted file mode 100644 index 35d306a19d..0000000000 --- a/BDInfo/TSCodecAC3.cs +++ /dev/null @@ -1,309 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -#undef DEBUG -using System.IO; - -namespace BDInfo -{ - public abstract class TSCodecAC3 - { - private static byte[] eac3_blocks = new byte[] { 1, 2, 3, 6 }; - - public static void Scan( - TSAudioStream stream, - TSStreamBuffer buffer, - ref string tag) - { - if (stream.IsInitialized) return; - - byte[] sync = buffer.ReadBytes(2); - if (sync == null || - sync[0] != 0x0B || - sync[1] != 0x77) - { - return; - } - - int sr_code = 0; - int frame_size = 0; - int frame_size_code = 0; - int channel_mode = 0; - int lfe_on = 0; - int dial_norm = 0; - int num_blocks = 0; - - byte[] hdr = buffer.ReadBytes(4); - int bsid = (hdr[3] & 0xF8) >> 3; - buffer.Seek(-4, SeekOrigin.Current); - if (bsid <= 10) - { - byte[] crc = buffer.ReadBytes(2); - sr_code = buffer.ReadBits(2); - frame_size_code = buffer.ReadBits(6); - bsid = buffer.ReadBits(5); - int bsmod = buffer.ReadBits(3); - - channel_mode = buffer.ReadBits(3); - int cmixlev = 0; - if (((channel_mode & 0x1) > 0) && (channel_mode != 0x1)) - { - cmixlev = buffer.ReadBits(2); - } - int surmixlev = 0; - if ((channel_mode & 0x4) > 0) - { - surmixlev = buffer.ReadBits(2); - } - int dsurmod = 0; - if (channel_mode == 0x2) - { - dsurmod = buffer.ReadBits(2); - if (dsurmod == 0x2) - { - stream.AudioMode = TSAudioMode.Surround; - } - } - lfe_on = buffer.ReadBits(1); - dial_norm = buffer.ReadBits(5); - int compr = 0; - if (1 == buffer.ReadBits(1)) - { - compr = buffer.ReadBits(8); - } - int langcod = 0; - if (1 == buffer.ReadBits(1)) - { - langcod = buffer.ReadBits(8); - } - int mixlevel = 0; - int roomtyp = 0; - if (1 == buffer.ReadBits(1)) - { - mixlevel = buffer.ReadBits(5); - roomtyp = buffer.ReadBits(2); - } - if (channel_mode == 0) - { - int dialnorm2 = buffer.ReadBits(5); - int compr2 = 0; - if (1 == buffer.ReadBits(1)) - { - compr2 = buffer.ReadBits(8); - } - int langcod2 = 0; - if (1 == buffer.ReadBits(1)) - { - langcod2 = buffer.ReadBits(8); - } - int mixlevel2 = 0; - int roomtyp2 = 0; - if (1 == buffer.ReadBits(1)) - { - mixlevel2 = buffer.ReadBits(5); - roomtyp2 = buffer.ReadBits(2); - } - } - int copyrightb = buffer.ReadBits(1); - int origbs = buffer.ReadBits(1); - if (bsid == 6) - { - if (1 == buffer.ReadBits(1)) - { - int dmixmod = buffer.ReadBits(2); - int ltrtcmixlev = buffer.ReadBits(3); - int ltrtsurmixlev = buffer.ReadBits(3); - int lorocmixlev = buffer.ReadBits(3); - int lorosurmixlev = buffer.ReadBits(3); - } - if (1 == buffer.ReadBits(1)) - { - int dsurexmod = buffer.ReadBits(2); - int dheadphonmod = buffer.ReadBits(2); - if (dheadphonmod == 0x2) - { - // TODO - } - int adconvtyp = buffer.ReadBits(1); - int xbsi2 = buffer.ReadBits(8); - int encinfo = buffer.ReadBits(1); - if (dsurexmod == 2) - { - stream.AudioMode = TSAudioMode.Extended; - } - } - } - } - else - { - int frame_type = buffer.ReadBits(2); - int substreamid = buffer.ReadBits(3); - frame_size = (buffer.ReadBits(11) + 1) << 1; - - sr_code = buffer.ReadBits(2); - if (sr_code == 3) - { - sr_code = buffer.ReadBits(2); - } - else - { - num_blocks = buffer.ReadBits(2); - } - channel_mode = buffer.ReadBits(3); - lfe_on = buffer.ReadBits(1); - } - - switch (channel_mode) - { - case 0: // 1+1 - stream.ChannelCount = 2; - if (stream.AudioMode == TSAudioMode.Unknown) - { - stream.AudioMode = TSAudioMode.DualMono; - } - break; - case 1: // 1/0 - stream.ChannelCount = 1; - break; - case 2: // 2/0 - stream.ChannelCount = 2; - if (stream.AudioMode == TSAudioMode.Unknown) - { - stream.AudioMode = TSAudioMode.Stereo; - } - break; - case 3: // 3/0 - stream.ChannelCount = 3; - break; - case 4: // 2/1 - stream.ChannelCount = 3; - break; - case 5: // 3/1 - stream.ChannelCount = 4; - break; - case 6: // 2/2 - stream.ChannelCount = 4; - break; - case 7: // 3/2 - stream.ChannelCount = 5; - break; - default: - stream.ChannelCount = 0; - break; - } - - switch (sr_code) - { - case 0: - stream.SampleRate = 48000; - break; - case 1: - stream.SampleRate = 44100; - break; - case 2: - stream.SampleRate = 32000; - break; - default: - stream.SampleRate = 0; - break; - } - - if (bsid <= 10) - { - switch (frame_size_code >> 1) - { - case 18: - stream.BitRate = 640000; - break; - case 17: - stream.BitRate = 576000; - break; - case 16: - stream.BitRate = 512000; - break; - case 15: - stream.BitRate = 448000; - break; - case 14: - stream.BitRate = 384000; - break; - case 13: - stream.BitRate = 320000; - break; - case 12: - stream.BitRate = 256000; - break; - case 11: - stream.BitRate = 224000; - break; - case 10: - stream.BitRate = 192000; - break; - case 9: - stream.BitRate = 160000; - break; - case 8: - stream.BitRate = 128000; - break; - case 7: - stream.BitRate = 112000; - break; - case 6: - stream.BitRate = 96000; - break; - case 5: - stream.BitRate = 80000; - break; - case 4: - stream.BitRate = 64000; - break; - case 3: - stream.BitRate = 56000; - break; - case 2: - stream.BitRate = 48000; - break; - case 1: - stream.BitRate = 40000; - break; - case 0: - stream.BitRate = 32000; - break; - default: - stream.BitRate = 0; - break; - } - } - else - { - stream.BitRate = (long) - (4.0 * frame_size * stream.SampleRate / (num_blocks * 256)); - } - - stream.LFE = lfe_on; - if (stream.StreamType != TSStreamType.AC3_PLUS_AUDIO && - stream.StreamType != TSStreamType.AC3_PLUS_SECONDARY_AUDIO) - { - stream.DialNorm = dial_norm - 31; - } - stream.IsVBR = false; - stream.IsInitialized = true; - } - } -} diff --git a/BDInfo/TSCodecAVC.cs b/BDInfo/TSCodecAVC.cs deleted file mode 100644 index 5833d169f4..0000000000 --- a/BDInfo/TSCodecAVC.cs +++ /dev/null @@ -1,148 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - - -namespace BDInfo -{ - public abstract class TSCodecAVC - { - public static void Scan( - TSVideoStream stream, - TSStreamBuffer buffer, - ref string tag) - { - uint parse = 0; - byte accessUnitDelimiterParse = 0; - byte sequenceParameterSetParse = 0; - string profile = null; - string level = null; - byte constraintSet0Flag = 0; - byte constraintSet1Flag = 0; - byte constraintSet2Flag = 0; - byte constraintSet3Flag = 0; - - for (int i = 0; i < buffer.Length; i++) - { - parse = (parse << 8) + buffer.ReadByte(); - - if (parse == 0x00000109) - { - accessUnitDelimiterParse = 1; - } - else if (accessUnitDelimiterParse > 0) - { - --accessUnitDelimiterParse; - if (accessUnitDelimiterParse == 0) - { - switch ((parse & 0xFF) >> 5) - { - case 0: // I - case 3: // SI - case 5: // I, SI - tag = "I"; - break; - - case 1: // I, P - case 4: // SI, SP - case 6: // I, SI, P, SP - tag = "P"; - break; - - case 2: // I, P, B - case 7: // I, SI, P, SP, B - tag = "B"; - break; - } - if (stream.IsInitialized) return; - } - } - else if (parse == 0x00000127 || parse == 0x00000167) - { - sequenceParameterSetParse = 3; - } - else if (sequenceParameterSetParse > 0) - { - --sequenceParameterSetParse; - switch (sequenceParameterSetParse) - { - case 2: - switch (parse & 0xFF) - { - case 66: - profile = "Baseline Profile"; - break; - case 77: - profile = "Main Profile"; - break; - case 88: - profile = "Extended Profile"; - break; - case 100: - profile = "High Profile"; - break; - case 110: - profile = "High 10 Profile"; - break; - case 122: - profile = "High 4:2:2 Profile"; - break; - case 144: - profile = "High 4:4:4 Profile"; - break; - default: - profile = "Unknown Profile"; - break; - } - break; - - case 1: - constraintSet0Flag = (byte) - ((parse & 0x80) >> 7); - constraintSet1Flag = (byte) - ((parse & 0x40) >> 6); - constraintSet2Flag = (byte) - ((parse & 0x20) >> 5); - constraintSet3Flag = (byte) - ((parse & 0x10) >> 4); - break; - - case 0: - byte b = (byte)(parse & 0xFF); - if (b == 11 && constraintSet3Flag == 1) - { - level = "1b"; - } - else - { - level = string.Format( - "{0:D}.{1:D}", - b / 10, (b - ((b / 10) * 10))); - } - stream.EncodingProfile = string.Format( - "{0} {1}", profile, level); - stream.IsVBR = true; - stream.IsInitialized = true; - break; - } - } - } - return; - } - } -} diff --git a/BDInfo/TSCodecDTS.cs b/BDInfo/TSCodecDTS.cs deleted file mode 100644 index ff94cb7022..0000000000 --- a/BDInfo/TSCodecDTS.cs +++ /dev/null @@ -1,159 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - - -namespace BDInfo -{ - public abstract class TSCodecDTS - { - private static int[] dca_sample_rates = - { - 0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0, - 12000, 24000, 48000, 96000, 192000 - }; - - private static int[] dca_bit_rates = - { - 32000, 56000, 64000, 96000, 112000, 128000, - 192000, 224000, 256000, 320000, 384000, - 448000, 512000, 576000, 640000, 768000, - 896000, 1024000, 1152000, 1280000, 1344000, - 1408000, 1411200, 1472000, 1509000, 1920000, - 2048000, 3072000, 3840000, 1/*open*/, 2/*variable*/, 3/*lossless*/ - }; - - private static int[] dca_channels = - { - 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6, 7, 8, 8 - }; - - private static int[] dca_bits_per_sample = - { - 16, 16, 20, 20, 0, 24, 24 - }; - - public static void Scan( - TSAudioStream stream, - TSStreamBuffer buffer, - long bitrate, - ref string tag) - { - if (stream.IsInitialized) return; - - bool syncFound = false; - uint sync = 0; - for (int i = 0; i < buffer.Length; i++) - { - sync = (sync << 8) + buffer.ReadByte(); - if (sync == 0x7FFE8001) - { - syncFound = true; - break; - } - } - if (!syncFound) return; - - int frame_type = buffer.ReadBits(1); - int samples_deficit = buffer.ReadBits(5); - int crc_present = buffer.ReadBits(1); - int sample_blocks = buffer.ReadBits(7); - int frame_size = buffer.ReadBits(14); - if (frame_size < 95) - { - return; - } - int amode = buffer.ReadBits(6); - int sample_rate = buffer.ReadBits(4); - if (sample_rate < 0 || sample_rate >= dca_sample_rates.Length) - { - return; - } - int bit_rate = buffer.ReadBits(5); - if (bit_rate < 0 || bit_rate >= dca_bit_rates.Length) - { - return; - } - int downmix = buffer.ReadBits(1); - int dynrange = buffer.ReadBits(1); - int timestamp = buffer.ReadBits(1); - int aux_data = buffer.ReadBits(1); - int hdcd = buffer.ReadBits(1); - int ext_descr = buffer.ReadBits(3); - int ext_coding = buffer.ReadBits(1); - int aspf = buffer.ReadBits(1); - int lfe = buffer.ReadBits(2); - int predictor_history = buffer.ReadBits(1); - if (crc_present == 1) - { - int crc = buffer.ReadBits(16); - } - int multirate_inter = buffer.ReadBits(1); - int version = buffer.ReadBits(4); - int copy_history = buffer.ReadBits(2); - int source_pcm_res = buffer.ReadBits(3); - int front_sum = buffer.ReadBits(1); - int surround_sum = buffer.ReadBits(1); - int dialog_norm = buffer.ReadBits(4); - if (source_pcm_res < 0 || source_pcm_res >= dca_bits_per_sample.Length) - { - return; - } - int subframes = buffer.ReadBits(4); - int total_channels = buffer.ReadBits(3) + 1 + ext_coding; - - stream.SampleRate = dca_sample_rates[sample_rate]; - stream.ChannelCount = total_channels; - stream.LFE = (lfe > 0 ? 1 : 0); - stream.BitDepth = dca_bits_per_sample[source_pcm_res]; - stream.DialNorm = -dialog_norm; - if ((source_pcm_res & 0x1) == 0x1) - { - stream.AudioMode = TSAudioMode.Extended; - } - - stream.BitRate = (uint)dca_bit_rates[bit_rate]; - switch (stream.BitRate) - { - case 1: - if (bitrate > 0) - { - stream.BitRate = bitrate; - stream.IsVBR = false; - stream.IsInitialized = true; - } - else - { - stream.BitRate = 0; - } - break; - - case 2: - case 3: - stream.IsVBR = true; - stream.IsInitialized = true; - break; - - default: - stream.IsVBR = false; - stream.IsInitialized = true; - break; - } - } - } -} diff --git a/BDInfo/TSCodecDTSHD.cs b/BDInfo/TSCodecDTSHD.cs deleted file mode 100644 index 57a136d2d5..0000000000 --- a/BDInfo/TSCodecDTSHD.cs +++ /dev/null @@ -1,246 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - - -namespace BDInfo -{ - public abstract class TSCodecDTSHD - { - private static int[] SampleRates = new int[] - { 0x1F40, 0x3E80, 0x7D00, 0x0FA00, 0x1F400, 0x5622, 0x0AC44, 0x15888, 0x2B110, 0x56220, 0x2EE0, 0x5DC0, 0x0BB80, 0x17700, 0x2EE00, 0x5DC00 }; - - public static void Scan( - TSAudioStream stream, - TSStreamBuffer buffer, - long bitrate, - ref string tag) - { - if (stream.IsInitialized && - (stream.StreamType == TSStreamType.DTS_HD_SECONDARY_AUDIO || - (stream.CoreStream != null && - stream.CoreStream.IsInitialized))) return; - - bool syncFound = false; - uint sync = 0; - for (int i = 0; i < buffer.Length; i++) - { - sync = (sync << 8) + buffer.ReadByte(); - if (sync == 0x64582025) - { - syncFound = true; - break; - } - } - - if (!syncFound) - { - tag = "CORE"; - if (stream.CoreStream == null) - { - stream.CoreStream = new TSAudioStream(); - stream.CoreStream.StreamType = TSStreamType.DTS_AUDIO; - } - if (!stream.CoreStream.IsInitialized) - { - buffer.BeginRead(); - TSCodecDTS.Scan(stream.CoreStream, buffer, bitrate, ref tag); - } - return; - } - - tag = "HD"; - int temp1 = buffer.ReadBits(8); - int nuSubStreamIndex = buffer.ReadBits(2); - int nuExtSSHeaderSize = 0; - int nuExtSSFSize = 0; - int bBlownUpHeader = buffer.ReadBits(1); - if (1 == bBlownUpHeader) - { - nuExtSSHeaderSize = buffer.ReadBits(12) + 1; - nuExtSSFSize = buffer.ReadBits(20) + 1; - } - else - { - nuExtSSHeaderSize = buffer.ReadBits(8) + 1; - nuExtSSFSize = buffer.ReadBits(16) + 1; - } - int nuNumAudioPresent = 1; - int nuNumAssets = 1; - int bStaticFieldsPresent = buffer.ReadBits(1); - if (1 == bStaticFieldsPresent) - { - int nuRefClockCode = buffer.ReadBits(2); - int nuExSSFrameDurationCode = buffer.ReadBits(3) + 1; - long nuTimeStamp = 0; - if (1 == buffer.ReadBits(1)) - { - nuTimeStamp = (buffer.ReadBits(18) << 18) + buffer.ReadBits(18); - } - nuNumAudioPresent = buffer.ReadBits(3) + 1; - nuNumAssets = buffer.ReadBits(3) + 1; - int[] nuActiveExSSMask = new int[nuNumAudioPresent]; - for (int i = 0; i < nuNumAudioPresent; i++) - { - nuActiveExSSMask[i] = buffer.ReadBits(nuSubStreamIndex + 1); //? - } - for (int i = 0; i < nuNumAudioPresent; i++) - { - for (int j = 0; j < nuSubStreamIndex + 1; j++) - { - if (((j + 1) % 2) == 1) - { - int mask = buffer.ReadBits(8); - } - } - } - if (1 == buffer.ReadBits(1)) - { - int nuMixMetadataAdjLevel = buffer.ReadBits(2); - int nuBits4MixOutMask = buffer.ReadBits(2) * 4 + 4; - int nuNumMixOutConfigs = buffer.ReadBits(2) + 1; - int[] nuMixOutChMask = new int[nuNumMixOutConfigs]; - for (int i = 0; i < nuNumMixOutConfigs; i++) - { - nuMixOutChMask[i] = buffer.ReadBits(nuBits4MixOutMask); - } - } - } - int[] AssetSizes = new int[nuNumAssets]; - for (int i = 0; i < nuNumAssets; i++) - { - if (1 == bBlownUpHeader) - { - AssetSizes[i] = buffer.ReadBits(20) + 1; - } - else - { - AssetSizes[i] = buffer.ReadBits(16) + 1; - } - } - for (int i = 0; i < nuNumAssets; i++) - { - long bufferPosition = buffer.Position; - int nuAssetDescriptorFSIZE = buffer.ReadBits(9) + 1; - int DescriptorDataForAssetIndex = buffer.ReadBits(3); - if (1 == bStaticFieldsPresent) - { - int AssetTypeDescrPresent = buffer.ReadBits(1); - if (1 == AssetTypeDescrPresent) - { - int AssetTypeDescriptor = buffer.ReadBits(4); - } - int LanguageDescrPresent = buffer.ReadBits(1); - if (1 == LanguageDescrPresent) - { - int LanguageDescriptor = buffer.ReadBits(24); - } - int bInfoTextPresent = buffer.ReadBits(1); - if (1 == bInfoTextPresent) - { - int nuInfoTextByteSize = buffer.ReadBits(10) + 1; - int[] InfoText = new int[nuInfoTextByteSize]; - for (int j = 0; j < nuInfoTextByteSize; j++) - { - InfoText[j] = buffer.ReadBits(8); - } - } - int nuBitResolution = buffer.ReadBits(5) + 1; - int nuMaxSampleRate = buffer.ReadBits(4); - int nuTotalNumChs = buffer.ReadBits(8) + 1; - int bOne2OneMapChannels2Speakers = buffer.ReadBits(1); - int nuSpkrActivityMask = 0; - if (1 == bOne2OneMapChannels2Speakers) - { - int bEmbeddedStereoFlag = 0; - if (nuTotalNumChs > 2) - { - bEmbeddedStereoFlag = buffer.ReadBits(1); - } - int bEmbeddedSixChFlag = 0; - if (nuTotalNumChs > 6) - { - bEmbeddedSixChFlag = buffer.ReadBits(1); - } - int bSpkrMaskEnabled = buffer.ReadBits(1); - int nuNumBits4SAMask = 0; - if (1 == bSpkrMaskEnabled) - { - nuNumBits4SAMask = buffer.ReadBits(2); - nuNumBits4SAMask = nuNumBits4SAMask * 4 + 4; - nuSpkrActivityMask = buffer.ReadBits(nuNumBits4SAMask); - } - // TODO... - } - stream.SampleRate = SampleRates[nuMaxSampleRate]; - stream.BitDepth = nuBitResolution; - - stream.LFE = 0; - if ((nuSpkrActivityMask & 0x8) == 0x8) - { - ++stream.LFE; - } - if ((nuSpkrActivityMask & 0x1000) == 0x1000) - { - ++stream.LFE; - } - stream.ChannelCount = nuTotalNumChs - stream.LFE; - } - if (nuNumAssets > 1) - { - // TODO... - break; - } - } - - // TODO - if (stream.CoreStream != null) - { - var coreStream = (TSAudioStream)stream.CoreStream; - if (coreStream.AudioMode == TSAudioMode.Extended && - stream.ChannelCount == 5) - { - stream.AudioMode = TSAudioMode.Extended; - } - /* - if (coreStream.DialNorm != 0) - { - stream.DialNorm = coreStream.DialNorm; - } - */ - } - - if (stream.StreamType == TSStreamType.DTS_HD_MASTER_AUDIO) - { - stream.IsVBR = true; - stream.IsInitialized = true; - } - else if (bitrate > 0) - { - stream.IsVBR = false; - stream.BitRate = bitrate; - if (stream.CoreStream != null) - { - stream.BitRate += stream.CoreStream.BitRate; - stream.IsInitialized = true; - } - stream.IsInitialized = (stream.BitRate > 0 ? true : false); - } - } - } -} diff --git a/BDInfo/TSCodecLPCM.cs b/BDInfo/TSCodecLPCM.cs deleted file mode 100644 index 5709d8689f..0000000000 --- a/BDInfo/TSCodecLPCM.cs +++ /dev/null @@ -1,123 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - - -namespace BDInfo -{ - public abstract class TSCodecLPCM - { - public static void Scan( - TSAudioStream stream, - TSStreamBuffer buffer, - ref string tag) - { - if (stream.IsInitialized) return; - - byte[] header = buffer.ReadBytes(4); - int flags = (header[2] << 8) + header[3]; - - switch ((flags & 0xF000) >> 12) - { - case 1: // 1/0/0 - stream.ChannelCount = 1; - stream.LFE = 0; - break; - case 3: // 2/0/0 - stream.ChannelCount = 2; - stream.LFE = 0; - break; - case 4: // 3/0/0 - stream.ChannelCount = 3; - stream.LFE = 0; - break; - case 5: // 2/1/0 - stream.ChannelCount = 3; - stream.LFE = 0; - break; - case 6: // 3/1/0 - stream.ChannelCount = 4; - stream.LFE = 0; - break; - case 7: // 2/2/0 - stream.ChannelCount = 4; - stream.LFE = 0; - break; - case 8: // 3/2/0 - stream.ChannelCount = 5; - stream.LFE = 0; - break; - case 9: // 3/2/1 - stream.ChannelCount = 5; - stream.LFE = 1; - break; - case 10: // 3/4/0 - stream.ChannelCount = 7; - stream.LFE = 0; - break; - case 11: // 3/4/1 - stream.ChannelCount = 7; - stream.LFE = 1; - break; - default: - stream.ChannelCount = 0; - stream.LFE = 0; - break; - } - - switch ((flags & 0xC0) >> 6) - { - case 1: - stream.BitDepth = 16; - break; - case 2: - stream.BitDepth = 20; - break; - case 3: - stream.BitDepth = 24; - break; - default: - stream.BitDepth = 0; - break; - } - - switch ((flags & 0xF00) >> 8) - { - case 1: - stream.SampleRate = 48000; - break; - case 4: - stream.SampleRate = 96000; - break; - case 5: - stream.SampleRate = 192000; - break; - default: - stream.SampleRate = 0; - break; - } - - stream.BitRate = (uint) - (stream.SampleRate * stream.BitDepth * - (stream.ChannelCount + stream.LFE)); - - stream.IsVBR = false; - stream.IsInitialized = true; - } - } -} diff --git a/BDInfo/TSCodecMPEG2.cs b/BDInfo/TSCodecMPEG2.cs deleted file mode 100644 index 8bcd07d020..0000000000 --- a/BDInfo/TSCodecMPEG2.cs +++ /dev/null @@ -1,208 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -#undef DEBUG - - -namespace BDInfo -{ - public abstract class TSCodecMPEG2 - { - public static void Scan( - TSVideoStream stream, - TSStreamBuffer buffer, - ref string tag) - { - int parse = 0; - int pictureParse = 0; - int sequenceHeaderParse = 0; - int extensionParse = 0; - int sequenceExtensionParse = 0; - - for (int i = 0; i < buffer.Length; i++) - { - parse = (parse << 8) + buffer.ReadByte(); - - if (parse == 0x00000100) - { - pictureParse = 2; - } - else if (parse == 0x000001B3) - { - sequenceHeaderParse = 7; - } - else if (sequenceHeaderParse > 0) - { - --sequenceHeaderParse; - switch (sequenceHeaderParse) - { -#if DEBUG - case 6: - break; - - case 5: - break; - - case 4: - stream.Width = - (int)((parse & 0xFFF000) >> 12); - stream.Height = - (int)(parse & 0xFFF); - break; - - case 3: - stream.AspectRatio = - (TSAspectRatio)((parse & 0xF0) >> 4); - - switch ((parse & 0xF0) >> 4) - { - case 0: // Forbidden - break; - case 1: // Square - break; - case 2: // 4:3 - break; - case 3: // 16:9 - break; - case 4: // 2.21:1 - break; - default: // Reserved - break; - } - - switch (parse & 0xF) - { - case 0: // Forbidden - break; - case 1: // 23.976 - stream.FrameRateEnumerator = 24000; - stream.FrameRateDenominator = 1001; - break; - case 2: // 24 - stream.FrameRateEnumerator = 24000; - stream.FrameRateDenominator = 1000; - break; - case 3: // 25 - stream.FrameRateEnumerator = 25000; - stream.FrameRateDenominator = 1000; - break; - case 4: // 29.97 - stream.FrameRateEnumerator = 30000; - stream.FrameRateDenominator = 1001; - break; - case 5: // 30 - stream.FrameRateEnumerator = 30000; - stream.FrameRateDenominator = 1000; - break; - case 6: // 50 - stream.FrameRateEnumerator = 50000; - stream.FrameRateDenominator = 1000; - break; - case 7: // 59.94 - stream.FrameRateEnumerator = 60000; - stream.FrameRateDenominator = 1001; - break; - case 8: // 60 - stream.FrameRateEnumerator = 60000; - stream.FrameRateDenominator = 1000; - break; - default: // Reserved - stream.FrameRateEnumerator = 0; - stream.FrameRateDenominator = 0; - break; - } - break; - - case 2: - break; - - case 1: - break; -#endif - - case 0: -#if DEBUG - stream.BitRate = - (((parse & 0xFFFFC0) >> 6) * 200); -#endif - stream.IsVBR = true; - stream.IsInitialized = true; - break; - } - } - else if (pictureParse > 0) - { - --pictureParse; - if (pictureParse == 0) - { - switch ((parse & 0x38) >> 3) - { - case 1: - tag = "I"; - break; - case 2: - tag = "P"; - break; - case 3: - tag = "B"; - break; - default: - break; - } - if (stream.IsInitialized) return; - } - } - else if (parse == 0x000001B5) - { - extensionParse = 1; - } - else if (extensionParse > 0) - { - --extensionParse; - if (extensionParse == 0) - { - if ((parse & 0xF0) == 0x10) - { - sequenceExtensionParse = 1; - } - } - } - else if (sequenceExtensionParse > 0) - { - --sequenceExtensionParse; -#if DEBUG - if (sequenceExtensionParse == 0) - { - uint sequenceExtension = - ((parse & 0x8) >> 3); - if (sequenceExtension == 0) - { - stream.IsInterlaced = true; - } - else - { - stream.IsInterlaced = false; - } - } -#endif - } - } - } - } -} diff --git a/BDInfo/TSCodecMVC.cs b/BDInfo/TSCodecMVC.cs deleted file mode 100644 index abff0c1b08..0000000000 --- a/BDInfo/TSCodecMVC.cs +++ /dev/null @@ -1,36 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - - -namespace BDInfo -{ - // TODO: Do something more interesting here... - - public abstract class TSCodecMVC - { - public static void Scan( - TSVideoStream stream, - TSStreamBuffer buffer, - ref string tag) - { - stream.IsVBR = true; - stream.IsInitialized = true; - } - } -} diff --git a/BDInfo/TSCodecTrueHD.cs b/BDInfo/TSCodecTrueHD.cs deleted file mode 100644 index 5e81e162c5..0000000000 --- a/BDInfo/TSCodecTrueHD.cs +++ /dev/null @@ -1,186 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - - -namespace BDInfo -{ - public abstract class TSCodecTrueHD - { - public static void Scan( - TSAudioStream stream, - TSStreamBuffer buffer, - ref string tag) - { - if (stream.IsInitialized && - stream.CoreStream != null && - stream.CoreStream.IsInitialized) return; - - bool syncFound = false; - uint sync = 0; - for (int i = 0; i < buffer.Length; i++) - { - sync = (sync << 8) + buffer.ReadByte(); - if (sync == 0xF8726FBA) - { - syncFound = true; - break; - } - } - - if (!syncFound) - { - tag = "CORE"; - if (stream.CoreStream == null) - { - stream.CoreStream = new TSAudioStream(); - stream.CoreStream.StreamType = TSStreamType.AC3_AUDIO; - } - if (!stream.CoreStream.IsInitialized) - { - buffer.BeginRead(); - TSCodecAC3.Scan(stream.CoreStream, buffer, ref tag); - } - return; - } - - tag = "HD"; - int ratebits = buffer.ReadBits(4); - if (ratebits != 0xF) - { - stream.SampleRate = - (((ratebits & 8) > 0 ? 44100 : 48000) << (ratebits & 7)); - } - int temp1 = buffer.ReadBits(8); - int channels_thd_stream1 = buffer.ReadBits(5); - int temp2 = buffer.ReadBits(2); - - stream.ChannelCount = 0; - stream.LFE = 0; - int c_LFE2 = buffer.ReadBits(1); - if (c_LFE2 == 1) - { - stream.LFE += 1; - } - int c_Cvh = buffer.ReadBits(1); - if (c_Cvh == 1) - { - stream.ChannelCount += 1; - } - int c_LRw = buffer.ReadBits(1); - if (c_LRw == 1) - { - stream.ChannelCount += 2; - } - int c_LRsd = buffer.ReadBits(1); - if (c_LRsd == 1) - { - stream.ChannelCount += 2; - } - int c_Ts = buffer.ReadBits(1); - if (c_Ts == 1) - { - stream.ChannelCount += 1; - } - int c_Cs = buffer.ReadBits(1); - if (c_Cs == 1) - { - stream.ChannelCount += 1; - } - int c_LRrs = buffer.ReadBits(1); - if (c_LRrs == 1) - { - stream.ChannelCount += 2; - } - int c_LRc = buffer.ReadBits(1); - if (c_LRc == 1) - { - stream.ChannelCount += 2; - } - int c_LRvh = buffer.ReadBits(1); - if (c_LRvh == 1) - { - stream.ChannelCount += 2; - } - int c_LRs = buffer.ReadBits(1); - if (c_LRs == 1) - { - stream.ChannelCount += 2; - } - int c_LFE = buffer.ReadBits(1); - if (c_LFE == 1) - { - stream.LFE += 1; - } - int c_C = buffer.ReadBits(1); - if (c_C == 1) - { - stream.ChannelCount += 1; - } - int c_LR = buffer.ReadBits(1); - if (c_LR == 1) - { - stream.ChannelCount += 2; - } - - int access_unit_size = 40 << (ratebits & 7); - int access_unit_size_pow2 = 64 << (ratebits & 7); - - int a1 = buffer.ReadBits(16); - int a2 = buffer.ReadBits(16); - int a3 = buffer.ReadBits(16); - - int is_vbr = buffer.ReadBits(1); - int peak_bitrate = buffer.ReadBits(15); - peak_bitrate = (peak_bitrate * stream.SampleRate) >> 4; - - double peak_bitdepth = - (double)peak_bitrate / - (stream.ChannelCount + stream.LFE) / - stream.SampleRate; - if (peak_bitdepth > 14) - { - stream.BitDepth = 24; - } - else - { - stream.BitDepth = 16; - } - -#if DEBUG - System.Diagnostics.Debug.WriteLine(string.Format( - "{0}\t{1}\t{2:F2}", - stream.PID, peak_bitrate, peak_bitdepth)); -#endif - /* - // TODO: Get THD dialnorm from metadata - if (stream.CoreStream != null) - { - TSAudioStream coreStream = (TSAudioStream)stream.CoreStream; - if (coreStream.DialNorm != 0) - { - stream.DialNorm = coreStream.DialNorm; - } - } - */ - - stream.IsVBR = true; - stream.IsInitialized = true; - } - } -} diff --git a/BDInfo/TSCodecVC1.cs b/BDInfo/TSCodecVC1.cs deleted file mode 100644 index e2fbbf692f..0000000000 --- a/BDInfo/TSCodecVC1.cs +++ /dev/null @@ -1,131 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - - -namespace BDInfo -{ - public abstract class TSCodecVC1 - { - public static void Scan( - TSVideoStream stream, - TSStreamBuffer buffer, - ref string tag) - { - int parse = 0; - byte frameHeaderParse = 0; - byte sequenceHeaderParse = 0; - bool isInterlaced = false; - - for (int i = 0; i < buffer.Length; i++) - { - parse = (parse << 8) + buffer.ReadByte(); - - if (parse == 0x0000010D) - { - frameHeaderParse = 4; - } - else if (frameHeaderParse > 0) - { - --frameHeaderParse; - if (frameHeaderParse == 0) - { - uint pictureType = 0; - if (isInterlaced) - { - if ((parse & 0x80000000) == 0) - { - pictureType = - (uint)((parse & 0x78000000) >> 13); - } - else - { - pictureType = - (uint)((parse & 0x3c000000) >> 12); - } - } - else - { - pictureType = - (uint)((parse & 0xf0000000) >> 14); - } - - if ((pictureType & 0x20000) == 0) - { - tag = "P"; - } - else if ((pictureType & 0x10000) == 0) - { - tag = "B"; - } - else if ((pictureType & 0x8000) == 0) - { - tag = "I"; - } - else if ((pictureType & 0x4000) == 0) - { - tag = "BI"; - } - else - { - tag = null; - } - if (stream.IsInitialized) return; - } - } - else if (parse == 0x0000010F) - { - sequenceHeaderParse = 6; - } - else if (sequenceHeaderParse > 0) - { - --sequenceHeaderParse; - switch (sequenceHeaderParse) - { - case 5: - int profileLevel = ((parse & 0x38) >> 3); - if (((parse & 0xC0) >> 6) == 3) - { - stream.EncodingProfile = string.Format( - "Advanced Profile {0}", profileLevel); - } - else - { - stream.EncodingProfile = string.Format( - "Main Profile {0}", profileLevel); - } - break; - - case 0: - if (((parse & 0x40) >> 6) > 0) - { - isInterlaced = true; - } - else - { - isInterlaced = false; - } - break; - } - stream.IsVBR = true; - stream.IsInitialized = true; - } - } - } - } -} diff --git a/BDInfo/TSInterleavedFile.cs b/BDInfo/TSInterleavedFile.cs deleted file mode 100644 index 0f35cfb2a1..0000000000 --- a/BDInfo/TSInterleavedFile.cs +++ /dev/null @@ -1,37 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -using MediaBrowser.Model.IO; - -// TODO: Do more interesting things here... - -namespace BDInfo -{ - public class TSInterleavedFile - { - public FileSystemMetadata FileInfo = null; - public string Name = null; - - public TSInterleavedFile(FileSystemMetadata fileInfo) - { - FileInfo = fileInfo; - Name = fileInfo.Name.ToUpper(); - } - } -} diff --git a/BDInfo/TSPlaylistFile.cs b/BDInfo/TSPlaylistFile.cs deleted file mode 100644 index 1cc629b1de..0000000000 --- a/BDInfo/TSPlaylistFile.cs +++ /dev/null @@ -1,1282 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -#undef DEBUG -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using MediaBrowser.Model.IO; - -namespace BDInfo -{ - public class TSPlaylistFile - { - private FileSystemMetadata FileInfo = null; - public string FileType = null; - public bool IsInitialized = false; - public string Name = null; - public BDROM BDROM = null; - public bool HasHiddenTracks = false; - public bool HasLoops = false; - public bool IsCustom = false; - - public List Chapters = new List(); - - public Dictionary Streams = - new Dictionary(); - public Dictionary PlaylistStreams = - new Dictionary(); - public List StreamClips = - new List(); - public List> AngleStreams = - new List>(); - public List> AngleClips = - new List>(); - public int AngleCount = 0; - - public List SortedStreams = - new List(); - public List VideoStreams = - new List(); - public List AudioStreams = - new List(); - public List TextStreams = - new List(); - public List GraphicsStreams = - new List(); - - public TSPlaylistFile(BDROM bdrom, - FileSystemMetadata fileInfo) - { - BDROM = bdrom; - FileInfo = fileInfo; - Name = fileInfo.Name.ToUpper(); - } - - public TSPlaylistFile(BDROM bdrom, - string name, - List clips) - { - BDROM = bdrom; - Name = name; - IsCustom = true; - foreach (var clip in clips) - { - var newClip = new TSStreamClip( - clip.StreamFile, clip.StreamClipFile); - - newClip.Name = clip.Name; - newClip.TimeIn = clip.TimeIn; - newClip.TimeOut = clip.TimeOut; - newClip.Length = newClip.TimeOut - newClip.TimeIn; - newClip.RelativeTimeIn = TotalLength; - newClip.RelativeTimeOut = newClip.RelativeTimeIn + newClip.Length; - newClip.AngleIndex = clip.AngleIndex; - newClip.Chapters.Add(clip.TimeIn); - StreamClips.Add(newClip); - - if (newClip.AngleIndex > AngleCount) - { - AngleCount = newClip.AngleIndex; - } - if (newClip.AngleIndex == 0) - { - Chapters.Add(newClip.RelativeTimeIn); - } - } - LoadStreamClips(); - IsInitialized = true; - } - - public override string ToString() - { - return Name; - } - - public ulong InterleavedFileSize - { - get - { - ulong size = 0; - foreach (var clip in StreamClips) - { - size += clip.InterleavedFileSize; - } - return size; - } - } - public ulong FileSize - { - get - { - ulong size = 0; - foreach (var clip in StreamClips) - { - size += clip.FileSize; - } - return size; - } - } - public double TotalLength - { - get - { - double length = 0; - foreach (var clip in StreamClips) - { - if (clip.AngleIndex == 0) - { - length += clip.Length; - } - } - return length; - } - } - - public double TotalAngleLength - { - get - { - double length = 0; - foreach (var clip in StreamClips) - { - length += clip.Length; - } - return length; - } - } - - public ulong TotalSize - { - get - { - ulong size = 0; - foreach (var clip in StreamClips) - { - if (clip.AngleIndex == 0) - { - size += clip.PacketSize; - } - } - return size; - } - } - - public ulong TotalAngleSize - { - get - { - ulong size = 0; - foreach (var clip in StreamClips) - { - size += clip.PacketSize; - } - return size; - } - } - - public ulong TotalBitRate - { - get - { - if (TotalLength > 0) - { - return (ulong)Math.Round(((TotalSize * 8.0) / TotalLength)); - } - return 0; - } - } - - public ulong TotalAngleBitRate - { - get - { - if (TotalAngleLength > 0) - { - return (ulong)Math.Round(((TotalAngleSize * 8.0) / TotalAngleLength)); - } - return 0; - } - } - - public void Scan( - Dictionary streamFiles, - Dictionary streamClipFiles) - { - Stream fileStream = null; - BinaryReader fileReader = null; - - try - { - Streams.Clear(); - StreamClips.Clear(); - - fileStream = File.OpenRead(FileInfo.FullName); - fileReader = new BinaryReader(fileStream); - - byte[] data = new byte[fileStream.Length]; - int dataLength = fileReader.Read(data, 0, data.Length); - - int pos = 0; - - FileType = ReadString(data, 8, ref pos); - if (FileType != "MPLS0100" && FileType != "MPLS0200") - { - throw new Exception(string.Format( - "Playlist {0} has an unknown file type {1}.", - FileInfo.Name, FileType)); - } - - int playlistOffset = ReadInt32(data, ref pos); - int chaptersOffset = ReadInt32(data, ref pos); - int extensionsOffset = ReadInt32(data, ref pos); - - pos = playlistOffset; - - int playlistLength = ReadInt32(data, ref pos); - int playlistReserved = ReadInt16(data, ref pos); - int itemCount = ReadInt16(data, ref pos); - int subitemCount = ReadInt16(data, ref pos); - - var chapterClips = new List(); - for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) - { - int itemStart = pos; - int itemLength = ReadInt16(data, ref pos); - string itemName = ReadString(data, 5, ref pos); - string itemType = ReadString(data, 4, ref pos); - - TSStreamFile streamFile = null; - string streamFileName = string.Format( - "{0}.M2TS", itemName); - if (streamFiles.ContainsKey(streamFileName)) - { - streamFile = streamFiles[streamFileName]; - } - if (streamFile == null) - { - // Error condition - } - - TSStreamClipFile streamClipFile = null; - string streamClipFileName = string.Format( - "{0}.CLPI", itemName); - if (streamClipFiles.ContainsKey(streamClipFileName)) - { - streamClipFile = streamClipFiles[streamClipFileName]; - } - if (streamClipFile == null) - { - throw new Exception(string.Format( - "Playlist {0} referenced missing file {1}.", - FileInfo.Name, streamFileName)); - } - - pos += 1; - int multiangle = (data[pos] >> 4) & 0x01; - int condition = data[pos] & 0x0F; - pos += 2; - - int inTime = ReadInt32(data, ref pos); - if (inTime < 0) inTime &= 0x7FFFFFFF; - double timeIn = (double)inTime / 45000; - - int outTime = ReadInt32(data, ref pos); - if (outTime < 0) outTime &= 0x7FFFFFFF; - double timeOut = (double)outTime / 45000; - - var streamClip = new TSStreamClip( - streamFile, streamClipFile); - - streamClip.Name = streamFileName; //TODO - streamClip.TimeIn = timeIn; - streamClip.TimeOut = timeOut; - streamClip.Length = streamClip.TimeOut - streamClip.TimeIn; - streamClip.RelativeTimeIn = TotalLength; - streamClip.RelativeTimeOut = streamClip.RelativeTimeIn + streamClip.Length; - StreamClips.Add(streamClip); - chapterClips.Add(streamClip); - - pos += 12; - if (multiangle > 0) - { - int angles = data[pos]; - pos += 2; - for (int angle = 0; angle < angles - 1; angle++) - { - string angleName = ReadString(data, 5, ref pos); - string angleType = ReadString(data, 4, ref pos); - pos += 1; - - TSStreamFile angleFile = null; - string angleFileName = string.Format( - "{0}.M2TS", angleName); - if (streamFiles.ContainsKey(angleFileName)) - { - angleFile = streamFiles[angleFileName]; - } - if (angleFile == null) - { - throw new Exception(string.Format( - "Playlist {0} referenced missing angle file {1}.", - FileInfo.Name, angleFileName)); - } - - TSStreamClipFile angleClipFile = null; - string angleClipFileName = string.Format( - "{0}.CLPI", angleName); - if (streamClipFiles.ContainsKey(angleClipFileName)) - { - angleClipFile = streamClipFiles[angleClipFileName]; - } - if (angleClipFile == null) - { - throw new Exception(string.Format( - "Playlist {0} referenced missing angle file {1}.", - FileInfo.Name, angleClipFileName)); - } - - var angleClip = - new TSStreamClip(angleFile, angleClipFile); - angleClip.AngleIndex = angle + 1; - angleClip.TimeIn = streamClip.TimeIn; - angleClip.TimeOut = streamClip.TimeOut; - angleClip.RelativeTimeIn = streamClip.RelativeTimeIn; - angleClip.RelativeTimeOut = streamClip.RelativeTimeOut; - angleClip.Length = streamClip.Length; - StreamClips.Add(angleClip); - } - if (angles - 1 > AngleCount) AngleCount = angles - 1; - } - - int streamInfoLength = ReadInt16(data, ref pos); - pos += 2; - int streamCountVideo = data[pos++]; - int streamCountAudio = data[pos++]; - int streamCountPG = data[pos++]; - int streamCountIG = data[pos++]; - int streamCountSecondaryAudio = data[pos++]; - int streamCountSecondaryVideo = data[pos++]; - int streamCountPIP = data[pos++]; - pos += 5; - -#if DEBUG - Debug.WriteLine(string.Format( - "{0} : {1} -> V:{2} A:{3} PG:{4} IG:{5} 2A:{6} 2V:{7} PIP:{8}", - Name, streamFileName, streamCountVideo, streamCountAudio, streamCountPG, streamCountIG, - streamCountSecondaryAudio, streamCountSecondaryVideo, streamCountPIP)); -#endif - - for (int i = 0; i < streamCountVideo; i++) - { - var stream = CreatePlaylistStream(data, ref pos); - if (stream != null) PlaylistStreams[stream.PID] = stream; - } - for (int i = 0; i < streamCountAudio; i++) - { - var stream = CreatePlaylistStream(data, ref pos); - if (stream != null) PlaylistStreams[stream.PID] = stream; - } - for (int i = 0; i < streamCountPG; i++) - { - var stream = CreatePlaylistStream(data, ref pos); - if (stream != null) PlaylistStreams[stream.PID] = stream; - } - for (int i = 0; i < streamCountIG; i++) - { - var stream = CreatePlaylistStream(data, ref pos); - if (stream != null) PlaylistStreams[stream.PID] = stream; - } - for (int i = 0; i < streamCountSecondaryAudio; i++) - { - var stream = CreatePlaylistStream(data, ref pos); - if (stream != null) PlaylistStreams[stream.PID] = stream; - pos += 2; - } - for (int i = 0; i < streamCountSecondaryVideo; i++) - { - var stream = CreatePlaylistStream(data, ref pos); - if (stream != null) PlaylistStreams[stream.PID] = stream; - pos += 6; - } - /* - * TODO - * - for (int i = 0; i < streamCountPIP; i++) - { - TSStream stream = CreatePlaylistStream(data, ref pos); - if (stream != null) PlaylistStreams[stream.PID] = stream; - } - */ - - pos += itemLength - (pos - itemStart) + 2; - } - - pos = chaptersOffset + 4; - - int chapterCount = ReadInt16(data, ref pos); - - for (int chapterIndex = 0; - chapterIndex < chapterCount; - chapterIndex++) - { - int chapterType = data[pos + 1]; - - if (chapterType == 1) - { - int streamFileIndex = - ((int)data[pos + 2] << 8) + data[pos + 3]; - - long chapterTime = - ((long)data[pos + 4] << 24) + - ((long)data[pos + 5] << 16) + - ((long)data[pos + 6] << 8) + - ((long)data[pos + 7]); - - var streamClip = chapterClips[streamFileIndex]; - - double chapterSeconds = (double)chapterTime / 45000; - - double relativeSeconds = - chapterSeconds - - streamClip.TimeIn + - streamClip.RelativeTimeIn; - - // TODO: Ignore short last chapter? - if (TotalLength - relativeSeconds > 1.0) - { - streamClip.Chapters.Add(chapterSeconds); - this.Chapters.Add(relativeSeconds); - } - } - else - { - // TODO: Handle other chapter types? - } - pos += 14; - } - } - finally - { - if (fileReader != null) - { - fileReader.Dispose(); - } - if (fileStream != null) - { - fileStream.Dispose(); - } - } - } - - public void Initialize() - { - LoadStreamClips(); - - var clipTimes = new Dictionary>(); - foreach (var clip in StreamClips) - { - if (clip.AngleIndex == 0) - { - if (clipTimes.ContainsKey(clip.Name)) - { - if (clipTimes[clip.Name].Contains(clip.TimeIn)) - { - HasLoops = true; - break; - } - else - { - clipTimes[clip.Name].Add(clip.TimeIn); - } - } - else - { - clipTimes[clip.Name] = new List { clip.TimeIn }; - } - } - } - ClearBitrates(); - IsInitialized = true; - } - - protected TSStream CreatePlaylistStream(byte[] data, ref int pos) - { - TSStream stream = null; - - int start = pos; - - int headerLength = data[pos++]; - int headerPos = pos; - int headerType = data[pos++]; - - int pid = 0; - int subpathid = 0; - int subclipid = 0; - - switch (headerType) - { - case 1: - pid = ReadInt16(data, ref pos); - break; - case 2: - subpathid = data[pos++]; - subclipid = data[pos++]; - pid = ReadInt16(data, ref pos); - break; - case 3: - subpathid = data[pos++]; - pid = ReadInt16(data, ref pos); - break; - case 4: - subpathid = data[pos++]; - subclipid = data[pos++]; - pid = ReadInt16(data, ref pos); - break; - default: - break; - } - - pos = headerPos + headerLength; - - int streamLength = data[pos++]; - int streamPos = pos; - - var streamType = (TSStreamType)data[pos++]; - switch (streamType) - { - case TSStreamType.MVC_VIDEO: - // TODO - break; - - case TSStreamType.AVC_VIDEO: - case TSStreamType.MPEG1_VIDEO: - case TSStreamType.MPEG2_VIDEO: - case TSStreamType.VC1_VIDEO: - - var videoFormat = (TSVideoFormat) - (data[pos] >> 4); - var frameRate = (TSFrameRate) - (data[pos] & 0xF); - var aspectRatio = (TSAspectRatio) - (data[pos + 1] >> 4); - - stream = new TSVideoStream(); - ((TSVideoStream)stream).VideoFormat = videoFormat; - ((TSVideoStream)stream).AspectRatio = aspectRatio; - ((TSVideoStream)stream).FrameRate = frameRate; - -#if DEBUG - Debug.WriteLine(string.Format( - "\t{0} {1} {2} {3} {4}", - pid, - streamType, - videoFormat, - frameRate, - aspectRatio)); -#endif - - break; - - case TSStreamType.AC3_AUDIO: - case TSStreamType.AC3_PLUS_AUDIO: - case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: - case TSStreamType.AC3_TRUE_HD_AUDIO: - case TSStreamType.DTS_AUDIO: - case TSStreamType.DTS_HD_AUDIO: - case TSStreamType.DTS_HD_MASTER_AUDIO: - case TSStreamType.DTS_HD_SECONDARY_AUDIO: - case TSStreamType.LPCM_AUDIO: - case TSStreamType.MPEG1_AUDIO: - case TSStreamType.MPEG2_AUDIO: - - int audioFormat = ReadByte(data, ref pos); - - var channelLayout = (TSChannelLayout) - (audioFormat >> 4); - var sampleRate = (TSSampleRate) - (audioFormat & 0xF); - - string audioLanguage = ReadString(data, 3, ref pos); - - stream = new TSAudioStream(); - ((TSAudioStream)stream).ChannelLayout = channelLayout; - ((TSAudioStream)stream).SampleRate = TSAudioStream.ConvertSampleRate(sampleRate); - ((TSAudioStream)stream).LanguageCode = audioLanguage; - -#if DEBUG - Debug.WriteLine(string.Format( - "\t{0} {1} {2} {3} {4}", - pid, - streamType, - audioLanguage, - channelLayout, - sampleRate)); -#endif - - break; - - case TSStreamType.INTERACTIVE_GRAPHICS: - case TSStreamType.PRESENTATION_GRAPHICS: - - string graphicsLanguage = ReadString(data, 3, ref pos); - - stream = new TSGraphicsStream(); - ((TSGraphicsStream)stream).LanguageCode = graphicsLanguage; - - if (data[pos] != 0) - { - } - -#if DEBUG - Debug.WriteLine(string.Format( - "\t{0} {1} {2}", - pid, - streamType, - graphicsLanguage)); -#endif - - break; - - case TSStreamType.SUBTITLE: - - int code = ReadByte(data, ref pos); // TODO - string textLanguage = ReadString(data, 3, ref pos); - - stream = new TSTextStream(); - ((TSTextStream)stream).LanguageCode = textLanguage; - -#if DEBUG - Debug.WriteLine(string.Format( - "\t{0} {1} {2}", - pid, - streamType, - textLanguage)); -#endif - - break; - - default: - break; - } - - pos = streamPos + streamLength; - - if (stream != null) - { - stream.PID = (ushort)pid; - stream.StreamType = streamType; - } - - return stream; - } - - private void LoadStreamClips() - { - AngleClips.Clear(); - if (AngleCount > 0) - { - for (int angleIndex = 0; angleIndex < AngleCount; angleIndex++) - { - AngleClips.Add(new Dictionary()); - } - } - - TSStreamClip referenceClip = null; - if (StreamClips.Count > 0) - { - referenceClip = StreamClips[0]; - } - foreach (var clip in StreamClips) - { - if (clip.StreamClipFile.Streams.Count > referenceClip.StreamClipFile.Streams.Count) - { - referenceClip = clip; - } - else if (clip.Length > referenceClip.Length) - { - referenceClip = clip; - } - if (AngleCount > 0) - { - if (clip.AngleIndex == 0) - { - for (int angleIndex = 0; angleIndex < AngleCount; angleIndex++) - { - AngleClips[angleIndex][clip.RelativeTimeIn] = clip; - } - } - else - { - AngleClips[clip.AngleIndex - 1][clip.RelativeTimeIn] = clip; - } - } - } - - foreach (var clipStream - in referenceClip.StreamClipFile.Streams.Values) - { - if (!Streams.ContainsKey(clipStream.PID)) - { - var stream = clipStream.Clone(); - Streams[clipStream.PID] = stream; - - if (!IsCustom && !PlaylistStreams.ContainsKey(stream.PID)) - { - stream.IsHidden = true; - HasHiddenTracks = true; - } - - if (stream.IsVideoStream) - { - VideoStreams.Add((TSVideoStream)stream); - } - else if (stream.IsAudioStream) - { - AudioStreams.Add((TSAudioStream)stream); - } - else if (stream.IsGraphicsStream) - { - GraphicsStreams.Add((TSGraphicsStream)stream); - } - else if (stream.IsTextStream) - { - TextStreams.Add((TSTextStream)stream); - } - } - } - - if (referenceClip.StreamFile != null) - { - // TODO: Better way to add this in? - if (BDInfoSettings.EnableSSIF && - referenceClip.StreamFile.InterleavedFile != null && - referenceClip.StreamFile.Streams.ContainsKey(4114) && - !Streams.ContainsKey(4114)) - { - var stream = referenceClip.StreamFile.Streams[4114].Clone(); - Streams[4114] = stream; - if (stream.IsVideoStream) - { - VideoStreams.Add((TSVideoStream)stream); - } - } - - foreach (var clipStream - in referenceClip.StreamFile.Streams.Values) - { - if (Streams.ContainsKey(clipStream.PID)) - { - var stream = Streams[clipStream.PID]; - - if (stream.StreamType != clipStream.StreamType) continue; - - if (clipStream.BitRate > stream.BitRate) - { - stream.BitRate = clipStream.BitRate; - } - stream.IsVBR = clipStream.IsVBR; - - if (stream.IsVideoStream && - clipStream.IsVideoStream) - { - ((TSVideoStream)stream).EncodingProfile = - ((TSVideoStream)clipStream).EncodingProfile; - } - else if (stream.IsAudioStream && - clipStream.IsAudioStream) - { - var audioStream = (TSAudioStream)stream; - var clipAudioStream = (TSAudioStream)clipStream; - - if (clipAudioStream.ChannelCount > audioStream.ChannelCount) - { - audioStream.ChannelCount = clipAudioStream.ChannelCount; - } - if (clipAudioStream.LFE > audioStream.LFE) - { - audioStream.LFE = clipAudioStream.LFE; - } - if (clipAudioStream.SampleRate > audioStream.SampleRate) - { - audioStream.SampleRate = clipAudioStream.SampleRate; - } - if (clipAudioStream.BitDepth > audioStream.BitDepth) - { - audioStream.BitDepth = clipAudioStream.BitDepth; - } - if (clipAudioStream.DialNorm < audioStream.DialNorm) - { - audioStream.DialNorm = clipAudioStream.DialNorm; - } - if (clipAudioStream.AudioMode != TSAudioMode.Unknown) - { - audioStream.AudioMode = clipAudioStream.AudioMode; - } - if (clipAudioStream.CoreStream != null && - audioStream.CoreStream == null) - { - audioStream.CoreStream = (TSAudioStream) - clipAudioStream.CoreStream.Clone(); - } - } - } - } - } - - for (int i = 0; i < AngleCount; i++) - { - AngleStreams.Add(new Dictionary()); - } - - if (!BDInfoSettings.KeepStreamOrder) - { - VideoStreams.Sort(CompareVideoStreams); - } - foreach (TSStream stream in VideoStreams) - { - SortedStreams.Add(stream); - for (int i = 0; i < AngleCount; i++) - { - var angleStream = stream.Clone(); - angleStream.AngleIndex = i + 1; - AngleStreams[i][angleStream.PID] = angleStream; - SortedStreams.Add(angleStream); - } - } - - if (!BDInfoSettings.KeepStreamOrder) - { - AudioStreams.Sort(CompareAudioStreams); - } - foreach (TSStream stream in AudioStreams) - { - SortedStreams.Add(stream); - } - - if (!BDInfoSettings.KeepStreamOrder) - { - GraphicsStreams.Sort(CompareGraphicsStreams); - } - foreach (TSStream stream in GraphicsStreams) - { - SortedStreams.Add(stream); - } - - if (!BDInfoSettings.KeepStreamOrder) - { - TextStreams.Sort(CompareTextStreams); - } - foreach (TSStream stream in TextStreams) - { - SortedStreams.Add(stream); - } - } - - public void ClearBitrates() - { - foreach (var clip in StreamClips) - { - clip.PayloadBytes = 0; - clip.PacketCount = 0; - clip.PacketSeconds = 0; - - if (clip.StreamFile != null) - { - foreach (var stream in clip.StreamFile.Streams.Values) - { - stream.PayloadBytes = 0; - stream.PacketCount = 0; - stream.PacketSeconds = 0; - } - - if (clip.StreamFile != null && - clip.StreamFile.StreamDiagnostics != null) - { - clip.StreamFile.StreamDiagnostics.Clear(); - } - } - } - - foreach (var stream in SortedStreams) - { - stream.PayloadBytes = 0; - stream.PacketCount = 0; - stream.PacketSeconds = 0; - } - } - - public bool IsValid - { - get - { - if (!IsInitialized) return false; - - if (BDInfoSettings.FilterShortPlaylists && - TotalLength < BDInfoSettings.FilterShortPlaylistsValue) - { - return false; - } - - if (HasLoops && - BDInfoSettings.FilterLoopingPlaylists) - { - return false; - } - - return true; - } - } - - public int CompareVideoStreams( - TSVideoStream x, - TSVideoStream y) - { - if (x == null && y == null) - { - return 0; - } - else if (x == null && y != null) - { - return 1; - } - else if (x != null && y == null) - { - return -1; - } - else - { - if (x.Height > y.Height) - { - return -1; - } - else if (y.Height > x.Height) - { - return 1; - } - else if (x.PID > y.PID) - { - return 1; - } - else if (y.PID > x.PID) - { - return -1; - } - else - { - return 0; - } - } - } - - public int CompareAudioStreams( - TSAudioStream x, - TSAudioStream y) - { - if (x == y) - { - return 0; - } - else if (x == null && y == null) - { - return 0; - } - else if (x == null && y != null) - { - return -1; - } - else if (x != null && y == null) - { - return 1; - } - else - { - if (x.ChannelCount > y.ChannelCount) - { - return -1; - } - else if (y.ChannelCount > x.ChannelCount) - { - return 1; - } - else - { - int sortX = GetStreamTypeSortIndex(x.StreamType); - int sortY = GetStreamTypeSortIndex(y.StreamType); - - if (sortX > sortY) - { - return -1; - } - else if (sortY > sortX) - { - return 1; - } - else - { - if (x.LanguageCode == "eng") - { - return -1; - } - else if (y.LanguageCode == "eng") - { - return 1; - } - else if (x.LanguageCode != y.LanguageCode) - { - return string.Compare( - x.LanguageName, y.LanguageName); - } - else if (x.PID < y.PID) - { - return -1; - } - else if (y.PID < x.PID) - { - return 1; - } - return 0; - } - } - } - } - - public int CompareTextStreams( - TSTextStream x, - TSTextStream y) - { - if (x == y) - { - return 0; - } - else if (x == null && y == null) - { - return 0; - } - else if (x == null && y != null) - { - return -1; - } - else if (x != null && y == null) - { - return 1; - } - else - { - if (x.LanguageCode == "eng") - { - return -1; - } - else if (y.LanguageCode == "eng") - { - return 1; - } - else - { - if (x.LanguageCode == y.LanguageCode) - { - if (x.PID > y.PID) - { - return 1; - } - else if (y.PID > x.PID) - { - return -1; - } - else - { - return 0; - } - } - else - { - return string.Compare( - x.LanguageName, y.LanguageName); - } - } - } - } - - private int CompareGraphicsStreams( - TSGraphicsStream x, - TSGraphicsStream y) - { - if (x == y) - { - return 0; - } - else if (x == null && y == null) - { - return 0; - } - else if (x == null && y != null) - { - return -1; - } - else if (x != null && y == null) - { - return 1; - } - else - { - int sortX = GetStreamTypeSortIndex(x.StreamType); - int sortY = GetStreamTypeSortIndex(y.StreamType); - - if (sortX > sortY) - { - return -1; - } - else if (sortY > sortX) - { - return 1; - } - else if (x.LanguageCode == "eng") - { - return -1; - } - else if (y.LanguageCode == "eng") - { - return 1; - } - else - { - if (x.LanguageCode == y.LanguageCode) - { - if (x.PID > y.PID) - { - return 1; - } - else if (y.PID > x.PID) - { - return -1; - } - else - { - return 0; - } - } - else - { - return string.Compare(x.LanguageName, y.LanguageName); - } - } - } - } - - private int GetStreamTypeSortIndex(TSStreamType streamType) - { - switch (streamType) - { - case TSStreamType.Unknown: - return 0; - case TSStreamType.MPEG1_VIDEO: - return 1; - case TSStreamType.MPEG2_VIDEO: - return 2; - case TSStreamType.AVC_VIDEO: - return 3; - case TSStreamType.VC1_VIDEO: - return 4; - case TSStreamType.MVC_VIDEO: - return 5; - - case TSStreamType.MPEG1_AUDIO: - return 1; - case TSStreamType.MPEG2_AUDIO: - return 2; - case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: - return 3; - case TSStreamType.DTS_HD_SECONDARY_AUDIO: - return 4; - case TSStreamType.AC3_AUDIO: - return 5; - case TSStreamType.DTS_AUDIO: - return 6; - case TSStreamType.AC3_PLUS_AUDIO: - return 7; - case TSStreamType.DTS_HD_AUDIO: - return 8; - case TSStreamType.AC3_TRUE_HD_AUDIO: - return 9; - case TSStreamType.DTS_HD_MASTER_AUDIO: - return 10; - case TSStreamType.LPCM_AUDIO: - return 11; - - case TSStreamType.SUBTITLE: - return 1; - case TSStreamType.INTERACTIVE_GRAPHICS: - return 2; - case TSStreamType.PRESENTATION_GRAPHICS: - return 3; - - default: - return 0; - } - } - - protected string ReadString( - byte[] data, - int count, - ref int pos) - { - string val = Encoding.ASCII.GetString(data, pos, count); - - pos += count; - - return val; - } - - protected int ReadInt32( - byte[] data, - ref int pos) - { - int val = - ((int)data[pos] << 24) + - ((int)data[pos + 1] << 16) + - ((int)data[pos + 2] << 8) + - ((int)data[pos + 3]); - - pos += 4; - - return val; - } - - protected int ReadInt16( - byte[] data, - ref int pos) - { - int val = - ((int)data[pos] << 8) + - ((int)data[pos + 1]); - - pos += 2; - - return val; - } - - protected byte ReadByte( - byte[] data, - ref int pos) - { - return data[pos++]; - } - } -} diff --git a/BDInfo/TSStream.cs b/BDInfo/TSStream.cs deleted file mode 100644 index 3c30a85971..0000000000 --- a/BDInfo/TSStream.cs +++ /dev/null @@ -1,780 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -using System; -using System.Collections.Generic; - -namespace BDInfo -{ - public enum TSStreamType : byte - { - Unknown = 0, - MPEG1_VIDEO = 0x01, - MPEG2_VIDEO = 0x02, - AVC_VIDEO = 0x1b, - MVC_VIDEO = 0x20, - VC1_VIDEO = 0xea, - MPEG1_AUDIO = 0x03, - MPEG2_AUDIO = 0x04, - LPCM_AUDIO = 0x80, - AC3_AUDIO = 0x81, - AC3_PLUS_AUDIO = 0x84, - AC3_PLUS_SECONDARY_AUDIO = 0xA1, - AC3_TRUE_HD_AUDIO = 0x83, - DTS_AUDIO = 0x82, - DTS_HD_AUDIO = 0x85, - DTS_HD_SECONDARY_AUDIO = 0xA2, - DTS_HD_MASTER_AUDIO = 0x86, - PRESENTATION_GRAPHICS = 0x90, - INTERACTIVE_GRAPHICS = 0x91, - SUBTITLE = 0x92 - } - - public enum TSVideoFormat : byte - { - Unknown = 0, - VIDEOFORMAT_480i = 1, - VIDEOFORMAT_576i = 2, - VIDEOFORMAT_480p = 3, - VIDEOFORMAT_1080i = 4, - VIDEOFORMAT_720p = 5, - VIDEOFORMAT_1080p = 6, - VIDEOFORMAT_576p = 7, - } - - public enum TSFrameRate : byte - { - Unknown = 0, - FRAMERATE_23_976 = 1, - FRAMERATE_24 = 2, - FRAMERATE_25 = 3, - FRAMERATE_29_97 = 4, - FRAMERATE_50 = 6, - FRAMERATE_59_94 = 7 - } - - public enum TSChannelLayout : byte - { - Unknown = 0, - CHANNELLAYOUT_MONO = 1, - CHANNELLAYOUT_STEREO = 3, - CHANNELLAYOUT_MULTI = 6, - CHANNELLAYOUT_COMBO = 12 - } - - public enum TSSampleRate : byte - { - Unknown = 0, - SAMPLERATE_48 = 1, - SAMPLERATE_96 = 4, - SAMPLERATE_192 = 5, - SAMPLERATE_48_192 = 12, - SAMPLERATE_48_96 = 14 - } - - public enum TSAspectRatio - { - Unknown = 0, - ASPECT_4_3 = 2, - ASPECT_16_9 = 3, - ASPECT_2_21 = 4 - } - - public class TSDescriptor - { - public byte Name; - public byte[] Value; - - public TSDescriptor(byte name, byte length) - { - Name = name; - Value = new byte[length]; - } - - public TSDescriptor Clone() - { - var descriptor = - new TSDescriptor(Name, (byte)Value.Length); - Value.CopyTo(descriptor.Value, 0); - return descriptor; - } - } - - public abstract class TSStream - { - public TSStream() - { - } - - public override string ToString() - { - return string.Format("{0} ({1})", CodecShortName, PID); - } - - public ushort PID; - public TSStreamType StreamType; - public List Descriptors = null; - public long BitRate = 0; - public long ActiveBitRate = 0; - public bool IsVBR = false; - public bool IsInitialized = false; - public string LanguageName; - public bool IsHidden = false; - - public ulong PayloadBytes = 0; - public ulong PacketCount = 0; - public double PacketSeconds = 0; - public int AngleIndex = 0; - - public ulong PacketSize => PacketCount * 192; - - private string _LanguageCode; - public string LanguageCode - { - get => _LanguageCode; - set - { - _LanguageCode = value; - LanguageName = LanguageCodes.GetName(value); - } - } - - public bool IsVideoStream - { - get - { - switch (StreamType) - { - case TSStreamType.MPEG1_VIDEO: - case TSStreamType.MPEG2_VIDEO: - case TSStreamType.AVC_VIDEO: - case TSStreamType.MVC_VIDEO: - case TSStreamType.VC1_VIDEO: - return true; - - default: - return false; - } - } - } - - public bool IsAudioStream - { - get - { - switch (StreamType) - { - case TSStreamType.MPEG1_AUDIO: - case TSStreamType.MPEG2_AUDIO: - case TSStreamType.LPCM_AUDIO: - case TSStreamType.AC3_AUDIO: - case TSStreamType.AC3_PLUS_AUDIO: - case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: - case TSStreamType.AC3_TRUE_HD_AUDIO: - case TSStreamType.DTS_AUDIO: - case TSStreamType.DTS_HD_AUDIO: - case TSStreamType.DTS_HD_SECONDARY_AUDIO: - case TSStreamType.DTS_HD_MASTER_AUDIO: - return true; - - default: - return false; - } - } - } - - public bool IsGraphicsStream - { - get - { - switch (StreamType) - { - case TSStreamType.PRESENTATION_GRAPHICS: - case TSStreamType.INTERACTIVE_GRAPHICS: - return true; - - default: - return false; - } - } - } - - public bool IsTextStream - { - get - { - switch (StreamType) - { - case TSStreamType.SUBTITLE: - return true; - - default: - return false; - } - } - } - - public string CodecName - { - get - { - switch (StreamType) - { - case TSStreamType.MPEG1_VIDEO: - return "MPEG-1 Video"; - case TSStreamType.MPEG2_VIDEO: - return "MPEG-2 Video"; - case TSStreamType.AVC_VIDEO: - return "MPEG-4 AVC Video"; - case TSStreamType.MVC_VIDEO: - return "MPEG-4 MVC Video"; - case TSStreamType.VC1_VIDEO: - return "VC-1 Video"; - case TSStreamType.MPEG1_AUDIO: - return "MP1 Audio"; - case TSStreamType.MPEG2_AUDIO: - return "MP2 Audio"; - case TSStreamType.LPCM_AUDIO: - return "LPCM Audio"; - case TSStreamType.AC3_AUDIO: - if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended) - return "Dolby Digital EX Audio"; - else - return "Dolby Digital Audio"; - case TSStreamType.AC3_PLUS_AUDIO: - case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: - return "Dolby Digital Plus Audio"; - case TSStreamType.AC3_TRUE_HD_AUDIO: - return "Dolby TrueHD Audio"; - case TSStreamType.DTS_AUDIO: - if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended) - return "DTS-ES Audio"; - else - return "DTS Audio"; - case TSStreamType.DTS_HD_AUDIO: - return "DTS-HD High-Res Audio"; - case TSStreamType.DTS_HD_SECONDARY_AUDIO: - return "DTS Express"; - case TSStreamType.DTS_HD_MASTER_AUDIO: - return "DTS-HD Master Audio"; - case TSStreamType.PRESENTATION_GRAPHICS: - return "Presentation Graphics"; - case TSStreamType.INTERACTIVE_GRAPHICS: - return "Interactive Graphics"; - case TSStreamType.SUBTITLE: - return "Subtitle"; - default: - return "UNKNOWN"; - } - } - } - - public string CodecAltName - { - get - { - switch (StreamType) - { - case TSStreamType.MPEG1_VIDEO: - return "MPEG-1"; - case TSStreamType.MPEG2_VIDEO: - return "MPEG-2"; - case TSStreamType.AVC_VIDEO: - return "AVC"; - case TSStreamType.MVC_VIDEO: - return "MVC"; - case TSStreamType.VC1_VIDEO: - return "VC-1"; - case TSStreamType.MPEG1_AUDIO: - return "MP1"; - case TSStreamType.MPEG2_AUDIO: - return "MP2"; - case TSStreamType.LPCM_AUDIO: - return "LPCM"; - case TSStreamType.AC3_AUDIO: - return "DD AC3"; - case TSStreamType.AC3_PLUS_AUDIO: - case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: - return "DD AC3+"; - case TSStreamType.AC3_TRUE_HD_AUDIO: - return "Dolby TrueHD"; - case TSStreamType.DTS_AUDIO: - return "DTS"; - case TSStreamType.DTS_HD_AUDIO: - return "DTS-HD Hi-Res"; - case TSStreamType.DTS_HD_SECONDARY_AUDIO: - return "DTS Express"; - case TSStreamType.DTS_HD_MASTER_AUDIO: - return "DTS-HD Master"; - case TSStreamType.PRESENTATION_GRAPHICS: - return "PGS"; - case TSStreamType.INTERACTIVE_GRAPHICS: - return "IGS"; - case TSStreamType.SUBTITLE: - return "SUB"; - default: - return "UNKNOWN"; - } - } - } - - public string CodecShortName - { - get - { - switch (StreamType) - { - case TSStreamType.MPEG1_VIDEO: - return "MPEG-1"; - case TSStreamType.MPEG2_VIDEO: - return "MPEG-2"; - case TSStreamType.AVC_VIDEO: - return "AVC"; - case TSStreamType.MVC_VIDEO: - return "MVC"; - case TSStreamType.VC1_VIDEO: - return "VC-1"; - case TSStreamType.MPEG1_AUDIO: - return "MP1"; - case TSStreamType.MPEG2_AUDIO: - return "MP2"; - case TSStreamType.LPCM_AUDIO: - return "LPCM"; - case TSStreamType.AC3_AUDIO: - if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended) - return "AC3-EX"; - else - return "AC3"; - case TSStreamType.AC3_PLUS_AUDIO: - case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: - return "AC3+"; - case TSStreamType.AC3_TRUE_HD_AUDIO: - return "TrueHD"; - case TSStreamType.DTS_AUDIO: - if (((TSAudioStream)this).AudioMode == TSAudioMode.Extended) - return "DTS-ES"; - else - return "DTS"; - case TSStreamType.DTS_HD_AUDIO: - return "DTS-HD HR"; - case TSStreamType.DTS_HD_SECONDARY_AUDIO: - return "DTS Express"; - case TSStreamType.DTS_HD_MASTER_AUDIO: - return "DTS-HD MA"; - case TSStreamType.PRESENTATION_GRAPHICS: - return "PGS"; - case TSStreamType.INTERACTIVE_GRAPHICS: - return "IGS"; - case TSStreamType.SUBTITLE: - return "SUB"; - default: - return "UNKNOWN"; - } - } - } - - public virtual string Description => ""; - - public abstract TSStream Clone(); - - protected void CopyTo(TSStream stream) - { - stream.PID = PID; - stream.StreamType = StreamType; - stream.IsVBR = IsVBR; - stream.BitRate = BitRate; - stream.IsInitialized = IsInitialized; - stream.LanguageCode = _LanguageCode; - if (Descriptors != null) - { - stream.Descriptors = new List(); - foreach (var descriptor in Descriptors) - { - stream.Descriptors.Add(descriptor.Clone()); - } - } - } - } - - public class TSVideoStream : TSStream - { - public TSVideoStream() - { - } - - public int Width; - public int Height; - public bool IsInterlaced; - public int FrameRateEnumerator; - public int FrameRateDenominator; - public TSAspectRatio AspectRatio; - public string EncodingProfile; - - private TSVideoFormat _VideoFormat; - public TSVideoFormat VideoFormat - { - get => _VideoFormat; - set - { - _VideoFormat = value; - switch (value) - { - case TSVideoFormat.VIDEOFORMAT_480i: - Height = 480; - IsInterlaced = true; - break; - case TSVideoFormat.VIDEOFORMAT_480p: - Height = 480; - IsInterlaced = false; - break; - case TSVideoFormat.VIDEOFORMAT_576i: - Height = 576; - IsInterlaced = true; - break; - case TSVideoFormat.VIDEOFORMAT_576p: - Height = 576; - IsInterlaced = false; - break; - case TSVideoFormat.VIDEOFORMAT_720p: - Height = 720; - IsInterlaced = false; - break; - case TSVideoFormat.VIDEOFORMAT_1080i: - Height = 1080; - IsInterlaced = true; - break; - case TSVideoFormat.VIDEOFORMAT_1080p: - Height = 1080; - IsInterlaced = false; - break; - } - } - } - - private TSFrameRate _FrameRate; - public TSFrameRate FrameRate - { - get => _FrameRate; - set - { - _FrameRate = value; - switch (value) - { - case TSFrameRate.FRAMERATE_23_976: - FrameRateEnumerator = 24000; - FrameRateDenominator = 1001; - break; - case TSFrameRate.FRAMERATE_24: - FrameRateEnumerator = 24000; - FrameRateDenominator = 1000; - break; - case TSFrameRate.FRAMERATE_25: - FrameRateEnumerator = 25000; - FrameRateDenominator = 1000; - break; - case TSFrameRate.FRAMERATE_29_97: - FrameRateEnumerator = 30000; - FrameRateDenominator = 1001; - break; - case TSFrameRate.FRAMERATE_50: - FrameRateEnumerator = 50000; - FrameRateDenominator = 1000; - break; - case TSFrameRate.FRAMERATE_59_94: - FrameRateEnumerator = 60000; - FrameRateDenominator = 1001; - break; - } - } - } - - public override string Description - { - get - { - string description = ""; - - if (Height > 0) - { - description += string.Format("{0:D}{1} / ", - Height, - IsInterlaced ? "i" : "p"); - } - if (FrameRateEnumerator > 0 && - FrameRateDenominator > 0) - { - if (FrameRateEnumerator % FrameRateDenominator == 0) - { - description += string.Format("{0:D} fps / ", - FrameRateEnumerator / FrameRateDenominator); - } - else - { - description += string.Format("{0:F3} fps / ", - (double)FrameRateEnumerator / FrameRateDenominator); - } - - } - if (AspectRatio == TSAspectRatio.ASPECT_4_3) - { - description += "4:3 / "; - } - else if (AspectRatio == TSAspectRatio.ASPECT_16_9) - { - description += "16:9 / "; - } - if (EncodingProfile != null) - { - description += EncodingProfile + " / "; - } - if (description.EndsWith(" / ")) - { - description = description.Substring(0, description.Length - 3); - } - return description; - } - } - - public override TSStream Clone() - { - var stream = new TSVideoStream(); - CopyTo(stream); - - stream.VideoFormat = _VideoFormat; - stream.FrameRate = _FrameRate; - stream.Width = Width; - stream.Height = Height; - stream.IsInterlaced = IsInterlaced; - stream.FrameRateEnumerator = FrameRateEnumerator; - stream.FrameRateDenominator = FrameRateDenominator; - stream.AspectRatio = AspectRatio; - stream.EncodingProfile = EncodingProfile; - - return stream; - } - } - - public enum TSAudioMode - { - Unknown, - DualMono, - Stereo, - Surround, - Extended - } - - public class TSAudioStream : TSStream - { - public TSAudioStream() - { - } - - public int SampleRate; - public int ChannelCount; - public int BitDepth; - public int LFE; - public int DialNorm; - public TSAudioMode AudioMode; - public TSAudioStream CoreStream; - public TSChannelLayout ChannelLayout; - - public static int ConvertSampleRate( - TSSampleRate sampleRate) - { - switch (sampleRate) - { - case TSSampleRate.SAMPLERATE_48: - return 48000; - - case TSSampleRate.SAMPLERATE_96: - case TSSampleRate.SAMPLERATE_48_96: - return 96000; - - case TSSampleRate.SAMPLERATE_192: - case TSSampleRate.SAMPLERATE_48_192: - return 192000; - } - return 0; - } - - public string ChannelDescription - { - get - { - if (ChannelLayout == TSChannelLayout.CHANNELLAYOUT_MONO && - ChannelCount == 2) - { - } - - string description = ""; - if (ChannelCount > 0) - { - description += string.Format( - "{0:D}.{1:D}", - ChannelCount, LFE); - } - else - { - switch (ChannelLayout) - { - case TSChannelLayout.CHANNELLAYOUT_MONO: - description += "1.0"; - break; - case TSChannelLayout.CHANNELLAYOUT_STEREO: - description += "2.0"; - break; - case TSChannelLayout.CHANNELLAYOUT_MULTI: - description += "5.1"; - break; - } - } - if (AudioMode == TSAudioMode.Extended) - { - if (StreamType == TSStreamType.AC3_AUDIO) - { - description += "-EX"; - } - if (StreamType == TSStreamType.DTS_AUDIO || - StreamType == TSStreamType.DTS_HD_AUDIO || - StreamType == TSStreamType.DTS_HD_MASTER_AUDIO) - { - description += "-ES"; - } - } - return description; - } - } - - public override string Description - { - get - { - string description = ChannelDescription; - - if (SampleRate > 0) - { - description += string.Format( - " / {0:D} kHz", SampleRate / 1000); - } - if (BitRate > 0) - { - description += string.Format( - " / {0:D} kbps", (uint)Math.Round((double)BitRate / 1000)); - } - if (BitDepth > 0) - { - description += string.Format( - " / {0:D}-bit", BitDepth); - } - if (DialNorm != 0) - { - description += string.Format( - " / DN {0}dB", DialNorm); - } - if (ChannelCount == 2) - { - switch (AudioMode) - { - case TSAudioMode.DualMono: - description += " / Dual Mono"; - break; - - case TSAudioMode.Surround: - description += " / Dolby Surround"; - break; - } - } - if (description.EndsWith(" / ")) - { - description = description.Substring(0, description.Length - 3); - } - if (CoreStream != null) - { - string codec = ""; - switch (CoreStream.StreamType) - { - case TSStreamType.AC3_AUDIO: - codec = "AC3 Embedded"; - break; - case TSStreamType.DTS_AUDIO: - codec = "DTS Core"; - break; - } - description += string.Format( - " ({0}: {1})", - codec, - CoreStream.Description); - } - return description; - } - } - - public override TSStream Clone() - { - var stream = new TSAudioStream(); - CopyTo(stream); - - stream.SampleRate = SampleRate; - stream.ChannelLayout = ChannelLayout; - stream.ChannelCount = ChannelCount; - stream.BitDepth = BitDepth; - stream.LFE = LFE; - stream.DialNorm = DialNorm; - stream.AudioMode = AudioMode; - if (CoreStream != null) - { - stream.CoreStream = (TSAudioStream)CoreStream.Clone(); - } - - return stream; - } - } - - public class TSGraphicsStream : TSStream - { - public TSGraphicsStream() - { - IsVBR = true; - IsInitialized = true; - } - - public override TSStream Clone() - { - var stream = new TSGraphicsStream(); - CopyTo(stream); - return stream; - } - } - - public class TSTextStream : TSStream - { - public TSTextStream() - { - IsVBR = true; - IsInitialized = true; - } - - public override TSStream Clone() - { - var stream = new TSTextStream(); - CopyTo(stream); - return stream; - } - } -} diff --git a/BDInfo/TSStreamBuffer.cs b/BDInfo/TSStreamBuffer.cs deleted file mode 100644 index 30bd1a3f44..0000000000 --- a/BDInfo/TSStreamBuffer.cs +++ /dev/null @@ -1,130 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -using System; -using System.Collections.Specialized; -using System.IO; - -namespace BDInfo -{ - public class TSStreamBuffer - { - private MemoryStream Stream = new MemoryStream(); - private int SkipBits = 0; - private byte[] Buffer; - private int BufferLength = 0; - public int TransferLength = 0; - - public TSStreamBuffer() - { - Buffer = new byte[4096]; - Stream = new MemoryStream(Buffer); - } - - public long Length => (long)BufferLength; - - public long Position => Stream.Position; - - public void Add( - byte[] buffer, - int offset, - int length) - { - TransferLength += length; - - if (BufferLength + length >= Buffer.Length) - { - length = Buffer.Length - BufferLength; - } - if (length > 0) - { - Array.Copy(buffer, offset, Buffer, BufferLength, length); - BufferLength += length; - } - } - - public void Seek( - long offset, - SeekOrigin loc) - { - Stream.Seek(offset, loc); - } - - public void Reset() - { - BufferLength = 0; - TransferLength = 0; - } - - public void BeginRead() - { - SkipBits = 0; - Stream.Seek(0, SeekOrigin.Begin); - } - - public void EndRead() - { - } - - public byte[] ReadBytes(int bytes) - { - if (Stream.Position + bytes >= BufferLength) - { - return null; - } - - byte[] value = new byte[bytes]; - Stream.Read(value, 0, bytes); - return value; - } - - public byte ReadByte() - { - return (byte)Stream.ReadByte(); - } - - public int ReadBits(int bits) - { - long pos = Stream.Position; - - int shift = 24; - int data = 0; - for (int i = 0; i < 4; i++) - { - if (pos + i >= BufferLength) break; - data += (Stream.ReadByte() << shift); - shift -= 8; - } - var vector = new BitVector32(data); - - int value = 0; - for (int i = SkipBits; i < SkipBits + bits; i++) - { - value <<= 1; - value += (vector[1 << (32 - i - 1)] ? 1 : 0); - } - - SkipBits += bits; - Stream.Seek(pos + (SkipBits >> 3), SeekOrigin.Begin); - SkipBits = SkipBits % 8; - - return value; - } - } -} diff --git a/BDInfo/TSStreamClip.cs b/BDInfo/TSStreamClip.cs deleted file mode 100644 index 295eeb6b18..0000000000 --- a/BDInfo/TSStreamClip.cs +++ /dev/null @@ -1,107 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -using System; -using System.Collections.Generic; - -namespace BDInfo -{ - public class TSStreamClip - { - public int AngleIndex = 0; - public string Name; - public double TimeIn; - public double TimeOut; - public double RelativeTimeIn; - public double RelativeTimeOut; - public double Length; - - public ulong FileSize = 0; - public ulong InterleavedFileSize = 0; - public ulong PayloadBytes = 0; - public ulong PacketCount = 0; - public double PacketSeconds = 0; - - public List Chapters = new List(); - - public TSStreamFile StreamFile = null; - public TSStreamClipFile StreamClipFile = null; - - public TSStreamClip( - TSStreamFile streamFile, - TSStreamClipFile streamClipFile) - { - if (streamFile != null) - { - Name = streamFile.Name; - StreamFile = streamFile; - FileSize = (ulong)StreamFile.FileInfo.Length; - if (StreamFile.InterleavedFile != null) - { - InterleavedFileSize = (ulong)StreamFile.InterleavedFile.FileInfo.Length; - } - } - StreamClipFile = streamClipFile; - } - - public string DisplayName - { - get - { - if (StreamFile != null && - StreamFile.InterleavedFile != null && - BDInfoSettings.EnableSSIF) - { - return StreamFile.InterleavedFile.Name; - } - return Name; - } - } - - public ulong PacketSize => PacketCount * 192; - - public ulong PacketBitRate - { - get - { - if (PacketSeconds > 0) - { - return (ulong)Math.Round(((PacketSize * 8.0) / PacketSeconds)); - } - return 0; - } - } - - public bool IsCompatible(TSStreamClip clip) - { - foreach (var stream1 in StreamFile.Streams.Values) - { - if (clip.StreamFile.Streams.ContainsKey(stream1.PID)) - { - var stream2 = clip.StreamFile.Streams[stream1.PID]; - if (stream1.StreamType != stream2.StreamType) - { - return false; - } - } - } - return true; - } - } -} diff --git a/BDInfo/TSStreamClipFile.cs b/BDInfo/TSStreamClipFile.cs deleted file mode 100644 index e1097b23da..0000000000 --- a/BDInfo/TSStreamClipFile.cs +++ /dev/null @@ -1,244 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -#undef DEBUG -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using MediaBrowser.Model.IO; - -namespace BDInfo -{ - public class TSStreamClipFile - { - public FileSystemMetadata FileInfo = null; - public string FileType = null; - public bool IsValid = false; - public string Name = null; - - public Dictionary Streams = - new Dictionary(); - - public TSStreamClipFile(FileSystemMetadata fileInfo) - { - FileInfo = fileInfo; - Name = fileInfo.Name.ToUpper(); - } - - public void Scan() - { - Stream fileStream = null; - BinaryReader fileReader = null; - - try - { -#if DEBUG - Debug.WriteLine(string.Format( - "Scanning {0}...", Name)); -#endif - Streams.Clear(); - - fileStream = File.OpenRead(FileInfo.FullName); - fileReader = new BinaryReader(fileStream); - - byte[] data = new byte[fileStream.Length]; - fileReader.Read(data, 0, data.Length); - - byte[] fileType = new byte[8]; - Array.Copy(data, 0, fileType, 0, fileType.Length); - - FileType = Encoding.ASCII.GetString(fileType, 0, fileType.Length); - if (FileType != "HDMV0100" && - FileType != "HDMV0200") - { - throw new Exception(string.Format( - "Clip info file {0} has an unknown file type {1}.", - FileInfo.Name, FileType)); - } -#if DEBUG - Debug.WriteLine(string.Format( - "\tFileType: {0}", FileType)); -#endif - int clipIndex = - ((int)data[12] << 24) + - ((int)data[13] << 16) + - ((int)data[14] << 8) + - ((int)data[15]); - - int clipLength = - ((int)data[clipIndex] << 24) + - ((int)data[clipIndex + 1] << 16) + - ((int)data[clipIndex + 2] << 8) + - ((int)data[clipIndex + 3]); - - byte[] clipData = new byte[clipLength]; - Array.Copy(data, clipIndex + 4, clipData, 0, clipData.Length); - - int streamCount = clipData[8]; -#if DEBUG - Debug.WriteLine(string.Format( - "\tStreamCount: {0}", streamCount)); -#endif - int streamOffset = 10; - for (int streamIndex = 0; - streamIndex < streamCount; - streamIndex++) - { - TSStream stream = null; - - ushort PID = (ushort) - ((clipData[streamOffset] << 8) + - clipData[streamOffset + 1]); - - streamOffset += 2; - - var streamType = (TSStreamType) - clipData[streamOffset + 1]; - switch (streamType) - { - case TSStreamType.MVC_VIDEO: - // TODO - break; - - case TSStreamType.AVC_VIDEO: - case TSStreamType.MPEG1_VIDEO: - case TSStreamType.MPEG2_VIDEO: - case TSStreamType.VC1_VIDEO: - { - var videoFormat = (TSVideoFormat) - (clipData[streamOffset + 2] >> 4); - var frameRate = (TSFrameRate) - (clipData[streamOffset + 2] & 0xF); - var aspectRatio = (TSAspectRatio) - (clipData[streamOffset + 3] >> 4); - - stream = new TSVideoStream(); - ((TSVideoStream)stream).VideoFormat = videoFormat; - ((TSVideoStream)stream).AspectRatio = aspectRatio; - ((TSVideoStream)stream).FrameRate = frameRate; -#if DEBUG - Debug.WriteLine(string.Format( - "\t{0} {1} {2} {3} {4}", - PID, - streamType, - videoFormat, - frameRate, - aspectRatio)); -#endif - } - break; - - case TSStreamType.AC3_AUDIO: - case TSStreamType.AC3_PLUS_AUDIO: - case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: - case TSStreamType.AC3_TRUE_HD_AUDIO: - case TSStreamType.DTS_AUDIO: - case TSStreamType.DTS_HD_AUDIO: - case TSStreamType.DTS_HD_MASTER_AUDIO: - case TSStreamType.DTS_HD_SECONDARY_AUDIO: - case TSStreamType.LPCM_AUDIO: - case TSStreamType.MPEG1_AUDIO: - case TSStreamType.MPEG2_AUDIO: - { - byte[] languageBytes = new byte[3]; - Array.Copy(clipData, streamOffset + 3, - languageBytes, 0, languageBytes.Length); - string languageCode = Encoding.ASCII.GetString(languageBytes, 0, languageBytes.Length); - - var channelLayout = (TSChannelLayout) - (clipData[streamOffset + 2] >> 4); - var sampleRate = (TSSampleRate) - (clipData[streamOffset + 2] & 0xF); - - stream = new TSAudioStream(); - ((TSAudioStream)stream).LanguageCode = languageCode; - ((TSAudioStream)stream).ChannelLayout = channelLayout; - ((TSAudioStream)stream).SampleRate = TSAudioStream.ConvertSampleRate(sampleRate); - ((TSAudioStream)stream).LanguageCode = languageCode; -#if DEBUG - Debug.WriteLine(string.Format( - "\t{0} {1} {2} {3} {4}", - PID, - streamType, - languageCode, - channelLayout, - sampleRate)); -#endif - } - break; - - case TSStreamType.INTERACTIVE_GRAPHICS: - case TSStreamType.PRESENTATION_GRAPHICS: - { - byte[] languageBytes = new byte[3]; - Array.Copy(clipData, streamOffset + 2, - languageBytes, 0, languageBytes.Length); - string languageCode = Encoding.ASCII.GetString(languageBytes, 0, languageBytes.Length); - - stream = new TSGraphicsStream(); - stream.LanguageCode = languageCode; -#if DEBUG - Debug.WriteLine(string.Format( - "\t{0} {1} {2}", - PID, - streamType, - languageCode)); -#endif - } - break; - - case TSStreamType.SUBTITLE: - { - byte[] languageBytes = new byte[3]; - Array.Copy(clipData, streamOffset + 3, - languageBytes, 0, languageBytes.Length); - string languageCode = Encoding.ASCII.GetString(languageBytes, 0, languageBytes.Length); -#if DEBUG - Debug.WriteLine(string.Format( - "\t{0} {1} {2}", - PID, - streamType, - languageCode)); -#endif - stream = new TSTextStream(); - stream.LanguageCode = languageCode; - } - break; - } - - if (stream != null) - { - stream.PID = PID; - stream.StreamType = streamType; - Streams.Add(PID, stream); - } - - streamOffset += clipData[streamOffset] + 1; - } - IsValid = true; - } - finally - { - if (fileReader != null) fileReader.Dispose(); - if (fileStream != null) fileStream.Dispose(); - } - } - } -} diff --git a/BDInfo/TSStreamFile.cs b/BDInfo/TSStreamFile.cs deleted file mode 100644 index ecf6609e2e..0000000000 --- a/BDInfo/TSStreamFile.cs +++ /dev/null @@ -1,1555 +0,0 @@ -//============================================================================ -// BDInfo - Blu-ray Video and Audio Analysis Tool -// Copyright © 2010 Cinema Squid -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -//============================================================================= - -#undef DEBUG -using System; -using System.Collections.Generic; -using System.IO; -using MediaBrowser.Model.IO; - -namespace BDInfo -{ - public class TSStreamState - { - public ulong TransferCount = 0; - - public string StreamTag = null; - - public ulong TotalPackets = 0; - public ulong WindowPackets = 0; - - public ulong TotalBytes = 0; - public ulong WindowBytes = 0; - - public long PeakTransferLength = 0; - public long PeakTransferRate = 0; - - public double TransferMarker = 0; - public double TransferInterval = 0; - - public TSStreamBuffer StreamBuffer = new TSStreamBuffer(); - - public uint Parse = 0; - public bool TransferState = false; - public int TransferLength = 0; - public int PacketLength = 0; - public byte PacketLengthParse = 0; - public byte PacketParse = 0; - - public byte PTSParse = 0; - public ulong PTS = 0; - public ulong PTSTemp = 0; - public ulong PTSLast = 0; - public ulong PTSPrev = 0; - public ulong PTSDiff = 0; - public ulong PTSCount = 0; - public ulong PTSTransfer = 0; - - public byte DTSParse = 0; - public ulong DTSTemp = 0; - public ulong DTSPrev = 0; - - public byte PESHeaderLength = 0; - public byte PESHeaderFlags = 0; -#if DEBUG - public byte PESHeaderIndex = 0; - public byte[] PESHeader = new byte[256 + 9]; -#endif - } - - public class TSPacketParser - { - public bool SyncState = false; - public byte TimeCodeParse = 4; - public byte PacketLength = 0; - public byte HeaderParse = 0; - - public uint TimeCode; - public byte TransportErrorIndicator; - public byte PayloadUnitStartIndicator; - public byte TransportPriority; - public ushort PID; - public byte TransportScramblingControl; - public byte AdaptionFieldControl; - - public bool AdaptionFieldState = false; - public byte AdaptionFieldParse = 0; - public byte AdaptionFieldLength = 0; - - public ushort PCRPID = 0xFFFF; - public byte PCRParse = 0; - public ulong PreviousPCR = 0; - public ulong PCR = 0; - public ulong PCRCount = 0; - public ulong PTSFirst = ulong.MaxValue; - public ulong PTSLast = ulong.MinValue; - public ulong PTSDiff = 0; - - public byte[] PAT = new byte[1024]; - public bool PATSectionStart = false; - public byte PATPointerField = 0; - public uint PATOffset = 0; - public byte PATSectionLengthParse = 0; - public ushort PATSectionLength = 0; - public uint PATSectionParse = 0; - public bool PATTransferState = false; - public byte PATSectionNumber = 0; - public byte PATLastSectionNumber = 0; - - public ushort TransportStreamId = 0xFFFF; - - public List PMTProgramDescriptors = new List(); - public ushort PMTPID = 0xFFFF; - public Dictionary PMT = new Dictionary(); - public bool PMTSectionStart = false; - public ushort PMTProgramInfoLength = 0; - public byte PMTProgramDescriptor = 0; - public byte PMTProgramDescriptorLengthParse = 0; - public byte PMTProgramDescriptorLength = 0; - public ushort PMTStreamInfoLength = 0; - public uint PMTStreamDescriptorLengthParse = 0; - public uint PMTStreamDescriptorLength = 0; - public byte PMTPointerField = 0; - public uint PMTOffset = 0; - public uint PMTSectionLengthParse = 0; - public ushort PMTSectionLength = 0; - public uint PMTSectionParse = 0; - public bool PMTTransferState = false; - public byte PMTSectionNumber = 0; - public byte PMTLastSectionNumber = 0; - - public byte PMTTemp = 0; - - public TSStream Stream = null; - public TSStreamState StreamState = null; - - public ulong TotalPackets = 0; - } - - public class TSStreamDiagnostics - { - public ulong Bytes = 0; - public ulong Packets = 0; - public double Marker = 0; - public double Interval = 0; - public string Tag = null; - } - - public class TSStreamFile - { - public FileSystemMetadata FileInfo = null; - public string Name = null; - public long Size = 0; - public double Length = 0; - - public TSInterleavedFile InterleavedFile = null; - - private Dictionary StreamStates = - new Dictionary(); - - public Dictionary Streams = - new Dictionary(); - - public Dictionary> StreamDiagnostics = - new Dictionary>(); - - private List Playlists = null; - - private readonly IFileSystem _fileSystem; - - public TSStreamFile(FileSystemMetadata fileInfo, IFileSystem fileSystem) - { - FileInfo = fileInfo; - _fileSystem = fileSystem; - Name = fileInfo.Name.ToUpper(); - } - - public string DisplayName - { - get - { - if (BDInfoSettings.EnableSSIF && - InterleavedFile != null) - { - return InterleavedFile.Name; - } - return Name; - } - } - - private bool ScanStream( - TSStream stream, - TSStreamState streamState, - TSStreamBuffer buffer) - { - streamState.StreamTag = null; - - long bitrate = 0; - if (stream.IsAudioStream && - streamState.PTSTransfer > 0) - { - bitrate = (long)Math.Round( - (buffer.TransferLength * 8.0) / - ((double)streamState.PTSTransfer / 90000)); - - if (bitrate > streamState.PeakTransferRate) - { - streamState.PeakTransferRate = bitrate; - } - } - if (buffer.TransferLength > streamState.PeakTransferLength) - { - streamState.PeakTransferLength = buffer.TransferLength; - } - - buffer.BeginRead(); - switch (stream.StreamType) - { - case TSStreamType.MPEG2_VIDEO: - TSCodecMPEG2.Scan( - (TSVideoStream)stream, buffer, ref streamState.StreamTag); - break; - - case TSStreamType.AVC_VIDEO: - TSCodecAVC.Scan( - (TSVideoStream)stream, buffer, ref streamState.StreamTag); - break; - - case TSStreamType.MVC_VIDEO: - TSCodecMVC.Scan( - (TSVideoStream)stream, buffer, ref streamState.StreamTag); - break; - - case TSStreamType.VC1_VIDEO: - TSCodecVC1.Scan( - (TSVideoStream)stream, buffer, ref streamState.StreamTag); - break; - - case TSStreamType.AC3_AUDIO: - TSCodecAC3.Scan( - (TSAudioStream)stream, buffer, ref streamState.StreamTag); - break; - - case TSStreamType.AC3_PLUS_AUDIO: - case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: - TSCodecAC3.Scan( - (TSAudioStream)stream, buffer, ref streamState.StreamTag); - break; - - case TSStreamType.AC3_TRUE_HD_AUDIO: - TSCodecTrueHD.Scan( - (TSAudioStream)stream, buffer, ref streamState.StreamTag); - break; - - case TSStreamType.LPCM_AUDIO: - TSCodecLPCM.Scan( - (TSAudioStream)stream, buffer, ref streamState.StreamTag); - break; - - case TSStreamType.DTS_AUDIO: - TSCodecDTS.Scan( - (TSAudioStream)stream, buffer, bitrate, ref streamState.StreamTag); - break; - - case TSStreamType.DTS_HD_AUDIO: - case TSStreamType.DTS_HD_MASTER_AUDIO: - case TSStreamType.DTS_HD_SECONDARY_AUDIO: - TSCodecDTSHD.Scan( - (TSAudioStream)stream, buffer, bitrate, ref streamState.StreamTag); - break; - - default: - stream.IsInitialized = true; - break; - } - buffer.EndRead(); - streamState.StreamBuffer.Reset(); - - bool isAVC = false; - bool isMVC = false; - foreach (var finishedStream in Streams.Values) - { - if (!finishedStream.IsInitialized) - { - return false; - } - if (finishedStream.StreamType == TSStreamType.AVC_VIDEO) - { - isAVC = true; - } - if (finishedStream.StreamType == TSStreamType.MVC_VIDEO) - { - isMVC = true; - } - } - if (isMVC && !isAVC) - { - return false; - } - return true; - } - - private void UpdateStreamBitrates( - ushort PTSPID, - ulong PTS, - ulong PTSDiff) - { - if (Playlists == null) return; - - foreach (ushort PID in StreamStates.Keys) - { - if (Streams.ContainsKey(PID) && - Streams[PID].IsVideoStream && - PID != PTSPID) - { - continue; - } - if (StreamStates[PID].WindowPackets == 0) - { - continue; - } - UpdateStreamBitrate(PID, PTSPID, PTS, PTSDiff); - } - - foreach (var playlist in Playlists) - { - double packetSeconds = 0; - foreach (var clip in playlist.StreamClips) - { - if (clip.AngleIndex == 0) - { - packetSeconds += clip.PacketSeconds; - } - } - if (packetSeconds > 0) - { - foreach (var playlistStream in playlist.SortedStreams) - { - if (playlistStream.IsVBR) - { - playlistStream.BitRate = (long)Math.Round( - ((playlistStream.PayloadBytes * 8.0) / packetSeconds)); - - if (playlistStream.StreamType == TSStreamType.AC3_TRUE_HD_AUDIO && - ((TSAudioStream)playlistStream).CoreStream != null) - { - playlistStream.BitRate -= - ((TSAudioStream)playlistStream).CoreStream.BitRate; - } - } - } - } - } - } - - private void UpdateStreamBitrate( - ushort PID, - ushort PTSPID, - ulong PTS, - ulong PTSDiff) - { - if (Playlists == null) return; - - var streamState = StreamStates[PID]; - double streamTime = (double)PTS / 90000; - double streamInterval = (double)PTSDiff / 90000; - double streamOffset = streamTime + streamInterval; - - foreach (var playlist in Playlists) - { - foreach (var clip in playlist.StreamClips) - { - if (clip.Name != this.Name) continue; - - if (streamTime == 0 || - (streamTime >= clip.TimeIn && - streamTime <= clip.TimeOut)) - { - clip.PayloadBytes += streamState.WindowBytes; - clip.PacketCount += streamState.WindowPackets; - - if (streamOffset > clip.TimeIn && - streamOffset - clip.TimeIn > clip.PacketSeconds) - { - clip.PacketSeconds = streamOffset - clip.TimeIn; - } - - var playlistStreams = playlist.Streams; - if (clip.AngleIndex > 0 && - clip.AngleIndex < playlist.AngleStreams.Count + 1) - { - playlistStreams = playlist.AngleStreams[clip.AngleIndex - 1]; - } - if (playlistStreams.ContainsKey(PID)) - { - var stream = playlistStreams[PID]; - - stream.PayloadBytes += streamState.WindowBytes; - stream.PacketCount += streamState.WindowPackets; - - if (stream.IsVideoStream) - { - stream.PacketSeconds += streamInterval; - - stream.ActiveBitRate = (long)Math.Round( - ((stream.PayloadBytes * 8.0) / - stream.PacketSeconds)); - } - - if (stream.StreamType == TSStreamType.AC3_TRUE_HD_AUDIO && - ((TSAudioStream)stream).CoreStream != null) - { - stream.ActiveBitRate -= - ((TSAudioStream)stream).CoreStream.BitRate; - } - } - } - } - } - - if (Streams.ContainsKey(PID)) - { - var stream = Streams[PID]; - stream.PayloadBytes += streamState.WindowBytes; - stream.PacketCount += streamState.WindowPackets; - - if (stream.IsVideoStream) - { - var diag = new TSStreamDiagnostics(); - diag.Marker = (double)PTS / 90000; - diag.Interval = (double)PTSDiff / 90000; - diag.Bytes = streamState.WindowBytes; - diag.Packets = streamState.WindowPackets; - diag.Tag = streamState.StreamTag; - StreamDiagnostics[PID].Add(diag); - - stream.PacketSeconds += streamInterval; - } - } - streamState.WindowPackets = 0; - streamState.WindowBytes = 0; - } - - public void Scan(List playlists, bool isFullScan) - { - if (playlists == null || playlists.Count == 0) - { - return; - } - - Playlists = playlists; - int dataSize = 16384; - Stream fileStream = null; - try - { - string fileName; - if (BDInfoSettings.EnableSSIF && - InterleavedFile != null) - { - fileName = InterleavedFile.FileInfo.FullName; - } - else - { - fileName = FileInfo.FullName; - } - fileStream = _fileSystem.GetFileStream( - fileName, - FileOpenMode.Open, - FileAccessMode.Read, - FileShareMode.Read, - false); - - Size = 0; - Length = 0; - - Streams.Clear(); - StreamStates.Clear(); - StreamDiagnostics.Clear(); - - var parser = - new TSPacketParser(); - - long fileLength = (uint)fileStream.Length; - byte[] buffer = new byte[dataSize]; - int bufferLength = 0; - while ((bufferLength = - fileStream.Read(buffer, 0, buffer.Length)) > 0) - { - int offset = 0; - for (int i = 0; i < bufferLength; i++) - { - if (parser.SyncState == false) - { - if (parser.TimeCodeParse > 0) - { - parser.TimeCodeParse--; - switch (parser.TimeCodeParse) - { - case 3: - parser.TimeCode = 0; - parser.TimeCode |= - ((uint)buffer[i] & 0x3F) << 24; - break; - case 2: - parser.TimeCode |= - ((uint)buffer[i] & 0xFF) << 16; - break; - case 1: - parser.TimeCode |= - ((uint)buffer[i] & 0xFF) << 8; - break; - case 0: - parser.TimeCode |= - ((uint)buffer[i] & 0xFF); - break; - } - } - else if (buffer[i] == 0x47) - { - parser.SyncState = true; - parser.PacketLength = 187; - parser.TimeCodeParse = 4; - parser.HeaderParse = 3; - } - } - else if (parser.HeaderParse > 0) - { - parser.PacketLength--; - parser.HeaderParse--; - - switch (parser.HeaderParse) - { - case 2: - { - parser.TransportErrorIndicator = - (byte)((buffer[i] >> 7) & 0x1); - parser.PayloadUnitStartIndicator = - (byte)((buffer[i] >> 6) & 0x1); - parser.TransportPriority = - (byte)((buffer[i] >> 5) & 0x1); - parser.PID = - (ushort)((buffer[i] & 0x1f) << 8); - } - break; - - case 1: - { - parser.PID |= (ushort)buffer[i]; - if (Streams.ContainsKey(parser.PID)) - { - parser.Stream = Streams[parser.PID]; - } - else - { - parser.Stream = null; - } - if (!StreamStates.ContainsKey(parser.PID)) - { - StreamStates[parser.PID] = new TSStreamState(); - } - parser.StreamState = StreamStates[parser.PID]; - parser.StreamState.TotalPackets++; - parser.StreamState.WindowPackets++; - parser.TotalPackets++; - } - break; - - case 0: - { - parser.TransportScramblingControl = - (byte)((buffer[i] >> 6) & 0x3); - parser.AdaptionFieldControl = - (byte)((buffer[i] >> 4) & 0x3); - - if ((parser.AdaptionFieldControl & 0x2) == 0x2) - { - parser.AdaptionFieldState = true; - } - if (parser.PayloadUnitStartIndicator == 1) - { - if (parser.PID == 0) - { - parser.PATSectionStart = true; - } - else if (parser.PID == parser.PMTPID) - { - parser.PMTSectionStart = true; - } - else if (parser.StreamState != null && - parser.StreamState.TransferState) - { - parser.StreamState.TransferState = false; - parser.StreamState.TransferCount++; - - bool isFinished = ScanStream( - parser.Stream, - parser.StreamState, - parser.StreamState.StreamBuffer); - - if (!isFullScan && isFinished) - { - return; - } - } - } - } - break; - } - } - else if (parser.AdaptionFieldState) - { - parser.PacketLength--; - parser.AdaptionFieldParse = buffer[i]; - parser.AdaptionFieldLength = buffer[i]; - parser.AdaptionFieldState = false; - } - else if (parser.AdaptionFieldParse > 0) - { - parser.PacketLength--; - parser.AdaptionFieldParse--; - if ((parser.AdaptionFieldLength - parser.AdaptionFieldParse) == 1) - { - if ((buffer[i] & 0x10) == 0x10) - { - parser.PCRParse = 6; - parser.PCR = 0; - } - } - else if (parser.PCRParse > 0) - { - parser.PCRParse--; - parser.PCR = (parser.PCR << 8) + (ulong)buffer[i]; - if (parser.PCRParse == 0) - { - parser.PreviousPCR = parser.PCR; - parser.PCR = (parser.PCR & 0x1FF) + - ((parser.PCR >> 15) * 300); - } - parser.PCRCount++; - } - if (parser.PacketLength == 0) - { - parser.SyncState = false; - } - } - else if (parser.PID == 0) - { - if (parser.PATTransferState) - { - if ((bufferLength - i) > parser.PATSectionLength) - { - offset = parser.PATSectionLength; - } - else - { - offset = (bufferLength - i); - } - if (parser.PacketLength <= offset) - { - offset = parser.PacketLength; - } - - for (int k = 0; k < offset; k++) - { - parser.PAT[parser.PATOffset++] = buffer[i++]; - parser.PATSectionLength--; - parser.PacketLength--; - } - --i; - - if (parser.PATSectionLength == 0) - { - parser.PATTransferState = false; - if (parser.PATSectionNumber == parser.PATLastSectionNumber) - { - for (int k = 0; k < (parser.PATOffset - 4); k += 4) - { - uint programNumber = (uint) - ((parser.PAT[k] << 8) + - parser.PAT[k + 1]); - - ushort programPID = (ushort) - (((parser.PAT[k + 2] & 0x1F) << 8) + - parser.PAT[k + 3]); - - if (programNumber == 1) - { - parser.PMTPID = programPID; - } - } - } - } - } - else - { - --parser.PacketLength; - if (parser.PATSectionStart) - { - parser.PATPointerField = buffer[i]; - if (parser.PATPointerField == 0) - { - parser.PATSectionLengthParse = 3; - } - parser.PATSectionStart = false; - } - else if (parser.PATPointerField > 0) - { - --parser.PATPointerField; - if (parser.PATPointerField == 0) - { - parser.PATSectionLengthParse = 3; - } - } - else if (parser.PATSectionLengthParse > 0) - { - --parser.PATSectionLengthParse; - switch (parser.PATSectionLengthParse) - { - case 2: - break; - case 1: - parser.PATSectionLength = (ushort) - ((buffer[i] & 0xF) << 8); - break; - case 0: - parser.PATSectionLength |= buffer[i]; - if (parser.PATSectionLength > 1021) - { - parser.PATSectionLength = 0; - } - else - { - parser.PATSectionParse = 5; - } - break; - } - } - else if (parser.PATSectionParse > 0) - { - --parser.PATSectionLength; - --parser.PATSectionParse; - - switch (parser.PATSectionParse) - { - case 4: - parser.TransportStreamId = (ushort) - (buffer[i] << 8); - break; - case 3: - parser.TransportStreamId |= buffer[i]; - break; - case 2: - break; - case 1: - parser.PATSectionNumber = buffer[i]; - if (parser.PATSectionNumber == 0) - { - parser.PATOffset = 0; - } - break; - case 0: - parser.PATLastSectionNumber = buffer[i]; - parser.PATTransferState = true; - break; - } - } - } - if (parser.PacketLength == 0) - { - parser.SyncState = false; - } - } - else if (parser.PID == parser.PMTPID) - { - if (parser.PMTTransferState) - { - if ((bufferLength - i) >= parser.PMTSectionLength) - { - offset = parser.PMTSectionLength; - } - else - { - offset = (bufferLength - i); - } - if (parser.PacketLength <= offset) - { - offset = parser.PacketLength; - } - if (!parser.PMT.ContainsKey(parser.PID)) - { - parser.PMT[parser.PID] = new byte[1024]; - } - - byte[] PMT = parser.PMT[parser.PID]; - for (int k = 0; k < offset; k++) - { - PMT[parser.PMTOffset++] = buffer[i++]; - --parser.PMTSectionLength; - --parser.PacketLength; - } - --i; - - if (parser.PMTSectionLength == 0) - { - parser.PMTTransferState = false; - if (parser.PMTSectionNumber == parser.PMTLastSectionNumber) - { - //Console.WriteLine("PMT Start: " + parser.PMTTemp); - try - { - for (int k = 0; k < (parser.PMTOffset - 4); k += 5) - { - byte streamType = PMT[k]; - - ushort streamPID = (ushort) - (((PMT[k + 1] & 0x1F) << 8) + - PMT[k + 2]); - - ushort streamInfoLength = (ushort) - (((PMT[k + 3] & 0xF) << 8) + - PMT[k + 4]); - - /* - if (streamInfoLength == 2) - { - // TODO: Cleanup - //streamInfoLength = 0; - } - - Console.WriteLine(string.Format( - "Type: {0} PID: {1} Length: {2}", - streamType, streamPID, streamInfoLength)); - */ - - if (!Streams.ContainsKey(streamPID)) - { - var streamDescriptors = - new List(); - - /* - * TODO: Getting bad streamInfoLength - if (streamInfoLength > 0) - { - for (int d = 0; d < streamInfoLength; d++) - { - byte name = PMT[k + d + 5]; - byte length = PMT[k + d + 6]; - TSDescriptor descriptor = - new TSDescriptor(name, length); - for (int v = 0; v < length; v++) - { - descriptor.Value[v] = - PMT[k + d + v + 7]; - } - streamDescriptors.Add(descriptor); - d += (length + 1); - } - } - */ - CreateStream(streamPID, streamType, streamDescriptors); - } - k += streamInfoLength; - } - } - catch - { - // TODO - //Console.WriteLine(ex.Message); - } - } - } - } - else - { - --parser.PacketLength; - if (parser.PMTSectionStart) - { - parser.PMTPointerField = buffer[i]; - if (parser.PMTPointerField == 0) - { - parser.PMTSectionLengthParse = 3; - } - parser.PMTSectionStart = false; - } - else if (parser.PMTPointerField > 0) - { - --parser.PMTPointerField; - if (parser.PMTPointerField == 0) - { - parser.PMTSectionLengthParse = 3; - } - } - else if (parser.PMTSectionLengthParse > 0) - { - --parser.PMTSectionLengthParse; - switch (parser.PMTSectionLengthParse) - { - case 2: - if (buffer[i] != 0x2) - { - parser.PMTSectionLengthParse = 0; - } - break; - case 1: - parser.PMTSectionLength = (ushort) - ((buffer[i] & 0xF) << 8); - break; - case 0: - parser.PMTSectionLength |= buffer[i]; - if (parser.PMTSectionLength > 1021) - { - parser.PMTSectionLength = 0; - } - else - { - parser.PMTSectionParse = 9; - } - break; - } - } - else if (parser.PMTSectionParse > 0) - { - --parser.PMTSectionLength; - --parser.PMTSectionParse; - - switch (parser.PMTSectionParse) - { - case 8: - case 7: - break; - case 6: - parser.PMTTemp = buffer[i]; - break; - case 5: - parser.PMTSectionNumber = buffer[i]; - if (parser.PMTSectionNumber == 0) - { - parser.PMTOffset = 0; - } - break; - case 4: - parser.PMTLastSectionNumber = buffer[i]; - break; - case 3: - parser.PCRPID = (ushort) - ((buffer[i] & 0x1F) << 8); - break; - case 2: - parser.PCRPID |= buffer[i]; - break; - case 1: - parser.PMTProgramInfoLength = (ushort) - ((buffer[i] & 0xF) << 8); - break; - case 0: - parser.PMTProgramInfoLength |= buffer[i]; - if (parser.PMTProgramInfoLength == 0) - { - parser.PMTTransferState = true; - } - else - { - parser.PMTProgramDescriptorLengthParse = 2; - } - break; - } - } - else if (parser.PMTProgramInfoLength > 0) - { - --parser.PMTSectionLength; - --parser.PMTProgramInfoLength; - - if (parser.PMTProgramDescriptorLengthParse > 0) - { - --parser.PMTProgramDescriptorLengthParse; - switch (parser.PMTProgramDescriptorLengthParse) - { - case 1: - parser.PMTProgramDescriptor = buffer[i]; - break; - case 0: - parser.PMTProgramDescriptorLength = buffer[i]; - parser.PMTProgramDescriptors.Add( - new TSDescriptor( - parser.PMTProgramDescriptor, - parser.PMTProgramDescriptorLength)); - break; - } - } - else if (parser.PMTProgramDescriptorLength > 0) - { - --parser.PMTProgramDescriptorLength; - - var descriptor = parser.PMTProgramDescriptors[ - parser.PMTProgramDescriptors.Count - 1]; - - int valueIndex = - descriptor.Value.Length - - parser.PMTProgramDescriptorLength - 1; - - descriptor.Value[valueIndex] = buffer[i]; - - if (parser.PMTProgramDescriptorLength == 0 && - parser.PMTProgramInfoLength > 0) - { - parser.PMTProgramDescriptorLengthParse = 2; - } - } - if (parser.PMTProgramInfoLength == 0) - { - parser.PMTTransferState = true; - } - } - } - if (parser.PacketLength == 0) - { - parser.SyncState = false; - } - } - else if (parser.Stream != null && - parser.StreamState != null && - parser.TransportScramblingControl == 0) - { - var stream = parser.Stream; - var streamState = parser.StreamState; - - streamState.Parse = - (streamState.Parse << 8) + buffer[i]; - - if (streamState.TransferState) - { - if ((bufferLength - i) >= streamState.PacketLength && - streamState.PacketLength > 0) - { - offset = streamState.PacketLength; - } - else - { - offset = (bufferLength - i); - } - if (parser.PacketLength <= offset) - { - offset = parser.PacketLength; - } - streamState.TransferLength = offset; - - if (!stream.IsInitialized || - stream.IsVideoStream) - { - streamState.StreamBuffer.Add( - buffer, i, offset); - } - else - { - streamState.StreamBuffer.TransferLength += offset; - } - - i += (int)(streamState.TransferLength - 1); - streamState.PacketLength -= streamState.TransferLength; - parser.PacketLength -= (byte)streamState.TransferLength; - - streamState.TotalBytes += (ulong)streamState.TransferLength; - streamState.WindowBytes += (ulong)streamState.TransferLength; - - if (streamState.PacketLength == 0) - { - streamState.TransferState = false; - streamState.TransferCount++; - bool isFinished = ScanStream( - stream, - streamState, - streamState.StreamBuffer); - - if (!isFullScan && isFinished) - { - return; - } - } - } - else - { - --parser.PacketLength; - - bool headerFound = false; - if (stream.IsVideoStream && - streamState.Parse == 0x000001FD) - { - headerFound = true; - } - if (stream.IsVideoStream && - streamState.Parse >= 0x000001E0 && - streamState.Parse <= 0x000001EF) - { - headerFound = true; - } - if (stream.IsAudioStream && - streamState.Parse == 0x000001BD) - { - headerFound = true; - } - if (stream.IsAudioStream && - (streamState.Parse == 0x000001FA || - streamState.Parse == 0x000001FD)) - { - headerFound = true; - } - - if (!stream.IsVideoStream && - !stream.IsAudioStream && - (streamState.Parse == 0x000001FA || - streamState.Parse == 0x000001FD || - streamState.Parse == 0x000001BD || - (streamState.Parse >= 0x000001E0 && - streamState.Parse <= 0x000001EF))) - { - headerFound = true; - } - - if (headerFound) - { - streamState.PacketLengthParse = 2; -#if DEBUG - streamState.PESHeaderIndex = 0; - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)((streamState.Parse >> 24) & 0xFF); - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)((streamState.Parse >> 16) & 0xFF); - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)((streamState.Parse >> 8) & 0xFF); - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - } - else if (streamState.PacketLengthParse > 0) - { - --streamState.PacketLengthParse; - switch (streamState.PacketLengthParse) - { - case 1: -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 0: - streamState.PacketLength = - (int)(streamState.Parse & 0xFFFF); - streamState.PacketParse = 3; -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - } - } - else if (streamState.PacketParse > 0) - { - --streamState.PacketLength; - --streamState.PacketParse; - - switch (streamState.PacketParse) - { - case 2: -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 1: - streamState.PESHeaderFlags = - (byte)(streamState.Parse & 0xFF); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 0: - streamState.PESHeaderLength = - (byte)(streamState.Parse & 0xFF); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - if ((streamState.PESHeaderFlags & 0xC0) == 0x80) - { - streamState.PTSParse = 5; - } - else if ((streamState.PESHeaderFlags & 0xC0) == 0xC0) - { - streamState.DTSParse = 10; - } - if (streamState.PESHeaderLength == 0) - { - streamState.TransferState = true; - } - break; - } - } - else if (streamState.PTSParse > 0) - { - --streamState.PacketLength; - --streamState.PESHeaderLength; - --streamState.PTSParse; - - switch (streamState.PTSParse) - { - case 4: - streamState.PTSTemp = - ((streamState.Parse & 0xE) << 29); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xff); -#endif - break; - - case 3: - streamState.PTSTemp |= - ((streamState.Parse & 0xFF) << 22); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 2: - streamState.PTSTemp |= - ((streamState.Parse & 0xFE) << 14); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 1: - streamState.PTSTemp |= - ((streamState.Parse & 0xFF) << 7); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 0: - streamState.PTSTemp |= - ((streamState.Parse & 0xFE) >> 1); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xff); -#endif - streamState.PTS = streamState.PTSTemp; - - if (streamState.PTS > streamState.PTSLast) - { - if (streamState.PTSLast > 0) - { - streamState.PTSTransfer = (streamState.PTS - streamState.PTSLast); - } - streamState.PTSLast = streamState.PTS; - } - - streamState.PTSDiff = streamState.PTS - streamState.DTSPrev; - - if (streamState.PTSCount > 0 && - stream.IsVideoStream) - { - UpdateStreamBitrates(stream.PID, streamState.PTS, streamState.PTSDiff); - if (streamState.DTSTemp < parser.PTSFirst) - { - parser.PTSFirst = streamState.DTSTemp; - } - if (streamState.DTSTemp > parser.PTSLast) - { - parser.PTSLast = streamState.DTSTemp; - } - Length = (double)(parser.PTSLast - parser.PTSFirst) / 90000; - } - - streamState.DTSPrev = streamState.PTS; - streamState.PTSCount++; - if (streamState.PESHeaderLength == 0) - { - streamState.TransferState = true; - } - break; - } - } - else if (streamState.DTSParse > 0) - { - --streamState.PacketLength; - --streamState.PESHeaderLength; - --streamState.DTSParse; - - switch (streamState.DTSParse) - { - case 9: - streamState.PTSTemp = - ((streamState.Parse & 0xE) << 29); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 8: - streamState.PTSTemp |= - ((streamState.Parse & 0xFF) << 22); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 7: - streamState.PTSTemp |= - ((streamState.Parse & 0xFE) << 14); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xff); -#endif - break; - - case 6: - streamState.PTSTemp |= - ((streamState.Parse & 0xFF) << 7); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 5: - streamState.PTSTemp |= - ((streamState.Parse & 0xFE) >> 1); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xff); -#endif - streamState.PTS = streamState.PTSTemp; - if (streamState.PTS > streamState.PTSLast) - { - streamState.PTSLast = streamState.PTS; - } - break; - - case 4: - streamState.DTSTemp = - ((streamState.Parse & 0xE) << 29); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xff); -#endif - break; - - case 3: - streamState.DTSTemp |= - ((streamState.Parse & 0xFF) << 22); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xff); -#endif - break; - - case 2: - streamState.DTSTemp |= - ((streamState.Parse & 0xFE) << 14); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xff); -#endif - break; - - case 1: - streamState.DTSTemp |= - ((streamState.Parse & 0xFF) << 7); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - break; - - case 0: - streamState.DTSTemp |= - ((streamState.Parse & 0xFE) >> 1); -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xff); -#endif - streamState.PTSDiff = streamState.DTSTemp - streamState.DTSPrev; - - if (streamState.PTSCount > 0 && - stream.IsVideoStream) - { - UpdateStreamBitrates(stream.PID, streamState.DTSTemp, streamState.PTSDiff); - if (streamState.DTSTemp < parser.PTSFirst) - { - parser.PTSFirst = streamState.DTSTemp; - } - if (streamState.DTSTemp > parser.PTSLast) - { - parser.PTSLast = streamState.DTSTemp; - } - Length = (double)(parser.PTSLast - parser.PTSFirst) / 90000; - } - streamState.DTSPrev = streamState.DTSTemp; - streamState.PTSCount++; - if (streamState.PESHeaderLength == 0) - { - streamState.TransferState = true; - } - break; - } - } - else if (streamState.PESHeaderLength > 0) - { - --streamState.PacketLength; - --streamState.PESHeaderLength; -#if DEBUG - streamState.PESHeader[streamState.PESHeaderIndex++] = - (byte)(streamState.Parse & 0xFF); -#endif - if (streamState.PESHeaderLength == 0) - { - streamState.TransferState = true; - } - } - } - if (parser.PacketLength == 0) - { - parser.SyncState = false; - } - } - else - { - parser.PacketLength--; - if ((bufferLength - i) >= parser.PacketLength) - { - i = i + parser.PacketLength; - parser.PacketLength = 0; - } - else - { - parser.PacketLength -= (byte)((bufferLength - i) + 1); - i = bufferLength; - } - if (parser.PacketLength == 0) - { - parser.SyncState = false; - } - } - } - Size += bufferLength; - } - - ulong PTSLast = 0; - ulong PTSDiff = 0; - foreach (var stream in Streams.Values) - { - if (!stream.IsVideoStream) continue; - - if (StreamStates.ContainsKey(stream.PID) && - StreamStates[stream.PID].PTSLast > PTSLast) - { - PTSLast = StreamStates[stream.PID].PTSLast; - PTSDiff = PTSLast - StreamStates[stream.PID].DTSPrev; - } - UpdateStreamBitrates(stream.PID, PTSLast, PTSDiff); - } - } - finally - { - if (fileStream != null) - { - fileStream.Dispose(); - } - } - } - - private TSStream CreateStream( - ushort streamPID, - byte streamType, - List streamDescriptors) - { - TSStream stream = null; - - switch ((TSStreamType)streamType) - { - case TSStreamType.MVC_VIDEO: - case TSStreamType.AVC_VIDEO: - case TSStreamType.MPEG1_VIDEO: - case TSStreamType.MPEG2_VIDEO: - case TSStreamType.VC1_VIDEO: - { - stream = new TSVideoStream(); - } - break; - - case TSStreamType.AC3_AUDIO: - case TSStreamType.AC3_PLUS_AUDIO: - case TSStreamType.AC3_PLUS_SECONDARY_AUDIO: - case TSStreamType.AC3_TRUE_HD_AUDIO: - case TSStreamType.DTS_AUDIO: - case TSStreamType.DTS_HD_AUDIO: - case TSStreamType.DTS_HD_MASTER_AUDIO: - case TSStreamType.DTS_HD_SECONDARY_AUDIO: - case TSStreamType.LPCM_AUDIO: - case TSStreamType.MPEG1_AUDIO: - case TSStreamType.MPEG2_AUDIO: - { - stream = new TSAudioStream(); - } - break; - - case TSStreamType.INTERACTIVE_GRAPHICS: - case TSStreamType.PRESENTATION_GRAPHICS: - { - stream = new TSGraphicsStream(); - } - break; - - case TSStreamType.SUBTITLE: - { - stream = new TSTextStream(); - } - break; - - default: - break; - } - - if (stream != null && - !Streams.ContainsKey(streamPID)) - { - stream.PID = streamPID; - stream.StreamType = (TSStreamType)streamType; - stream.Descriptors = streamDescriptors; - Streams[stream.PID] = stream; - } - if (!StreamDiagnostics.ContainsKey(streamPID)) - { - StreamDiagnostics[streamPID] = - new List(); - } - - return stream; - } - } -} diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs new file mode 100644 index 0000000000..91c8b27925 --- /dev/null +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using BDInfo.IO; +using MediaBrowser.Model.IO; + +namespace MediaBrowser.MediaEncoding.BdInfo +{ + class BdInfoDirectoryInfo : BDInfo.IO.IDirectoryInfo + { + IFileSystem _fileSystem = null; + + FileSystemMetadata _impl = null; + + public string Name => _impl.Name; + + public string FullName => _impl.FullName; + + public IDirectoryInfo Parent + { + get + { + var parentFolder = System.IO.Path.GetDirectoryName(_impl.FullName); + if (parentFolder != null) + { + return new BdInfoDirectoryInfo(_fileSystem, parentFolder); + } + return null; + } + } + + public BdInfoDirectoryInfo(IFileSystem fileSystem, string path) + { + _fileSystem = fileSystem; + _impl = _fileSystem.GetDirectoryInfo(path); + } + + private BdInfoDirectoryInfo(IFileSystem fileSystem, FileSystemMetadata impl) + { + _fileSystem = fileSystem; + _impl = impl; + } + + public IDirectoryInfo[] GetDirectories() + { + return Array.ConvertAll(_fileSystem.GetDirectories(_impl.FullName).ToArray(), + x => new BdInfoDirectoryInfo(_fileSystem, x)); + } + + public IFileInfo[] GetFiles() + { + return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName).ToArray(), + x => new BdInfoFileInfo(_fileSystem, x)); + } + + public IFileInfo[] GetFiles(string searchPattern) + { + return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(), + x => new BdInfoFileInfo(_fileSystem, x)); + } + + public IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption) + { + return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, + searchOption.HasFlag(System.IO.SearchOption.AllDirectories)).ToArray(), + x => new BdInfoFileInfo(_fileSystem, x)); + } + + public static IDirectoryInfo FromFileSystemPath(Model.IO.IFileSystem fs, string path) + { + return new BdInfoDirectoryInfo(fs, path); + } + } +} diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs index 3b6b91684c..3260f3051e 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using BDInfo; @@ -32,7 +32,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo throw new ArgumentNullException(nameof(path)); } - var bdrom = new BDROM(path, _fileSystem); + var bdrom = new BDROM(BdInfoDirectoryInfo.FromFileSystemPath(_fileSystem, path)); bdrom.Scan(); diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs new file mode 100644 index 0000000000..de9d7cb784 --- /dev/null +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs @@ -0,0 +1,40 @@ +using MediaBrowser.Model.IO; + +namespace MediaBrowser.MediaEncoding.BdInfo +{ + class BdInfoFileInfo : BDInfo.IO.IFileInfo + { + IFileSystem _fileSystem = null; + + FileSystemMetadata _impl = null; + + public string Name => _impl.Name; + + public string FullName => _impl.FullName; + + public string Extension => _impl.Extension; + + public long Length => _impl.Length; + + public bool IsDir => _impl.IsDirectory; + + public BdInfoFileInfo(IFileSystem fileSystem, FileSystemMetadata impl) + { + _fileSystem = fileSystem; + _impl = impl; + } + + public System.IO.Stream OpenRead() + { + return _fileSystem.GetFileStream(FullName, + FileOpenMode.Open, + FileAccessMode.Read, + FileShareMode.Read); + } + + public System.IO.StreamReader OpenText() + { + return new System.IO.StreamReader(OpenRead()); + } + } +} diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 558ea7d671..74a5e53b26 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -11,13 +11,13 @@ - + diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 27c8c1668f..0d2a3b8859 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -25,8 +25,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Photos", "Emby.Photos\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DvdLib", "DvdLib\DvdLib.csproj", "{713F42B5-878E-499D-A878-E4C652B1D5E8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BDInfo", "BDInfo\BDInfo.csproj", "{88AE38DF-19D7-406F-A6A9-09527719A21E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}" From 5cab79c839d2212ae638db403ec4d7ba0699f9a1 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 4 Dec 2019 21:39:27 +0100 Subject: [PATCH 057/252] 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 +++ .../EmbyTV/NfoConfigurationExtensions.cs | 19 + .../LiveTv/LiveTvManager.cs | 5 +- .../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 diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 7a26e0c37d..059b27e877 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 8dee7046e7..84e8c31f95 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 ef5928e480..420209edaf 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); - public EmbyTV(IServerApplicationHost appHost, + private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1); + + 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: + 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 (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; - } - - 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 0000000000..498aa3c266 --- /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 0000000000..83f5e84131 --- /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 d4bd598e38..d1f405679d 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 4bd729aac8..d6f5bb0c02 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 da8f99a3dd..ee440f5dbe 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 fbae4edb09..1bf981d79c 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 d5fa76c3ab..67c7fa343a 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 75b5573b67..d3d3001ed4 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -31,7 +31,9 @@ - + + + From 35151553e3fc9ddbe352744af8d832b1337491c8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 6 Dec 2019 20:40:06 +0100 Subject: [PATCH 058/252] 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 + .../Music/MultiDiscAlbumTests.cs | 57 +++ .../Subtitles/SubtitleParserTests.cs | 40 ++ .../TV/AbsoluteEpisodeNumberTests.cs | 61 +++ .../TV/DailyEpisodeTests.cs | 69 +++ .../TV/EpisodeNumberTests.cs | 421 +++++++++++++++ .../TV/EpisodeNumberWithoutSeasonTests.cs | 127 +++++ .../{ => TV}/EpisodePathParserTest.cs | 6 +- .../TV/EpisodeWithoutSeasonTests.cs | 56 ++ .../TV/MultiEpisodeTests.cs | 105 ++++ .../TV/SeasonFolderTests.cs | 112 ++++ .../TV/SeasonNumberTests.cs | 305 +++++++++++ .../TV/SimpleEpisodeTests.cs | 95 ++++ .../Video/BaseVideoTest.cs | 15 + .../Video/CleanDateTimeTests.cs | 143 ++++++ .../Video/CleanStringTests.cs | 133 +++++ .../Jellyfin.Naming.Tests/Video/ExtraTests.cs | 77 +++ .../Video/Format3DTests.cs | 78 +++ .../Video/MultiVersionTests.cs | 438 ++++++++++++++++ .../Jellyfin.Naming.Tests/Video/StackTests.cs | 478 ++++++++++++++++++ .../Jellyfin.Naming.Tests/Video/StubTests.cs | 55 ++ .../Video/VideoListResolverTests.cs | 457 +++++++++++++++++ .../Video/VideoResolverTests.cs | 275 ++++++++++ 34 files changed, 3653 insertions(+), 64 deletions(-) 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 rename tests/Jellyfin.Naming.Tests/{ => TV}/EpisodePathParserTest.cs (91%) 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 diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index d37be0e639..f8601c59fd 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 02b90310d7..e2da121dbb 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 9f70494d01..f83da44a43 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 ff6f20c47f..60d6b80ec5 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 b8eb8427e7..62ec701638 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 b78244cb33..97f3178e5c 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 2f42f77845..8416821c2a 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 5fa0041e07..e43e920c4c 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 91f443500f..05ba0c2e51 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 528636ecd3..8589b1b33c 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 c56c8f8f22..d8b02c9eae 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 75b5573b67..1d424a27df 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -24,6 +24,8 @@ + + diff --git a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs new file mode 100644 index 0000000000..eb69d915ca --- /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 0000000000..e8f14cdc45 --- /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 0000000000..9abbcc7bf0 --- /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 0000000000..29daf8cc37 --- /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 0000000000..03fae31596 --- /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 0000000000..00aa9ee7c2 --- /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/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs similarity index 91% rename from tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs rename to tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs index dd1e042155..8e8adb35b3 100644 --- a/tests/Jellyfin.Naming.Tests/EpisodePathParserTest.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs @@ -2,7 +2,7 @@ using Emby.Naming.Common; using Emby.Naming.TV; using Xunit; -namespace Jellyfin.Naming.Tests +namespace Jellyfin.Naming.Tests.TV { public class EpisodePathParserTest { @@ -23,7 +23,7 @@ namespace Jellyfin.Naming.Tests Assert.Equal(episode, res.EpisodeNumber); // testing other paths delimeter - var res2 = p.Parse(path.Replace('/', '\\'), false); + var res2 = p.Parse(path.Replace('/', '/'), false); Assert.True(res2.Success); Assert.Equal(name, res2.SeriesName); Assert.Equal(season, res2.SeasonNumber); @@ -45,7 +45,7 @@ namespace Jellyfin.Naming.Tests Assert.Equal(episode, res.EpisodeNumber); // testing other paths delimeter - var res2 = p.Parse(path.Replace('/', '\\'), false, fillExtendedInfo: false); + var res2 = p.Parse(path.Replace('/', '/'), false, fillExtendedInfo: false); Assert.True(res2.Success); Assert.Equal(name, res2.SeriesName); Assert.Null(res2.SeasonNumber); diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeWithoutSeasonTests.cs new file mode 100644 index 0000000000..c2851ccdb1 --- /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 0000000000..b15dd6b74c --- /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 0000000000..ffa8d34838 --- /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 0000000000..ba3c5ecac9 --- /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 0000000000..c9323c218e --- /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 0000000000..b993e241c0 --- /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 0000000000..bba73ad918 --- /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 0000000000..cd90ac236c --- /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 0000000000..1646237a0e --- /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 0000000000..ed3112936d --- /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 0000000000..b8674ec495 --- /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 0000000000..5faef0e3db --- /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 0000000000..96fa8c5a55 --- /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 0000000000..ef8a178989 --- /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 0000000000..5a3ce88869 --- /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); + } + } +} From c8f403238e3e98aee79959d6e52be9dcd7876256 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 6 Dec 2019 21:28:47 +0100 Subject: [PATCH 059/252] Move to .Net Core 3.1 --- Dockerfile | 2 +- Dockerfile.arm | 2 +- Dockerfile.arm64 | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 2 +- .../Jellyfin.MediaEncoding.Tests.csproj | 4 ++-- tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2a60bf1845..53a4252627 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG DOTNET_VERSION=3.0 +ARG DOTNET_VERSION=3.1 ARG FFMPEG_VERSION=latest FROM node:alpine as web-builder diff --git a/Dockerfile.arm b/Dockerfile.arm index fd3d1e0704..4d8fbb77d1 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -1,6 +1,6 @@ # Requires binfm_misc registration # https://github.com/multiarch/qemu-user-static#binfmt_misc-register -ARG DOTNET_VERSION=3.0 +ARG DOTNET_VERSION=3.1 FROM node:alpine as web-builder diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 3c1b2e3eab..d41268f13e 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -1,6 +1,6 @@ # Requires binfm_misc registration # https://github.com/multiarch/qemu-user-static#binfmt_misc-register -ARG DOTNET_VERSION=3.0 +ARG DOTNET_VERSION=3.1 FROM node:alpine as web-builder diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 110028176c..0ba7b328ed 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -3,7 +3,7 @@ jellyfin Exe - netcoreapp3.0 + netcoreapp3.1 false true diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index aa005b31d0..bc0114d1ef 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 false diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index 70e2d18516..7f6b905332 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -1,12 +1,12 @@ - netcoreapp3.0 + netcoreapp3.1 false - + diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index fe1518131c..79d2f21441 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 false From 2ef4ffd698fb4ae95754f76238e52cfb6162db24 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 11 Dec 2019 00:13:57 +0100 Subject: [PATCH 060/252] 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 + .../ApplicationHost.cs | 13 +- .../Branding/BrandingConfigurationFactory.cs | 1 + .../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 + .../SqliteDisplayPreferencesRepository.cs | 1 + .../Data/SqliteExtensions.cs | 1 + .../Data/SqliteUserDataRepository.cs | 1 + .../Data/SqliteUserRepository.cs | 1 + .../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 + .../Security/AuthorizationContext.cs | 1 + .../HttpServer/Security/SessionContext.cs | 1 + .../IO/ExtendedFileSystemInfo.cs | 1 + .../IO/FileRefresher.cs | 1 + .../IO/LibraryMonitor.cs | 1 + .../IO/ManagedFileSystem.cs | 1 + .../IO/MbLinkShortcutHandler.cs | 1 + .../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 + .../Resolvers/SpecialFolderResolver.cs | 1 + .../Library/Resolvers/TV/SeriesResolver.cs | 1 + .../Library/Resolvers/VideoResolver.cs | 1 + .../Library/SearchEngine.cs | 1 + .../Library/UserDataManager.cs | 1 + .../Library/UserManager.cs | 3 +- .../Library/UserViewManager.cs | 1 + Jellyfin.Api/Jellyfin.Api.csproj | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 14 +- .../ConfigurationUpdateEventArgs.cs | 1 + .../Configuration/IConfigurationFactory.cs | 1 + .../Configuration/IConfigurationManager.cs | 1 + .../Cryptography/PasswordHash.cs | 25 +-- MediaBrowser.Common/Events/EventHelper.cs | 8 +- .../Extensions/BaseExtensions.cs | 7 +- .../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.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 | 154 +++++++++--------- MediaBrowser.Common/Plugins/IPlugin.cs | 31 ++-- .../Plugins/IPluginAssembly.cs | 14 ++ .../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, 397 insertions(+), 294 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 diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 7258beaf49..3be5bbbaf1 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 64692c3703..d3c87edffe 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 b622a31674..ac8af66a20 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 a30e939121..b03c4d1824 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 7be72319ea..633343bb6d 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 8c625539ab..ae979682e6 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 93000ae127..15aee63a05 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 6016fed079..aae416b374 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 62aeb9bcb9..fe64f1b157 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 6e1baddfed..de2e123af3 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 2712fc8c5e..36e0e5e26d 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 5774c04153..039e2c1383 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 1fa556ec95..8006b86948 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 2b8a5bdc56..5a25b5d3e6 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 776074b728..de83b023d7 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 0654132f41..b7f6438193 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 2a8f2d6b33..8a5387e9b4 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 5c094ddd2d..2c2f19cd30 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 d474f1c6ba..8087419ceb 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 c87793072e..55c24ccc05 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 22955850ab..f6c37e4e5b 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 a042320c91..c82c93ffc3 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 f0d43e665b..ff75efa592 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 2393f1f458..ef73170506 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 bfa49ac5ff..f8b7541515 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 02ad3c1a89..219f73c785 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 3d622b3fce..fcf0360c79 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 fde4d70599..5ca5087765 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 d69b0909dd..a6eb1152fb 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 e290c62e16..4e4ef3be01 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 5f938e59a8..f85d52dbc1 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 dbb3503c41..e0aa18e895 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 e431da1481..3e22080fc0 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 c1c8c3eb30..1795651fd7 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 2aefc9fe5d..b0126f7fa5 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 a62b4e7aff..cefcaa835f 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 5015937256..1c3496e5d5 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 8b9028f6bc..7cb113a58c 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 58421aaf19..03b5b748df 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 129faeaab0..e8884bca04 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 166952c646..a6a0f5b032 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 3150f3367c..5be1444525 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 4b5b11f01f..cf92ddbcd7 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 b1fb8cc635..7777efc3b1 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 442fbabd17..d8da0888e4 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 e6696b8c4c..574b63ae63 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 40b397edc2..c99018e40c 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 fd50f156af..acf3a3b231 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 94f60ea621..ab036eca7a 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 9a71868988..3eb64c29c6 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 6942088fe8..d8ef8192ac 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 ed7d8aa402..f28f4a538f 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 22193c997e..0899832a72 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 6b9f4d052c..1652ad9747 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 1ec5783716..29af6670b4 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 9d4bd9e593..7e3b27a123 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 c4bb861b8d..43302bb3fe 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 0b93ebeb81..1e2e0704c1 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 a71ae82502..e1eb23652d 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 a68562fc2c..5e672f221a 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 1030ed39d2..eca60b1336 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 7cc9eabc8e..e39d85bc9b 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 62268fce90..6404d64762 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 11d6c737ac..76ae147206 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 48d33c26c1..5d4f17861e 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 85bfa154af..c7f6de24f9 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 322819b052..935deb71cc 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 a2818b45da..f4e5337de2 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 110028176c..63410f6060 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 344aecf530..828415c185 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 4c4060096d..9b4ed772db 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 caf2edd836..7773596afe 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 19b8be47a9..3477c1c041 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 b67315df62..c9d3226ac8 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 33473c2be7..08964420e7 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 78a73f07e0..2ecbc6539b 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 0000000000..48e758ee4c --- /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 0000000000..4e5d4e9ca8 --- /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 9b064a40df..22130c5a1e 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 b2d9aea3a7..863192809f 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 6668e98aa5..68a24aabaa 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 889fbfa5ab..567fcdda19 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 5ca9897eb4..8cc48c55f5 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 18c4b181f5..8207a45f35 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 0de034b0ee..d711ad64a4 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 23ba341738..534e22eddf 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 0b99dc9103..6bd7dd1d64 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 6ef891d66e..25519ccc0d 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,11 +166,38 @@ namespace MediaBrowser.Common.Plugins } } } + return _configuration; } + protected set => _configuration = value; } + /// + /// 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. + /// + /// The configuration file path. + public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName); + + /// + /// Gets the plugin's configuration. + /// + /// The configuration. + BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration; + + /// + public void SetStartupInfo(Action directoryCreateFn) + { + // hack alert, until the .net core transition is complete + _directoryCreateFn = directoryCreateFn; + } + private TConfigurationType LoadConfiguration() { var path = ConfigurationFilePath; @@ -169,35 +213,7 @@ namespace MediaBrowser.Common.Plugins } /// - /// 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 - /// - /// The configuration file path. - public string ConfigurationFilePath => Path.Combine(ApplicationPaths.PluginConfigurationsPath, ConfigurationFileName); - - /// - /// Initializes a new instance of the class. - /// - /// The application paths. - /// The XML serializer. - protected BasePlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) - { - ApplicationPaths = applicationPaths; - XmlSerializer = xmlSerializer; - } - - /// - /// The _save lock - /// - private readonly object _configurationSaveLock = new object(); - - /// - /// 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 7bd90c964b..001ca8be8a 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,25 +62,25 @@ 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 - /// - /// The configuration. - /// configuration - void UpdateConfiguration(BasePluginConfiguration configuration); - - /// - /// Gets the plugin's configuration + /// Gets the plugin's configuration. /// /// The configuration. BasePluginConfiguration Configuration { get; } + /// + /// Completely overwrites the current configuration with a new copy + /// Returns true or false indicating success or failure. + /// + /// 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 0000000000..388ac61ab9 --- /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 af69055aa9..92141ba526 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 0445397ad8..a6422e2c80 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 7d38ddb6e5..f23af47993 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 e49812f150..a09c1916c5 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 36e124ddfc..8bbb231ce1 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 46f10c84fd..c8967f9dbf 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 3c65156e30..5248ea4c13 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 1fd706857a..cf8f030023 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 75b5573b67..b6f5cf01b0 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -31,8 +31,12 @@ + + + + + + + + + + - - From 554c967dd6a0e7e75e737912e869b7269cd72081 Mon Sep 17 00:00:00 2001 From: Neil Burrows Date: Fri, 13 Dec 2019 10:29:38 +0000 Subject: [PATCH 061/252] Add Excluded Tags using SQLite parameters --- .../Data/SqliteItemRepository.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 69cfcb67b5..3f96b43b3b 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4593,10 +4593,26 @@ namespace Emby.Server.Implementations.Data if (query.ExcludeInheritedTags.Length > 0) { - var tagValues = query.ExcludeInheritedTags.Select(i => "'" + GetCleanValue(i) + "'"); - var tagValuesList = string.Join(",", tagValues); + var paramName = "@ExcludeInheritedTags"; - whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + tagValuesList + ")) is null)"); + if (statement == null) + { + List tagParamList = new List(); + + for (int index = 0; index < query.ExcludeInheritedTags.Length; index++) + { + tagParamList.Add(paramName + index); + } + + whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + string.Join(",", tagParamList) + ")) is null)"); + } + else + { + for (int index = 0; index < query.ExcludeInheritedTags.Length; index++) + { + statement.TryBind(paramName + index, GetCleanValue(query.ExcludeInheritedTags[0])); + } + } } if (query.SeriesStatuses.Length > 0) From 12bb4a92eb69a0dfe06add8f4aebf5da749c6126 Mon Sep 17 00:00:00 2001 From: Neil Burrows Date: Fri, 13 Dec 2019 11:08:19 +0000 Subject: [PATCH 062/252] Tidying up code --- .../Data/SqliteItemRepository.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 3f96b43b3b..2a74a826b9 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4594,17 +4594,10 @@ namespace Emby.Server.Implementations.Data if (query.ExcludeInheritedTags.Length > 0) { var paramName = "@ExcludeInheritedTags"; - if (statement == null) { - List tagParamList = new List(); - - for (int index = 0; index < query.ExcludeInheritedTags.Length; index++) - { - tagParamList.Add(paramName + index); - } - - whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + string.Join(",", tagParamList) + ")) is null)"); + int index = 0; + whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + string.Join(",", query.ExcludeInheritedTags.Select(t => paramName + index++)) + ")) is null)"); } else { From 55317b5c749a36411f6582f8cf6a9477f1a2c880 Mon Sep 17 00:00:00 2001 From: Neil Burrows Date: Fri, 13 Dec 2019 13:45:04 +0000 Subject: [PATCH 063/252] Fixing index error & split out code for readability --- .../Data/SqliteItemRepository.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2a74a826b9..b664738cb5 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4596,14 +4596,19 @@ namespace Emby.Server.Implementations.Data var paramName = "@ExcludeInheritedTags"; if (statement == null) { - int index = 0; - whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + string.Join(",", query.ExcludeInheritedTags.Select(t => paramName + index++)) + ")) is null)"); + List paramList = new List(); + for (int index = 0; index < query.ExcludeInheritedTags.Length; index++) + { + paramList.Add(paramName + index); + } + + whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + string.Join(",", paramList) + ")) is null)"); } else { for (int index = 0; index < query.ExcludeInheritedTags.Length; index++) { - statement.TryBind(paramName + index, GetCleanValue(query.ExcludeInheritedTags[0])); + statement.TryBind(paramName + index, GetCleanValue(query.ExcludeInheritedTags[index])); } } } From 5a8e972952e45ff7361d8370e289eb44911b914c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 13 Dec 2019 20:11:37 +0100 Subject: [PATCH 064/252] Enable TreatWarningsAsErrors for some projects Analyzers are only run in debug build, so setting TreatWarningsAsErrors for release build will catch the compiler warnings until we resolve all analyzer warnings. --- Emby.Naming/Audio/AlbumParser.cs | 3 +++ Emby.Naming/Audio/AudioFileParser.cs | 3 +++ Emby.Naming/Audio/MultiPartResult.cs | 3 +++ .../AudioBook/AudioBookFilePathParser.cs | 3 +++ .../AudioBook/AudioBookFilePathParserResult.cs | 3 +++ Emby.Naming/AudioBook/AudioBookInfo.cs | 3 +++ Emby.Naming/AudioBook/AudioBookListResolver.cs | 3 +++ Emby.Naming/AudioBook/AudioBookResolver.cs | 3 +++ Emby.Naming/Common/EpisodeExpression.cs | 3 +++ Emby.Naming/Common/MediaType.cs | 3 +++ Emby.Naming/Common/NamingOptions.cs | 3 +++ Emby.Naming/Emby.Naming.csproj | 10 +++++++--- Emby.Naming/Subtitles/SubtitleInfo.cs | 3 +++ Emby.Naming/Subtitles/SubtitleParser.cs | 3 +++ Emby.Naming/TV/EpisodeInfo.cs | 3 +++ Emby.Naming/TV/EpisodePathParser.cs | 3 +++ Emby.Naming/TV/EpisodePathParserResult.cs | 3 +++ Emby.Naming/TV/EpisodeResolver.cs | 3 +++ Emby.Naming/TV/SeasonPathParser.cs | 3 +++ Emby.Naming/TV/SeasonPathParserResult.cs | 3 +++ Emby.Naming/Video/CleanDateTimeParser.cs | 3 +++ Emby.Naming/Video/CleanDateTimeResult.cs | 5 +++++ Emby.Naming/Video/CleanStringParser.cs | 3 +++ Emby.Naming/Video/CleanStringResult.cs | 4 ++++ Emby.Naming/Video/ExtraResolver.cs | 3 +++ Emby.Naming/Video/ExtraResult.cs | 4 ++++ Emby.Naming/Video/ExtraRule.cs | 6 ++++++ Emby.Naming/Video/ExtraRuleType.cs | 5 +++++ Emby.Naming/Video/FileStack.cs | 13 +++++++++---- Emby.Naming/Video/FlagParser.cs | 3 +++ Emby.Naming/Video/Format3DParser.cs | 3 +++ Emby.Naming/Video/Format3DResult.cs | 3 +++ Emby.Naming/Video/Format3DRule.cs | 4 ++++ Emby.Naming/Video/StackResolver.cs | 3 +++ Emby.Naming/Video/StackResult.cs | 3 +++ Emby.Naming/Video/StubResolver.cs | 3 +++ Emby.Naming/Video/StubResult.cs | 3 +++ Emby.Naming/Video/StubTypeRule.cs | 3 +++ Emby.Naming/Video/VideoFileInfo.cs | 1 + Emby.Naming/Video/VideoInfo.cs | 17 ++++++++++------- Emby.Naming/Video/VideoListResolver.cs | 3 +++ Emby.Naming/Video/VideoResolver.cs | 3 +++ Emby.Photos/Emby.Photos.csproj | 10 +++++++--- .../Emby.Server.Implementations.csproj | 6 +++--- .../Library/UserManager.cs | 4 ++-- .../LiveTv/EmbyTV/DirectRecorder.cs | 3 ++- .../LiveTv/Listings/SchedulesDirect.cs | 6 +++--- .../LiveTv/TunerHosts/SharedHttpStream.cs | 3 ++- .../Updates/InstallationManager.cs | 4 ++-- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- 51 files changed, 175 insertions(+), 32 deletions(-) diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs index e8d7655525..4975b8e19d 100644 --- a/Emby.Naming/Audio/AlbumParser.cs +++ b/Emby.Naming/Audio/AlbumParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Globalization; using System.IO; diff --git a/Emby.Naming/Audio/AudioFileParser.cs b/Emby.Naming/Audio/AudioFileParser.cs index 609eb779ad..9f21e93dc4 100644 --- a/Emby.Naming/Audio/AudioFileParser.cs +++ b/Emby.Naming/Audio/AudioFileParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.IO; using System.Linq; diff --git a/Emby.Naming/Audio/MultiPartResult.cs b/Emby.Naming/Audio/MultiPartResult.cs index 00e4a9eb2e..8f68d97fa8 100644 --- a/Emby.Naming/Audio/MultiPartResult.cs +++ b/Emby.Naming/Audio/MultiPartResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Audio { public class MultiPartResult diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs index ea7f06c8cb..8dc2e1b97c 100644 --- a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs +++ b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Globalization; using System.IO; diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs index f845e82435..68d6ca4d46 100644 --- a/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs +++ b/Emby.Naming/AudioBook/AudioBookFilePathParserResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.AudioBook { public class AudioBookFilePathParserResult diff --git a/Emby.Naming/AudioBook/AudioBookInfo.cs b/Emby.Naming/AudioBook/AudioBookInfo.cs index d53f53c528..b0b5bd881f 100644 --- a/Emby.Naming/AudioBook/AudioBookInfo.cs +++ b/Emby.Naming/AudioBook/AudioBookInfo.cs @@ -7,6 +7,9 @@ namespace Emby.Naming.AudioBook /// public class AudioBookInfo { + /// + /// Initializes a new instance of the class. + /// public AudioBookInfo() { Files = new List(); diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index 414ef11830..97f3592857 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Collections.Generic; using System.Linq; using Emby.Naming.Common; diff --git a/Emby.Naming/AudioBook/AudioBookResolver.cs b/Emby.Naming/AudioBook/AudioBookResolver.cs index 4a2b516d0e..0b0d2035e7 100644 --- a/Emby.Naming/AudioBook/AudioBookResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookResolver.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.IO; using System.Linq; diff --git a/Emby.Naming/Common/EpisodeExpression.cs b/Emby.Naming/Common/EpisodeExpression.cs index 136d8189dd..30a74fb657 100644 --- a/Emby.Naming/Common/EpisodeExpression.cs +++ b/Emby.Naming/Common/EpisodeExpression.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Text.RegularExpressions; diff --git a/Emby.Naming/Common/MediaType.cs b/Emby.Naming/Common/MediaType.cs index a7b08bf793..a61f10489c 100644 --- a/Emby.Naming/Common/MediaType.cs +++ b/Emby.Naming/Common/MediaType.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Common { public enum MediaType diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 4c2c43437a..69e68660d4 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Linq; using System.Text.RegularExpressions; diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 7258beaf49..c69b36894c 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -6,6 +6,10 @@ true + + true + + @@ -21,13 +25,13 @@ https://github.com/jellyfin/jellyfin - - + + ../jellyfin.ruleset diff --git a/Emby.Naming/Subtitles/SubtitleInfo.cs b/Emby.Naming/Subtitles/SubtitleInfo.cs index 96fce04d76..fe42846c61 100644 --- a/Emby.Naming/Subtitles/SubtitleInfo.cs +++ b/Emby.Naming/Subtitles/SubtitleInfo.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Subtitles { public class SubtitleInfo diff --git a/Emby.Naming/Subtitles/SubtitleParser.cs b/Emby.Naming/Subtitles/SubtitleParser.cs index ac9432d57d..99680c6221 100644 --- a/Emby.Naming/Subtitles/SubtitleParser.cs +++ b/Emby.Naming/Subtitles/SubtitleParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.IO; using System.Linq; diff --git a/Emby.Naming/TV/EpisodeInfo.cs b/Emby.Naming/TV/EpisodeInfo.cs index de79b8bbaf..667129a57d 100644 --- a/Emby.Naming/TV/EpisodeInfo.cs +++ b/Emby.Naming/TV/EpisodeInfo.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.TV { public class EpisodeInfo diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs index a98e8221a5..4fac543f92 100644 --- a/Emby.Naming/TV/EpisodePathParser.cs +++ b/Emby.Naming/TV/EpisodePathParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.Globalization; diff --git a/Emby.Naming/TV/EpisodePathParserResult.cs b/Emby.Naming/TV/EpisodePathParserResult.cs index 996edfc506..3acbbc101c 100644 --- a/Emby.Naming/TV/EpisodePathParserResult.cs +++ b/Emby.Naming/TV/EpisodePathParserResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.TV { public class EpisodePathParserResult diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs index 2d7bcb6382..5e115fc75d 100644 --- a/Emby.Naming/TV/EpisodeResolver.cs +++ b/Emby.Naming/TV/EpisodeResolver.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.IO; using System.Linq; diff --git a/Emby.Naming/TV/SeasonPathParser.cs b/Emby.Naming/TV/SeasonPathParser.cs index f34faf8e83..e5f90e9660 100644 --- a/Emby.Naming/TV/SeasonPathParser.cs +++ b/Emby.Naming/TV/SeasonPathParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Globalization; using System.IO; diff --git a/Emby.Naming/TV/SeasonPathParserResult.cs b/Emby.Naming/TV/SeasonPathParserResult.cs index 548dbd5d22..57c2347548 100644 --- a/Emby.Naming/TV/SeasonPathParserResult.cs +++ b/Emby.Naming/TV/SeasonPathParserResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.TV { public class SeasonPathParserResult diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index c6b6039d4d..a9db4ccccd 100644 --- a/Emby.Naming/Video/CleanDateTimeParser.cs +++ b/Emby.Naming/Video/CleanDateTimeParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Globalization; using System.IO; diff --git a/Emby.Naming/Video/CleanDateTimeResult.cs b/Emby.Naming/Video/CleanDateTimeResult.cs index 6bf24e4d85..a7581972e4 100644 --- a/Emby.Naming/Video/CleanDateTimeResult.cs +++ b/Emby.Naming/Video/CleanDateTimeResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Video { public class CleanDateTimeResult @@ -7,11 +10,13 @@ namespace Emby.Naming.Video /// /// The name. public string Name { get; set; } + /// /// Gets or sets the year. /// /// The year. public int? Year { get; set; } + /// /// Gets or sets a value indicating whether this instance has changed. /// diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs index 02b90310d7..be028c662e 100644 --- a/Emby.Naming/Video/CleanStringParser.cs +++ b/Emby.Naming/Video/CleanStringParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Collections.Generic; using System.Text.RegularExpressions; diff --git a/Emby.Naming/Video/CleanStringResult.cs b/Emby.Naming/Video/CleanStringResult.cs index b3bc597125..786fe9e028 100644 --- a/Emby.Naming/Video/CleanStringResult.cs +++ b/Emby.Naming/Video/CleanStringResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Video { public class CleanStringResult @@ -7,6 +10,7 @@ namespace Emby.Naming.Video /// /// The name. public string Name { get; set; } + /// /// Gets or sets a value indicating whether this instance has changed. /// diff --git a/Emby.Naming/Video/ExtraResolver.cs b/Emby.Naming/Video/ExtraResolver.cs index 9f70494d01..989ede206e 100644 --- a/Emby.Naming/Video/ExtraResolver.cs +++ b/Emby.Naming/Video/ExtraResolver.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.IO; using System.Linq; diff --git a/Emby.Naming/Video/ExtraResult.cs b/Emby.Naming/Video/ExtraResult.cs index ff6f20c47f..6081a44942 100644 --- a/Emby.Naming/Video/ExtraResult.cs +++ b/Emby.Naming/Video/ExtraResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Video { public class ExtraResult @@ -7,6 +10,7 @@ namespace Emby.Naming.Video /// /// The type of the extra. public string ExtraType { get; set; } + /// /// Gets or sets the rule. /// diff --git a/Emby.Naming/Video/ExtraRule.cs b/Emby.Naming/Video/ExtraRule.cs index b8eb8427e7..cfce79fd08 100644 --- a/Emby.Naming/Video/ExtraRule.cs +++ b/Emby.Naming/Video/ExtraRule.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using Emby.Naming.Common; namespace Emby.Naming.Video @@ -9,16 +12,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; } + /// /// 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/ExtraRuleType.cs b/Emby.Naming/Video/ExtraRuleType.cs index 565239ff9a..2bf2799ff7 100644 --- a/Emby.Naming/Video/ExtraRuleType.cs +++ b/Emby.Naming/Video/ExtraRuleType.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Video { public enum ExtraRuleType @@ -6,10 +9,12 @@ namespace Emby.Naming.Video /// The suffix /// Suffix = 0, + /// /// The filename /// Filename = 1, + /// /// The regex /// diff --git a/Emby.Naming/Video/FileStack.cs b/Emby.Naming/Video/FileStack.cs index 584bdf2d2f..56adf6add0 100644 --- a/Emby.Naming/Video/FileStack.cs +++ b/Emby.Naming/Video/FileStack.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.Linq; @@ -6,15 +9,17 @@ namespace Emby.Naming.Video { public class FileStack { - public string Name { get; set; } - public List Files { get; set; } - public bool IsDirectoryStack { get; set; } - public FileStack() { Files = new List(); } + public string Name { get; set; } + + public List Files { get; set; } + + public bool IsDirectoryStack { get; set; } + public bool ContainsFile(string file, bool isDirectory) { if (IsDirectoryStack == isDirectory) diff --git a/Emby.Naming/Video/FlagParser.cs b/Emby.Naming/Video/FlagParser.cs index bb129499be..acf3438c22 100644 --- a/Emby.Naming/Video/FlagParser.cs +++ b/Emby.Naming/Video/FlagParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.IO; using Emby.Naming.Common; diff --git a/Emby.Naming/Video/Format3DParser.cs b/Emby.Naming/Video/Format3DParser.cs index 333a48641e..25905f33c1 100644 --- a/Emby.Naming/Video/Format3DParser.cs +++ b/Emby.Naming/Video/Format3DParser.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Linq; using Emby.Naming.Common; diff --git a/Emby.Naming/Video/Format3DResult.cs b/Emby.Naming/Video/Format3DResult.cs index 40fc31e082..6ebd72f6ba 100644 --- a/Emby.Naming/Video/Format3DResult.cs +++ b/Emby.Naming/Video/Format3DResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Collections.Generic; namespace Emby.Naming.Video diff --git a/Emby.Naming/Video/Format3DRule.cs b/Emby.Naming/Video/Format3DRule.cs index dc260175af..ae9fb5b19f 100644 --- a/Emby.Naming/Video/Format3DRule.cs +++ b/Emby.Naming/Video/Format3DRule.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Video { public class Format3DRule @@ -7,6 +10,7 @@ namespace Emby.Naming.Video /// /// The token. public string Token { get; set; } + /// /// Gets or sets the preceeding token. /// diff --git a/Emby.Naming/Video/StackResolver.cs b/Emby.Naming/Video/StackResolver.cs index b8ba42da4f..e7a769ae6b 100644 --- a/Emby.Naming/Video/StackResolver.cs +++ b/Emby.Naming/Video/StackResolver.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.IO; diff --git a/Emby.Naming/Video/StackResult.cs b/Emby.Naming/Video/StackResult.cs index de35d2825a..31ef2d69c5 100644 --- a/Emby.Naming/Video/StackResult.cs +++ b/Emby.Naming/Video/StackResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System.Collections.Generic; namespace Emby.Naming.Video diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs index b78244cb33..bbf399677d 100644 --- a/Emby.Naming/Video/StubResolver.cs +++ b/Emby.Naming/Video/StubResolver.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.IO; using System.Linq; diff --git a/Emby.Naming/Video/StubResult.cs b/Emby.Naming/Video/StubResult.cs index 7a62e7b981..5ac85528f5 100644 --- a/Emby.Naming/Video/StubResult.cs +++ b/Emby.Naming/Video/StubResult.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Video { public struct StubResult diff --git a/Emby.Naming/Video/StubTypeRule.cs b/Emby.Naming/Video/StubTypeRule.cs index d765321504..17c3ef8c5e 100644 --- a/Emby.Naming/Video/StubTypeRule.cs +++ b/Emby.Naming/Video/StubTypeRule.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + namespace Emby.Naming.Video { public class StubTypeRule diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs index 2f42f77845..250a1ec45d 100644 --- a/Emby.Naming/Video/VideoFileInfo.cs +++ b/Emby.Naming/Video/VideoFileInfo.cs @@ -77,6 +77,7 @@ namespace Emby.Naming.Video /// The file name without extension. public string FileNameWithoutExtension => !IsDirectory ? System.IO.Path.GetFileNameWithoutExtension(Path) : System.IO.Path.GetFileName(Path); + /// public override string ToString() { // Makes debugging easier diff --git a/Emby.Naming/Video/VideoInfo.cs b/Emby.Naming/Video/VideoInfo.cs index f576b6ca28..a585bb99a2 100644 --- a/Emby.Naming/Video/VideoInfo.cs +++ b/Emby.Naming/Video/VideoInfo.cs @@ -7,6 +7,16 @@ namespace Emby.Naming.Video /// public class VideoInfo { + /// + /// Initializes a new instance of the class. + /// + public VideoInfo() + { + Files = new List(); + Extras = new List(); + AlternateVersions = new List(); + } + /// /// Gets or sets the name. /// @@ -36,12 +46,5 @@ namespace Emby.Naming.Video /// /// The alternate versions. public List AlternateVersions { get; set; } - - public VideoInfo() - { - Files = new List(); - Extras = new List(); - AlternateVersions = new List(); - } } } diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 5fa0041e07..5a32846bf3 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.Collections.Generic; using System.IO; diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 91f443500f..5a93e1eafe 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -1,3 +1,6 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1600 + using System; using System.IO; using System.Linq; diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 64692c3703..4bd0f1e4a1 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -1,5 +1,9 @@ + + true + + @@ -20,12 +24,12 @@ true - - + + ../jellyfin.ruleset diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index fde4d70599..6b45f75bca 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -50,13 +50,13 @@ true - - + + ../jellyfin.ruleset diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 85bfa154af..fd414616f1 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -392,7 +392,7 @@ namespace Emby.Server.Implementations.Library if (providers.Length == 0) { // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found - _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user.Name, user.Policy.AuthenticationProviderId); + _logger.LogWarning("User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", user?.Name, user?.Policy.AuthenticationProviderId); providers = new IAuthenticationProvider[] { _invalidAuthProvider }; } @@ -472,7 +472,7 @@ namespace Emby.Server.Implementations.Library if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) - && user.Configuration.EnableLocalPassword + && user?.Configuration.EnableLocalPassword == true && !string.IsNullOrEmpty(user.EasyPassword)) { // Check easy password diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 8dee7046e7..84e8c31f95 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/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 838ac97d77..1dd794da0d 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; @@ -12,7 +13,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; @@ -663,7 +663,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings try { - return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false); + return await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false); } catch (HttpException ex) { @@ -738,7 +738,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings httpOptions.RequestHeaders["token"] = token; - using (await _httpClient.SendAsync(httpOptions, "PUT").ConfigureAwait(false)) + using (await _httpClient.SendAsync(httpOptions, HttpMethod.Put).ConfigureAwait(false)) { } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index 7584953624..0d94f4b329 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -64,7 +65,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts httpRequestOptions.RequestHeaders[header.Key] = header.Value; } - var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false); + var response = await _httpClient.SendAsync(httpRequestOptions, HttpMethod.Get).ConfigureAwait(false); var extension = "ts"; var requiresRemux = false; diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 2705e0628b..c897036eb8 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; +using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; @@ -189,7 +189,7 @@ namespace Emby.Server.Implementations.Updates } /// - public async IAsyncEnumerable GetAvailablePluginUpdates(CancellationToken cancellationToken = default) + public async IAsyncEnumerable GetAvailablePluginUpdates([EnumeratorCancellation] CancellationToken cancellationToken = default) { var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false); diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index a2818b45da..73ffaa53d9 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 110028176c..5ec791af9a 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -24,8 +24,8 @@ - - + + From 1b2453b4e90b90f19e0527f34f60912eb8ad9474 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sat, 14 Dec 2019 12:51:27 +0900 Subject: [PATCH 065/252] fix playlist deletion --- .../Library/LibraryManager.cs | 2 +- .../Playlists/PlaylistManager.cs | 35 +++++++++++++------ MediaBrowser.Api/Library/LibraryService.cs | 5 ++- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6942088fe8..b03b7e67eb 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -394,7 +394,7 @@ namespace Emby.Server.Implementations.Library foreach (var fileSystemInfo in item.GetDeletePaths().ToList()) { - if (File.Exists(fileSystemInfo.FullName)) + if (File.Exists(fileSystemInfo.FullName) || Directory.Exists(fileSystemInfo.FullName)) { try { diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 0f58e43ed1..b26f4026c5 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -56,10 +56,8 @@ namespace Emby.Server.Implementations.Playlists { var name = options.Name; - var folderName = _fileSystem.GetValidFilename(name) + " [playlist]"; - + var folderName = _fileSystem.GetValidFilename(name); var parentFolder = GetPlaylistsFolder(Guid.Empty); - if (parentFolder == null) { throw new ArgumentException(); @@ -253,11 +251,13 @@ namespace Emby.Server.Implementations.Playlists SavePlaylistFile(playlist); } - _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) - { - ForceSave = true - - }, RefreshPriority.High); + _providerManager.QueueRefresh( + playlist.Id, + new MetadataRefreshOptions(new DirectoryService(_fileSystem)) + { + ForceSave = true + }, + RefreshPriority.High); } public void MoveItem(string playlistId, string entryId, int newIndex) @@ -303,7 +303,8 @@ namespace Emby.Server.Implementations.Playlists private void SavePlaylistFile(Playlist item) { - // This is probably best done as a metatata provider, but saving a file over itself will first require some core work to prevent this from happening when not needed + // this is probably best done as a metadata provider + // saving a file over itself will require some work to prevent this from happening when not needed var playlistPath = item.Path; var extension = Path.GetExtension(playlistPath); @@ -335,12 +336,14 @@ namespace Emby.Server.Implementations.Playlists { entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value); } + playlist.PlaylistEntries.Add(entry); } string text = new WplContent().ToText(playlist); File.WriteAllText(playlistPath, text); } + if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase)) { var playlist = new ZplPlaylist(); @@ -375,6 +378,7 @@ namespace Emby.Server.Implementations.Playlists string text = new ZplContent().ToText(playlist); File.WriteAllText(playlistPath, text); } + if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase)) { var playlist = new M3uPlaylist(); @@ -398,12 +402,14 @@ namespace Emby.Server.Implementations.Playlists { entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value); } + playlist.PlaylistEntries.Add(entry); } string text = new M3uContent().ToText(playlist); File.WriteAllText(playlistPath, text); } + if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase)) { var playlist = new M3uPlaylist(); @@ -427,12 +433,14 @@ namespace Emby.Server.Implementations.Playlists { entry.Duration = TimeSpan.FromTicks(child.RunTimeTicks.Value); } + playlist.PlaylistEntries.Add(entry); } string text = new M3u8Content().ToText(playlist); File.WriteAllText(playlistPath, text); } + if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase)) { var playlist = new PlsPlaylist(); @@ -448,6 +456,7 @@ namespace Emby.Server.Implementations.Playlists { entry.Length = TimeSpan.FromTicks(child.RunTimeTicks.Value); } + playlist.PlaylistEntries.Add(entry); } @@ -473,7 +482,7 @@ namespace Emby.Server.Implementations.Playlists throw new ArgumentException("File absolute path was null or empty.", nameof(fileAbsolutePath)); } - if (!folderPath.EndsWith(Path.DirectorySeparatorChar.ToString())) + if (!folderPath.EndsWith(Path.DirectorySeparatorChar)) { folderPath = folderPath + Path.DirectorySeparatorChar; } @@ -481,7 +490,11 @@ namespace Emby.Server.Implementations.Playlists var folderUri = new Uri(folderPath); var fileAbsoluteUri = new Uri(fileAbsolutePath); - if (folderUri.Scheme != fileAbsoluteUri.Scheme) { return fileAbsolutePath; } // path can't be made relative. + // path can't be made relative + if (folderUri.Scheme != fileAbsoluteUri.Scheme) + { + return fileAbsolutePath; + } var relativeUri = folderUri.MakeRelativeUri(fileAbsoluteUri); string relativePath = Uri.UnescapeDataString(relativeUri.ToString()); diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 0cc5e112f4..b1ea3e2627 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -1006,8 +1006,8 @@ namespace MediaBrowser.Api.Library public void Delete(DeleteItems request) { var ids = string.IsNullOrWhiteSpace(request.Ids) - ? Array.Empty() - : request.Ids.Split(','); + ? Array.Empty() + : request.Ids.Split(','); foreach (var i in ids) { @@ -1028,7 +1028,6 @@ namespace MediaBrowser.Api.Library _libraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = true - }, true); } } From 623e85a9e4472b8dd5b31574daf15e85866a25f7 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sat, 14 Dec 2019 22:34:18 +0900 Subject: [PATCH 066/252] add suggested changes Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Library/LibraryManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index b03b7e67eb..3bb07bc31c 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -392,9 +392,9 @@ namespace Emby.Server.Implementations.Library // Add this flag to GetDeletePaths if required in the future var isRequiredForDelete = true; - foreach (var fileSystemInfo in item.GetDeletePaths().ToList()) + foreach (var fileSystemInfo in item.GetDeletePaths()) { - if (File.Exists(fileSystemInfo.FullName) || Directory.Exists(fileSystemInfo.FullName)) + if (fileSystemInfo.IsDirectory ? Directory.Exists(fileSystemInfo.FullName) : File.Exists(fileSystemInfo.FullName)) { try { From d9c0721e3dee52ec8a101cf3d35ca18ef5bd6cf3 Mon Sep 17 00:00:00 2001 From: Neil Burrows Date: Sat, 14 Dec 2019 17:48:30 +0000 Subject: [PATCH 067/252] Reformatting code --- .../Data/SqliteItemRepository.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index b664738cb5..ef1fe07c16 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4596,13 +4596,9 @@ namespace Emby.Server.Implementations.Data var paramName = "@ExcludeInheritedTags"; if (statement == null) { - List paramList = new List(); - for (int index = 0; index < query.ExcludeInheritedTags.Length; index++) - { - paramList.Add(paramName + index); - } - - whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + string.Join(",", paramList) + ")) is null)"); + int index = 0; + string excludedTags = string.Join(",", query.ExcludeInheritedTags.Select(t => paramName + index++)); + whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + excludedTags + ")) is null)"); } else { From 28e922326dd7ac480a3ec0dc7f3465ea351ffed1 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 15 Dec 2019 13:38:59 +0900 Subject: [PATCH 068/252] remove space Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 3bb07bc31c..1f5bd7deb5 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -392,7 +392,7 @@ namespace Emby.Server.Implementations.Library // Add this flag to GetDeletePaths if required in the future var isRequiredForDelete = true; - foreach (var fileSystemInfo in item.GetDeletePaths()) + foreach (var fileSystemInfo in item.GetDeletePaths()) { if (fileSystemInfo.IsDirectory ? Directory.Exists(fileSystemInfo.FullName) : File.Exists(fileSystemInfo.FullName)) { From 742d84e53fa7b5048383a41d31399d5f6a27fa50 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 17 Dec 2019 17:41:12 +0100 Subject: [PATCH 069/252] Enable analyzers for Emby.Naming --- Emby.Naming/Emby.Naming.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index c69b36894c..ed1670adf8 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -26,12 +26,12 @@ - + ../jellyfin.ruleset From 8723bdbb4fb73ed261ac1ba3b6932773e523d78b Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 18 Dec 2019 11:52:32 +0100 Subject: [PATCH 070/252] Fix tests --- .../TV/EpisodeNumberTests.cs | 7 +++++-- .../TV/EpisodePathParserTest.cs | 20 ++++++------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index 03fae31596..1ae6372817 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -87,19 +87,22 @@ namespace Jellyfin.Naming.Tests.TV // 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] + + // [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] + + // [Fact] public void TestEpisodeNumber56() { // This is not supported. Expected to fail, although it would be a good one to add support for. diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs index 8e8adb35b3..da6e993100 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodePathParserTest.cs @@ -11,6 +11,10 @@ namespace Jellyfin.Naming.Tests.TV [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)] + [InlineData("D:\\media\\Foo\\Foo-S01E01", "Foo", 1, 1)] + [InlineData("D:\\media\\Foo - S04E011", "Foo", 4, 11)] + [InlineData("D:\\media\\Foo\\Foo s01x01", "Foo", 1, 1)] + [InlineData("D:\\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(); @@ -21,18 +25,13 @@ namespace Jellyfin.Naming.Tests.TV 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)] + [InlineData("D:\\media\\Foo\\Foo 889", "Foo", 889)] + [InlineData("D:\\media\\Foo\\[Bar] Foo Baz - 11 [1080p]", "Foo Baz", 11)] public void ParseEpisodeWithoutSeason(string path, string name, int episode) { NamingOptions o = new NamingOptions(); @@ -43,13 +42,6 @@ namespace Jellyfin.Naming.Tests.TV 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); } } } From 5c9e849b85f92f542cd23022b9bbcda209111d09 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 18 Dec 2019 12:03:54 +0100 Subject: [PATCH 071/252] Enable analyzers --- Emby.Photos/Emby.Photos.csproj | 4 ++-- .../Emby.Server.Implementations.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 4bd0f1e4a1..23b7a819b2 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -25,11 +25,11 @@ - + ../jellyfin.ruleset diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 6b45f75bca..14cda0868c 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -51,12 +51,12 @@ - + ../jellyfin.ruleset From eb12754fc5b74d1bed7236c7f454903232a9b144 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 18 Dec 2019 17:18:55 +0100 Subject: [PATCH 072/252] Add Tmdb as a Provider for Season Images Changes to be committed: modified: CONTRIBUTORS.md new file: MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs modified: MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs --- CONTRIBUTORS.md | 1 + .../Tmdb/TV/TmdbSeasonImageProvider.cs | 152 ++++++++++++++++++ .../Tmdb/TV/TmdbSeasonProvider.cs | 3 + 3 files changed, 156 insertions(+) create mode 100644 MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d69e6330bb..458944778e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -31,6 +31,7 @@ - [fhriley](https://github.com/fhriley) - [nevado](https://github.com/nevado) - [mark-monteiro](https://github.com/mark-monteiro) + - [ullmie02](https://github.com/ullmie02) # Emby Contributors diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs new file mode 100644 index 0000000000..3a0062ef73 --- /dev/null +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Providers; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Tmdb.Movies; + +namespace MediaBrowser.Providers.Tmdb.TV +{ + public class TmdbSeasonImageProvider : IRemoteImageProvider, IHasOrder + { + private readonly IJsonSerializer _jsonSerializer; + private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; + private readonly ILocalizationManager _localization; + + public TmdbSeasonImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, ILocalizationManager localization) + { + _jsonSerializer = jsonSerializer; + _httpClient = httpClient; + _fileSystem = fileSystem; + _localization = localization; + } + + public int Order => 1; + + public string Name => ProviderName; + + public static string ProviderName => TmdbUtils.ProviderName; + + public Task GetImageResponse(string url, CancellationToken cancellationToken) + { + return _httpClient.GetResponse(new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = url + }); + } + + public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) + { + var season = (Season)item; + var series = season.Series; + + var seriesId = series != null ? series.GetProviderId(MetadataProviders.Tmdb) : null; + + var list = new List(); + + if (string.IsNullOrEmpty(seriesId)) + { + return list; + } + + var seasonNumber = season.IndexNumber; + + if (!seasonNumber.HasValue) + { + return list; + } + + var language = item.GetPreferredMetadataLanguage(); + + var results = await FetchImages(season, seriesId, language, _jsonSerializer, cancellationToken).ConfigureAwait(false); + + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + + var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); + + list.AddRange(results.Select(i => new RemoteImageInfo + { + Url = tmdbImageUrl + i.File_Path, + CommunityRating = i.Vote_Average, + VoteCount = i.Vote_Count, + Width = i.Width, + Height = i.Height, + Language = TmdbMovieProvider.AdjustImageLanguage(i.Iso_639_1, language), + ProviderName = Name, + Type = ImageType.Primary, + RatingType = RatingType.Score + })); + + + var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); + + return list.OrderByDescending(i => + { + if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + if (!isLanguageEn) + { + if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 2; + } + } + if (string.IsNullOrEmpty(i.Language)) + { + return isLanguageEn ? 3 : 2; + } + return 0; + }) + .ThenByDescending(i => i.CommunityRating ?? 0) + .ThenByDescending(i => i.VoteCount ?? 0); + } + + private async Task> FetchImages(Season item, string tmdbId, string language, IJsonSerializer jsonSerializer, + CancellationToken cancellationToken) + { + await TmdbSeasonProvider.Current.EnsureSeasonInfo(tmdbId, item.IndexNumber.GetValueOrDefault(), language, cancellationToken).ConfigureAwait(false); + + var path = TmdbSeriesProvider.Current.GetDataFilePath(tmdbId, language); + + if (!string.IsNullOrEmpty(path)) + { + var fileInfo = _fileSystem.GetFileInfo(path); + + if (fileInfo.Exists) + { + return jsonSerializer.DeserializeFromFile(path).Images.Posters; + } + } + + return null; + } + + public IEnumerable GetSupportedImages(BaseItem item) + { + return new List + { + ImageType.Primary + }; + } + + public bool Supports(BaseItem item) + { + return item is Season; + } + } +} diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs index 2f2ac58e8e..fc0cde8b37 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs @@ -32,6 +32,8 @@ namespace MediaBrowser.Providers.Tmdb.TV private readonly ILocalizationManager _localization; private readonly ILogger _logger; + internal static TmdbSeasonProvider Current { get; private set; } + public TmdbSeasonProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILoggerFactory loggerFactory) { _httpClient = httpClient; @@ -40,6 +42,7 @@ namespace MediaBrowser.Providers.Tmdb.TV _localization = localization; _jsonSerializer = jsonSerializer; _logger = loggerFactory.CreateLogger(GetType().Name); + Current = this; } public async Task> GetMetadata(SeasonInfo info, CancellationToken cancellationToken) From a5cd11735cc0320e576605410b68e4dace6caa05 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 19 Dec 2019 15:50:25 +0100 Subject: [PATCH 073/252] Minor changes to MediaInfoService --- MediaBrowser.Api/Playback/MediaInfoService.cs | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index c3032416b8..8caa033039 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -102,12 +102,6 @@ namespace MediaBrowser.Api.Playback public object Get(GetBitrateTestBytes request) { var bytes = new byte[request.Size]; - - for (var i = 0; i < bytes.Length; i++) - { - bytes[i] = 0; - } - return ResultFactory.GetResult(null, bytes, "application/octet-stream"); } @@ -166,8 +160,7 @@ namespace MediaBrowser.Api.Playback public void Post(CloseMediaSource request) { - var task = _mediaSourceManager.CloseLiveStream(request.LiveStreamId); - Task.WaitAll(task); + _mediaSourceManager.CloseLiveStream(request.LiveStreamId).GetAwaiter().GetResult(); } public async Task GetPlaybackInfo(GetPostedPlaybackInfo request) @@ -176,7 +169,7 @@ namespace MediaBrowser.Api.Playback var profile = request.DeviceProfile; - //Logger.LogInformation("GetPostedPlaybackInfo profile: {profile}", _json.SerializeToString(profile)); + Logger.LogInformation("GetPostedPlaybackInfo profile: {profile}", _json.SerializeToString(profile)); if (profile == null) { @@ -215,9 +208,7 @@ namespace MediaBrowser.Api.Playback StartTimeTicks = request.StartTimeTicks, SubtitleStreamIndex = request.SubtitleStreamIndex, UserId = request.UserId, - OpenToken = mediaSource.OpenToken, - //EnableMediaProbe = request.EnableMediaProbe - + OpenToken = mediaSource.OpenToken }).ConfigureAwait(false); info.MediaSources = new MediaSourceInfo[] { openStreamResult.MediaSource }; @@ -311,7 +302,8 @@ namespace MediaBrowser.Api.Playback return result; } - private void SetDeviceSpecificData(Guid itemId, + private void SetDeviceSpecificData( + Guid itemId, PlaybackInfoResponse result, DeviceProfile profile, AuthorizationInfo auth, @@ -339,7 +331,8 @@ namespace MediaBrowser.Api.Playback SortMediaSources(result, maxBitrate); } - private void SetDeviceSpecificData(BaseItem item, + private void SetDeviceSpecificData( + BaseItem item, MediaSourceInfo mediaSource, DeviceProfile profile, AuthorizationInfo auth, @@ -383,10 +376,12 @@ namespace MediaBrowser.Api.Playback { mediaSource.SupportsDirectPlay = false; } + if (!enableDirectStream) { mediaSource.SupportsDirectStream = false; } + if (!enableTranscoding) { mediaSource.SupportsTranscoding = false; @@ -434,9 +429,9 @@ namespace MediaBrowser.Api.Playback } // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? - streamBuilder.BuildAudioItem(options) : - streamBuilder.BuildVideoItem(options); + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) + ? streamBuilder.BuildAudioItem(options) + : streamBuilder.BuildVideoItem(options); if (streamInfo == null || !streamInfo.IsDirectStream) { @@ -473,9 +468,9 @@ namespace MediaBrowser.Api.Playback } // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? - streamBuilder.BuildAudioItem(options) : - streamBuilder.BuildVideoItem(options); + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) + ? streamBuilder.BuildAudioItem(options) + : streamBuilder.BuildVideoItem(options); if (streamInfo == null || !streamInfo.IsDirectStream) { @@ -493,9 +488,9 @@ namespace MediaBrowser.Api.Playback options.MaxBitrate = GetMaxBitrate(maxBitrate, user); // The MediaSource supports direct stream, now test to see if the client supports it - var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ? - streamBuilder.BuildAudioItem(options) : - streamBuilder.BuildVideoItem(options); + var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) + ? streamBuilder.BuildAudioItem(options) + : streamBuilder.BuildVideoItem(options); if (streamInfo != null) { @@ -510,10 +505,12 @@ namespace MediaBrowser.Api.Playback { mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; } + if (!allowAudioStreamCopy) { mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; } + mediaSource.TranscodingContainer = streamInfo.Container; mediaSource.TranscodingSubProtocol = streamInfo.SubProtocol; } @@ -599,14 +596,11 @@ namespace MediaBrowser.Api.Playback }).ThenBy(i => { - switch (i.Protocol) + return i.Protocol switch { - case MediaProtocol.File: - return 0; - default: - return 1; - } - + MediaProtocol.File => 0, + _ => 1, + }; }).ThenBy(i => { if (maxBitrate.HasValue) From bb62dd14c2fee3f7a6fa8cb3d8592500046ed3c9 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 19 Dec 2019 17:46:17 +0100 Subject: [PATCH 074/252] Limit size for playbacktest --- MediaBrowser.Api/Playback/MediaInfoService.cs | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 8caa033039..ac1cba09ab 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -54,7 +55,7 @@ namespace MediaBrowser.Api.Playback public class GetBitrateTestBytes { [ApiMember(Name = "Size", Description = "Size", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "GET")] - public long Size { get; set; } + public int Size { get; set; } public GetBitrateTestBytes() { @@ -101,8 +102,31 @@ namespace MediaBrowser.Api.Playback public object Get(GetBitrateTestBytes request) { - var bytes = new byte[request.Size]; - return ResultFactory.GetResult(null, bytes, "application/octet-stream"); + const int MaxSize = 10_000_000; + + var size = request.Size; + + if (size <= 0) + { + throw new ArgumentException($"The requested size can't be equal or smaller than 0.", nameof(request)); + } + + if (size > MaxSize) + { + throw new ArgumentException($"The requested size can't be larger than the max allowed value ({MaxSize}).", nameof(request)); + } + + byte[] buffer = ArrayPool.Shared.Rent(size); + try + { + // ArrayPool.Shared.Rent doesn't guarantee that the returned buffer is zeroed + Array.Fill(buffer, 0); + return ResultFactory.GetResult(null, buffer, "application/octet-stream"); + } + finally + { + ArrayPool.Shared.Return(buffer); + } } public async Task Get(GetPlaybackInfo request) From 1f323683b3b8cfbfd64b74dfc862a678962c7d38 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 19 Dec 2019 18:59:55 +0100 Subject: [PATCH 075/252] Add requested changes --- MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs index 3a0062ef73..747676966e 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -59,14 +59,14 @@ namespace MediaBrowser.Providers.Tmdb.TV if (string.IsNullOrEmpty(seriesId)) { - return list; + return Enumerable.Empty(); } var seasonNumber = season.IndexNumber; if (!seasonNumber.HasValue) { - return list; + return Enumerable.Empty(); } var language = item.GetPreferredMetadataLanguage(); @@ -88,7 +88,7 @@ namespace MediaBrowser.Providers.Tmdb.TV ProviderName = Name, Type = ImageType.Primary, RatingType = RatingType.Score - })); + }).ToList()); var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); From ce8754d05286f0cebf4710da928f91167137ddb2 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 19 Dec 2019 19:58:00 +0100 Subject: [PATCH 076/252] Refactor list of RemoteImageInfo --- MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs index 747676966e..51db2fb130 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -55,8 +55,6 @@ namespace MediaBrowser.Providers.Tmdb.TV var seriesId = series != null ? series.GetProviderId(MetadataProviders.Tmdb) : null; - var list = new List(); - if (string.IsNullOrEmpty(seriesId)) { return Enumerable.Empty(); @@ -77,7 +75,7 @@ namespace MediaBrowser.Providers.Tmdb.TV var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - list.AddRange(results.Select(i => new RemoteImageInfo + var list = results.Select(i => new RemoteImageInfo { Url = tmdbImageUrl + i.File_Path, CommunityRating = i.Vote_Average, @@ -88,8 +86,7 @@ namespace MediaBrowser.Providers.Tmdb.TV ProviderName = Name, Type = ImageType.Primary, RatingType = RatingType.Score - }).ToList()); - + }).ToList(); var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); From 137db45fc7a91d75d81023709b77277f34958ff0 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 19 Dec 2019 22:01:05 +0100 Subject: [PATCH 077/252] Apply suggestions from code review Co-Authored-By: dkanada --- MediaBrowser.Common/Hex.cs | 2 +- MediaBrowser.Common/Plugins/BasePlugin.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Common/Hex.cs b/MediaBrowser.Common/Hex.cs index 863192809f..559109f74f 100644 --- a/MediaBrowser.Common/Hex.cs +++ b/MediaBrowser.Common/Hex.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Common internal const int LastHexSymbol = 0x66; // 102: f /// - /// Gets an map from an ASCII char to its hex value shifted, + /// Gets a 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[] diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index 25519ccc0d..b24d10ff10 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -96,19 +96,19 @@ namespace MediaBrowser.Common.Plugins where TConfigurationType : BasePluginConfiguration { /// - /// The _configuration sync lock. + /// The configuration sync lock. /// private readonly object _configurationSyncLock = new object(); /// - /// The _save lock. + /// The save lock. /// private readonly object _configurationSaveLock = new object(); private Action _directoryCreateFn; /// - /// The _configuration. + /// The configuration. /// private TConfigurationType _configuration; From d720d8f3569a0db11ba170c92d8d4ff055c4eaa9 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 20 Dec 2019 20:04:27 +0100 Subject: [PATCH 078/252] Add requested changes --- .../Tmdb/TV/TmdbSeasonImageProvider.cs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs index 51db2fb130..f0b2cd88ef 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -22,15 +23,11 @@ namespace MediaBrowser.Providers.Tmdb.TV { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - private readonly ILocalizationManager _localization; - public TmdbSeasonImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, ILocalizationManager localization) + public TmdbSeasonImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; - _fileSystem = fileSystem; - _localization = localization; } public int Order => 1; @@ -69,7 +66,7 @@ namespace MediaBrowser.Providers.Tmdb.TV var language = item.GetPreferredMetadataLanguage(); - var results = await FetchImages(season, seriesId, language, _jsonSerializer, cancellationToken).ConfigureAwait(false); + var results = await FetchImages(season, seriesId, language, cancellationToken).ConfigureAwait(false); var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); @@ -86,7 +83,7 @@ namespace MediaBrowser.Providers.Tmdb.TV ProviderName = Name, Type = ImageType.Primary, RatingType = RatingType.Score - }).ToList(); + }); var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); @@ -96,6 +93,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { return 3; } + if (!isLanguageEn) { if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) @@ -103,18 +101,19 @@ namespace MediaBrowser.Providers.Tmdb.TV return 2; } } + if (string.IsNullOrEmpty(i.Language)) { return isLanguageEn ? 3 : 2; } + return 0; }) .ThenByDescending(i => i.CommunityRating ?? 0) .ThenByDescending(i => i.VoteCount ?? 0); } - private async Task> FetchImages(Season item, string tmdbId, string language, IJsonSerializer jsonSerializer, - CancellationToken cancellationToken) + private async Task> FetchImages(Season item, string tmdbId, string language, CancellationToken cancellationToken) { await TmdbSeasonProvider.Current.EnsureSeasonInfo(tmdbId, item.IndexNumber.GetValueOrDefault(), language, cancellationToken).ConfigureAwait(false); @@ -122,11 +121,9 @@ namespace MediaBrowser.Providers.Tmdb.TV if (!string.IsNullOrEmpty(path)) { - var fileInfo = _fileSystem.GetFileInfo(path); - - if (fileInfo.Exists) + if (File.Exists(path)) { - return jsonSerializer.DeserializeFromFile(path).Images.Posters; + return _jsonSerializer.DeserializeFromFile(path).Images.Posters; } } From 94d3dda3243dae2daea564b9a1b5c6493b07061e Mon Sep 17 00:00:00 2001 From: David Date: Fri, 20 Dec 2019 20:10:02 +0100 Subject: [PATCH 079/252] Refactor seriesId --- MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs index f0b2cd88ef..24cc8c73b2 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.Tmdb.TV var season = (Season)item; var series = season.Series; - var seriesId = series != null ? series.GetProviderId(MetadataProviders.Tmdb) : null; + var seriesId = series?.GetProviderId(MetadataProviders.Tmdb); if (string.IsNullOrEmpty(seriesId)) { From 5751d865363606e2ee1a773adaaf58c2864b08f8 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 20 Dec 2019 21:49:16 +0100 Subject: [PATCH 080/252] Fix warnings and move to System.Text.Json --- MediaBrowser.Api/Playback/MediaInfoService.cs | 18 ++++++++++-------- .../Playback/UniversalAudioService.cs | 2 -- .../MediaInfo/PlaybackInfoResponse.cs | 12 ++++++++++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index ac1cba09ab..80acdc4554 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -1,7 +1,13 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1402 +#pragma warning disable SA1600 +#pragma warning disable SA1649 + using System; using System.Buffers; using System.Collections.Generic; using System.Globalization; +using System.Text.Json; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -73,7 +79,6 @@ namespace MediaBrowser.Api.Playback private readonly INetworkManager _networkManager; private readonly IMediaEncoder _mediaEncoder; private readonly IUserManager _userManager; - private readonly IJsonSerializer _json; private readonly IAuthorizationContext _authContext; public MediaInfoService( @@ -86,7 +91,6 @@ namespace MediaBrowser.Api.Playback INetworkManager networkManager, IMediaEncoder mediaEncoder, IUserManager userManager, - IJsonSerializer json, IAuthorizationContext authContext) : base(logger, serverConfigurationManager, httpResultFactory) { @@ -96,7 +100,6 @@ namespace MediaBrowser.Api.Playback _networkManager = networkManager; _mediaEncoder = mediaEncoder; _userManager = userManager; - _json = json; _authContext = authContext; } @@ -193,7 +196,7 @@ namespace MediaBrowser.Api.Playback var profile = request.DeviceProfile; - Logger.LogInformation("GetPostedPlaybackInfo profile: {profile}", _json.SerializeToString(profile)); + Logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", profile); if (profile == null) { @@ -266,9 +269,8 @@ namespace MediaBrowser.Api.Playback { // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it // Should we move this directly into MediaSourceManager? - - var json = _json.SerializeToString(obj); - return _json.DeserializeFromString(json); + var json = JsonSerializer.SerializeToUtf8Bytes(obj); + return JsonSerializer.Deserialize(json); } private async Task GetPlaybackInfo(Guid id, Guid userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null) @@ -309,7 +311,7 @@ namespace MediaBrowser.Api.Playback result.MediaSources = new MediaSourceInfo[] { mediaSource }; } - if (result.MediaSources.Length == 0) + if (result.MediaSources.Count == 0) { if (!result.ErrorCode.HasValue) { diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index 9cba9df139..bcac5093e0 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -74,7 +74,6 @@ namespace MediaBrowser.Api.Playback [Authenticated] public class UniversalAudioService : BaseApiService { - private readonly ILoggerFactory _loggerFactory; private readonly EncodingHelper _encodingHelper; public UniversalAudioService( @@ -243,7 +242,6 @@ namespace MediaBrowser.Api.Playback NetworkManager, MediaEncoder, UserManager, - JsonSerializer, AuthorizationContext) { Request = Request diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs index 38638af42c..440818c3ef 100644 --- a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs @@ -1,15 +1,20 @@ +using System; +using System.Collections.Generic; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; namespace MediaBrowser.Model.MediaInfo { + /// + /// Class PlaybackInfoResponse. + /// public class PlaybackInfoResponse { /// /// Gets or sets the media sources. /// /// The media sources. - public MediaSourceInfo[] MediaSources { get; set; } + public IReadOnlyList MediaSources { get; set; } /// /// Gets or sets the play session identifier. @@ -23,9 +28,12 @@ namespace MediaBrowser.Model.MediaInfo /// The error code. public PlaybackErrorCode? ErrorCode { get; set; } + /// + /// Initializes a new instance of the class. + /// public PlaybackInfoResponse() { - MediaSources = new MediaSourceInfo[] { }; + MediaSources = Array.Empty(); } } } From b87750d8e06db371c6161f78b74b057ec9346749 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 20 Dec 2019 21:50:15 +0100 Subject: [PATCH 081/252] Update vs code workspace --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index e2a09c0f15..73f347c9fe 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/netcoreapp3.0/jellyfin.dll", + "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/netcoreapp3.1/jellyfin.dll", "args": [], "cwd": "${workspaceFolder}/Jellyfin.Server", // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window From f47c7959af72a971d1b812194bf03098e671364f Mon Sep 17 00:00:00 2001 From: Ben Magee Date: Sun, 22 Dec 2019 09:46:09 +0000 Subject: [PATCH 082/252] Wrote tests to cover CustomAuthenticationHandler --- .../Auth/CustomAuthenticationHandlerTests.cs | 170 ++++++++++++++++++ .../Jellyfin.Api.Tests.csproj | 11 ++ 2 files changed, 181 insertions(+) create mode 100644 tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs new file mode 100644 index 0000000000..bd23ca869b --- /dev/null +++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs @@ -0,0 +1,170 @@ +using System; +using System.Linq; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.AutoMoq; +using Jellyfin.Api.Auth; +using Jellyfin.Api.Constants; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Net; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Jellyfin.Api.Tests.Auth +{ + public class CustomAuthenticationHandlerTests + { + private readonly IFixture _fixture; + + private readonly Mock _authServiceMock; + private readonly Mock> _optionsMock; + private readonly Mock _clockMock; + private readonly Mock _serviceProviderMock; + private readonly Mock _authenticationServiceMock; + private readonly UrlEncoder _urlEncoder; + private readonly HttpContext _context; + + private readonly CustomAuthenticationHandler _sut; + private readonly AuthenticationScheme _scheme; + + public CustomAuthenticationHandlerTests() + { + _fixture = new Fixture().Customize(new AutoMoqCustomization {ConfigureMembers = true}); + AllowFixtureCircularDependencies(); + + _authServiceMock = _fixture.Freeze>(); + _optionsMock = _fixture.Freeze>>(); + _clockMock = _fixture.Freeze>(); + _serviceProviderMock = _fixture.Freeze>(); + _authenticationServiceMock = _fixture.Freeze>(); + _fixture.Register(() => new NullLoggerFactory()); + + _urlEncoder = UrlEncoder.Default; + + _serviceProviderMock.Setup(s => s.GetService(typeof(IAuthenticationService))) + .Returns(_authenticationServiceMock.Object); + + _optionsMock.Setup(o => o.Get(It.IsAny())) + .Returns(new AuthenticationSchemeOptions + { + ForwardAuthenticate = null + }); + + _context = new DefaultHttpContext + { + RequestServices = _serviceProviderMock.Object + }; + + _scheme = new AuthenticationScheme( + _fixture.Create(), + null, + typeof(CustomAuthenticationHandler)); + + _sut = _fixture.Create(); + _sut.InitializeAsync(_scheme, _context).Wait(); + } + + [Fact] + public async Task HandleAuthenticateAsyncShouldFailWithNullUser() + { + _authServiceMock.Setup( + a => a.Authenticate( + It.IsAny(), + It.IsAny())) + .Returns((User) null); + + var authenticateResult = await _sut.AuthenticateAsync(); + + Assert.False(authenticateResult.Succeeded); + Assert.Equal("Invalid user", authenticateResult.Failure.Message); + } + + [Fact] + public async Task HandleAuthenticateAsyncShouldFailOnSecurityException() + { + var errorMessage = _fixture.Create(); + + _authServiceMock.Setup( + a => a.Authenticate( + It.IsAny(), + It.IsAny())) + .Throws(new SecurityException(errorMessage)); + + var authenticateResult = await _sut.AuthenticateAsync(); + + Assert.False(authenticateResult.Succeeded); + Assert.Equal(errorMessage, authenticateResult.Failure.Message); + } + + [Fact] + public async Task HandleAuthenticateAsyncShouldSucceedWithUser() + { + SetupUser(); + var authenticateResult = await _sut.AuthenticateAsync(); + + Assert.True(authenticateResult.Succeeded); + Assert.Null(authenticateResult.Failure); + } + + [Fact] + public async Task HandleAuthenticateAsyncShouldAssignNameClaim() + { + var user = SetupUser(); + var authenticateResult = await _sut.AuthenticateAsync(); + + Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Name, user.Name)); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task HandleAuthenticateAsyncShouldAssignRoleClaim(bool isAdmin) + { + var user = SetupUser(isAdmin); + var authenticateResult = await _sut.AuthenticateAsync(); + + var expectedRole = user.Policy.IsAdministrator ? UserRoles.Administrator : UserRoles.User; + Assert.True(authenticateResult.Principal.HasClaim(ClaimTypes.Role, expectedRole)); + } + + [Fact] + public async Task HandleAuthenticateAsyncShouldAssignTicketCorrectScheme() + { + SetupUser(); + var authenticatedResult = await _sut.AuthenticateAsync(); + + Assert.Equal(_scheme.Name, authenticatedResult.Ticket.AuthenticationScheme); + } + + private User SetupUser(bool isAdmin=false) + { + var user = _fixture.Create(); + user.Policy.IsAdministrator = isAdmin; + + _authServiceMock.Setup( + a => a.Authenticate( + It.IsAny(), + It.IsAny())) + .Returns(user); + + return user; + } + + private void AllowFixtureCircularDependencies() + { + // A circular dependency exists in the User entity around parent folders, + // this allows Autofixture to generate a User regardless, rather than throw + // an error. + _fixture.Behaviors.OfType().ToList() + .ForEach(b => _fixture.Behaviors.Remove(b)); + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + } + } +} diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 1671b8d797..77c58040e4 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -6,6 +6,10 @@ + + + + @@ -15,6 +19,13 @@ + + + + + + ..\..\..\..\..\..\usr\local\share\dotnet\packs\Microsoft.AspNetCore.App.Ref\3.0.0\ref\netcoreapp3.0\Microsoft.AspNetCore.Authentication.dll + From 57aec873d08159d0e5ffb43ecf1e0a05f43038be Mon Sep 17 00:00:00 2001 From: Waldemar Tomme Date: Sun, 22 Dec 2019 13:01:18 +0100 Subject: [PATCH 083/252] Add check if output container supports "global_header" flag --- .../MediaEncoding/EncodingHelper.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 020f0553ed..424dd0dbe8 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2750,6 +2750,8 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -mpegts_m2ts_mode 1"; } + var supportsGlobalHeaderFlag = state.OutputContainer != "mkv"; + if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { if (state.VideoStream != null @@ -2770,7 +2772,12 @@ namespace MediaBrowser.Controller.MediaEncoding if (!state.RunTimeTicks.HasValue) { - args += " -flags -global_header -fflags +genpts"; + if(supportsGlobalHeaderFlag) + { + args += " -flags -global_header"; + } + + args += " -fflags +genpts"; } } else @@ -2816,7 +2823,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += " " + qualityParam.Trim(); } - if (!state.RunTimeTicks.HasValue) + if (supportsGlobalHeaderFlag && !state.RunTimeTicks.HasValue) { args += " -flags -global_header"; } From cf2e2a3f309d59c3c31696fc7f3ef2b6668c89dd Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 22 Dec 2019 21:39:26 +0100 Subject: [PATCH 084/252] Fix exceptions while scanning Fixes these exceptions: ``` [2019-12-22 20:48:14.779 +01:00] [ERR] Error in WaitForExit System.InvalidOperationException: No process is associated with this object. at System.Diagnostics.Process.EnsureState(State state) at System.Diagnostics.Process.EnsureState(State state) at System.Diagnostics.Process.GetWaitState() at System.Diagnostics.Process.WaitForExitCore(Int32 milliseconds) at System.Diagnostics.Process.WaitForExit(Int32 milliseconds) at Emby.Server.Implementations.Diagnostics.CommonProcess.WaitForExit(Int32 timeMs) in /home/pi/dev/jellyfin/Emby.Server.Implementations/Diagnostics/CommonProcess.cs:line 100 at MediaBrowser.MediaEncoding.Encoder.MediaEncoder.StopProcess(ProcessWrapper process, Int32 waitTimeMs) in /home/pi/dev/jellyfin/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs:line 785 [2019-12-22 20:48:14.790 +01:00] [INF] Killing ffmpeg process [2019-12-22 20:48:14.795 +01:00] [ERR] Error killing process System.InvalidOperationException: No process is associated with this object. at System.Diagnostics.Process.EnsureState(State state) at System.Diagnostics.Process.EnsureState(State state) at System.Diagnostics.Process.Kill() at Emby.Server.Implementations.Diagnostics.CommonProcess.Kill() in /home/pi/dev/jellyfin/Emby.Server.Implementations/Diagnostics/CommonProcess.cs:line 95 at MediaBrowser.MediaEncoding.Encoder.MediaEncoder.StopProcess(ProcessWrapper process, Int32 waitTimeMs) in /home/pi/dev/jellyfin/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs:line 799 [2019-12-22 20:48:14.808 +01:00] [ERR] Error in "ffprobe" System.Text.Json.JsonException: The JSON value could not be converted to System.String. Path: $.streams[0].start_pts | LineNumber: 32 | BytePositionInLine: 26. ---> System.InvalidOperationException: Cannot get the value of a token type 'Number' as a string. at System.Text.Json.Utf8JsonReader.GetString() at System.Text.Json.Serialization.Converters.JsonConverterString.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options) at System.Text.Json.JsonPropertyInfoNotNullable`4.OnRead(JsonTokenType tokenType, ReadStack& state, Utf8JsonReader& reader) at System.Text.Json.JsonPropertyInfo.Read(JsonTokenType tokenType, ReadStack& state, Utf8JsonReader& reader) at System.Text.Json.JsonSerializer.HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& state) at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack) --- End of inner exception stack trace --- at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& readStack, Utf8JsonReader& reader, Exception ex) at System.Text.Json.JsonSerializer.ReadCore(JsonSerializerOptions options, Utf8JsonReader& reader, ReadStack& readStack) at System.Text.Json.JsonSerializer.ReadCore(JsonReaderState& readerState, Boolean isFinalBlock, ReadOnlySpan`1 buffer, JsonSerializerOptions options, ReadStack& readStack) at System.Text.Json.JsonSerializer.ReadAsync[TValue](Stream utf8Json, Type returnType, JsonSerializerOptions options, CancellationToken cancellationToken) at MediaBrowser.MediaEncoding.Encoder.MediaEncoder.GetMediaInfoInternal(String inputPath, String primaryPath, MediaProtocol protocol, Boolean extractChapters, String probeSizeArgument, Boolean isAudio, Nullable`1 videoType, Boolean forceEnableLogging, CancellationToken cancellationToken) in /home/pi/dev/jellyfin/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs:line 399 at MediaBrowser.Providers.MediaInfo.FFProbeVideoInfo.ProbeVideo[T](T item, MetadataRefreshOptions options, CancellationToken cancellationToken) in /home/pi/dev/jellyfin/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs:line 122 at MediaBrowser.Providers.Manager.MetadataService`2.RunCustomProvider(ICustomMetadataProvider`1 provider, TItemType item, String logName, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken) in /home/pi/dev/jellyfin/MediaBrowser.Providers/Manager/MetadataService.cs:line 815 ``` --- ...{GuidConverter.cs => JsonGuidConverter.cs} | 2 +- .../Json/Converters/JsonInt32Converter.cs | 40 +++ MediaBrowser.Common/Json/JsonDefaults.cs | 2 +- .../Encoder/MediaEncoder.cs | 30 +- .../Probing/FFProbeHelpers.cs | 21 +- .../Probing/InternalMediaInfoResult.cs | 325 +----------------- .../Probing/MediaChapter.cs | 32 ++ .../Probing/MediaFormatInfo.cs | 81 +++++ .../Probing/MediaStreamInfo.cs | 282 +++++++++++++++ .../Probing/ProbeResultNormalizer.cs | 181 +++++----- 10 files changed, 560 insertions(+), 436 deletions(-) rename MediaBrowser.Common/Json/Converters/{GuidConverter.cs => JsonGuidConverter.cs} (91%) create mode 100644 MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs create mode 100644 MediaBrowser.MediaEncoding/Probing/MediaChapter.cs create mode 100644 MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs create mode 100644 MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs diff --git a/MediaBrowser.Common/Json/Converters/GuidConverter.cs b/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs similarity index 91% rename from MediaBrowser.Common/Json/Converters/GuidConverter.cs rename to MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs index 3081e12eee..d35a761f35 100644 --- a/MediaBrowser.Common/Json/Converters/GuidConverter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Common.Json.Converters /// /// Converts a GUID object or value to/from JSON. /// - public class GuidConverter : JsonConverter + public class JsonGuidConverter : JsonConverter { /// public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) diff --git a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs new file mode 100644 index 0000000000..0fd68babe1 --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs @@ -0,0 +1,40 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Converts a GUID object or value to/from JSON. + /// + public class JsonInt32Converter : JsonConverter + { + /// + public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + static void ThrowFormatException() => throw new FormatException("Invalid format for an integer."); + ReadOnlySpan span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; + if (!Utf8Parser.TryParse(span, out int number, out _)) + { + ThrowFormatException(); + } + + return number; + } + + /// + public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) + { + static void ThrowInvalidOperationException() => throw new InvalidOperationException(); + Span span = new byte[16]; + if (Utf8Formatter.TryFormat(value, span, out int bytesWritten)) + { + writer.WriteStringValue(span.Slice(0, bytesWritten)); + } + + ThrowInvalidOperationException(); + } + } +} diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 4ba0d5a1a5..4a6ee0a793 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -21,7 +21,7 @@ namespace MediaBrowser.Common.Json WriteIndented = false }; - options.Converters.Add(new GuidConverter()); + options.Converters.Add(new JsonGuidConverter()); options.Converters.Add(new JsonStringEnumConverter()); return options; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 6bcd6cd46a..e0f7b992ce 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -397,7 +397,8 @@ namespace MediaBrowser.MediaEncoding.Encoder try { result = await JsonSerializer.DeserializeAsync( - process.StandardOutput.BaseStream).ConfigureAwait(false); + process.StandardOutput.BaseStream, + cancellationToken: cancellationToken).ConfigureAwait(false); } catch { @@ -406,24 +407,24 @@ namespace MediaBrowser.MediaEncoding.Encoder throw; } - if (result == null || (result.streams == null && result.format == null)) + if (result == null || (result.Streams == null && result.Format == null)) { throw new Exception("ffprobe failed - streams and format are both null."); } - if (result.streams != null) + if (result.Streams != null) { // Normalize aspect ratio if invalid - foreach (var stream in result.streams) + foreach (var stream in result.Streams) { - if (string.Equals(stream.display_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(stream.DisplayAspectRatio, "0:1", StringComparison.OrdinalIgnoreCase)) { - stream.display_aspect_ratio = string.Empty; + stream.DisplayAspectRatio = string.Empty; } - if (string.Equals(stream.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(stream.SampleAspectRatio, "0:1", StringComparison.OrdinalIgnoreCase)) { - stream.sample_aspect_ratio = string.Empty; + stream.SampleAspectRatio = string.Empty; } } } @@ -778,6 +779,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _runningProcesses.Add(process); } } + private void StopProcess(ProcessWrapper process, int waitTimeMs) { try @@ -786,18 +788,16 @@ namespace MediaBrowser.MediaEncoding.Encoder { return; } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in WaitForExit"); - } - try - { _logger.LogInformation("Killing ffmpeg process"); process.Process.Kill(); } + catch (InvalidOperationException) + { + // The process has already exited or + // there is no process associated with this Process object. + } catch (Exception ex) { _logger.LogError(ex, "Error killing process"); diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs index e4eabaf384..cd3d82e864 100644 --- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs +++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs @@ -16,24 +16,19 @@ namespace MediaBrowser.MediaEncoding.Probing throw new ArgumentNullException(nameof(result)); } - if (result.format != null && result.format.tags != null) + if (result.Format != null && result.Format.Tags != null) { - result.format.tags = ConvertDictionaryToCaseInSensitive(result.format.tags); + result.Format.Tags = ConvertDictionaryToCaseInSensitive(result.Format.Tags); } - if (result.streams != null) + if (result.Streams != null) { // Convert all dictionaries to case insensitive - foreach (var stream in result.streams) + foreach (var stream in result.Streams) { - if (stream.tags != null) + if (stream.Tags != null) { - stream.tags = ConvertDictionaryToCaseInSensitive(stream.tags); - } - - if (stream.disposition != null) - { - stream.disposition = ConvertDictionaryToCaseInSensitive(stream.disposition); + stream.Tags = ConvertDictionaryToCaseInSensitive(stream.Tags); } } } @@ -45,7 +40,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// The tags. /// The key. /// System.String. - public static string GetDictionaryValue(Dictionary tags, string key) + public static string GetDictionaryValue(IReadOnlyDictionary tags, string key) { if (tags == null) { @@ -103,7 +98,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// /// The dict. /// Dictionary{System.StringSystem.String}. - private static Dictionary ConvertDictionaryToCaseInSensitive(Dictionary dict) + private static Dictionary ConvertDictionaryToCaseInSensitive(IReadOnlyDictionary dict) { return new Dictionary(dict, StringComparer.OrdinalIgnoreCase); } diff --git a/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs b/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs index cc9d27608b..0e319c1a8b 100644 --- a/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs +++ b/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; namespace MediaBrowser.MediaEncoding.Probing { /// - /// Class MediaInfoResult + /// Class MediaInfoResult. /// public class InternalMediaInfoResult { @@ -11,331 +12,21 @@ namespace MediaBrowser.MediaEncoding.Probing /// Gets or sets the streams. /// /// The streams. - public MediaStreamInfo[] streams { get; set; } + [JsonPropertyName("streams")] + public IReadOnlyList Streams { get; set; } /// /// Gets or sets the format. /// /// The format. - public MediaFormatInfo format { get; set; } + [JsonPropertyName("format")] + public MediaFormatInfo Format { get; set; } /// /// Gets or sets the chapters. /// /// The chapters. - public MediaChapter[] Chapters { get; set; } - } - - public class MediaChapter - { - public int id { get; set; } - public string time_base { get; set; } - public long start { get; set; } - public string start_time { get; set; } - public long end { get; set; } - public string end_time { get; set; } - public Dictionary tags { get; set; } - } - - /// - /// Represents a stream within the output - /// - public class MediaStreamInfo - { - /// - /// Gets or sets the index. - /// - /// The index. - public int index { get; set; } - - /// - /// Gets or sets the profile. - /// - /// The profile. - public string profile { get; set; } - - /// - /// Gets or sets the codec_name. - /// - /// The codec_name. - public string codec_name { get; set; } - - /// - /// Gets or sets the codec_long_name. - /// - /// The codec_long_name. - public string codec_long_name { get; set; } - - /// - /// Gets or sets the codec_type. - /// - /// The codec_type. - public string codec_type { get; set; } - - /// - /// Gets or sets the sample_rate. - /// - /// The sample_rate. - public string sample_rate { get; set; } - - /// - /// Gets or sets the channels. - /// - /// The channels. - public int channels { get; set; } - - /// - /// Gets or sets the channel_layout. - /// - /// The channel_layout. - public string channel_layout { get; set; } - - /// - /// Gets or sets the avg_frame_rate. - /// - /// The avg_frame_rate. - public string avg_frame_rate { get; set; } - - /// - /// Gets or sets the duration. - /// - /// The duration. - public string duration { get; set; } - - /// - /// Gets or sets the bit_rate. - /// - /// The bit_rate. - public string bit_rate { get; set; } - - /// - /// Gets or sets the width. - /// - /// The width. - public int width { get; set; } - - /// - /// Gets or sets the refs. - /// - /// The refs. - public int refs { get; set; } - - /// - /// Gets or sets the height. - /// - /// The height. - public int height { get; set; } - - /// - /// Gets or sets the display_aspect_ratio. - /// - /// The display_aspect_ratio. - public string display_aspect_ratio { get; set; } - - /// - /// Gets or sets the tags. - /// - /// The tags. - public Dictionary tags { get; set; } - - /// - /// Gets or sets the bits_per_sample. - /// - /// The bits_per_sample. - public int bits_per_sample { get; set; } - - /// - /// Gets or sets the bits_per_raw_sample. - /// - /// The bits_per_raw_sample. - public int bits_per_raw_sample { get; set; } - - /// - /// Gets or sets the r_frame_rate. - /// - /// The r_frame_rate. - public string r_frame_rate { get; set; } - - /// - /// Gets or sets the has_b_frames. - /// - /// The has_b_frames. - public int has_b_frames { get; set; } - - /// - /// Gets or sets the sample_aspect_ratio. - /// - /// The sample_aspect_ratio. - public string sample_aspect_ratio { get; set; } - - /// - /// Gets or sets the pix_fmt. - /// - /// The pix_fmt. - public string pix_fmt { get; set; } - - /// - /// Gets or sets the level. - /// - /// The level. - public int level { get; set; } - - /// - /// Gets or sets the time_base. - /// - /// The time_base. - public string time_base { get; set; } - - /// - /// Gets or sets the start_time. - /// - /// The start_time. - public string start_time { get; set; } - - /// - /// Gets or sets the codec_time_base. - /// - /// The codec_time_base. - public string codec_time_base { get; set; } - - /// - /// Gets or sets the codec_tag. - /// - /// The codec_tag. - public string codec_tag { get; set; } - - /// - /// Gets or sets the codec_tag_string. - /// - /// The codec_tag_string. - public string codec_tag_string { get; set; } - - /// - /// Gets or sets the sample_fmt. - /// - /// The sample_fmt. - public string sample_fmt { get; set; } - - /// - /// Gets or sets the dmix_mode. - /// - /// The dmix_mode. - public string dmix_mode { get; set; } - - /// - /// Gets or sets the start_pts. - /// - /// The start_pts. - public string start_pts { get; set; } - - /// - /// Gets or sets the is_avc. - /// - /// The is_avc. - public string is_avc { get; set; } - - /// - /// Gets or sets the nal_length_size. - /// - /// The nal_length_size. - public string nal_length_size { get; set; } - - /// - /// Gets or sets the ltrt_cmixlev. - /// - /// The ltrt_cmixlev. - public string ltrt_cmixlev { get; set; } - - /// - /// Gets or sets the ltrt_surmixlev. - /// - /// The ltrt_surmixlev. - public string ltrt_surmixlev { get; set; } - - /// - /// Gets or sets the loro_cmixlev. - /// - /// The loro_cmixlev. - public string loro_cmixlev { get; set; } - - /// - /// Gets or sets the loro_surmixlev. - /// - /// The loro_surmixlev. - public string loro_surmixlev { get; set; } - - public string field_order { get; set; } - - /// - /// Gets or sets the disposition. - /// - /// The disposition. - public Dictionary disposition { get; set; } - } - - /// - /// Class MediaFormat - /// - public class MediaFormatInfo - { - /// - /// Gets or sets the filename. - /// - /// The filename. - public string filename { get; set; } - - /// - /// Gets or sets the nb_streams. - /// - /// The nb_streams. - public int nb_streams { get; set; } - - /// - /// Gets or sets the format_name. - /// - /// The format_name. - public string format_name { get; set; } - - /// - /// Gets or sets the format_long_name. - /// - /// The format_long_name. - public string format_long_name { get; set; } - - /// - /// Gets or sets the start_time. - /// - /// The start_time. - public string start_time { get; set; } - - /// - /// Gets or sets the duration. - /// - /// The duration. - public string duration { get; set; } - - /// - /// Gets or sets the size. - /// - /// The size. - public string size { get; set; } - - /// - /// Gets or sets the bit_rate. - /// - /// The bit_rate. - public string bit_rate { get; set; } - - /// - /// Gets or sets the probe_score. - /// - /// The probe_score. - public int probe_score { get; set; } - - /// - /// Gets or sets the tags. - /// - /// The tags. - public Dictionary tags { get; set; } + [JsonPropertyName("chapters")] + public IReadOnlyList Chapters { get; set; } } } diff --git a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs new file mode 100644 index 0000000000..a3607a7600 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace MediaBrowser.MediaEncoding.Probing +{ + /// + /// Class MediaChapter. + /// + public class MediaChapter + { + [JsonPropertyName("id")] + public int id { get; set; } + + [JsonPropertyName("time_base")] + public string TimeBase { get; set; } + + [JsonPropertyName("start")] + public long Start { get; set; } + + [JsonPropertyName("start_time")] + public string StartTime { get; set; } + + [JsonPropertyName("end")] + public long End { get; set; } + + [JsonPropertyName("end_time")] + public string EndTime { get; set; } + + [JsonPropertyName("tags")] + public IReadOnlyDictionary Tags { get; set; } + } +} diff --git a/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs new file mode 100644 index 0000000000..d5529e56c8 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace MediaBrowser.MediaEncoding.Probing +{ + /// + /// Class MediaFormat. + /// + public class MediaFormatInfo + { + /// + /// Gets or sets the filename. + /// + /// The filename. + [JsonPropertyName("filename")] + public string Fileame { get; set; } + + /// + /// Gets or sets the nb_streams. + /// + /// The nb_streams. + [JsonPropertyName("nb_streams")] + public int NbStreams { get; set; } + + /// + /// Gets or sets the format_name. + /// + /// The format_name. + [JsonPropertyName("format_name")] + public string FormatName { get; set; } + + /// + /// Gets or sets the format_long_name. + /// + /// The format_long_name. + [JsonPropertyName("format_long_name")] + public string FormatLongName { get; set; } + + /// + /// Gets or sets the start_time. + /// + /// The start_time. + [JsonPropertyName("start_time")] + public string StartTime { get; set; } + + /// + /// Gets or sets the duration. + /// + /// The duration. + [JsonPropertyName("duration")] + public string Duration { get; set; } + + /// + /// Gets or sets the size. + /// + /// The size. + [JsonPropertyName("size")] + public string Size { get; set; } + + /// + /// Gets or sets the bit_rate. + /// + /// The bit_rate. + [JsonPropertyName("bit_rate")] + public string BitRate { get; set; } + + /// + /// Gets or sets the probe_score. + /// + /// The probe_score. + [JsonPropertyName("probe_score")] + public int ProbeScore { get; set; } + + /// + /// Gets or sets the tags. + /// + /// The tags. + [JsonPropertyName("tags")] + public IReadOnlyDictionary Tags { get; set; } + } +} diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs new file mode 100644 index 0000000000..7fa7afa5b3 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -0,0 +1,282 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using MediaBrowser.Common.Json.Converters; + +namespace MediaBrowser.MediaEncoding.Probing +{ + /// + /// Represents a stream within the output. + /// + public class MediaStreamInfo + { + /// + /// Gets or sets the index. + /// + /// The index. + [JsonPropertyName("index")] + public int Index { get; set; } + + /// + /// Gets or sets the profile. + /// + /// The profile. + [JsonPropertyName("profile")] + public string Profile { get; set; } + + /// + /// Gets or sets the codec_name. + /// + /// The codec_name. + [JsonPropertyName("codec_name")] + public string CodecName { get; set; } + + /// + /// Gets or sets the codec_long_name. + /// + /// The codec_long_name. + [JsonPropertyName("codec_long_name")] + public string CodecLongName { get; set; } + + /// + /// Gets or sets the codec_type. + /// + /// The codec_type. + [JsonPropertyName("codec_type")] + public string CodecType { get; set; } + + /// + /// Gets or sets the sample_rate. + /// + /// The sample_rate. + [JsonPropertyName("sample_rate")] + public string SampleRate { get; set; } + + /// + /// Gets or sets the channels. + /// + /// The channels. + [JsonPropertyName("channels")] + public int Channels { get; set; } + + /// + /// Gets or sets the channel_layout. + /// + /// The channel_layout. + [JsonPropertyName("channel_layout")] + public string ChannelLayout { get; set; } + + /// + /// Gets or sets the avg_frame_rate. + /// + /// The avg_frame_rate. + [JsonPropertyName("avg_frame_rate")] + public string AverageFrameRate { get; set; } + + /// + /// Gets or sets the duration. + /// + /// The duration. + [JsonPropertyName("duration")] + public string Duration { get; set; } + + /// + /// Gets or sets the bit_rate. + /// + /// The bit_rate. + [JsonPropertyName("bit_rate")] + public string BitRate { get; set; } + + /// + /// Gets or sets the width. + /// + /// The width. + [JsonPropertyName("width")] + public int Width { get; set; } + + /// + /// Gets or sets the refs. + /// + /// The refs. + [JsonPropertyName("refs")] + public int Refs { get; set; } + + /// + /// Gets or sets the height. + /// + /// The height. + [JsonPropertyName("height")] + public int Height { get; set; } + + /// + /// Gets or sets the display_aspect_ratio. + /// + /// The display_aspect_ratio. + [JsonPropertyName("display_aspect_ratio")] + public string DisplayAspectRatio { get; set; } + + /// + /// Gets or sets the tags. + /// + /// The tags. + [JsonPropertyName("tags")] + public IReadOnlyDictionary Tags { get; set; } + + /// + /// Gets or sets the bits_per_sample. + /// + /// The bits_per_sample. + [JsonPropertyName("bits_per_sample")] + public int BitsPerSample { get; set; } + + /// + /// Gets or sets the bits_per_raw_sample. + /// + /// The bits_per_raw_sample. + [JsonPropertyName("bits_per_raw_sample")] + [JsonConverter(typeof(JsonInt32Converter))] + public int BitsPerRawSample { get; set; } + + /// + /// Gets or sets the r_frame_rate. + /// + /// The r_frame_rate. + [JsonPropertyName("r_frame_rate")] + public string RFrameRate { get; set; } + + /// + /// Gets or sets the has_b_frames. + /// + /// The has_b_frames. + [JsonPropertyName("has_b_frames")] + public int HasBFrames { get; set; } + + /// + /// Gets or sets the sample_aspect_ratio. + /// + /// The sample_aspect_ratio. + [JsonPropertyName("sample_aspect_ratio")] + public string SampleAspectRatio { get; set; } + + /// + /// Gets or sets the pix_fmt. + /// + /// The pix_fmt. + [JsonPropertyName("pix_fmt")] + public string PixelFormat { get; set; } + + /// + /// Gets or sets the level. + /// + /// The level. + [JsonPropertyName("level")] + public int Level { get; set; } + + /// + /// Gets or sets the time_base. + /// + /// The time_base. + [JsonPropertyName("time_base")] + public string TimeBase { get; set; } + + /// + /// Gets or sets the start_time. + /// + /// The start_time. + [JsonPropertyName("start_time")] + public string StartTime { get; set; } + + /// + /// Gets or sets the codec_time_base. + /// + /// The codec_time_base. + [JsonPropertyName("codec_time_base")] + public string CodecTimeBase { get; set; } + + /// + /// Gets or sets the codec_tag. + /// + /// The codec_tag. + [JsonPropertyName("codec_tag")] + public string CodecTag { get; set; } + + /// + /// Gets or sets the codec_tag_string. + /// + /// The codec_tag_string. + [JsonPropertyName("codec_tag_string")] + public string CodecTagString { get; set; } + + /// + /// Gets or sets the sample_fmt. + /// + /// The sample_fmt. + [JsonPropertyName("sample_fmt")] + public string SampleFmt { get; set; } + + /// + /// Gets or sets the dmix_mode. + /// + /// The dmix_mode. + [JsonPropertyName("dmix_mode")] + public string DmixMode { get; set; } + + /// + /// Gets or sets the start_pts. + /// + /// The start_pts. + [JsonPropertyName("start_pts")] + public int StartPts { get; set; } + + /// + /// Gets or sets the is_avc. + /// + /// The is_avc. + [JsonPropertyName("is_avc")] + public string IsAvc { get; set; } + + /// + /// Gets or sets the nal_length_size. + /// + /// The nal_length_size. + [JsonPropertyName("nal_length_size")] + public string NalLengthSize { get; set; } + + /// + /// Gets or sets the ltrt_cmixlev. + /// + /// The ltrt_cmixlev. + [JsonPropertyName("ltrt_cmixlev")] + public string LtrtCmixlev { get; set; } + + /// + /// Gets or sets the ltrt_surmixlev. + /// + /// The ltrt_surmixlev. + [JsonPropertyName("ltrt_surmixlev")] + public string LtrtSurmixlev { get; set; } + + /// + /// Gets or sets the loro_cmixlev. + /// + /// The loro_cmixlev. + [JsonPropertyName("loro_cmixlev")] + public string LoroCmixlev { get; set; } + + /// + /// Gets or sets the loro_surmixlev. + /// + /// The loro_surmixlev. + [JsonPropertyName("loro_surmixlev")] + public string LoroSurmixlev { get; set; } + + [JsonPropertyName("field_order")] + public string FieldOrder { get; set; } + + /// + /// Gets or sets the disposition. + /// + /// The disposition. + [JsonPropertyName("disposition")] + public IReadOnlyDictionary Disposition { get; set; } + } +} diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 54d02fc9f7..99f0df60fe 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -8,7 +8,6 @@ using System.Xml; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; @@ -41,21 +40,21 @@ namespace MediaBrowser.MediaEncoding.Probing FFProbeHelpers.NormalizeFFProbeResult(data); SetSize(data, info); - var internalStreams = data.streams ?? new MediaStreamInfo[] { }; + var internalStreams = data.Streams ?? new MediaStreamInfo[] { }; - info.MediaStreams = internalStreams.Select(s => GetMediaStream(isAudio, s, data.format)) + info.MediaStreams = internalStreams.Select(s => GetMediaStream(isAudio, s, data.Format)) .Where(i => i != null) // Drop subtitle streams if we don't know the codec because it will just cause failures if we don't know how to handle them .Where(i => i.Type != MediaStreamType.Subtitle || !string.IsNullOrWhiteSpace(i.Codec)) .ToList(); - if (data.format != null) + if (data.Format != null) { - info.Container = NormalizeFormat(data.format.format_name); + info.Container = NormalizeFormat(data.Format.FormatName); - if (!string.IsNullOrEmpty(data.format.bit_rate)) + if (!string.IsNullOrEmpty(data.Format.BitRate)) { - if (int.TryParse(data.format.bit_rate, NumberStyles.Any, _usCulture, out var value)) + if (int.TryParse(data.Format.BitRate, NumberStyles.Any, _usCulture, out var value)) { info.Bitrate = value; } @@ -65,22 +64,22 @@ namespace MediaBrowser.MediaEncoding.Probing var tags = new Dictionary(StringComparer.OrdinalIgnoreCase); var tagStreamType = isAudio ? "audio" : "video"; - if (data.streams != null) + if (data.Streams != null) { - var tagStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, tagStreamType, StringComparison.OrdinalIgnoreCase)); + var tagStream = data.Streams.FirstOrDefault(i => string.Equals(i.CodecType, tagStreamType, StringComparison.OrdinalIgnoreCase)); - if (tagStream != null && tagStream.tags != null) + if (tagStream != null && tagStream.Tags != null) { - foreach (var pair in tagStream.tags) + foreach (var pair in tagStream.Tags) { tags[pair.Key] = pair.Value; } } } - if (data.format != null && data.format.tags != null) + if (data.Format != null && data.Format.Tags != null) { - foreach (var pair in data.format.tags) + foreach (var pair in data.Format.Tags) { tags[pair.Key] = pair.Value; } @@ -153,9 +152,9 @@ namespace MediaBrowser.MediaEncoding.Probing FetchFromItunesInfo(itunesXml, info); } - if (data.format != null && !string.IsNullOrEmpty(data.format.duration)) + if (data.Format != null && !string.IsNullOrEmpty(data.Format.Duration)) { - info.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks; + info.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.Format.Duration, _usCulture)).Ticks; } FetchWtvInfo(info, data); @@ -523,7 +522,7 @@ namespace MediaBrowser.MediaEncoding.Probing private MediaStream GetMediaStream(bool isAudio, MediaStreamInfo streamInfo, MediaFormatInfo formatInfo) { // These are mp4 chapters - if (string.Equals(streamInfo.codec_name, "mov_text", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(streamInfo.CodecName, "mov_text", StringComparison.OrdinalIgnoreCase)) { // Edit: but these are also sometimes subtitles? //return null; @@ -531,71 +530,71 @@ namespace MediaBrowser.MediaEncoding.Probing var stream = new MediaStream { - Codec = streamInfo.codec_name, - Profile = streamInfo.profile, - Level = streamInfo.level, - Index = streamInfo.index, - PixelFormat = streamInfo.pix_fmt, - NalLengthSize = streamInfo.nal_length_size, - TimeBase = streamInfo.time_base, - CodecTimeBase = streamInfo.codec_time_base + Codec = streamInfo.CodecName, + Profile = streamInfo.Profile, + Level = streamInfo.Level, + Index = streamInfo.Index, + PixelFormat = streamInfo.PixelFormat, + NalLengthSize = streamInfo.NalLengthSize, + TimeBase = streamInfo.TimeBase, + CodecTimeBase = streamInfo.CodecTimeBase }; - if (string.Equals(streamInfo.is_avc, "true", StringComparison.OrdinalIgnoreCase) || - string.Equals(streamInfo.is_avc, "1", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(streamInfo.IsAvc, "true", StringComparison.OrdinalIgnoreCase) || + string.Equals(streamInfo.IsAvc, "1", StringComparison.OrdinalIgnoreCase)) { stream.IsAVC = true; } - else if (string.Equals(streamInfo.is_avc, "false", StringComparison.OrdinalIgnoreCase) || - string.Equals(streamInfo.is_avc, "0", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(streamInfo.IsAvc, "false", StringComparison.OrdinalIgnoreCase) || + string.Equals(streamInfo.IsAvc, "0", StringComparison.OrdinalIgnoreCase)) { stream.IsAVC = false; } - if (!string.IsNullOrWhiteSpace(streamInfo.field_order) && !string.Equals(streamInfo.field_order, "progressive", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrWhiteSpace(streamInfo.FieldOrder) && !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase)) { stream.IsInterlaced = true; } // Filter out junk - if (!string.IsNullOrWhiteSpace(streamInfo.codec_tag_string) && streamInfo.codec_tag_string.IndexOf("[0]", StringComparison.OrdinalIgnoreCase) == -1) + if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString) && streamInfo.CodecTagString.IndexOf("[0]", StringComparison.OrdinalIgnoreCase) == -1) { - stream.CodecTag = streamInfo.codec_tag_string; + stream.CodecTag = streamInfo.CodecTagString; } - if (streamInfo.tags != null) + if (streamInfo.Tags != null) { - stream.Language = GetDictionaryValue(streamInfo.tags, "language"); - stream.Comment = GetDictionaryValue(streamInfo.tags, "comment"); - stream.Title = GetDictionaryValue(streamInfo.tags, "title"); + stream.Language = GetDictionaryValue(streamInfo.Tags, "language"); + stream.Comment = GetDictionaryValue(streamInfo.Tags, "comment"); + stream.Title = GetDictionaryValue(streamInfo.Tags, "title"); } - if (string.Equals(streamInfo.codec_type, "audio", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase)) { stream.Type = MediaStreamType.Audio; - stream.Channels = streamInfo.channels; + stream.Channels = streamInfo.Channels; - if (!string.IsNullOrEmpty(streamInfo.sample_rate)) + if (!string.IsNullOrEmpty(streamInfo.SampleRate)) { - if (int.TryParse(streamInfo.sample_rate, NumberStyles.Any, _usCulture, out var value)) + if (int.TryParse(streamInfo.SampleRate, NumberStyles.Any, _usCulture, out var value)) { stream.SampleRate = value; } } - stream.ChannelLayout = ParseChannelLayout(streamInfo.channel_layout); + stream.ChannelLayout = ParseChannelLayout(streamInfo.ChannelLayout); - if (streamInfo.bits_per_sample > 0) + if (streamInfo.BitsPerSample > 0) { - stream.BitDepth = streamInfo.bits_per_sample; + stream.BitDepth = streamInfo.BitsPerSample; } - else if (streamInfo.bits_per_raw_sample > 0) + else if (streamInfo.BitsPerRawSample > 0) { - stream.BitDepth = streamInfo.bits_per_raw_sample; + stream.BitDepth = streamInfo.BitsPerRawSample; } } - else if (string.Equals(streamInfo.codec_type, "subtitle", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(streamInfo.CodecType, "subtitle", StringComparison.OrdinalIgnoreCase)) { stream.Type = MediaStreamType.Subtitle; stream.Codec = NormalizeSubtitleCodec(stream.Codec); @@ -603,14 +602,14 @@ namespace MediaBrowser.MediaEncoding.Probing stream.localizedDefault = _localization.GetLocalizedString("Default"); stream.localizedForced = _localization.GetLocalizedString("Forced"); } - else if (string.Equals(streamInfo.codec_type, "video", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase)) { stream.Type = isAudio || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase) ? MediaStreamType.EmbeddedImage : MediaStreamType.Video; - stream.AverageFrameRate = GetFrameRate(streamInfo.avg_frame_rate); - stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate); + stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate); + stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate); if (isAudio || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) || string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase)) @@ -635,17 +634,17 @@ namespace MediaBrowser.MediaEncoding.Probing stream.Type = MediaStreamType.Video; } - stream.Width = streamInfo.width; - stream.Height = streamInfo.height; + stream.Width = streamInfo.Width; + stream.Height = streamInfo.Height; stream.AspectRatio = GetAspectRatio(streamInfo); - if (streamInfo.bits_per_sample > 0) + if (streamInfo.BitsPerSample > 0) { - stream.BitDepth = streamInfo.bits_per_sample; + stream.BitDepth = streamInfo.BitsPerSample; } - else if (streamInfo.bits_per_raw_sample > 0) + else if (streamInfo.BitsPerRawSample > 0) { - stream.BitDepth = streamInfo.bits_per_raw_sample; + stream.BitDepth = streamInfo.BitsPerRawSample; } //stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) || @@ -653,11 +652,11 @@ namespace MediaBrowser.MediaEncoding.Probing // string.Equals(stream.AspectRatio, "2.40:1", StringComparison.OrdinalIgnoreCase); // http://stackoverflow.com/questions/17353387/how-to-detect-anamorphic-video-with-ffprobe - stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase); + stream.IsAnamorphic = string.Equals(streamInfo.SampleAspectRatio, "0:1", StringComparison.OrdinalIgnoreCase); - if (streamInfo.refs > 0) + if (streamInfo.Refs > 0) { - stream.RefFrames = streamInfo.refs; + stream.RefFrames = streamInfo.Refs; } } else @@ -668,18 +667,18 @@ namespace MediaBrowser.MediaEncoding.Probing // Get stream bitrate var bitrate = 0; - if (!string.IsNullOrEmpty(streamInfo.bit_rate)) + if (!string.IsNullOrEmpty(streamInfo.BitRate)) { - if (int.TryParse(streamInfo.bit_rate, NumberStyles.Any, _usCulture, out var value)) + if (int.TryParse(streamInfo.BitRate, NumberStyles.Any, _usCulture, out var value)) { bitrate = value; } } - if (bitrate == 0 && formatInfo != null && !string.IsNullOrEmpty(formatInfo.bit_rate) && stream.Type == MediaStreamType.Video) + if (bitrate == 0 && formatInfo != null && !string.IsNullOrEmpty(formatInfo.BitRate) && stream.Type == MediaStreamType.Video) { // If the stream info doesn't have a bitrate get the value from the media format info - if (int.TryParse(formatInfo.bit_rate, NumberStyles.Any, _usCulture, out var value)) + if (int.TryParse(formatInfo.BitRate, NumberStyles.Any, _usCulture, out var value)) { bitrate = value; } @@ -690,14 +689,18 @@ namespace MediaBrowser.MediaEncoding.Probing stream.BitRate = bitrate; } - if (streamInfo.disposition != null) + var disposition = streamInfo.Disposition; + if (disposition != null) { - var isDefault = GetDictionaryValue(streamInfo.disposition, "default"); - var isForced = GetDictionaryValue(streamInfo.disposition, "forced"); + if (disposition.GetValueOrDefault("default") == 1) + { + stream.IsDefault = true; + } - stream.IsDefault = string.Equals(isDefault, "1", StringComparison.OrdinalIgnoreCase); - - stream.IsForced = string.Equals(isForced, "1", StringComparison.OrdinalIgnoreCase); + if (disposition.GetValueOrDefault("forced") == 1) + { + stream.IsForced = true; + } } NormalizeStreamTitle(stream); @@ -724,7 +727,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// The tags. /// The key. /// System.String. - private string GetDictionaryValue(Dictionary tags, string key) + private string GetDictionaryValue(IReadOnlyDictionary tags, string key) { if (tags == null) { @@ -747,7 +750,7 @@ namespace MediaBrowser.MediaEncoding.Probing private string GetAspectRatio(MediaStreamInfo info) { - var original = info.display_aspect_ratio; + var original = info.DisplayAspectRatio; var parts = (original ?? string.Empty).Split(':'); if (!(parts.Length == 2 && @@ -756,8 +759,8 @@ namespace MediaBrowser.MediaEncoding.Probing width > 0 && height > 0)) { - width = info.width; - height = info.height; + width = info.Width; + height = info.Height; } if (width > 0 && height > 0) @@ -850,20 +853,20 @@ namespace MediaBrowser.MediaEncoding.Probing private void SetAudioRuntimeTicks(InternalMediaInfoResult result, MediaInfo data) { - if (result.streams != null) + if (result.Streams != null) { // Get the first info stream - var stream = result.streams.FirstOrDefault(s => string.Equals(s.codec_type, "audio", StringComparison.OrdinalIgnoreCase)); + var stream = result.Streams.FirstOrDefault(s => string.Equals(s.CodecType, "audio", StringComparison.OrdinalIgnoreCase)); if (stream != null) { // Get duration from stream properties - var duration = stream.duration; + var duration = stream.Duration; // If it's not there go into format properties if (string.IsNullOrEmpty(duration)) { - duration = result.format.duration; + duration = result.Format.Duration; } // If we got something, parse it @@ -877,11 +880,11 @@ namespace MediaBrowser.MediaEncoding.Probing private void SetSize(InternalMediaInfoResult data, MediaInfo info) { - if (data.format != null) + if (data.Format != null) { - if (!string.IsNullOrEmpty(data.format.size)) + if (!string.IsNullOrEmpty(data.Format.Size)) { - info.Size = long.Parse(data.format.size, _usCulture); + info.Size = long.Parse(data.Format.Size, _usCulture); } else { @@ -1194,16 +1197,16 @@ namespace MediaBrowser.MediaEncoding.Probing { var info = new ChapterInfo(); - if (chapter.tags != null) + if (chapter.Tags != null) { - if (chapter.tags.TryGetValue("title", out string name)) + if (chapter.Tags.TryGetValue("title", out string name)) { info.Name = name; } } // Limit accuracy to milliseconds to match xml saving - var secondsString = chapter.start_time; + var secondsString = chapter.StartTime; if (double.TryParse(secondsString, NumberStyles.Any, CultureInfo.InvariantCulture, out var seconds)) { @@ -1218,12 +1221,12 @@ namespace MediaBrowser.MediaEncoding.Probing private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data) { - if (data.format == null || data.format.tags == null) + if (data.Format == null || data.Format.Tags == null) { return; } - var genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/Genre"); + var genres = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/Genre"); if (!string.IsNullOrWhiteSpace(genres)) { @@ -1239,14 +1242,14 @@ namespace MediaBrowser.MediaEncoding.Probing } } - var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating"); + var officialRating = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/ParentalRating"); if (!string.IsNullOrWhiteSpace(officialRating)) { video.OfficialRating = officialRating; } - var people = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaCredits"); + var people = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/MediaCredits"); if (!string.IsNullOrEmpty(people)) { @@ -1256,7 +1259,7 @@ namespace MediaBrowser.MediaEncoding.Probing .ToArray(); } - var year = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/OriginalReleaseTime"); + var year = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/OriginalReleaseTime"); if (!string.IsNullOrWhiteSpace(year)) { if (int.TryParse(year, NumberStyles.Integer, _usCulture, out var val)) @@ -1265,7 +1268,7 @@ namespace MediaBrowser.MediaEncoding.Probing } } - var premiereDateString = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaOriginalBroadcastDateTime"); + var premiereDateString = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/MediaOriginalBroadcastDateTime"); if (!string.IsNullOrWhiteSpace(premiereDateString)) { // Credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ @@ -1276,9 +1279,9 @@ namespace MediaBrowser.MediaEncoding.Probing } } - var description = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitleDescription"); + var description = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/SubTitleDescription"); - var subTitle = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitle"); + var subTitle = FFProbeHelpers.GetDictionaryValue(data.Format.Tags, "WM/SubTitle"); // For below code, credit to MCEBuddy: https://mcebuddy2x.codeplex.com/ From f9a454628d7204bc30d64f9c0589d766ad5f3109 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 22 Dec 2019 22:21:41 +0100 Subject: [PATCH 085/252] Preformance!!! --- .../Json/Converters/JsonInt32Converter.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs index 0fd68babe1..fe5dd6cd4d 100644 --- a/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonInt32Converter.cs @@ -15,7 +15,20 @@ namespace MediaBrowser.Common.Json.Converters public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { static void ThrowFormatException() => throw new FormatException("Invalid format for an integer."); - ReadOnlySpan span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; + ReadOnlySpan span = stackalloc byte[0]; + + if (reader.HasValueSequence) + { + long sequenceLength = reader.ValueSequence.Length; + Span stackSpan = stackalloc byte[(int)sequenceLength]; + reader.ValueSequence.CopyTo(stackSpan); + span = stackSpan; + } + else + { + span = reader.ValueSpan; + } + if (!Utf8Parser.TryParse(span, out int number, out _)) { ThrowFormatException(); @@ -28,7 +41,7 @@ namespace MediaBrowser.Common.Json.Converters public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) { static void ThrowInvalidOperationException() => throw new InvalidOperationException(); - Span span = new byte[16]; + Span span = stackalloc byte[16]; if (Utf8Formatter.TryFormat(value, span, out int bytesWritten)) { writer.WriteStringValue(span.Slice(0, bytesWritten)); From 8c4e679ff4c78e5361d14f151bf4d40b2312478c Mon Sep 17 00:00:00 2001 From: Ben Magee Date: Sun, 22 Dec 2019 22:36:11 +0000 Subject: [PATCH 086/252] PR style comments --- .../Auth/CustomAuthenticationHandlerTests.cs | 9 +++++++-- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs index bd23ca869b..f202f59e61 100644 --- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs @@ -36,7 +36,12 @@ namespace Jellyfin.Api.Tests.Auth public CustomAuthenticationHandlerTests() { - _fixture = new Fixture().Customize(new AutoMoqCustomization {ConfigureMembers = true}); + var fixtureCustomizations = new AutoMoqCustomization + { + ConfigureMembers = true + }; + + _fixture = new Fixture().Customize(fixtureCustomizations); AllowFixtureCircularDependencies(); _authServiceMock = _fixture.Freeze>(); @@ -143,7 +148,7 @@ namespace Jellyfin.Api.Tests.Auth Assert.Equal(_scheme.Name, authenticatedResult.Ticket.AuthenticationScheme); } - private User SetupUser(bool isAdmin=false) + private User SetupUser(bool isAdmin = false) { var user = _fixture.Create(); user.Policy.IsAdministrator = isAdmin; diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 77c58040e4..208a7bb30d 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -19,7 +19,7 @@ - + From 8d06d0dbdf7bbb156df0c002f2f0af6806be2b8b Mon Sep 17 00:00:00 2001 From: Ben Magee Date: Sun, 22 Dec 2019 22:39:29 +0000 Subject: [PATCH 087/252] Removed unneeded dependency --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 208a7bb30d..ac54cb26e3 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -22,10 +22,4 @@ - - - ..\..\..\..\..\..\usr\local\share\dotnet\packs\Microsoft.AspNetCore.App.Ref\3.0.0\ref\netcoreapp3.0\Microsoft.AspNetCore.Authentication.dll - - - From ef75455178dda5542cbe9b32cafc4705de299723 Mon Sep 17 00:00:00 2001 From: Ben Magee Date: Sun, 22 Dec 2019 22:54:46 +0000 Subject: [PATCH 088/252] Test RequiresElevationHandler for all roles --- .../RequiresElevationHandlerTests.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs diff --git a/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs new file mode 100644 index 0000000000..e2beea1ad8 --- /dev/null +++ b/tests/Jellyfin.Api.Tests/Auth/RequiresElevationPolicy/RequiresElevationHandlerTests.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Jellyfin.Api.Auth.RequiresElevationPolicy; +using Jellyfin.Api.Constants; +using Microsoft.AspNetCore.Authorization; +using Xunit; + +namespace Jellyfin.Api.Tests.Auth.RequiresElevationPolicy +{ + public class RequiresElevationHandlerTests + { + private readonly RequiresElevationHandler _sut; + + public RequiresElevationHandlerTests() + { + _sut = new RequiresElevationHandler(); + } + + [Theory] + [InlineData(UserRoles.Administrator, true)] + [InlineData(UserRoles.User, false)] + [InlineData(UserRoles.Guest, false)] + public async Task ShouldHandleRolesCorrectly(string role, bool shouldSucceed) + { + var requirements = new List {new RequiresElevationRequirement()}; + + var claims = new[] {new Claim(ClaimTypes.Role, role)}; + var identity = new ClaimsIdentity(claims); + var user = new ClaimsPrincipal(identity); + + var context = new AuthorizationHandlerContext(requirements, user, null); + + await _sut.HandleAsync(context); + Assert.Equal(shouldSucceed, context.HasSucceeded); + } + } +} From 00c6d392a13bb6a54b27c4c8c177eac5d8de5652 Mon Sep 17 00:00:00 2001 From: Ben Magee Date: Sun, 22 Dec 2019 23:18:42 +0000 Subject: [PATCH 089/252] Added tests for FirstTimeSetupOrElevatedHandler --- .../FirstTimeSetupOrElevatedHandlerTests.cs | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs diff --git a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs new file mode 100644 index 0000000000..84cdbe360d --- /dev/null +++ b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupOrElevatedPolicy/FirstTimeSetupOrElevatedHandlerTests.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.AutoMoq; +using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy; +using Jellyfin.Api.Constants; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using Microsoft.AspNetCore.Authorization; +using Moq; +using Xunit; + +namespace Jellyfin.Api.Tests.Auth.FirstTimeSetupOrElevatedPolicy +{ + public class FirstTimeSetupOrElevatedHandlerTests + { + private readonly Mock _configurationManagerMock; + private readonly List _requirements; + private readonly FirstTimeSetupOrElevatedHandler _sut; + + public FirstTimeSetupOrElevatedHandlerTests() + { + var fixture = new Fixture().Customize(new AutoMoqCustomization()); + _configurationManagerMock = fixture.Freeze>(); + _requirements = new List {new FirstTimeSetupOrElevatedRequirement()}; + + _sut = fixture.Create(); + } + + [Theory] + [InlineData(UserRoles.Administrator)] + [InlineData(UserRoles.Guest)] + [InlineData(UserRoles.User)] + public async Task ShouldSucceedIfStartupWizardIncomplete(string userRole) + { + SetupConfigurationManager(false); + var user = SetupUser(userRole); + var context = new AuthorizationHandlerContext(_requirements, user, null); + + await _sut.HandleAsync(context); + Assert.True(context.HasSucceeded); + } + + [Theory] + [InlineData(UserRoles.Administrator, true)] + [InlineData(UserRoles.Guest, false)] + [InlineData(UserRoles.User, false)] + public async Task ShouldRequireAdministratorIfStartupWizardComplete(string userRole, bool shouldSucceed) + { + SetupConfigurationManager(true); + var user = SetupUser(userRole); + var context = new AuthorizationHandlerContext(_requirements, user, null); + + await _sut.HandleAsync(context); + Assert.Equal(shouldSucceed, context.HasSucceeded); + } + + private static ClaimsPrincipal SetupUser(string role) + { + var claims = new[] {new Claim(ClaimTypes.Role, role)}; + var identity = new ClaimsIdentity(claims); + return new ClaimsPrincipal(identity); + } + + private void SetupConfigurationManager(bool startupWizardCompleted) + { + var commonConfiguration = new BaseApplicationConfiguration + { + IsStartupWizardCompleted = startupWizardCompleted + }; + + _configurationManagerMock.Setup(c => c.CommonConfiguration) + .Returns(commonConfiguration); + } + } +} From d1461b4238cdaee32137787e1eef5c3a7db697fa Mon Sep 17 00:00:00 2001 From: Ben Magee Date: Sun, 22 Dec 2019 23:21:20 +0000 Subject: [PATCH 090/252] Changed cast style as suggested, improved some member names to make them less ambiguous --- .../Auth/CustomAuthenticationHandlerTests.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs index f202f59e61..a2f5c25016 100644 --- a/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/CustomAuthenticationHandlerTests.cs @@ -23,8 +23,8 @@ namespace Jellyfin.Api.Tests.Auth { private readonly IFixture _fixture; - private readonly Mock _authServiceMock; - private readonly Mock> _optionsMock; + private readonly Mock _jellyfinAuthServiceMock; + private readonly Mock> _optionsMonitorMock; private readonly Mock _clockMock; private readonly Mock _serviceProviderMock; private readonly Mock _authenticationServiceMock; @@ -44,8 +44,8 @@ namespace Jellyfin.Api.Tests.Auth _fixture = new Fixture().Customize(fixtureCustomizations); AllowFixtureCircularDependencies(); - _authServiceMock = _fixture.Freeze>(); - _optionsMock = _fixture.Freeze>>(); + _jellyfinAuthServiceMock = _fixture.Freeze>(); + _optionsMonitorMock = _fixture.Freeze>>(); _clockMock = _fixture.Freeze>(); _serviceProviderMock = _fixture.Freeze>(); _authenticationServiceMock = _fixture.Freeze>(); @@ -56,7 +56,7 @@ namespace Jellyfin.Api.Tests.Auth _serviceProviderMock.Setup(s => s.GetService(typeof(IAuthenticationService))) .Returns(_authenticationServiceMock.Object); - _optionsMock.Setup(o => o.Get(It.IsAny())) + _optionsMonitorMock.Setup(o => o.Get(It.IsAny())) .Returns(new AuthenticationSchemeOptions { ForwardAuthenticate = null @@ -79,11 +79,11 @@ namespace Jellyfin.Api.Tests.Auth [Fact] public async Task HandleAuthenticateAsyncShouldFailWithNullUser() { - _authServiceMock.Setup( + _jellyfinAuthServiceMock.Setup( a => a.Authenticate( It.IsAny(), It.IsAny())) - .Returns((User) null); + .Returns((User)null); var authenticateResult = await _sut.AuthenticateAsync(); @@ -96,7 +96,7 @@ namespace Jellyfin.Api.Tests.Auth { var errorMessage = _fixture.Create(); - _authServiceMock.Setup( + _jellyfinAuthServiceMock.Setup( a => a.Authenticate( It.IsAny(), It.IsAny())) @@ -153,7 +153,7 @@ namespace Jellyfin.Api.Tests.Auth var user = _fixture.Create(); user.Policy.IsAdministrator = isAdmin; - _authServiceMock.Setup( + _jellyfinAuthServiceMock.Setup( a => a.Authenticate( It.IsAny(), It.IsAny())) From a22f1dabb4a26e46c4843b9bd8946e03293b8adb Mon Sep 17 00:00:00 2001 From: arg2009 Date: Mon, 23 Dec 2019 09:13:46 +0000 Subject: [PATCH 091/252] Added translation using Weblate (Indonesian) --- Emby.Server.Implementations/Localization/Core/id.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/id.json diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -0,0 +1 @@ +{} From a41ec5c9d4642f28cc1a277f555fc6a4f2929b85 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 23 Dec 2019 15:29:30 +0100 Subject: [PATCH 092/252] Fix typo --- MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs index d5529e56c8..6b60b66c0a 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// /// The filename. [JsonPropertyName("filename")] - public string Fileame { get; set; } + public string Filename { get; set; } /// /// Gets or sets the nb_streams. From ef62df69dfe08dc162bffb7507d095df43f95ab2 Mon Sep 17 00:00:00 2001 From: arg2009 Date: Mon, 23 Dec 2019 09:15:45 +0000 Subject: [PATCH 093/252] Translated using Weblate (Indonesian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/id/ --- .../Localization/Core/id.json | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json index 0967ef424b..8d17ad38e1 100644 --- a/Emby.Server.Implementations/Localization/Core/id.json +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -1 +1,32 @@ -{} +{ + "Albums": "Album", + "AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi", + "AppDeviceValues": "Aplikasi: {0}, Alat: {1}", + "LabelRunningTimeValue": "Waktu berjalan: {0}", + "MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}", + "MessageApplicationUpdated": "Jellyfin Server sudah diperbarui", + "Latest": "Terbaru", + "LabelIpAddressValue": "IP address: {0}", + "ItemRemovedWithName": "{0} sudah dikeluarkan dari perpustakaan", + "ItemAddedWithName": "{0} sudah dimasukkan ke dalam perpustakaan", + "Inherit": "Warisan", + "HomeVideos": "Video Rumah", + "HeaderRecordingGroups": "Grup Rekaman", + "HeaderNextUp": "Selanjutnya", + "HeaderLiveTV": "TV Live", + "HeaderFavoriteSongs": "Lagu Favorit", + "HeaderFavoriteShows": "Tayangan Favorit", + "HeaderFavoriteEpisodes": "Episode Favorit", + "HeaderFavoriteArtists": "Artis Favorit", + "HeaderFavoriteAlbums": "Album Favorit", + "HeaderContinueWatching": "Masih Melihat", + "HeaderCameraUploads": "Uplod Kamera", + "HeaderAlbumArtists": "Album Artis", + "Genres": "Genre", + "Folders": "Folder", + "Favorites": "Favorit", + "Collections": "Koleksi", + "Books": "Buku", + "Artists": "Artis", + "Application": "Aplikasi" +} From 5bf0eb2b0ef29a9398d638a910e2cd3945331252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= Date: Wed, 25 Dec 2019 00:15:55 +0000 Subject: [PATCH 094/252] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)=20Translation:=20Jellyfin/Jellyfin=20Translat?= =?UTF-8?q?e-URL:=20https://translate.jellyfin.org/projects/jellyfin/jelly?= =?UTF-8?q?fin-core/nb=5FNO/?= 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index 1237fb70a7..48216f71c2 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -45,7 +45,7 @@ "NameSeasonNumber": "Sesong {0}", "NameSeasonUnknown": "Sesong ukjent", "NewVersionIsAvailable": "En ny versjon av Jellyfin-serveren er tilgjengelig for nedlastning.", - "NotificationOptionApplicationUpdateAvailable": "Applikasjon oppdatering tilgjengelig", + "NotificationOptionApplicationUpdateAvailable": "Programvareoppdatering er tilgjengelig", "NotificationOptionApplicationUpdateInstalled": "Applikasjonsoppdatering installert", "NotificationOptionAudioPlayback": "Lyd tilbakespilling startet", "NotificationOptionAudioPlaybackStopped": "Lyd avspilling stoppet", From d7164d2eb758fa9045b92bacc930f86f1b4b71a3 Mon Sep 17 00:00:00 2001 From: Allan <176101024@qq.com> Date: Thu, 26 Dec 2019 08:47:55 +0000 Subject: [PATCH 095/252] 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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 87f8553ae0..6a72a28e57 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -24,7 +24,7 @@ "HeaderFavoriteShows": "最爱的节目", "HeaderFavoriteSongs": "最爱的歌曲", "HeaderLiveTV": "电视直播", - "HeaderNextUp": "接下来", + "HeaderNextUp": "下一步", "HeaderRecordingGroups": "录制组", "HomeVideos": "家庭视频", "Inherit": "继承", @@ -34,7 +34,7 @@ "LabelRunningTimeValue": "运行时间:{0}", "Latest": "最新", "MessageApplicationUpdated": "Jellyfin 服务器已更新", - "MessageApplicationUpdatedTo": "Jellyfin Server 的版本已更新为 {0}", + "MessageApplicationUpdatedTo": "Jellyfin Server 版本已更新为 {0}", "MessageNamedServerConfigurationUpdatedWithValue": "服务器配置 {0} 部分已更新", "MessageServerConfigurationUpdated": "服务器配置已更新", "MixedContent": "混合内容", From 49ce876c2c2bdbd9fb51f2cae1ba7680bf675de5 Mon Sep 17 00:00:00 2001 From: Z Yang Date: Fri, 27 Dec 2019 03:52:01 +0000 Subject: [PATCH 096/252] 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 6a72a28e57..a4d53c57e9 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -42,7 +42,7 @@ "Music": "音乐", "MusicVideos": "音乐视频", "NameInstallFailed": "{0} 安装失败", - "NameSeasonNumber": "季 {0}", + "NameSeasonNumber": "第 {0} 季", "NameSeasonUnknown": "未知季", "NewVersionIsAvailable": "Jellyfin Server 有新版本可以下载。", "NotificationOptionApplicationUpdateAvailable": "有可用的应用程序更新", From ab57b504fe2dabb8e493caaf4289c0eec3bddaed Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 29 Dec 2019 19:32:12 +0100 Subject: [PATCH 097/252] Delete user config dir on user deletion --- .../Library/UserManager.cs | 84 +++++++------------ .../Library/IUserManager.cs | 2 - 2 files changed, 29 insertions(+), 57 deletions(-) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 85bfa154af..bfb3ddae23 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -42,13 +42,13 @@ namespace Emby.Server.Implementations.Library /// public class UserManager : IUserManager { + private readonly object _policySyncLock = new object(); + private readonly object _configSyncLock = new object(); /// /// The logger. /// private readonly ILogger _logger; - private readonly object _policySyncLock = new object(); - /// /// Gets the active user repository. /// @@ -255,7 +255,12 @@ namespace Emby.Server.Implementations.Library return builder.ToString(); } - public async Task AuthenticateUser(string username, string password, string hashedPassword, string remoteEndPoint, bool isUserSession) + public async Task AuthenticateUser( + string username, + string password, + string hashedPassword, + string remoteEndPoint, + bool isUserSession) { if (string.IsNullOrWhiteSpace(username)) { @@ -754,13 +759,10 @@ namespace Emby.Server.Implementations.Library return user; } - /// - /// Deletes the user. - /// - /// The user. - /// Task. - /// user - /// + /// + /// The user is null. + /// The user doesn't exist, or is the last administrator. + /// The user can't be deleted; there are no other users. public void DeleteUser(User user) { if (user == null) @@ -779,7 +781,7 @@ namespace Emby.Server.Implementations.Library if (_users.Count == 1) { - throw new ArgumentException(string.Format( + throw new InvalidOperationException(string.Format( CultureInfo.InvariantCulture, "The user '{0}' cannot be deleted because there must be at least one user in the system.", user.Name)); @@ -800,16 +802,19 @@ namespace Emby.Server.Implementations.Library _userRepository.DeleteUser(user); - try + // Delete user config dir + lock (_configSyncLock) + lock (_policySyncLock) { - _fileSystem.DeleteFile(configPath); + try + { + Directory.Delete(user.ConfigurationDirectoryPath, true); + } + catch (IOException ex) + { + _logger.LogError(ex, "Error deleting user config dir: {Path}", user.ConfigurationDirectoryPath); + } } - catch (IOException ex) - { - _logger.LogError(ex, "Error deleting file {path}", configPath); - } - - DeleteUserPolicy(user); _users.TryRemove(user.Id, out _); @@ -918,10 +923,9 @@ namespace Emby.Server.Implementations.Library public UserPolicy GetUserPolicy(User user) { var path = GetPolicyFilePath(user); - if (!File.Exists(path)) { - return GetDefaultPolicy(user); + return GetDefaultPolicy(); } try @@ -931,19 +935,15 @@ namespace Emby.Server.Implementations.Library return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path); } } - catch (IOException) - { - return GetDefaultPolicy(user); - } catch (Exception ex) { - _logger.LogError(ex, "Error reading policy file: {path}", path); + _logger.LogError(ex, "Error reading policy file: {Path}", path); - return GetDefaultPolicy(user); + return GetDefaultPolicy(); } } - private static UserPolicy GetDefaultPolicy(User user) + private static UserPolicy GetDefaultPolicy() { return new UserPolicy { @@ -983,27 +983,6 @@ namespace Emby.Server.Implementations.Library } } - private void DeleteUserPolicy(User user) - { - var path = GetPolicyFilePath(user); - - try - { - lock (_policySyncLock) - { - _fileSystem.DeleteFile(path); - } - } - catch (IOException) - { - - } - catch (Exception ex) - { - _logger.LogError(ex, "Error deleting policy file"); - } - } - private static string GetPolicyFilePath(User user) { return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml"); @@ -1030,19 +1009,14 @@ namespace Emby.Server.Implementations.Library return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path); } } - catch (IOException) - { - return new UserConfiguration(); - } catch (Exception ex) { - _logger.LogError(ex, "Error reading policy file: {path}", path); + _logger.LogError(ex, "Error reading policy file: {Path}", path); return new UserConfiguration(); } } - private readonly object _configSyncLock = new object(); public void UpdateConfiguration(Guid userId, UserConfiguration config) { var user = GetUserById(userId); diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 8d92c9f6f2..be7b4ce59d 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -102,8 +102,6 @@ namespace MediaBrowser.Controller.Library /// /// The user. /// Task. - /// user - /// void DeleteUser(User user); /// From 49330e30827eb4fc6b94cfbf6cd611e1d297920d Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 30 Dec 2019 16:03:20 +0100 Subject: [PATCH 098/252] Fix a couple of TODOs --- Emby.Dlna/Eventing/EventManager.cs | 36 +++++--------- .../ApplicationHost.cs | 3 +- .../Collections/CollectionManager.cs | 48 ++++--------------- 3 files changed, 23 insertions(+), 64 deletions(-) diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs index 4b542a820c..b76a0066d1 100644 --- a/Emby.Dlna/Eventing/EventManager.cs +++ b/Emby.Dlna/Eventing/EventManager.cs @@ -29,25 +29,15 @@ namespace Emby.Dlna.Eventing { var subscription = GetSubscription(subscriptionId, false); - int timeoutSeconds; + subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300; + int timeoutSeconds = subscription.TimeoutSeconds; + subscription.SubscriptionTime = DateTime.UtcNow; - // Remove logging for now because some devices are sending this very frequently - // TODO re-enable with dlna debug logging setting - //_logger.LogDebug("Renewing event subscription for {0} with timeout of {1} to {2}", - // subscription.NotificationType, - // timeout, - // subscription.CallbackUrl); - - if (subscription != null) - { - subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300; - timeoutSeconds = subscription.TimeoutSeconds; - subscription.SubscriptionTime = DateTime.UtcNow; - } - else - { - timeoutSeconds = 300; - } + _logger.LogDebug( + "Renewing event subscription for {0} with timeout of {1} to {2}", + subscription.NotificationType, + timeoutSeconds, + subscription.CallbackUrl); return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds); } @@ -57,12 +47,10 @@ namespace Emby.Dlna.Eventing var timeout = ParseTimeout(requestedTimeoutString) ?? 300; var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); - // Remove logging for now because some devices are sending this very frequently - // TODO re-enable with dlna debug logging setting - //_logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}", - // notificationType, - // timeout, - // callbackUrl); + _logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}", + notificationType, + timeout, + callbackUrl); _subscriptions.TryAdd(id, new EventSubscription { diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index aed0c14a29..67bc0cd2b1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -764,9 +764,8 @@ namespace Emby.Server.Implementations LibraryManager = new LibraryManager(this, LoggerFactory, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager); serviceCollection.AddSingleton(LibraryManager); - // TODO wtaylor: investigate use of second music manager var musicManager = new MusicManager(LibraryManager); - serviceCollection.AddSingleton(new MusicManager(LibraryManager)); + serviceCollection.AddSingleton(musicManager); LibraryMonitor = new LibraryMonitor(LoggerFactory, LibraryManager, ServerConfigurationManager, FileSystemManager); serviceCollection.AddSingleton(LibraryMonitor); diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 2b8a5bdc56..1d7c11989a 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -31,11 +31,7 @@ namespace Emby.Server.Implementations.Collections private readonly ILogger _logger; private readonly IProviderManager _providerManager; private readonly ILocalizationManager _localizationManager; - private IApplicationPaths _appPaths; - - public event EventHandler CollectionCreated; - public event EventHandler ItemsAddedToCollection; - public event EventHandler ItemsRemovedFromCollection; + private readonly IApplicationPaths _appPaths; public CollectionManager( ILibraryManager libraryManager, @@ -55,6 +51,10 @@ namespace Emby.Server.Implementations.Collections _appPaths = appPaths; } + public event EventHandler CollectionCreated; + public event EventHandler ItemsAddedToCollection; + public event EventHandler ItemsRemovedFromCollection; + private IEnumerable FindFolders(string path) { return _libraryManager @@ -341,11 +341,11 @@ namespace Emby.Server.Implementations.Collections } } - public class CollectionManagerEntryPoint : IServerEntryPoint + public sealed class CollectionManagerEntryPoint : IServerEntryPoint { private readonly CollectionManager _collectionManager; private readonly IServerConfigurationManager _config; - private ILogger _logger; + private readonly ILogger _logger; public CollectionManagerEntryPoint(ICollectionManager collectionManager, IServerConfigurationManager config, ILogger logger) { @@ -354,6 +354,7 @@ namespace Emby.Server.Implementations.Collections _logger = logger; } + /// public async Task RunAsync() { if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted) @@ -377,39 +378,10 @@ namespace Emby.Server.Implementations.Collections } } - #region IDisposable Support - private bool disposedValue = false; // To detect redundant calls - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - // TODO: dispose managed state (managed objects). - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~CollectionManagerEntryPoint() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - // This code added to correctly implement the disposable pattern. + /// public void Dispose() { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); + // Nothing to dispose } - #endregion } } From f672ecbac5c8dee8fc4c21cbbc27bf4a33d08e03 Mon Sep 17 00:00:00 2001 From: marty Date: Sun, 29 Dec 2019 11:37:18 +0000 Subject: [PATCH 099/252] Translated using Weblate (Lithuanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lt/ --- .../Localization/Core/lt-LT.json | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index e2f3ba3dc8..bfacb65569 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -18,13 +18,13 @@ "HeaderAlbumArtists": "Albumo atlikėjai", "HeaderCameraUploads": "Camera Uploads", "HeaderContinueWatching": "Žiūrėti toliau", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteArtists": "Favorite Artists", - "HeaderFavoriteEpisodes": "Favorite Episodes", - "HeaderFavoriteShows": "Favorite Shows", - "HeaderFavoriteSongs": "Favorite Songs", - "HeaderLiveTV": "Live TV", - "HeaderNextUp": "Next Up", + "HeaderFavoriteAlbums": "Mėgstami Albumai", + "HeaderFavoriteArtists": "Mėgstami Atlikėjai", + "HeaderFavoriteEpisodes": "Mėgstamiausios serijos", + "HeaderFavoriteShows": "Mėgstamiausi serialai", + "HeaderFavoriteSongs": "Mėgstamos dainos", + "HeaderLiveTV": "TV gyvai", + "HeaderNextUp": "Toliau eilėje", "HeaderRecordingGroups": "Recording Groups", "HomeVideos": "Home videos", "Inherit": "Inherit", @@ -50,19 +50,19 @@ "NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionInstallationFailed": "Diegimo klaida", "NotificationOptionNewLibraryContent": "New content added", "NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginInstalled": "Plugin installed", "NotificationOptionPluginUninstalled": "Plugin uninstalled", "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Server restart required", + "NotificationOptionServerRestartRequired": "Reikalingas serverio perleidimas.", "NotificationOptionTaskFailed": "Scheduled task failure", "NotificationOptionUserLockedOut": "User locked out", "NotificationOptionVideoPlayback": "Video playback started", "NotificationOptionVideoPlaybackStopped": "Video playback stopped", - "Photos": "Photos", - "Playlists": "Playlists", + "Photos": "Nuotraukos", + "Playlists": "Grojaraštis", "Plugin": "Plugin", "PluginInstalledWithName": "{0} was installed", "PluginUninstalledWithName": "{0} was uninstalled", @@ -71,8 +71,8 @@ "ScheduledTaskFailedWithName": "{0} failed", "ScheduledTaskStartedWithName": "{0} started", "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", - "Shows": "Shows", - "Songs": "Songs", + "Shows": "Laidos", + "Songs": "Kūriniai", "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", From 167549f5f1563c5d7bcb4808b08231be0f924528 Mon Sep 17 00:00:00 2001 From: gnattu Date: Tue, 31 Dec 2019 22:33:14 -0500 Subject: [PATCH 100/252] Let HLS fallback to mpegts in case device reported unsupported container --- MediaBrowser.Api/Playback/UniversalAudioService.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index 9cba9df139..563e09ae69 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -300,6 +300,9 @@ namespace MediaBrowser.Api.Playback var transcodingProfile = deviceProfile.TranscodingProfiles[0]; + //HLS Segment container can only be mpegts or fmp4 per ffmpeg documentation + var supoortedHLSContainer = new string[] { "mpegts", "fmp4" }; + var newRequest = new GetMasterHlsAudioPlaylist { AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)), @@ -312,7 +315,8 @@ namespace MediaBrowser.Api.Playback PlaySessionId = playbackInfoResult.PlaySessionId, StartTimeTicks = request.StartTimeTicks, Static = isStatic, - SegmentContainer = request.TranscodingContainer, + //fallback to mpegts if device reports some wierd value that is not supported by HLS + SegmentContainer = Array.Exists(supoortedHLSContainer, element => element == request.TranscodingContainer) ? request.TranscodingContainer : "mpegts", AudioSampleRate = request.MaxAudioSampleRate, MaxAudioBitDepth = request.MaxAudioBitDepth, BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames, From c10cb661694fca763ec4e5be30110ce077a0b428 Mon Sep 17 00:00:00 2001 From: dkanada Date: Wed, 1 Jan 2020 14:45:09 +0900 Subject: [PATCH 101/252] remove directory check --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 1f5bd7deb5..cee51479ec 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -394,7 +394,7 @@ namespace Emby.Server.Implementations.Library foreach (var fileSystemInfo in item.GetDeletePaths()) { - if (fileSystemInfo.IsDirectory ? Directory.Exists(fileSystemInfo.FullName) : File.Exists(fileSystemInfo.FullName)) + if (Directory.Exists(fileSystemInfo.FullName) || File.Exists(fileSystemInfo.FullName)) { try { From c62599f8736ee64defdb920a205dd113d3653029 Mon Sep 17 00:00:00 2001 From: marty Date: Mon, 30 Dec 2019 20:02:47 +0000 Subject: [PATCH 102/252] Translated using Weblate (Lithuanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lt/ --- .../Localization/Core/lt-LT.json | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index bfacb65569..9ba2748110 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -1,22 +1,22 @@ { "Albums": "Albumai", - "AppDeviceValues": "App: {0}, Device: {1}", - "Application": "Application", + "AppDeviceValues": "Programa: {0}, Įrenginys: {1}", + "Application": "Programa", "Artists": "Atlikėjai", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", + "AuthenticationSucceededWithUserName": "{0} sėkmingai autentifikuota", "Books": "Knygos", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", + "CameraImageUploadedFrom": "Nauja nuotrauka įkelta iš kameros {0}", "Channels": "Kanalai", - "ChapterNameValue": "Chapter {0}", + "ChapterNameValue": "Scena{0}", "Collections": "Kolekcijos", - "DeviceOfflineWithName": "{0} has disconnected", - "DeviceOnlineWithName": "{0} is connected", - "FailedLoginAttemptWithUserName": "Failed login attempt from {0}", + "DeviceOfflineWithName": "{0} buvo atjungtas", + "DeviceOnlineWithName": "{0} prisijungęs", + "FailedLoginAttemptWithUserName": "{0} - nesėkmingas bandymas prisijungti", "Favorites": "Mėgstami", "Folders": "Katalogai", "Genres": "Žanrai", "HeaderAlbumArtists": "Albumo atlikėjai", - "HeaderCameraUploads": "Camera Uploads", + "HeaderCameraUploads": "Kameros", "HeaderContinueWatching": "Žiūrėti toliau", "HeaderFavoriteAlbums": "Mėgstami Albumai", "HeaderFavoriteArtists": "Mėgstami Atlikėjai", @@ -25,73 +25,73 @@ "HeaderFavoriteSongs": "Mėgstamos dainos", "HeaderLiveTV": "TV gyvai", "HeaderNextUp": "Toliau eilėje", - "HeaderRecordingGroups": "Recording Groups", - "HomeVideos": "Home videos", - "Inherit": "Inherit", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", + "HeaderRecordingGroups": "Įrašų grupės", + "HomeVideos": "Namų vaizdo įrašai", + "Inherit": "Paveldėti", + "ItemAddedWithName": "{0} - buvo įkeltas į mediateką", + "ItemRemovedWithName": "{0} - buvo pašalinta iš mediatekos", "LabelIpAddressValue": "Ip address: {0}", - "LabelRunningTimeValue": "Running time: {0}", - "Latest": "Latest", - "MessageApplicationUpdated": "Jellyfin Server has been updated", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", - "MessageServerConfigurationUpdated": "Server configuration has been updated", + "LabelRunningTimeValue": "Trukmė: {0}", + "Latest": "Naujausi", + "MessageApplicationUpdated": "\"Jellyfin Server\" atnaujintas", + "MessageApplicationUpdatedTo": "\"Jellyfin Server\" buvo atnaujinta iki {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "Serverio nustatymai (skyrius {0}) buvo atnaujinti", + "MessageServerConfigurationUpdated": "Serverio nustatymai buvo atnaujinti", "MixedContent": "Mixed content", "Movies": "Filmai", - "Music": "Music", - "MusicVideos": "Music videos", - "NameInstallFailed": "{0} installation failed", - "NameSeasonNumber": "Season {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", - "NotificationOptionApplicationUpdateAvailable": "Application update available", - "NotificationOptionApplicationUpdateInstalled": "Application update installed", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "Music": "Muzika", + "MusicVideos": "Muzikiniai klipai", + "NameInstallFailed": "{0} diegimo klaida", + "NameSeasonNumber": "Sezonas {0}", + "NameSeasonUnknown": "Sezonas neatpažintas", + "NewVersionIsAvailable": "Nauja \"Jellyfin Server\" versija yra prieinama atsisiuntimui.", + "NotificationOptionApplicationUpdateAvailable": "Galimi programos atnaujinimai", + "NotificationOptionApplicationUpdateInstalled": "Programos atnaujinimai įdiegti", + "NotificationOptionAudioPlayback": "Garso atkūrimas pradėtas", + "NotificationOptionAudioPlaybackStopped": "Garso atkūrimas sustabdytas", + "NotificationOptionCameraImageUploaded": "Kameros vaizdai įkelti", "NotificationOptionInstallationFailed": "Diegimo klaida", - "NotificationOptionNewLibraryContent": "New content added", - "NotificationOptionPluginError": "Plugin failure", - "NotificationOptionPluginInstalled": "Plugin installed", - "NotificationOptionPluginUninstalled": "Plugin uninstalled", - "NotificationOptionPluginUpdateInstalled": "Plugin update installed", - "NotificationOptionServerRestartRequired": "Reikalingas serverio perleidimas.", - "NotificationOptionTaskFailed": "Scheduled task failure", - "NotificationOptionUserLockedOut": "User locked out", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", + "NotificationOptionNewLibraryContent": "Naujas turinys įkeltas", + "NotificationOptionPluginError": "Įskiepio klaida", + "NotificationOptionPluginInstalled": "Įskiepis įdiegtas", + "NotificationOptionPluginUninstalled": "Įskiepis pašalintas", + "NotificationOptionPluginUpdateInstalled": "Įskiepio atnaujinimas įdiegtas", + "NotificationOptionServerRestartRequired": "Reikalingas serverio perleidimas", + "NotificationOptionTaskFailed": "Suplanuotos užduoties klaida", + "NotificationOptionUserLockedOut": "Vartotojas užblokuotas", + "NotificationOptionVideoPlayback": "Vaizdo įrašo atkūrimas pradėtas", + "NotificationOptionVideoPlaybackStopped": "Vaizdo įrašo atkūrimas sustabdytas", "Photos": "Nuotraukos", "Playlists": "Grojaraštis", "Plugin": "Plugin", - "PluginInstalledWithName": "{0} was installed", - "PluginUninstalledWithName": "{0} was uninstalled", - "PluginUpdatedWithName": "{0} was updated", + "PluginInstalledWithName": "{0} buvo įdiegtas", + "PluginUninstalledWithName": "{0} buvo pašalintas", + "PluginUpdatedWithName": "{0} buvo atnaujintas", "ProviderValue": "Provider: {0}", - "ScheduledTaskFailedWithName": "{0} failed", - "ScheduledTaskStartedWithName": "{0} started", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", + "ScheduledTaskFailedWithName": "{0} klaida", + "ScheduledTaskStartedWithName": "{0} paleista", + "ServerNameNeedsToBeRestarted": "{0} reikia iš naujo paleisti", "Shows": "Laidos", "Songs": "Kūriniai", - "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", + "StartupEmbyServerIsLoading": "Jellyfin Server kraunasi. Netrukus pabandykite dar kartą.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", + "SubtitleDownloadFailureFromForItem": "{1} subtitrai buvo nesėkmingai parsiųsti iš {0}", + "SubtitlesDownloadedForItem": "{0} subtitrai parsiųsti", "Sync": "Sinchronizuoti", "System": "System", - "TvShows": "TV Shows", + "TvShows": "TV Serialai", "User": "User", - "UserCreatedWithName": "User {0} has been created", - "UserDeletedWithName": "User {0} has been deleted", - "UserDownloadingItemWithValues": "{0} is downloading {1}", - "UserLockedOutWithName": "User {0} has been locked out", - "UserOfflineFromDevice": "{0} has disconnected from {1}", - "UserOnlineFromDevice": "{0} is online from {1}", - "UserPasswordChangedWithName": "Password has been changed for user {0}", - "UserPolicyUpdatedWithName": "User policy has been updated for {0}", - "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", - "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", + "UserCreatedWithName": "Vartotojas {0} buvo sukurtas", + "UserDeletedWithName": "Vartotojas {0} ištrintas", + "UserDownloadingItemWithValues": "{0} siunčiasi {1}", + "UserLockedOutWithName": "Vartotojas {0} užblokuotas", + "UserOfflineFromDevice": "{0} buvo atjungtas nuo {1}", + "UserOnlineFromDevice": "{0} prisijungęs iš {1}", + "UserPasswordChangedWithName": "Slaptažodis pakeistas vartotojui {0}", + "UserPolicyUpdatedWithName": "Vartotojo {0} teisės buvo pakeistos", + "UserStartedPlayingItemWithValues": "{0} leidžia {1} į {2}", + "UserStoppedPlayingItemWithValues": "{0} baigė leisti {1} į {2}", + "ValueHasBeenAddedToLibrary": "{0} pridėtas į mediateką", "ValueSpecialEpisodeName": "Ypatinga - {0}", "VersionNumber": "Version {0}" } From 72d2b5910ad2d7ba5b020247e9ae5d03edb5aeb6 Mon Sep 17 00:00:00 2001 From: erikasne6152 Date: Mon, 30 Dec 2019 21:38:20 +0000 Subject: [PATCH 103/252] Translated using Weblate (Lithuanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lt/ --- Emby.Server.Implementations/Localization/Core/lt-LT.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index 9ba2748110..e8e1b7740e 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -30,7 +30,7 @@ "Inherit": "Paveldėti", "ItemAddedWithName": "{0} - buvo įkeltas į mediateką", "ItemRemovedWithName": "{0} - buvo pašalinta iš mediatekos", - "LabelIpAddressValue": "Ip address: {0}", + "LabelIpAddressValue": "IP adresas: {0}", "LabelRunningTimeValue": "Trukmė: {0}", "Latest": "Naujausi", "MessageApplicationUpdated": "\"Jellyfin Server\" atnaujintas", From 59cdfdc2d9435f06dc535837ae9d1fe63543bb08 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 2 Jan 2020 14:38:13 +0100 Subject: [PATCH 104/252] Fix JSON subtitle writer --- MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs index 241ebc6df5..1b452b0cec 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/JsonWriter.cs @@ -16,6 +16,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles using (var writer = new Utf8JsonWriter(stream)) { var trackevents = info.TrackEvents; + writer.WriteStartObject(); writer.WriteStartArray("TrackEvents"); for (int i = 0; i < trackevents.Count; i++) @@ -33,7 +34,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles writer.WriteEndObject(); } + writer.WriteEndArray(); writer.WriteEndObject(); + + writer.Flush(); } } } From 09d1f976d994f13dd6e69bae3d371262abb0d56d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 2 Jan 2020 20:19:01 +0100 Subject: [PATCH 105/252] Replace unicode char with its integer value --- .../Probing/ProbeResultNormalizer.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 54d02fc9f7..3067238f73 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -1334,24 +1334,25 @@ namespace MediaBrowser.MediaEncoding.Probing { video.Timestamp = GetMpegTimestamp(video.Path); - _logger.LogDebug("Video has {timestamp} timestamp", video.Timestamp); + _logger.LogDebug("Video has {Timestamp} timestamp", video.Timestamp); } catch (Exception ex) { - _logger.LogError(ex, "Error extracting timestamp info from {path}", video.Path); + _logger.LogError(ex, "Error extracting timestamp info from {Path}", video.Path); video.Timestamp = null; } } } } + // REVIEW: private TransportStreamTimestamp GetMpegTimestamp(string path) { - var packetBuffer = new byte['Å']; + var packetBuffer = new byte[197]; - using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { - fs.Read(packetBuffer, 0, packetBuffer.Length); + fs.Read(packetBuffer); } if (packetBuffer[0] == 71) @@ -1359,7 +1360,7 @@ namespace MediaBrowser.MediaEncoding.Probing return TransportStreamTimestamp.None; } - if ((packetBuffer[4] == 71) && (packetBuffer['Ä'] == 71)) + if ((packetBuffer[4] == 71) && (packetBuffer[196] == 71)) { if ((packetBuffer[0] == 0) && (packetBuffer[1] == 0) && (packetBuffer[2] == 0) && (packetBuffer[3] == 0)) { From 599432890311c9255cb376bbeb6f5f4f673fbf1b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 2 Jan 2020 21:22:10 +0100 Subject: [PATCH 106/252] Fix baseurl (again) --- .../ApplicationHost.cs | 40 +++++++++++++------ .../IServerApplicationHost.cs | 8 ++-- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 67bc0cd2b1..9b853c6d13 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://"); } - return string.Format("http://{0}:{1}", - host, - HttpPort.ToString(CultureInfo.InvariantCulture)); + url.Append(host) + .Append(':') + .Append(HttpPort); + + string baseUrl = ServerConfigurationManager.Configuration.BaseUrl; + if (baseUrl.Length != 0) + { + url.Append('/').Append(baseUrl); + } + + return url.ToString(); } public Task> GetLocalIpAddresses(CancellationToken cancellationToken) diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index b3c56bdd5f..25f0905eb8 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); From 295eaa2ccb65f2a8d4d2f9d97f6c3cbe410b1588 Mon Sep 17 00:00:00 2001 From: Constantin Matel Date: Fri, 3 Jan 2020 15:36:58 +0000 Subject: [PATCH 107/252] Added translation using Weblate (Romanian) --- Emby.Server.Implementations/Localization/Core/ro.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/ro.json diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/ro.json @@ -0,0 +1 @@ +{} From 5cd8338e73669d28c894aac9918a67af2cbc9730 Mon Sep 17 00:00:00 2001 From: shoeflydbm Date: Thu, 2 Jan 2020 21:35:07 +0000 Subject: [PATCH 108/252] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)=20Translation:=20Jellyfin/Jellyfin=20Translat?= =?UTF-8?q?e-URL:=20https://translate.jellyfin.org/projects/jellyfin/jelly?= =?UTF-8?q?fin-core/nb=5FNO/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Localization/Core/nb.json | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index 48216f71c2..7d4b2bdac1 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -17,7 +17,7 @@ "Genres": "Sjangre", "HeaderAlbumArtists": "Albumartister", "HeaderCameraUploads": "Kameraopplastinger", - "HeaderContinueWatching": "Forsett å se på", + "HeaderContinueWatching": "Fortsett å se", "HeaderFavoriteAlbums": "Favorittalbum", "HeaderFavoriteArtists": "Favorittartister", "HeaderFavoriteEpisodes": "Favorittepisoder", @@ -25,18 +25,18 @@ "HeaderFavoriteSongs": "Favorittsanger", "HeaderLiveTV": "Direkte-TV", "HeaderNextUp": "Neste", - "HeaderRecordingGroups": "Opptak Grupper", - "HomeVideos": "Hjemmelaget filmer", + "HeaderRecordingGroups": "Opptaksgrupper", + "HomeVideos": "Hjemmelagde filmer", "Inherit": "Arve", "ItemAddedWithName": "{0} ble lagt til i biblioteket", "ItemRemovedWithName": "{0} ble fjernet fra biblioteket", - "LabelIpAddressValue": "IP adresse: {0}", - "LabelRunningTimeValue": "Løpetid {0}", + "LabelIpAddressValue": "IP-adresse: {0}", + "LabelRunningTimeValue": "Kjøretid {0}", "Latest": "Siste", - "MessageApplicationUpdated": "Jellyfin server har blitt oppdatert", - "MessageApplicationUpdatedTo": "Jellyfin-serveren ble oppdatert til {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Server konfigurasjon seksjon {0} har blitt oppdatert", - "MessageServerConfigurationUpdated": "Server konfigurasjon er oppdatert", + "MessageApplicationUpdated": "Jellyfin Server har blitt oppdatert", + "MessageApplicationUpdatedTo": "Jellyfin Server ble oppdatert til {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfigurasjon seksjon {0} har blitt oppdatert", + "MessageServerConfigurationUpdated": "Serverkonfigurasjon er oppdatert", "MixedContent": "Blandet innhold", "Movies": "Filmer", "Music": "Musikk", @@ -44,38 +44,38 @@ "NameInstallFailed": "{0}-installasjonen mislyktes", "NameSeasonNumber": "Sesong {0}", "NameSeasonUnknown": "Sesong ukjent", - "NewVersionIsAvailable": "En ny versjon av Jellyfin-serveren er tilgjengelig for nedlastning.", + "NewVersionIsAvailable": "En ny versjon av Jellyfin Server er tilgjengelig for nedlasting.", "NotificationOptionApplicationUpdateAvailable": "Programvareoppdatering er tilgjengelig", "NotificationOptionApplicationUpdateInstalled": "Applikasjonsoppdatering installert", - "NotificationOptionAudioPlayback": "Lyd tilbakespilling startet", - "NotificationOptionAudioPlaybackStopped": "Lyd avspilling stoppet", - "NotificationOptionCameraImageUploaded": "Kamera bilde lastet opp", + "NotificationOptionAudioPlayback": "Lydavspilling startet", + "NotificationOptionAudioPlaybackStopped": "Lydavspilling stoppet", + "NotificationOptionCameraImageUploaded": "Kamerabilde lastet opp", "NotificationOptionInstallationFailed": "Installasjonsfeil", - "NotificationOptionNewLibraryContent": "Ny innhold er lagt til", - "NotificationOptionPluginError": "Plugin feil", + "NotificationOptionNewLibraryContent": "Nytt innhold lagt til", + "NotificationOptionPluginError": "Pluginfeil", "NotificationOptionPluginInstalled": "Plugin installert", "NotificationOptionPluginUninstalled": "Plugin avinstallert", - "NotificationOptionPluginUpdateInstalled": "Plugin oppdatering installert", - "NotificationOptionServerRestartRequired": "Server omstart er nødvendig", - "NotificationOptionTaskFailed": "Feil under utføring av planlagt oppgaver", + "NotificationOptionPluginUpdateInstalled": "Pluginoppdatering installert", + "NotificationOptionServerRestartRequired": "Serveromstart er nødvendig", + "NotificationOptionTaskFailed": "Feil under utføring av planlagt oppgave", "NotificationOptionUserLockedOut": "Bruker er utestengt", - "NotificationOptionVideoPlayback": "Video tilbakespilling startet", - "NotificationOptionVideoPlaybackStopped": "Video avspilling stoppet", + "NotificationOptionVideoPlayback": "Videoavspilling startet", + "NotificationOptionVideoPlaybackStopped": "Videoavspilling stoppet", "Photos": "Bilder", - "Playlists": "Spillelister", + "Playlists": "Spliielister", "Plugin": "Plugin", "PluginInstalledWithName": "{0} ble installert", "PluginUninstalledWithName": "{0} ble avinstallert", "PluginUpdatedWithName": "{0} ble oppdatert", - "ProviderValue": "Leverandører: {0}", - "ScheduledTaskFailedWithName": "{0} Mislykkes", - "ScheduledTaskStartedWithName": "{0} Startet", + "ProviderValue": "Leverandør: {0}", + "ScheduledTaskFailedWithName": "{0} mislykkes", + "ScheduledTaskStartedWithName": "{0} startet", "ServerNameNeedsToBeRestarted": "{0} må startes på nytt", "Shows": "Programmer", "Songs": "Sanger", - "StartupEmbyServerIsLoading": "Jellyfin server laster. Prøv igjen snart.", + "StartupEmbyServerIsLoading": "Jellyfin Server laster. Prøv igjen snart.", "SubtitleDownloadFailureForItem": "En feil oppstå under nedlasting av undertekster for {0}", - "SubtitleDownloadFailureFromForItem": "Kunne ikke laste ned teksting fra {0} for {1}", + "SubtitleDownloadFailureFromForItem": "Kunne ikke laste ned undertekster fra {0} for {1}", "SubtitlesDownloadedForItem": "Undertekster lastet ned for {0}", "Sync": "Synkroniser", "System": "System", From 2497573b7f0427074c4e885d1ba1806e47d490d8 Mon Sep 17 00:00:00 2001 From: Sacha Korban Date: Fri, 3 Jan 2020 18:30:35 +1100 Subject: [PATCH 109/252] Update the build script to use .NET core SDK 3.1 --- deployment/centos-package-x64/Dockerfile | 2 +- deployment/debian-package-arm64/Dockerfile.amd64 | 4 ++-- deployment/debian-package-arm64/Dockerfile.arm64 | 4 ++-- deployment/debian-package-arm64/docker-build.sh | 4 ++-- deployment/debian-package-armhf/Dockerfile.amd64 | 4 ++-- deployment/debian-package-armhf/Dockerfile.armhf | 4 ++-- deployment/debian-package-armhf/docker-build.sh | 4 ++-- deployment/debian-package-x64/Dockerfile | 4 ++-- deployment/debian-package-x64/docker-build.sh | 4 ++-- deployment/debian-package-x64/pkg-src/control | 2 +- deployment/fedora-package-x64/Dockerfile | 2 +- deployment/fedora-package-x64/pkg-src/jellyfin.spec | 2 +- deployment/linux-x64/Dockerfile | 4 ++-- deployment/macos/Dockerfile | 4 ++-- deployment/portable/Dockerfile | 4 ++-- deployment/ubuntu-package-arm64/Dockerfile.amd64 | 4 ++-- deployment/ubuntu-package-arm64/Dockerfile.arm64 | 4 ++-- deployment/ubuntu-package-arm64/docker-build.sh | 4 ++-- deployment/ubuntu-package-armhf/Dockerfile.amd64 | 4 ++-- deployment/ubuntu-package-armhf/Dockerfile.armhf | 4 ++-- deployment/ubuntu-package-armhf/docker-build.sh | 4 ++-- deployment/ubuntu-package-x64/Dockerfile | 4 ++-- deployment/ubuntu-package-x64/docker-build.sh | 4 ++-- deployment/win-x64/Dockerfile | 4 ++-- deployment/win-x86/Dockerfile | 4 ++-- 25 files changed, 46 insertions(+), 46 deletions(-) diff --git a/deployment/centos-package-x64/Dockerfile b/deployment/centos-package-x64/Dockerfile index 04daef93cd..2b346f46a2 100644 --- a/deployment/centos-package-x64/Dockerfile +++ b/deployment/centos-package-x64/Dockerfile @@ -3,7 +3,7 @@ FROM centos:7 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/centos-package-x64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist diff --git a/deployment/debian-package-arm64/Dockerfile.amd64 b/deployment/debian-package-arm64/Dockerfile.amd64 index 069c2ed352..b63e08b7dd 100644 --- a/deployment/debian-package-arm64/Dockerfile.amd64 +++ b/deployment/debian-package-arm64/Dockerfile.amd64 @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/debian-package-arm64/Dockerfile.arm64 b/deployment/debian-package-arm64/Dockerfile.arm64 index d2e1c1f121..9ca4868441 100644 --- a/deployment/debian-package-arm64/Dockerfile.arm64 +++ b/deployment/debian-package-arm64/Dockerfile.arm64 @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-arm64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/89fb60b1-3359-414e-94cf-359f57f37c7c/256e6dac8f44f9bad01f23f9a27b01ee/dotnet-sdk-3.0.101-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5a4c8f96-1c73-401c-a6de-8e100403188a/0ce6ab39747e2508366d498f9c0a0669/dotnet-sdk-3.1.100-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/debian-package-arm64/docker-build.sh b/deployment/debian-package-arm64/docker-build.sh index b36b928ba1..67ab6bd74b 100755 --- a/deployment/debian-package-arm64/docker-build.sh +++ b/deployment/debian-package-arm64/docker-build.sh @@ -8,8 +8,8 @@ set -o xtrace # Move to source directory pushd ${SOURCE_DIR} -# Remove build-dep for dotnet-sdk-3.0, since it's not a package in this image -sed -i '/dotnet-sdk-3.0,/d' debian/control +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control # Build DEB export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} diff --git a/deployment/debian-package-armhf/Dockerfile.amd64 b/deployment/debian-package-armhf/Dockerfile.amd64 index d0afbed51c..1b64b53148 100644 --- a/deployment/debian-package-armhf/Dockerfile.amd64 +++ b/deployment/debian-package-armhf/Dockerfile.amd64 @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-armhf ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/debian-package-armhf/Dockerfile.armhf b/deployment/debian-package-armhf/Dockerfile.armhf index dd9e3297e8..dd398b5aa5 100644 --- a/deployment/debian-package-armhf/Dockerfile.armhf +++ b/deployment/debian-package-armhf/Dockerfile.armhf @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-armhf ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/0b30374c-3d52-45ad-b4e5-9a39d0bf5bf0/deb17f7b32968b3a2186650711456152/dotnet-sdk-3.0.101-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/debian-package-armhf/docker-build.sh b/deployment/debian-package-armhf/docker-build.sh index 1b3af9a937..1bd7fb2911 100755 --- a/deployment/debian-package-armhf/docker-build.sh +++ b/deployment/debian-package-armhf/docker-build.sh @@ -8,8 +8,8 @@ set -o xtrace # Move to source directory pushd ${SOURCE_DIR} -# Remove build-dep for dotnet-sdk-3.0, since it's not a package in this image -sed -i '/dotnet-sdk-3.0,/d' debian/control +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control # Build DEB export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} diff --git a/deployment/debian-package-x64/Dockerfile b/deployment/debian-package-x64/Dockerfile index 36e8cf3224..e863d1edf9 100644 --- a/deployment/debian-package-x64/Dockerfile +++ b/deployment/debian-package-x64/Dockerfile @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/debian-package-x64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/debian-package-x64/docker-build.sh b/deployment/debian-package-x64/docker-build.sh index bb27bc7ee8..962a522ebc 100755 --- a/deployment/debian-package-x64/docker-build.sh +++ b/deployment/debian-package-x64/docker-build.sh @@ -8,8 +8,8 @@ set -o xtrace # Move to source directory pushd ${SOURCE_DIR} -# Remove build-dep for dotnet-sdk-3.0, since it's not a package in this image -sed -i '/dotnet-sdk-3.0,/d' debian/control +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control # Build DEB dpkg-buildpackage -us -uc diff --git a/deployment/debian-package-x64/pkg-src/control b/deployment/debian-package-x64/pkg-src/control index 07e82069fc..13fd3ecabb 100644 --- a/deployment/debian-package-x64/pkg-src/control +++ b/deployment/debian-package-x64/pkg-src/control @@ -3,7 +3,7 @@ Section: misc Priority: optional Maintainer: Jellyfin Team Build-Depends: debhelper (>= 9), - dotnet-sdk-3.0, + dotnet-sdk-3.1, libc6-dev, libcurl4-openssl-dev, libfontconfig1-dev, diff --git a/deployment/fedora-package-x64/Dockerfile b/deployment/fedora-package-x64/Dockerfile index 769c62ab2c..f5c3ab7a66 100644 --- a/deployment/fedora-package-x64/Dockerfile +++ b/deployment/fedora-package-x64/Dockerfile @@ -3,7 +3,7 @@ FROM fedora:29 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/fedora-package-x64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist diff --git a/deployment/fedora-package-x64/pkg-src/jellyfin.spec b/deployment/fedora-package-x64/pkg-src/jellyfin.spec index 7118fcf3f9..914f3d44a1 100644 --- a/deployment/fedora-package-x64/pkg-src/jellyfin.spec +++ b/deployment/fedora-package-x64/pkg-src/jellyfin.spec @@ -38,7 +38,7 @@ Requires: libcurl, fontconfig, freetype, openssl, glibc libicu # Requirements not packaged in main repos # COPR @dotnet-sig/dotnet or # https://packages.microsoft.com/rhel/7/prod/ -BuildRequires: dotnet-runtime-3.0, dotnet-sdk-3.0 +BuildRequires: dotnet-runtime-3.1, dotnet-sdk-3.1 # RPMfusion free Requires: ffmpeg diff --git a/deployment/linux-x64/Dockerfile b/deployment/linux-x64/Dockerfile index 169d07a574..c47057546d 100644 --- a/deployment/linux-x64/Dockerfile +++ b/deployment/linux-x64/Dockerfile @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/linux-x64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/macos/Dockerfile b/deployment/macos/Dockerfile index c8b4e80bfc..b522df8848 100644 --- a/deployment/macos/Dockerfile +++ b/deployment/macos/Dockerfile @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/macos ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/portable/Dockerfile b/deployment/portable/Dockerfile index 17297a298f..965eb82b86 100644 --- a/deployment/portable/Dockerfile +++ b/deployment/portable/Dockerfile @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/portable ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/ubuntu-package-arm64/Dockerfile.amd64 b/deployment/ubuntu-package-arm64/Dockerfile.amd64 index fac00ffeab..ac4f7404d6 100644 --- a/deployment/ubuntu-package-arm64/Dockerfile.amd64 +++ b/deployment/ubuntu-package-arm64/Dockerfile.amd64 @@ -3,7 +3,7 @@ FROM ubuntu:bionic ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/ubuntu-package-arm64/Dockerfile.arm64 b/deployment/ubuntu-package-arm64/Dockerfile.arm64 index 304cd0efd0..af70844591 100644 --- a/deployment/ubuntu-package-arm64/Dockerfile.arm64 +++ b/deployment/ubuntu-package-arm64/Dockerfile.arm64 @@ -3,7 +3,7 @@ FROM ubuntu:bionic ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-arm64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/89fb60b1-3359-414e-94cf-359f57f37c7c/256e6dac8f44f9bad01f23f9a27b01ee/dotnet-sdk-3.0.101-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/5a4c8f96-1c73-401c-a6de-8e100403188a/0ce6ab39747e2508366d498f9c0a0669/dotnet-sdk-3.1.100-linux-arm64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/ubuntu-package-arm64/docker-build.sh b/deployment/ubuntu-package-arm64/docker-build.sh index b36b928ba1..67ab6bd74b 100755 --- a/deployment/ubuntu-package-arm64/docker-build.sh +++ b/deployment/ubuntu-package-arm64/docker-build.sh @@ -8,8 +8,8 @@ set -o xtrace # Move to source directory pushd ${SOURCE_DIR} -# Remove build-dep for dotnet-sdk-3.0, since it's not a package in this image -sed -i '/dotnet-sdk-3.0,/d' debian/control +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control # Build DEB export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} diff --git a/deployment/ubuntu-package-armhf/Dockerfile.amd64 b/deployment/ubuntu-package-armhf/Dockerfile.amd64 index 3c60537759..590eecab7f 100644 --- a/deployment/ubuntu-package-armhf/Dockerfile.amd64 +++ b/deployment/ubuntu-package-armhf/Dockerfile.amd64 @@ -3,7 +3,7 @@ FROM ubuntu:bionic ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-armhf ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/ubuntu-package-armhf/Dockerfile.armhf b/deployment/ubuntu-package-armhf/Dockerfile.armhf index 1d019bf2df..06a8dace29 100644 --- a/deployment/ubuntu-package-armhf/Dockerfile.armhf +++ b/deployment/ubuntu-package-armhf/Dockerfile.armhf @@ -3,7 +3,7 @@ FROM ubuntu:bionic ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-armhf ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/0b30374c-3d52-45ad-b4e5-9a39d0bf5bf0/deb17f7b32968b3a2186650711456152/dotnet-sdk-3.0.101-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/67766a96-eb8c-4cd2-bca4-ea63d2cc115c/7bf13840aa2ed88793b7315d5e0d74e6/dotnet-sdk-3.1.100-linux-arm.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/ubuntu-package-armhf/docker-build.sh b/deployment/ubuntu-package-armhf/docker-build.sh index 1b3af9a937..1bd7fb2911 100755 --- a/deployment/ubuntu-package-armhf/docker-build.sh +++ b/deployment/ubuntu-package-armhf/docker-build.sh @@ -8,8 +8,8 @@ set -o xtrace # Move to source directory pushd ${SOURCE_DIR} -# Remove build-dep for dotnet-sdk-3.0, since it's not a package in this image -sed -i '/dotnet-sdk-3.0,/d' debian/control +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control # Build DEB export CONFIG_SITE=/etc/dpkg-cross/cross-config.${ARCH} diff --git a/deployment/ubuntu-package-x64/Dockerfile b/deployment/ubuntu-package-x64/Dockerfile index d881c04e35..8237ced299 100644 --- a/deployment/ubuntu-package-x64/Dockerfile +++ b/deployment/ubuntu-package-x64/Dockerfile @@ -3,7 +3,7 @@ FROM ubuntu:bionic ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/ubuntu-package-x64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -18,7 +18,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/ubuntu-package-x64/docker-build.sh b/deployment/ubuntu-package-x64/docker-build.sh index bb27bc7ee8..962a522ebc 100755 --- a/deployment/ubuntu-package-x64/docker-build.sh +++ b/deployment/ubuntu-package-x64/docker-build.sh @@ -8,8 +8,8 @@ set -o xtrace # Move to source directory pushd ${SOURCE_DIR} -# Remove build-dep for dotnet-sdk-3.0, since it's not a package in this image -sed -i '/dotnet-sdk-3.0,/d' debian/control +# Remove build-dep for dotnet-sdk-3.1, since it's not a package in this image +sed -i '/dotnet-sdk-3.1,/d' debian/control # Build DEB dpkg-buildpackage -us -uc diff --git a/deployment/win-x64/Dockerfile b/deployment/win-x64/Dockerfile index 0f85a07d86..8a33749541 100644 --- a/deployment/win-x64/Dockerfile +++ b/deployment/win-x64/Dockerfile @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/win-x64 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/win-x86/Dockerfile b/deployment/win-x86/Dockerfile index f07a8d7fe3..f8dc5be83d 100644 --- a/deployment/win-x86/Dockerfile +++ b/deployment/win-x86/Dockerfile @@ -3,7 +3,7 @@ FROM debian:10 ARG SOURCE_DIR=/jellyfin ARG PLATFORM_DIR=/jellyfin/deployment/win-x86 ARG ARTIFACT_DIR=/dist -ARG SDK_VERSION=3.0 +ARG SDK_VERSION=3.1 # Docker run environment ENV SOURCE_DIR=/jellyfin ENV ARTIFACT_DIR=/dist @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/4f51cfd8-311d-43fe-a887-c80b40358cfd/440d10dc2091b8d0f1a12b7124034e49/dotnet-sdk-3.0.101-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/d731f991-8e68-4c7c-8ea0-fad5605b077a/49497b5420eecbd905158d86d738af64/dotnet-sdk-3.1.100-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet From aa7aadb2b21f09b14fb99b9f4e3282e57164a0ea Mon Sep 17 00:00:00 2001 From: Cota Fainado <20hvoby5pb@protonmail.com> Date: Sat, 4 Jan 2020 23:01:24 +0000 Subject: [PATCH 110/252] Added translation using Weblate (Portuguese) --- Emby.Server.Implementations/Localization/Core/pt.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/pt.json diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -0,0 +1 @@ +{} From 0b592376d59d10d14dbdd248c24f7ec6397c3508 Mon Sep 17 00:00:00 2001 From: Constantin Matel Date: Fri, 3 Jan 2020 15:38:12 +0000 Subject: [PATCH 111/252] Translated using Weblate (Romanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ro/ --- .../Localization/Core/ro.json | 97 ++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index 0967ef424b..b871626f0d 100644 --- a/Emby.Server.Implementations/Localization/Core/ro.json +++ b/Emby.Server.Implementations/Localization/Core/ro.json @@ -1 +1,96 @@ -{} +{ + "HeaderNextUp": "Urmează", + "VersionNumber": "Versiunea {0}", + "ValueSpecialEpisodeName": "Special - {0}", + "ValueHasBeenAddedToLibrary": "{0} a fost adăugat la biblioteca multimedia", + "UserStoppedPlayingItemWithValues": "{0} a terminat rularea {1} pe {2}", + "UserStartedPlayingItemWithValues": "{0} ruleaza {1} pe {2}", + "UserPolicyUpdatedWithName": "Politica utilizatorului {0} a fost actualizată", + "UserPasswordChangedWithName": "Parola utilizatorului {0} a fost schimbată", + "UserOnlineFromDevice": "{0} este conectat de la {1}", + "UserOfflineFromDevice": "{0} s-a deconectat de la {1}", + "UserLockedOutWithName": "Utilizatorul {0} a fost blocat", + "UserDownloadingItemWithValues": "{0} descarcă {1}", + "UserDeletedWithName": "Utilizatorul {0} a fost eliminat", + "UserCreatedWithName": "Utilizatorul {0} a fost creat", + "User": "Utilizator", + "TvShows": "Spectacole TV", + "System": "Sistem", + "Sync": "Sincronizare", + "SubtitlesDownloadedForItem": "Subtitrari descarcate pentru {0}", + "SubtitleDownloadFailureFromForItem": "Subtitrările nu au putut fi descărcate de la {0} pentru {1}", + "StartupEmbyServerIsLoading": "Se încarcă serverul Jellyfin. Încercați din nou în scurt timp.", + "Songs": "Melodii", + "Shows": "Spectacole", + "ServerNameNeedsToBeRestarted": "{0} trebuie repornit", + "ScheduledTaskStartedWithName": "{0} pornit/ă", + "ScheduledTaskFailedWithName": "{0} eșuat/ă", + "ProviderValue": "Furnizor: {0}", + "PluginUpdatedWithName": "{0} a fost actualizat/ă", + "PluginUninstalledWithName": "{0} a fost dezinstalat", + "PluginInstalledWithName": "{0} a fost instalat", + "Plugin": "Complement", + "Playlists": "Liste redare", + "Photos": "Fotografii", + "NotificationOptionVideoPlaybackStopped": "Redarea video oprită", + "NotificationOptionVideoPlayback": "Începută redarea video", + "NotificationOptionUserLockedOut": "Utilizatorul a fost blocat", + "NotificationOptionTaskFailed": "Activitate programata eșuată", + "NotificationOptionServerRestartRequired": "Este necesară repornirea Serverului", + "NotificationOptionPluginUpdateInstalled": "Actualizare plugin instalată", + "NotificationOptionPluginUninstalled": "Plugin dezinstalat", + "NotificationOptionPluginInstalled": "Plugin instalat", + "NotificationOptionPluginError": "Plugin-ul a eșuat", + "NotificationOptionNewLibraryContent": "Adăugat conținut nou", + "NotificationOptionInstallationFailed": "Eșec la instalare", + "NotificationOptionCameraImageUploaded": "Încarcată imagine cameră", + "NotificationOptionAudioPlaybackStopped": "Redare audio oprită", + "NotificationOptionAudioPlayback": "A inceput redarea audio", + "NotificationOptionApplicationUpdateInstalled": "Actualizarea aplicației a fost instalată", + "NotificationOptionApplicationUpdateAvailable": "Disponibilă o actualizare a aplicației", + "NewVersionIsAvailable": "O nouă versiune a Jellyfin Server este disponibilă pentru descărcare.", + "NameSeasonUnknown": "Sezon Necunoscut", + "NameSeasonNumber": "Sezonul {0}", + "NameInstallFailed": "{0} instalare eșuată", + "MusicVideos": "Videoclipuri muzicale", + "Music": "Muzică", + "Movies": "Filme", + "MixedContent": "Conținut mixt", + "MessageServerConfigurationUpdated": "Configurația serverului a fost actualizată", + "MessageNamedServerConfigurationUpdatedWithValue": "Secțiunea de configurare a serverului {0} a fost acualizata", + "MessageApplicationUpdatedTo": "Jellyfin Server a fost actualizat la {0}", + "MessageApplicationUpdated": "Jellyfin Server a fost actualizat", + "Latest": "Cele mai recente", + "LabelRunningTimeValue": "Durată: {0}", + "LabelIpAddressValue": "Adresa IP: {0}", + "ItemRemovedWithName": "{0} a fost eliminat din bibliotecă", + "ItemAddedWithName": "{0} a fost adăugat în bibliotecă", + "Inherit": "moștenit", + "HomeVideos": "Videoclipuri personale", + "HeaderRecordingGroups": "Grupuri de înregistrare", + "HeaderLiveTV": "TV în Direct", + "HeaderFavoriteSongs": "Melodii Favorite", + "HeaderFavoriteShows": "Spectacole Favorite", + "HeaderFavoriteEpisodes": "Episoade Favorite", + "HeaderFavoriteArtists": "Artiști Favoriți", + "HeaderFavoriteAlbums": "Albume Favorite", + "HeaderContinueWatching": "Vizionează în continuare", + "HeaderCameraUploads": "Incărcări Cameră Foto", + "HeaderAlbumArtists": "Album Artiști", + "Genres": "Genuri", + "Folders": "Dosare", + "Favorites": "Favorite", + "FailedLoginAttemptWithUserName": "Încercare de conectare nereușită de la {0}", + "DeviceOnlineWithName": "{0} este conectat", + "DeviceOfflineWithName": "{0} s-a deconectat", + "Collections": "Colecții", + "ChapterNameValue": "Capitol {0}", + "Channels": "Canale", + "CameraImageUploadedFrom": "O nouă fotografie a fost încărcată din {0}", + "Books": "Cărți", + "AuthenticationSucceededWithUserName": "{0} autentificare reușită", + "Artists": "Artiști", + "Application": "Aplicație", + "AppDeviceValues": "Aplicație: {0}, Dispozitiv: {1}", + "Albums": "Albume" +} From e5e0166951414044b4c251fc562bb8d40620ca1f Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 5 Jan 2020 15:59:31 +0900 Subject: [PATCH 112/252] update dotnet version for ci --- .ci/azure-pipelines.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 13cc67528f..6e6cd00587 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -66,6 +66,12 @@ jobs: overWrite: true # Optional flattenFolders: false # Optional + - task: UseDotNet@2 + displayName: Use Correct DotNet Version + inputs: + packageType: sdk + version: 3.1.100 + - task: DotNetCoreCLI@2 displayName: Publish inputs: From f1008ab58d1b7dc9139f31e0a60f15e4be96aa4f Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 5 Jan 2020 16:01:42 +0900 Subject: [PATCH 113/252] update job names --- .ci/azure-pipelines.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 6e6cd00587..9927536238 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -19,9 +19,9 @@ jobs: vmImage: ubuntu-latest strategy: matrix: - release: + Release: BuildConfiguration: Release - debug: + Debug: BuildConfiguration: Debug maxParallel: 2 steps: @@ -191,12 +191,12 @@ jobs: # #publishRunAttachments: true # Optional - job: main_build_win - displayName: Main Build Windows + displayName: Publish Windows pool: vmImage: windows-latest strategy: matrix: - release: + Release: BuildConfiguration: Release maxParallel: 2 steps: @@ -373,5 +373,3 @@ jobs: script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines' workingDirectory: $(System.ArtifactsDirectory) # Optional #failOnStderr: false # Optional - - From 303385dfa2799df9162aeb64294991b3bd4108c8 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 5 Jan 2020 16:08:19 +0900 Subject: [PATCH 114/252] remove unused parameters --- .ci/azure-pipelines.yml | 61 +---------------------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 9927536238..7427190e61 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -141,54 +141,12 @@ jobs: !**\obj\** !**\xunit.runner.visualstudio.testadapter.dll !**\xunit.runner.visualstudio.dotnetcore.testadapter.dll - #testPlan: # Required when testSelector == TestPlan - #testSuite: # Required when testSelector == TestPlan - #testConfiguration: # Required when testSelector == TestPlan - #tcmTestRun: '$(test.RunId)' # Optional searchFolder: '$(System.DefaultWorkingDirectory)' - #testFiltercriteria: # Optional - #runOnlyImpactedTests: False # Optional - #runAllTestsAfterXBuilds: '50' # Optional - #uiTests: false # Optional - #vstestLocationMethod: 'version' # Optional. Options: version, location - #vsTestVersion: 'latest' # Optional. Options: latest, 16.0, 15.0, 14.0, toolsInstaller - #vstestLocation: # Optional - #runSettingsFile: # Optional - #overrideTestrunParameters: # Optional - #pathtoCustomTestAdapters: # Optional runInParallel: True # Optional runTestsInIsolation: True # Optional codeCoverageEnabled: True # Optional - #otherConsoleOptions: # Optional - #distributionBatchType: 'basedOnTestCases' # Optional. Options: basedOnTestCases, basedOnExecutionTime, basedOnAssembly - #batchingBasedOnAgentsOption: 'autoBatchSize' # Optional. Options: autoBatchSize, customBatchSize - #customBatchSizeValue: '10' # Required when distributionBatchType == BasedOnTestCases && BatchingBasedOnAgentsOption == CustomBatchSize - #batchingBasedOnExecutionTimeOption: 'autoBatchSize' # Optional. Options: autoBatchSize, customTimeBatchSize - #customRunTimePerBatchValue: '60' # Required when distributionBatchType == BasedOnExecutionTime && BatchingBasedOnExecutionTimeOption == CustomTimeBatchSize - #dontDistribute: False # Optional - #testRunTitle: # Optional - #platform: # Optional configuration: 'Debug' # Optional publishRunAttachments: true # Optional - #diagnosticsEnabled: false # Optional - #collectDumpOn: 'onAbortOnly' # Optional. Options: onAbortOnly, always, never - #rerunFailedTests: False # Optional - #rerunType: 'basedOnTestFailurePercentage' # Optional. Options: basedOnTestFailurePercentage, basedOnTestFailureCount - #rerunFailedThreshold: '30' # Optional - #rerunFailedTestCasesMaxLimit: '5' # Optional - #rerunMaxAttempts: '3' # Optional - - # - task: PublishTestResults@2 - # inputs: - # testResultsFormat: 'VSTest' # Options: JUnit, NUnit, VSTest, xUnit, cTest - # testResultsFiles: '**/*.trx' - # #searchFolder: '$(System.DefaultWorkingDirectory)' # Optional - # mergeTestResults: true # Optional - # #failTaskOnFailedTests: false # Optional - # #testRunTitle: # Optional - # #buildPlatform: # Optional - # #buildConfiguration: # Optional - # #publishRunAttachments: true # Optional - job: main_build_win displayName: Publish Windows @@ -252,11 +210,7 @@ jobs: targetType: 'filePath' # Optional. Options: filePath, inline filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory) - #script: '# Write your PowerShell commands here.Write-Host Hello World' # Required when targetType == Inline errorActionPreference: 'stop' # Optional. Options: stop, continue, silentlyContinue - #failOnStderr: false # Optional - #ignoreLASTEXITCODE: false # Optional - #pwsh: false # Optional workingDirectory: $(Build.SourcesDirectory) # Optional - task: CopyFiles@2 @@ -304,16 +258,9 @@ jobs: displayName: Download the New Assembly Build Artifact inputs: source: 'current' # Options: current, specific - #preferTriggeringPipeline: false # Optional - #tags: # Optional artifact: '$(NugetPackageName)' # Optional - #patterns: '**' # Optional path: '$(System.ArtifactsDirectory)/new-artifacts' - #project: # Required when source == Specific - #pipeline: # Required when source == Specific runVersion: 'latest' # Required when source == Specific. Options: latest, latestFromBranch, specific - #runBranch: 'refs/heads/master' # Required when source == Specific && runVersion == LatestFromBranch - #runId: # Required when source == Specific && runVersion == Specific - task: CopyFiles@2 displayName: Copy New Assembly to new-release folder @@ -329,16 +276,12 @@ jobs: displayName: Download the Reference Assembly Build Artifact inputs: source: 'specific' # Options: current, specific - #preferTriggeringPipeline: false # Optional - #tags: # Optional artifact: '$(NugetPackageName)' # Optional - #patterns: '**' # Optional path: '$(System.ArtifactsDirectory)/current-artifacts' project: '$(System.TeamProjectId)' # Required when source == Specific pipeline: '$(System.DefinitionId)' # Required when source == Specific runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific runBranch: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when source == Specific && runVersion == LatestFromBranch - #runId: # Required when source == Specific && runVersion == Specific - task: CopyFiles@2 displayName: Copy Reference Assembly to current-release folder @@ -356,7 +299,6 @@ jobs: connection: Jellyfin Release Download userRepository: EraYaN/dotnet-compatibility defaultVersionType: 'latest' # Options: latest, specificVersion, specificTag - #version: # Required when defaultVersionType != Latest itemPattern: '**-ci.zip' # Optional downloadPath: '$(System.ArtifactsDirectory)' @@ -371,5 +313,4 @@ jobs: displayName: Execute ABI compatibility check tool inputs: script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines' - workingDirectory: $(System.ArtifactsDirectory) # Optional - #failOnStderr: false # Optional + workingDirectory: $(System.ArtifactsDirectory) From c7d5f35eb7e246191eb2d861eec8eb35be86a277 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 5 Jan 2020 16:32:24 +0900 Subject: [PATCH 115/252] update display names for some ci steps --- .ci/azure-pipelines.yml | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 7427190e61..6d2a0a42be 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -31,32 +31,32 @@ jobs: persistCredentials: true - task: CmdLine@2 - displayName: "Check out web" + displayName: "Clone Web Client (Master, Release, or Tag)" condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' - task: CmdLine@2 - displayName: "Check out web (PR)" + displayName: "Clone Web Client (PR)" condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest')) inputs: script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' - task: NodeTool@0 - displayName: 'Install Node.js' + displayName: 'Install Node' condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: versionSpec: '10.x' - task: CmdLine@2 - displayName: "Build Web UI" + displayName: "Build Web Client" condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: script: yarn install workingDirectory: $(Agent.TempDirectory)/jellyfin-web - task: CopyFiles@2 - displayName: Copy the web UI + displayName: 'Copy Web Client' condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional @@ -67,13 +67,13 @@ jobs: flattenFolders: false # Optional - task: UseDotNet@2 - displayName: Use Correct DotNet Version + displayName: 'Update DotNet' inputs: packageType: sdk version: 3.1.100 - task: DotNetCoreCLI@2 - displayName: Publish + displayName: 'Publish Server' inputs: command: publish publishWebProjects: false @@ -164,32 +164,32 @@ jobs: persistCredentials: true - task: CmdLine@2 - displayName: "Check out web (master, release or tag)" + displayName: "Clone Web Client (Master, Release, or Tag)" condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' - task: CmdLine@2 - displayName: "Check out web (PR)" + displayName: "Clone Web Client (PR)" condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest')) inputs: script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' - task: NodeTool@0 - displayName: 'Install Node.js' + displayName: 'Install Node' condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: versionSpec: '10.x' - task: CmdLine@2 - displayName: "Build Web UI" + displayName: "Build Web Client" condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: script: yarn install workingDirectory: $(Agent.TempDirectory)/jellyfin-web - task: CopyFiles@2 - displayName: Copy the web UI + displayName: 'Copy Web Client' condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional @@ -200,12 +200,12 @@ jobs: flattenFolders: false # Optional - task: CmdLine@2 - displayName: Clone the UX repository + displayName: 'Clone UX Repository' inputs: script: git clone --depth=1 https://github.com/jellyfin/jellyfin-ux $(Agent.TempDirectory)\jellyfin-ux - task: PowerShell@2 - displayName: Build the NSIS Installer + displayName: 'Build NSIS Installer' inputs: targetType: 'filePath' # Optional. Options: filePath, inline filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath @@ -214,7 +214,7 @@ jobs: workingDirectory: $(Build.SourcesDirectory) # Optional - task: CopyFiles@2 - displayName: Copy the NSIS Installer to the artifact directory + displayName: 'Copy NSIS Installer' inputs: sourceFolder: $(Build.SourcesDirectory)/deployment/windows/ # Optional contents: 'jellyfin*.exe' @@ -224,7 +224,7 @@ jobs: flattenFolders: true # Optional - task: PublishPipelineArtifact@0 - displayName: 'Publish Setup Artifact' + displayName: 'Publish Artifact Setup' condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) inputs: targetPath: '$(build.artifactstagingdirectory)/setup' @@ -255,7 +255,7 @@ jobs: - checkout: none - task: DownloadPipelineArtifact@2 - displayName: Download the New Assembly Build Artifact + displayName: 'Download New Assembly Build Artifact' inputs: source: 'current' # Options: current, specific artifact: '$(NugetPackageName)' # Optional @@ -263,7 +263,7 @@ jobs: runVersion: 'latest' # Required when source == Specific. Options: latest, latestFromBranch, specific - task: CopyFiles@2 - displayName: Copy New Assembly to new-release folder + displayName: 'Copy New Assembly Build Artifact' inputs: sourceFolder: $(System.ArtifactsDirectory)/new-artifacts # Optional contents: '**/*.dll' @@ -273,7 +273,7 @@ jobs: flattenFolders: true # Optional - task: DownloadPipelineArtifact@2 - displayName: Download the Reference Assembly Build Artifact + displayName: 'Download Reference Assembly Build Artifact' inputs: source: 'specific' # Options: current, specific artifact: '$(NugetPackageName)' # Optional @@ -284,7 +284,7 @@ jobs: runBranch: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when source == Specific && runVersion == LatestFromBranch - task: CopyFiles@2 - displayName: Copy Reference Assembly to current-release folder + displayName: 'Copy Reference Assembly Build Artifact' inputs: sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional contents: '**/*.dll' @@ -294,7 +294,7 @@ jobs: flattenFolders: true # Optional - task: DownloadGitHubRelease@0 - displayName: Download ABI compatibility check tool from GitHub + displayName: 'Download ABI Compatibility Check Tool' inputs: connection: Jellyfin Release Download userRepository: EraYaN/dotnet-compatibility @@ -303,14 +303,14 @@ jobs: downloadPath: '$(System.ArtifactsDirectory)' - task: ExtractFiles@1 - displayName: Extract ABI compatibility check tool + displayName: 'Extract ABI Compatibility Check Tool' inputs: archiveFilePatterns: '$(System.ArtifactsDirectory)/*-ci.zip' destinationFolder: $(System.ArtifactsDirectory)/tools cleanDestinationFolder: true - task: CmdLine@2 - displayName: Execute ABI compatibility check tool + displayName: 'Execute ABI Compatibility Check Tool' inputs: script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines' - workingDirectory: $(System.ArtifactsDirectory) + workingDirectory: $(System.ArtifactsDirectory) # Optional From d59912450819f6b9e8cb5b2e8eeb70c4d9de1465 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 5 Jan 2020 16:39:48 +0900 Subject: [PATCH 116/252] manually disable compatibility check --- .ci/azure-pipelines.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 6d2a0a42be..fef2ecda96 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -235,7 +235,9 @@ jobs: pool: vmImage: ubuntu-latest dependsOn: main_build - condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber']) # Only execute if the pullrequest numer is defined. (So not for normal CI builds) + # disabled until ABI is expected to remain unchanged + # only execute for pull requests + condition: and(eq(1, 2), succeeded(), variables['System.PullRequest.PullRequestNumber']) strategy: matrix: Naming: From 2ca7200a0526e5374f0b0e10d6f5d22965bacaa5 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Sun, 5 Jan 2020 19:24:24 +0100 Subject: [PATCH 117/252] Updated for v0.0.5 of the compat checker. --- .ci/azure-pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index fef2ecda96..ba9989e70f 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -311,8 +311,9 @@ jobs: destinationFolder: $(System.ArtifactsDirectory)/tools cleanDestinationFolder: true + # The `--warnings-only` switch will swallow the return code and not emit any errors. - task: CmdLine@2 displayName: 'Execute ABI Compatibility Check Tool' inputs: - script: 'dotnet tools/CompatibilityCheckerCoreCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines' + script: 'dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only' workingDirectory: $(System.ArtifactsDirectory) # Optional From 65f65db8b9c7dcc2022ff6542a50481cb8d0cabf Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Sun, 5 Jan 2020 19:25:33 +0100 Subject: [PATCH 118/252] Re-enable compat checks. --- .ci/azure-pipelines.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index ba9989e70f..e5d09b0aba 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -235,9 +235,8 @@ jobs: pool: vmImage: ubuntu-latest dependsOn: main_build - # disabled until ABI is expected to remain unchanged # only execute for pull requests - condition: and(eq(1, 2), succeeded(), variables['System.PullRequest.PullRequestNumber']) + condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber']) strategy: matrix: Naming: From 21b78f44c8efe7eaa01430f23075d605c3d05356 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Sun, 5 Jan 2020 19:30:32 +0100 Subject: [PATCH 119/252] Add new dotnet version to compat stage as well. --- .ci/azure-pipelines.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index e5d09b0aba..143873266d 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -254,6 +254,12 @@ jobs: maxParallel: 2 steps: - checkout: none + + - task: UseDotNet@2 + displayName: 'Update DotNet' + inputs: + packageType: sdk + version: 3.1.100 - task: DownloadPipelineArtifact@2 displayName: 'Download New Assembly Build Artifact' From 0a483c83f94983e8e12acc8643c26452a3cb5536 Mon Sep 17 00:00:00 2001 From: Cota Fainado <20hvoby5pb@protonmail.com> Date: Sat, 4 Jan 2020 23:03:09 +0000 Subject: [PATCH 120/252] Translated using Weblate (Portuguese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/ --- .../Localization/Core/pt.json | 97 ++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index 0967ef424b..c8d4b4c323 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -1 +1,96 @@ -{} +{ + "HeaderLiveTV": "TV em Directo", + "Collections": "Colecções", + "Books": "Livros", + "Artists": "Artistas", + "Albums": "Álbuns", + "HeaderNextUp": "A Seguir", + "HeaderFavoriteSongs": "Músicas Favoritas", + "HeaderFavoriteArtists": "Artistas Favoritos", + "HeaderFavoriteAlbums": "Álbuns Favoritos", + "HeaderFavoriteEpisodes": "Episódios Favoritos", + "HeaderFavoriteShows": "Séries Favoritas", + "HeaderContinueWatching": "Continuar a Ver", + "HeaderAlbumArtists": "Artistas do Álbum", + "Genres": "Géneros", + "Folders": "Pastas", + "Favorites": "Favoritos", + "Channels": "Canais", + "UserDownloadingItemWithValues": "{0} está a transferir {1}", + "VersionNumber": "Versão {0}", + "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca multimédia", + "UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}", + "UserStartedPlayingItemWithValues": "{0} está a reproduzir {1} em {2}", + "UserPolicyUpdatedWithName": "A política do utilizador {0} foi alterada", + "UserPasswordChangedWithName": "A palavra-passe do utilizador {0} foi alterada", + "UserOnlineFromDevice": "{0} ligou-se a partir de {1}", + "UserOfflineFromDevice": "{0} desligou-se a partir de {1}", + "UserLockedOutWithName": "Utilizador {0} bloqueado", + "UserDeletedWithName": "Utilizador {0} removido", + "UserCreatedWithName": "Utilizador {0} criado", + "User": "Utilizador", + "TvShows": "Programas", + "System": "Sistema", + "SubtitlesDownloadedForItem": "Legendas transferidas para {0}", + "SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas de {0} para {1}", + "StartupEmbyServerIsLoading": "O servidor Jellyfin está a iniciar. Tente novamente dentro de momentos.", + "ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciado", + "ScheduledTaskStartedWithName": "{0} iniciou", + "ScheduledTaskFailedWithName": "{0} falhou", + "ProviderValue": "Fornecedor: {0}", + "PluginUpdatedWithName": "{0} foi actualizado", + "PluginUninstalledWithName": "{0} foi desinstalado", + "PluginInstalledWithName": "{0} foi instalado", + "Plugin": "Extensão", + "NotificationOptionVideoPlaybackStopped": "Reprodução de vídeo parada", + "NotificationOptionVideoPlayback": "Reprodução de vídeo iniciada", + "NotificationOptionUserLockedOut": "Utilizador bloqueado", + "NotificationOptionTaskFailed": "Falha em tarefa agendada", + "NotificationOptionServerRestartRequired": "É necessário reiniciar o servidor", + "NotificationOptionPluginUpdateInstalled": "Extensão actualizada", + "NotificationOptionPluginUninstalled": "Extensão desinstalada", + "NotificationOptionPluginInstalled": "Extensão instalada", + "NotificationOptionPluginError": "Falha na extensão", + "NotificationOptionNewLibraryContent": "Novo conteúdo adicionado", + "NotificationOptionInstallationFailed": "Falha de instalação", + "NotificationOptionCameraImageUploaded": "Imagem da câmara enviada", + "NotificationOptionAudioPlaybackStopped": "Reprodução Parada", + "NotificationOptionAudioPlayback": "Reprodução Iniciada", + "NotificationOptionApplicationUpdateInstalled": "A actualização da aplicação foi instalada", + "NotificationOptionApplicationUpdateAvailable": "Uma actualização da aplicação está disponível", + "NewVersionIsAvailable": "Uma nova versão do servidor Jellyfin está disponível para transferência.", + "NameSeasonUnknown": "Temporada Desconhecida", + "NameSeasonNumber": "Temporada {0}", + "NameInstallFailed": "Falha na instalação de {0}", + "MusicVideos": "Videoclips", + "Music": "Música", + "MixedContent": "Conteúdo Misto", + "MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada", + "MessageNamedServerConfigurationUpdatedWithValue": "Configurações do servidor na secção {0} foram atualizadas", + "MessageApplicationUpdatedTo": "O servidor Jellyfin foi actualizado para a versão {0}", + "MessageApplicationUpdated": "O servidor Jellyfin foi actualizado", + "Latest": "Mais Recente", + "LabelRunningTimeValue": "Duração: {0}", + "LabelIpAddressValue": "Endereço IP: {0}", + "ItemRemovedWithName": "{0} foi removido da biblioteca", + "ItemAddedWithName": "{0} foi adicionado à biblioteca", + "Inherit": "Herdar", + "HomeVideos": "Vídeos Caseiros", + "HeaderRecordingGroups": "Grupos de Gravação", + "ValueSpecialEpisodeName": "Especial - {0}", + "Sync": "Sincronização", + "Songs": "Músicas", + "Shows": "Séries", + "Playlists": "Listas de Reprodução", + "Photos": "Fotografias", + "Movies": "Filmes", + "HeaderCameraUploads": "Envios a partir da câmara", + "FailedLoginAttemptWithUserName": "Tentativa de ligação a partir de {0} falhou", + "DeviceOnlineWithName": "{0} ligou-se", + "DeviceOfflineWithName": "{0} desligou-se", + "ChapterNameValue": "Capítulo {0}", + "CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}", + "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", + "Application": "Aplicação", + "AppDeviceValues": "Aplicação {0}, Dispositivo: {1}" +} From a4d0e4b38ee23a7edde067f093580199a1014658 Mon Sep 17 00:00:00 2001 From: Thoscellen Date: Sat, 4 Jan 2020 23:16:08 +0000 Subject: [PATCH 121/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 9805992be9..e1dce82ffe 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -19,10 +19,10 @@ "HeaderCameraUploads": "Photos transférées", "HeaderContinueWatching": "Continuer à regarder", "HeaderFavoriteAlbums": "Albums favoris", - "HeaderFavoriteArtists": "Artistes favoris", + "HeaderFavoriteArtists": "Artistes préférés", "HeaderFavoriteEpisodes": "Épisodes favoris", "HeaderFavoriteShows": "Séries favorites", - "HeaderFavoriteSongs": "Chansons favorites", + "HeaderFavoriteSongs": "Chansons préférées", "HeaderLiveTV": "TV en direct", "HeaderNextUp": "À suivre", "HeaderRecordingGroups": "Groupes d'enregistrements", From 80dfc78ba5ec3895fd374a5bd761d0d0e9e6a862 Mon Sep 17 00:00:00 2001 From: samucamg Date: Sun, 5 Jan 2020 21:47:39 +0000 Subject: [PATCH 122/252] Translated using Weblate (Portuguese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/ --- Emby.Server.Implementations/Localization/Core/pt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index c8d4b4c323..ef8d988c87 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -1,5 +1,5 @@ { - "HeaderLiveTV": "TV em Directo", + "HeaderLiveTV": "TV ao Vivo", "Collections": "Colecções", "Books": "Livros", "Artists": "Artistas", From 70210b47a4dedd3d3905fc4d234c0346f58d064f Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 6 Jan 2020 10:39:01 +0100 Subject: [PATCH 123/252] Update MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs Co-Authored-By: dkanada --- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 3067238f73..ff3596a85e 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -1345,7 +1345,7 @@ namespace MediaBrowser.MediaEncoding.Probing } } - // REVIEW: + // REVIEW: find out why the byte array needs to be 197 bytes long and comment the reason private TransportStreamTimestamp GetMpegTimestamp(string path) { var packetBuffer = new byte[197]; From 49fef5f09c12ff4b40b7213a9e155802acf45fff Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 7 Jan 2020 11:13:11 +0100 Subject: [PATCH 124/252] Update MediaBrowser.MediaEncoding/Probing/MediaChapter.cs Co-Authored-By: Erwin de Haan --- MediaBrowser.MediaEncoding/Probing/MediaChapter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs index a3607a7600..6a45ccf495 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.MediaEncoding.Probing public class MediaChapter { [JsonPropertyName("id")] - public int id { get; set; } + public int Id { get; set; } [JsonPropertyName("time_base")] public string TimeBase { get; set; } From 9dfafb9e9fcddb253b157fe03b085b7fceef4290 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 7 Jan 2020 11:13:47 +0100 Subject: [PATCH 125/252] Update MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs Co-Authored-By: Erwin de Haan --- MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs index 6b60b66c0a..8af122ef9b 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// /// The filename. [JsonPropertyName("filename")] - public string Filename { get; set; } + public string FileName { get; set; } /// /// Gets or sets the nb_streams. From a253fa616da3fd982ca2190b69d25853893665f1 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 26 Dec 2019 23:09:00 +0100 Subject: [PATCH 126/252] Fix build and address comments --- .../ApplicationHost.cs | 2 +- .../Data/SqliteItemRepository.cs | 34 ++++--- .../Library/MediaSourceManager.cs | 9 -- Jellyfin.Api/Controllers/StartupController.cs | 1 - .../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(-) diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7c3f59af2e..c5ac27ed48 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 2206816a50..91ca8477d9 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 4eff2807cf..ba1564d1f8 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 1014c8c56b..afc9b8f3da 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 1ebfaa14bc..ef09951b65 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 dda8d397a1..09e6fda885 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 59c0a3f214..7c7e84de64 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 cb22343c48..c371e8b946 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 d4aede572b..c5da42089a 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) { From 8a0ef4103632b2888249af2f385e1e7bfc06fbfe Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 26 Dec 2019 23:20:31 +0100 Subject: [PATCH 127/252] Minor improvements --- .../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(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 91ca8477d9..2ff19a6397 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 68df20c3af..5a5b7f58f0 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 c5da42089a..d4aede572b 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 8a1aa55b69..5cb0565666 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 ae3e584d4b..2b178d4d4e 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; From 73fac50e57982ac46aea2b487e9906826c3dc3b2 Mon Sep 17 00:00:00 2001 From: dkanada Date: Wed, 8 Jan 2020 10:52:48 +0900 Subject: [PATCH 128/252] rename two properties based on code suggestions --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 8 ++++---- MediaBrowser.Api/Attachments/AttachmentService.cs | 2 +- .../Probing/ProbeResultNormalizer.cs | 4 ++-- MediaBrowser.Model/Entities/MediaAttachment.cs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2ff19a6397..c514846e58 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 ef09951b65..1632ca1b06 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 f2056c5663..6664b34a5c 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 26279b72b8..8f8c3efd20 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. From 75f19a762cf4cf769df7545612e68e95bb6905f2 Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Wed, 8 Jan 2020 04:05:07 -0500 Subject: [PATCH 129/252] 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(-) diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 442fbabd17..9568f62dff 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) { From 277e9d2b0b9859aa374ed4c4add10f3c46dbdfd2 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 8 Jan 2020 18:13:11 +0100 Subject: [PATCH 130/252] fix build --- .../Probing/ProbeResultNormalizer.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 37baef5b0c..bd89c6cae9 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -52,7 +52,7 @@ namespace MediaBrowser.MediaEncoding.Probing .Where(i => i != null) .ToList(); - if (data.format != null) + if (data.Format != null) { info.Container = NormalizeFormat(data.Format.FormatName); @@ -523,27 +523,27 @@ namespace MediaBrowser.MediaEncoding.Probing /// MediaAttachments. private MediaAttachment GetMediaAttachment(MediaStreamInfo streamInfo) { - if (!string.Equals(streamInfo.codec_type, "attachment", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(streamInfo.CodecType, "attachment", StringComparison.OrdinalIgnoreCase)) { return null; } var attachment = new MediaAttachment { - Codec = streamInfo.codec_name, - Index = streamInfo.index + Codec = streamInfo.CodecName, + Index = streamInfo.Index }; - if (!string.IsNullOrWhiteSpace(streamInfo.codec_tag_string)) + if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString)) { - attachment.CodecTag = streamInfo.codec_tag_string; + attachment.CodecTag = streamInfo.CodecTagString; } - if (streamInfo.tags != null) + if (streamInfo.Tags != null) { - attachment.FileName = GetDictionaryValue(streamInfo.tags, "filename"); - attachment.MimeType = GetDictionaryValue(streamInfo.tags, "mimetype"); - attachment.Comment = GetDictionaryValue(streamInfo.tags, "comment"); + attachment.FileName = GetDictionaryValue(streamInfo.Tags, "filename"); + attachment.MimeType = GetDictionaryValue(streamInfo.Tags, "mimetype"); + attachment.Comment = GetDictionaryValue(streamInfo.Tags, "comment"); } return attachment; From b1af8a4178b5d993b09e5682a0149ea37766ff91 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 8 Jan 2020 18:14:01 +0100 Subject: [PATCH 131/252] Rename function --- MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs index cd3d82e864..78dc7b6078 100644 --- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs +++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.MediaEncoding.Probing if (result.Format != null && result.Format.Tags != null) { - result.Format.Tags = ConvertDictionaryToCaseInSensitive(result.Format.Tags); + result.Format.Tags = ConvertDictionaryToCaseInsensitive(result.Format.Tags); } if (result.Streams != null) @@ -28,7 +28,7 @@ namespace MediaBrowser.MediaEncoding.Probing { if (stream.Tags != null) { - stream.Tags = ConvertDictionaryToCaseInSensitive(stream.Tags); + stream.Tags = ConvertDictionaryToCaseInsensitive(stream.Tags); } } } @@ -98,7 +98,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// /// The dict. /// Dictionary{System.StringSystem.String}. - private static Dictionary ConvertDictionaryToCaseInSensitive(IReadOnlyDictionary dict) + private static Dictionary ConvertDictionaryToCaseInsensitive(IReadOnlyDictionary dict) { return new Dictionary(dict, StringComparer.OrdinalIgnoreCase); } From 43c76b48c9f90afc34a5fd40aa1a28c8ae7b736a Mon Sep 17 00:00:00 2001 From: Ben Magee Date: Wed, 8 Jan 2020 22:52:33 +0000 Subject: [PATCH 132/252] Added a test to prevent a regression of the issue seen in #1874. This issue was fixed in PR#2240 --- MediaBrowser.sln | 9 +++++- .../Emby.Server.Implementations.Tests.csproj | 23 ++++++++++++++ .../IO/ManagedFileSystemTests.cs | 31 +++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj create mode 100644 tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 416a434f47..cf1bab11c6 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.3 MinimumVisualStudioVersion = 10.0.40219.1 @@ -58,6 +58,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Naming.Tests", "te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Api.Tests", "tests\Jellyfin.Api.Tests\Jellyfin.Api.Tests.csproj", "{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations.Tests", "tests\Emby.Server.Implementations.Tests\Emby.Server.Implementations.Tests.csproj", "{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -164,6 +166,10 @@ Global {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}.Release|Any CPU.Build.0 = Release|Any CPU + {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -194,5 +200,6 @@ Global {28464062-0939-4AA7-9F7B-24DDDA61A7C0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {3998657B-1CCC-49DD-A19F-275DC8495F57} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection EndGlobal diff --git a/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj b/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj new file mode 100644 index 0000000000..c635a90f17 --- /dev/null +++ b/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj @@ -0,0 +1,23 @@ + + + + netcoreapp3.1 + + false + + + + + + + + + + + + + + + + + diff --git a/tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs new file mode 100644 index 0000000000..586c50db6a --- /dev/null +++ b/tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -0,0 +1,31 @@ +using AutoFixture; +using AutoFixture.AutoMoq; +using Emby.Server.Implementations.IO; +using Xunit; + +namespace Emby.Server.Implementations.Tests.IO +{ + public class ManagedFileSystemTests + { + private readonly IFixture _fixture; + private readonly ManagedFileSystem _sut; + + public ManagedFileSystemTests() + { + _fixture = new Fixture().Customize(new AutoMoqCustomization { ConfigureMembers = true }); + _sut = _fixture.Create(); + } + + [Theory] + [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Beethoven/Misc/Moonlight Sonata.mp3")] + [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Beethoven/Misc/Moonlight Sonata.mp3")] + public void MakeAbsolutePathCorrectlyHandlesRelativeFilePaths( + string folderPath, + string filePath, + string expectedAbsolutePath) + { + var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath); + Assert.Equal(expectedAbsolutePath, generatedPath); + } + } +} From 140673fcf6186c559c97829a14991f7f81d22430 Mon Sep 17 00:00:00 2001 From: Ben Magee Date: Wed, 8 Jan 2020 23:03:17 +0000 Subject: [PATCH 133/252] Add a test case which doesn't specify the parent directory. --- .../IO/ManagedFileSystemTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index 586c50db6a..0044196a7f 100644 --- a/tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -19,6 +19,7 @@ namespace Emby.Server.Implementations.Tests.IO [Theory] [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Beethoven/Misc/Moonlight Sonata.mp3")] [InlineData("/Volumes/Library/Sample/Music/Playlists/", "../../Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Beethoven/Misc/Moonlight Sonata.mp3")] + [InlineData("/Volumes/Library/Sample/Music/Playlists/", "Beethoven/Misc/Moonlight Sonata.mp3", "/Volumes/Library/Sample/Music/Playlists/Beethoven/Misc/Moonlight Sonata.mp3")] public void MakeAbsolutePathCorrectlyHandlesRelativeFilePaths( string folderPath, string filePath, From d5204f572a1e1c377edfd14e680f438ac104e967 Mon Sep 17 00:00:00 2001 From: gnattu Date: Thu, 9 Jan 2020 02:25:05 -0500 Subject: [PATCH 134/252] Fix typo and plural --- MediaBrowser.Api/Playback/UniversalAudioService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index 563e09ae69..c9d29f8ef6 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -300,8 +300,8 @@ namespace MediaBrowser.Api.Playback var transcodingProfile = deviceProfile.TranscodingProfiles[0]; - //HLS Segment container can only be mpegts or fmp4 per ffmpeg documentation - var supoortedHLSContainer = new string[] { "mpegts", "fmp4" }; + // hls segment container can only be mpegts or fmp4 per ffmpeg documentation + var supportedHLSContainers = new string[] { "mpegts", "fmp4" }; var newRequest = new GetMasterHlsAudioPlaylist { @@ -315,8 +315,8 @@ namespace MediaBrowser.Api.Playback PlaySessionId = playbackInfoResult.PlaySessionId, StartTimeTicks = request.StartTimeTicks, Static = isStatic, - //fallback to mpegts if device reports some wierd value that is not supported by HLS - SegmentContainer = Array.Exists(supoortedHLSContainer, element => element == request.TranscodingContainer) ? request.TranscodingContainer : "mpegts", + // fallback to mpegts if device reports some weird value unsupported by hls + SegmentContainer = Array.Exists(supportedHLSContainers, element => element == request.TranscodingContainer) ? request.TranscodingContainer : "mpegts", AudioSampleRate = request.MaxAudioSampleRate, MaxAudioBitDepth = request.MaxAudioBitDepth, BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames, From fdbb32911835b5ed8e39f518ccc20b0a26bd8bab Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 9 Jan 2020 17:07:13 +0100 Subject: [PATCH 135/252] 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 +++++++++--------- .../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(-) diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj index 9dbaa9e2f0..f4df6a9f52 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 a0e67870e9..e7f9577ee6 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 3b6bfce6a7..4e09f90b4b 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 3629d15470..caaceda1bc 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 8d8fe9eb5b..f152ee8808 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 672784589f..333cab60da 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 cf92633c31..8a00f4ae4f 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 4f47c28219..8919938814 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 586e322e48..e039ac5d6f 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 10efb9b383..7962b51ce2 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 5652962daf..5122f42b8a 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 b5bd077028..d751f77a77 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 75ba12a178..9ce1cf79ff 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)); + if (char.IsUpper(str[0])) + { + return str; + } - return sb.ToString(); - } - - 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 de5e58d220..42fff3775a 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 f48b5ee7fc..38600b9c87 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; } From 1fecb9efb2bec4fccfa39bb0b04ad2206cbb7c86 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 9 Jan 2020 17:17:52 +0100 Subject: [PATCH 136/252] Fix the VSTest runner --- .ci/azure-pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 143873266d..6cedf32fd4 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -132,7 +132,7 @@ jobs: inputs: packageFeedSelector: 'nugetOrg' # Options: nugetOrg, customFeed, netShare versionSelector: 'latestPreRelease' # Required when packageFeedSelector == NugetOrg || PackageFeedSelector == CustomFeed# Options: latestPreRelease, latestStable, specificVersion - + - task: VSTest@2 inputs: testSelector: 'testAssemblies' # Options: testAssemblies, testPlan, testRun @@ -147,6 +147,7 @@ jobs: codeCoverageEnabled: True # Optional configuration: 'Debug' # Optional publishRunAttachments: true # Optional + otherConsoleOptions: '/platform:x64 /Framework:.NETCoreApp,Version=v3.1 /logger:console;verbosity="normal" ' - job: main_build_win displayName: Publish Windows From 1ea613a9bd4fd5d1fa4cb556fe238ecd4720e1e0 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 9 Jan 2020 17:26:42 +0100 Subject: [PATCH 137/252] Update Jellyfin.Api.Test to 3.1 --- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 1671b8d797..e0deeeabb8 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.0 + netcoreapp3.1 false From 6792fa111ec8bed026a2c30afe2dc39ca2b02245 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 9 Jan 2020 17:27:48 +0100 Subject: [PATCH 138/252] Fix the test selection glob. --- .ci/azure-pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 6cedf32fd4..4905a6fdb9 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -137,7 +137,7 @@ jobs: inputs: testSelector: 'testAssemblies' # Options: testAssemblies, testPlan, testRun testAssemblyVer2: | # Required when testSelector == TestAssemblies - **\bin\$(BuildConfiguration)\**\*test*.dll + **\bin\$(BuildConfiguration)\**\*test.dll !**\obj\** !**\xunit.runner.visualstudio.testadapter.dll !**\xunit.runner.visualstudio.dotnetcore.testadapter.dll @@ -147,6 +147,7 @@ jobs: codeCoverageEnabled: True # Optional configuration: 'Debug' # Optional publishRunAttachments: true # Optional + testRunTitle: $(Agent.JobName) otherConsoleOptions: '/platform:x64 /Framework:.NETCoreApp,Version=v3.1 /logger:console;verbosity="normal" ' - job: main_build_win From d328fc51cf72a2231b8b16d68c416214e12ed093 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 9 Jan 2020 17:28:20 +0100 Subject: [PATCH 139/252] Apply suggestions from code review Co-Authored-By: Bond-009 Co-Authored-By: dkanada --- .ci/azure-pipelines.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 4905a6fdb9..b69195fcce 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -132,7 +132,6 @@ jobs: inputs: packageFeedSelector: 'nugetOrg' # Options: nugetOrg, customFeed, netShare versionSelector: 'latestPreRelease' # Required when packageFeedSelector == NugetOrg || PackageFeedSelector == CustomFeed# Options: latestPreRelease, latestStable, specificVersion - - task: VSTest@2 inputs: testSelector: 'testAssemblies' # Options: testAssemblies, testPlan, testRun @@ -148,7 +147,7 @@ jobs: configuration: 'Debug' # Optional publishRunAttachments: true # Optional testRunTitle: $(Agent.JobName) - otherConsoleOptions: '/platform:x64 /Framework:.NETCoreApp,Version=v3.1 /logger:console;verbosity="normal" ' + otherConsoleOptions: '/platform:x64 /Framework:.NETCoreApp,Version=v3.1 /logger:console;verbosity="normal"' - job: main_build_win displayName: Publish Windows From 654336990f8109c12ce1383d94dcdc84e7a3c5b6 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 9 Jan 2020 17:31:44 +0100 Subject: [PATCH 140/252] Added plural glob for test assemblies --- .ci/azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index b69195fcce..3adc6f85c3 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -136,6 +136,7 @@ jobs: inputs: testSelector: 'testAssemblies' # Options: testAssemblies, testPlan, testRun testAssemblyVer2: | # Required when testSelector == TestAssemblies + **\bin\$(BuildConfiguration)\**\*tests.dll **\bin\$(BuildConfiguration)\**\*test.dll !**\obj\** !**\xunit.runner.visualstudio.testadapter.dll From 312987aea57d5a7f1366a07220ed1aae27e235a6 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 9 Jan 2020 17:39:17 +0100 Subject: [PATCH 141/252] Build all test projects using a wildcard --- .ci/azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 3adc6f85c3..7bcaed70cd 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -2,7 +2,7 @@ name: $(Date:yyyyMMdd)$(Rev:.r) variables: - name: TestProjects - value: 'tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj' + value: 'tests/**/*Tests.csproj' - name: RestoreBuildProjects value: 'Jellyfin.Server/Jellyfin.Server.csproj' From 64baca9facae83832fc45f627f9249b38e37d168 Mon Sep 17 00:00:00 2001 From: Ben Magee Date: Thu, 9 Jan 2020 22:03:57 +0000 Subject: [PATCH 142/252] Renamed project and namespace --- MediaBrowser.sln | 2 +- .../IO/ManagedFileSystemTests.cs | 2 +- .../Jellyfin.Server.Implementations.Tests.csproj} | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) rename tests/{Emby.Server.Implementations.Tests => Jellyfin.Server.Implementations.Tests}/IO/ManagedFileSystemTests.cs (96%) rename tests/{Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj => Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj} (91%) diff --git a/MediaBrowser.sln b/MediaBrowser.sln index cf1bab11c6..50570deecf 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -58,7 +58,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Naming.Tests", "te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Api.Tests", "tests\Jellyfin.Api.Tests\Jellyfin.Api.Tests.csproj", "{A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Server.Implementations.Tests", "tests\Emby.Server.Implementations.Tests\Emby.Server.Implementations.Tests.csproj", "{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Implementations.Tests", "tests\Jellyfin.Server.Implementations.Tests\Jellyfin.Server.Implementations.Tests.csproj", "{2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs similarity index 96% rename from tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs rename to tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index 0044196a7f..f2a8e447fa 100644 --- a/tests/Emby.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -3,7 +3,7 @@ using AutoFixture.AutoMoq; using Emby.Server.Implementations.IO; using Xunit; -namespace Emby.Server.Implementations.Tests.IO +namespace Jellyfin.Server.Implementations.Tests.IO { public class ManagedFileSystemTests { diff --git a/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj similarity index 91% rename from tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj rename to tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index c635a90f17..bb2afea168 100644 --- a/tests/Emby.Server.Implementations.Tests/Emby.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -4,6 +4,8 @@ netcoreapp3.1 false + + Jellyfin.Server.Implementations.Tests From c3ff4e0b9f52827872d50b890ea4e4fbab22b604 Mon Sep 17 00:00:00 2001 From: Narfinger Date: Fri, 10 Jan 2020 19:42:07 +0900 Subject: [PATCH 143/252] fixes dockerfile building on recent linux installations --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 53a4252627..1cd864e0b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,9 @@ FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster as builder WORKDIR /repo COPY . . ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 -RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" +# because of changes in docker and systemd we need to not build in parallel at the moment +# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting +RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg FROM debian:buster-slim From 0a92200c6fe0adc6c28294e0c7dca7029973d1b7 Mon Sep 17 00:00:00 2001 From: keitaro62 Date: Thu, 9 Jan 2020 10:54:06 +0000 Subject: [PATCH 144/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index e1dce82ffe..d6d5bba72d 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", From 81ae6d8afda06a77f5e3a8647c9432959bd191a6 Mon Sep 17 00:00:00 2001 From: Philmo67 Date: Fri, 10 Jan 2020 15:17:02 +0000 Subject: [PATCH 145/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index d6d5bba72d..6b25e3df0f 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", From 0a39e54984ae2eb7ab9ec3382a9122a6de9e56b1 Mon Sep 17 00:00:00 2001 From: archon eleven Date: Fri, 10 Jan 2020 06:34:37 +0000 Subject: [PATCH 146/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index c10fbe58f6..b91c98ca0c 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}" } From e95239e28132b4d6486ee9272d20ec5ada46b66c Mon Sep 17 00:00:00 2001 From: Nyanmisaka <799610810@qq.com> Date: Sat, 11 Jan 2020 01:36:25 +0800 Subject: [PATCH 147/252] add support for AMD h264_amf & hevc_amf --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 1feca0ec92..993cd3f747 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -55,7 +55,9 @@ namespace MediaBrowser.MediaEncoding.Encoder "h264_vaapi", "hevc_vaapi", "h264_v4l2m2m", - "ac3" + "ac3", + "h264_amf", + "hevc_amf" }; // Try and use the individual library versions to determine a FFmpeg version From d9ec502ff985918cf94326780b41c6382b9e8937 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 10 Jan 2020 21:25:45 +0100 Subject: [PATCH 148/252] Address comments --- MediaBrowser.Api/Playback/MediaInfoService.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 80acdc4554..3d8231a2ab 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -111,19 +111,18 @@ namespace MediaBrowser.Api.Playback if (size <= 0) { - throw new ArgumentException($"The requested size can't be equal or smaller than 0.", nameof(request)); + throw new ArgumentException($"The requested size ({size}) is equal to or smaller than 0.", nameof(request)); } if (size > MaxSize) { - throw new ArgumentException($"The requested size can't be larger than the max allowed value ({MaxSize}).", nameof(request)); + throw new ArgumentException($"The requested size ({size}) is larger than the max allowed value ({MaxSize}).", nameof(request)); } byte[] buffer = ArrayPool.Shared.Rent(size); try { - // ArrayPool.Shared.Rent doesn't guarantee that the returned buffer is zeroed - Array.Fill(buffer, 0); + new Random().NextBytes(buffer); return ResultFactory.GetResult(null, buffer, "application/octet-stream"); } finally From fa1aeeb18a766fd7b2265dc0352ee100d8377163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slobodan=20Simi=C4=87?= Date: Fri, 10 Jan 2020 20:27:17 +0000 Subject: [PATCH 149/252] 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 diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/sr.json @@ -0,0 +1 @@ +{} From 0d6a4c2909632229b3c6a0f1b65f03c48d0f9eb2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 00:08:47 +0100 Subject: [PATCH 150/252] Fix build --- MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index c371e8b946..c530c9fd81 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -59,7 +59,7 @@ namespace MediaBrowser.MediaEncoding.Attachments 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 .FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)); if (mediaSource == null) @@ -196,7 +196,7 @@ namespace MediaBrowser.MediaEncoding.Attachments var exitCode = ranToCompletion ? process.ExitCode : -1; process.Dispose(); - + var failed = false; if (exitCode != 0) From 801c356d668069240e9197deb4ff648908c4ddbd Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 00:23:14 +0100 Subject: [PATCH 151/252] Fix regex for movies released after 2019 --- Emby.Naming/Common/NamingOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index f3f70d3fed..a2105889b9 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -177,7 +177,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])*" + @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](\d{4})([ _\,\.\(\)\[\]\-][^\d]|).*(\d{4})*" }; CleanStrings = new[] From a647dc57052182a6171dae9ffba3026b66b26be7 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 19:39:11 +0100 Subject: [PATCH 152/252] Cleanup tests --- Emby.Naming/Common/NamingOptions.cs | 2 +- .../Jellyfin.Naming.Tests/Video/StackTests.cs | 26 ------------------- 2 files changed, 1 insertion(+), 27 deletions(-) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index a2105889b9..71521e1fd8 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -340,7 +340,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 diff --git a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs index 5faef0e3db..5c121d7387 100644 --- a/tests/Jellyfin.Naming.Tests/Video/StackTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/StackTests.cs @@ -382,32 +382,6 @@ namespace Jellyfin.Naming.Tests.Video 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() { From b1dc595be1a7cf331702fd645b1441ac86afa464 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 20:25:06 +0100 Subject: [PATCH 153/252] Fix a couple of tests --- Emby.Naming/Common/NamingOptions.cs | 2 +- Emby.Naming/Video/CleanDateTimeParser.cs | 6 +- Emby.Naming/Video/CleanStringParser.cs | 4 +- Emby.Naming/Video/VideoResolver.cs | 2 +- .../Video/BaseVideoTest.cs | 8 +- .../Video/CleanDateTimeTests.cs | 170 +++++------------- .../Video/CleanStringTests.cs | 150 +++------------- .../Video/MultiVersionTests.cs | 1 - 8 files changed, 80 insertions(+), 263 deletions(-) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 71521e1fd8..07259e00a1 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -182,7 +182,7 @@ namespace Emby.Naming.Common CleanStrings = new[] { - @"[ _\,\.\(\)\[\]\-](ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)", + @"[ _\,\.\(\)\[\]\-](HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)", @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|\[.*\])([ _\,\.\(\)\[\]\-]|$)", @"(\[.*\])" }; diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index a9db4ccccd..9edb14a074 100644 --- a/Emby.Naming/Video/CleanDateTimeParser.cs +++ b/Emby.Naming/Video/CleanDateTimeParser.cs @@ -53,7 +53,7 @@ namespace Emby.Naming.Video } // Make a second pass, running clean string first - var cleanStringResult = new CleanStringParser().Clean(name, _options.CleanStringRegexes); + var cleanStringResult = CleanStringParser.Clean(name, _options.CleanStringRegexes); if (!cleanStringResult.HasChanged) { @@ -72,12 +72,12 @@ namespace Emby.Naming.Video var match = expression.Match(name); if (match.Success - && match.Groups.Count == 4 + && match.Groups.Count == 5 && match.Groups[1].Success && match.Groups[2].Success && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year)) { - name = match.Groups[1].Value; + name = match.Groups[1].Value.TrimEnd(); result.Year = year; result.HasChanged = true; } diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs index fcd4b65c75..fe2a91bdb9 100644 --- a/Emby.Naming/Video/CleanStringParser.cs +++ b/Emby.Naming/Video/CleanStringParser.cs @@ -9,9 +9,9 @@ namespace Emby.Naming.Video /// /// . /// - public class CleanStringParser + public static class CleanStringParser { - public CleanStringResult Clean(string name, IEnumerable expressions) + public static CleanStringResult Clean(string name, IEnumerable expressions) { var hasChanged = false; diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 41b79697cc..97f59121b1 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -132,7 +132,7 @@ namespace Emby.Naming.Video public CleanStringResult CleanString(string name) { - return new CleanStringParser().Clean(name, _options.CleanStringRegexes); + return CleanStringParser.Clean(name, _options.CleanStringRegexes); } public CleanDateTimeResult CleanDateTime(string name) diff --git a/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs b/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs index b993e241c0..0c2978aca9 100644 --- a/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs +++ b/tests/Jellyfin.Naming.Tests/Video/BaseVideoTest.cs @@ -5,11 +5,9 @@ namespace Jellyfin.Naming.Tests.Video { public abstract class BaseVideoTest { - protected VideoResolver GetParser() - { - var options = new NamingOptions(); + private readonly NamingOptions _namingOptions = new NamingOptions(); - return new VideoResolver(options); - } + protected VideoResolver GetParser() + => new VideoResolver(_namingOptions); } } diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs index bba73ad918..a2ef2dcd64 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanDateTimeTests.cs @@ -1,143 +1,59 @@ using System.IO; +using Emby.Naming.Common; +using Emby.Naming.Video; using Xunit; namespace Jellyfin.Naming.Tests.Video { - public class CleanDateTimeTests : BaseVideoTest + public sealed class CleanDateTimeTests { - // 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); + private readonly NamingOptions _namingOptions = new NamingOptions(); - 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) + [Theory] + [InlineData(@"The Wolf of Wall Street (2013).mkv", "The Wolf of Wall Street", 2013)] + [InlineData(@"The Wolf of Wall Street 2 (2013).mkv", "The Wolf of Wall Street 2", 2013)] + [InlineData(@"The Wolf of Wall Street - 2 (2013).mkv", "The Wolf of Wall Street - 2", 2013)] + [InlineData(@"The Wolf of Wall Street 2001 (2013).mkv", "The Wolf of Wall Street 2001", 2013)] + [InlineData(@"300 (2006).mkv", "300", 2006)] + [InlineData(@"d:/movies/300 (2006).mkv", "300", 2006)] + [InlineData(@"300 2 (2006).mkv", "300 2", 2006)] + [InlineData(@"300 - 2 (2006).mkv", "300 - 2", 2006)] + [InlineData(@"300 2001 (2006).mkv", "300 2001", 2006)] + [InlineData(@"curse.of.chucky.2013.stv.unrated.multi.1080p.bluray.x264-rough", "curse.of.chucky", 2013)] + [InlineData(@"curse.of.chucky.2013.stv.unrated.multi.2160p.bluray.x264-rough", "curse.of.chucky", 2013)] + [InlineData(@"/server/Movies/300 (2007)/300 (2006).bluray.disc", "300", 2006)] + [InlineData(@"Arrival.2016.2160p.Blu-Ray.HEVC.mkv", "Arrival", 2016)] + [InlineData(@"The Wolf of Wall Street (2013)", "The Wolf of Wall Street", 2013)] + [InlineData(@"The Wolf of Wall Street 2 (2013)", "The Wolf of Wall Street 2", 2013)] + [InlineData(@"The Wolf of Wall Street - 2 (2013)", "The Wolf of Wall Street - 2", 2013)] + [InlineData(@"The Wolf of Wall Street 2001 (2013)", "The Wolf of Wall Street 2001", 2013)] + [InlineData(@"300 (2006)", "300", 2006)] + [InlineData(@"d:/movies/300 (2006)", "300", 2006)] + [InlineData(@"300 2 (2006)", "300 2", 2006)] + [InlineData(@"300 - 2 (2006)", "300 - 2", 2006)] + [InlineData(@"300 2001 (2006)", "300 2001", 2006)] + [InlineData(@"/server/Movies/300 (2007)/300 (2006)", "300", 2006)] + [InlineData(@"/server/Movies/300 (2007)/300 (2006).mkv", "300", 2006)] + [InlineData(@"American.Psycho.mkv", "American.Psycho.mkv", null)] + [InlineData(@"American Psycho.mkv", "American Psycho.mkv", null)] + [InlineData(@"[rec].mkv", "[rec].mkv", null)] + [InlineData(@"St. Vincent (2014)", "St. Vincent", 2014)] + [InlineData("Super movie(2009).mp4", "Super movie", 2009)] + // FIXME: [InlineData("Drug War 2013.mp4", "Drug War", 2013)] + [InlineData("My Movie (1997) - GreatestReleaseGroup 2019.mp4", "My Movie", 1997)] + // FIXME: [InlineData("First Man 2018 1080p.mkv", "First Man", 2018)] + [InlineData("First Man (2018) 1080p.mkv", "First Man", 2018)] + // FIXME: [InlineData("Maximum Ride - 2016 - WEBDL-1080p - x264 AC3.mkv", "Maximum Ride", 2016)] + // FIXME: [InlineData("Robin Hood [Multi-Subs] [2018].mkv", "Robin Hood", 2018)] + [InlineData(@"3.Days.to.Kill.2014.720p.BluRay.x264.YIFY.mkv", "3.Days.to.Kill", 2014)] // In this test case, running CleanDateTime first produces no date, so it will attempt to run CleanString first and then CleanDateTime again + public void CleanDateTimeTest(string input, string expectedName, int? expectedYear) { input = Path.GetFileName(input); - var result = GetParser().CleanDateTime(input); + var result = new VideoResolver(_namingOptions).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 index cd90ac236c..7c3270ebcc 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs @@ -1,133 +1,37 @@ -using System; -using System.Globalization; +using Emby.Naming.Common; +using Emby.Naming.Video; using Xunit; namespace Jellyfin.Naming.Tests.Video { - public class CleanStringTests : BaseVideoTest + public sealed class CleanStringTests { - // 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"); - } + private readonly NamingOptions _namingOptions = new NamingOptions(); - // FIXME - // [Fact] - public void TestCleanString1() + [Theory] + [InlineData("Super movie 480p.mp4", "Super movie")] + [InlineData("Super movie 480p 2001.mp4", "Super movie")] + [InlineData("Super movie [480p].mp4", "Super movie")] + [InlineData("480 Super movie [tmdbid=12345].mp4", "480 Super movie")] + [InlineData("Super movie(2009).mp4", "Super movie(2009).mp4")] + [InlineData("Run lola run (lola rennt) (2009).mp4", "Run lola run (lola rennt) (2009).mp4")] + [InlineData(@"American.Psycho.mkv", "American.Psycho.mkv")] + [InlineData(@"American Psycho.mkv", "American Psycho.mkv")] + [InlineData(@"[rec].mkv", "[rec].mkv")] + [InlineData("Crouching.Tiger.Hidden.Dragon.4k.mkv", "Crouching.Tiger.Hidden.Dragon")] + [InlineData("Crouching.Tiger.Hidden.Dragon.UltraHD.mkv", "Crouching.Tiger.Hidden.Dragon")] + [InlineData("Crouching.Tiger.Hidden.Dragon.UHD.mkv", "Crouching.Tiger.Hidden.Dragon")] + [InlineData("Crouching.Tiger.Hidden.Dragon.HDR.mkv", "Crouching.Tiger.Hidden.Dragon")] + [InlineData("Crouching.Tiger.Hidden.Dragon.HDC.mkv", "Crouching.Tiger.Hidden.Dragon")] + [InlineData("Crouching.Tiger.Hidden.Dragon-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")] + [InlineData("Crouching.Tiger.Hidden.Dragon.BDrip.mkv", "Crouching.Tiger.Hidden.Dragon")] + [InlineData("Crouching.Tiger.Hidden.Dragon.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")] + [InlineData("Crouching.Tiger.Hidden.Dragon.4K.UltraHD.HDR.BDrip-HDC.mkv", "Crouching.Tiger.Hidden.Dragon")] + // FIXME: [InlineData("After The Sunset - [0004].mkv", "After The Sunset")] + public void CleanStringTest(string input, string expectedName) { - 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); + var result = new VideoResolver(_namingOptions).CleanString(input); + Assert.Equal(expectedName, result.Name); } } } diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index b8674ec495..b8fbb2cb25 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -62,7 +62,6 @@ namespace Jellyfin.Naming.Tests.Video [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", From dd254eddac4b805f0020e5899fb903ef10714527 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 20:31:59 +0100 Subject: [PATCH 154/252] Simplify CleanDateTimeParser --- Emby.Naming/Video/CleanDateTimeParser.cs | 44 ++---------------------- 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index 9edb14a074..9723fb71a3 100644 --- a/Emby.Naming/Video/CleanDateTimeParser.cs +++ b/Emby.Naming/Video/CleanDateTimeParser.cs @@ -1,9 +1,7 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 -using System; using System.Globalization; -using System.IO; using System.Linq; using System.Text.RegularExpressions; using Emby.Naming.Common; @@ -23,47 +21,9 @@ namespace Emby.Naming.Video } public CleanDateTimeResult Clean(string name) - { - var originalName = name; - - try - { - var extension = Path.GetExtension(name) ?? string.Empty; - // Check supported extensions - if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase) - && !_options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) - { - // Dummy up a file extension because the expressions will fail without one - // This is tricky because we can't just check Path.GetExtension for empty - // If the input is "St. Vincent (2014)", it will produce ". Vincent (2014)" as the extension - name += ".mkv"; - } - } - catch (ArgumentException) - { - } - - var result = _options.CleanDateTimeRegexes.Select(i => Clean(name, i)) + => _options.CleanDateTimeRegexes.Select(i => Clean(name, i)) .FirstOrDefault(i => i.HasChanged) ?? - new CleanDateTimeResult { Name = originalName }; - - if (result.HasChanged) - { - return result; - } - - // Make a second pass, running clean string first - var cleanStringResult = CleanStringParser.Clean(name, _options.CleanStringRegexes); - - if (!cleanStringResult.HasChanged) - { - return result; - } - - return _options.CleanDateTimeRegexes.Select(i => Clean(cleanStringResult.Name, i)) - .FirstOrDefault(i => i.HasChanged) ?? - result; - } + new CleanDateTimeResult { Name = name }; private static CleanDateTimeResult Clean(string name, Regex expression) { From cd0592ea8f4a373a4318d2aba42349a1b89d4b32 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 21:16:36 +0100 Subject: [PATCH 155/252] Improve parsers --- Emby.Naming/Common/NamingOptions.cs | 3 +- Emby.Naming/Video/CleanDateTimeParser.cs | 36 +++++++++++------ Emby.Naming/Video/CleanDateTimeResult.cs | 29 ++++++++------ Emby.Naming/Video/CleanStringParser.cs | 39 ++++++++----------- Emby.Naming/Video/CleanStringResult.cs | 20 ---------- Emby.Naming/Video/VideoResolver.cs | 9 +++-- .../Video/CleanStringTests.cs | 14 +++++-- 7 files changed, 75 insertions(+), 75 deletions(-) delete mode 100644 Emby.Naming/Video/CleanStringResult.cs diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 07259e00a1..41060426fa 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -182,8 +182,7 @@ namespace Emby.Naming.Common CleanStrings = new[] { - @"[ _\,\.\(\)\[\]\-](HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)", - @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|\[.*\])([ _\,\.\(\)\[\]\-]|$)", + @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)", @"(\[.*\])" }; diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index 9723fb71a3..8638dddd42 100644 --- a/Emby.Naming/Video/CleanDateTimeParser.cs +++ b/Emby.Naming/Video/CleanDateTimeParser.cs @@ -1,8 +1,8 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable using System.Globalization; -using System.Linq; using System.Text.RegularExpressions; using Emby.Naming.Common; @@ -21,14 +21,28 @@ namespace Emby.Naming.Video } public CleanDateTimeResult Clean(string name) - => _options.CleanDateTimeRegexes.Select(i => Clean(name, i)) - .FirstOrDefault(i => i.HasChanged) ?? - new CleanDateTimeResult { Name = name }; - - private static CleanDateTimeResult Clean(string name, Regex expression) { - var result = new CleanDateTimeResult(); + var regexes = _options.CleanDateTimeRegexes; + var len = regexes.Length; + CleanDateTimeResult result = new CleanDateTimeResult(name); + if (len == 0) + { + return result; + } + for (int i = 0; i < len; i++) + { + if (TryClean(name, regexes[i], ref result)) + { + return result; + } + } + + return result; + } + + private static bool TryClean(string name, Regex expression, ref CleanDateTimeResult result) + { var match = expression.Match(name); if (match.Success @@ -37,13 +51,11 @@ namespace Emby.Naming.Video && match.Groups[2].Success && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year)) { - name = match.Groups[1].Value.TrimEnd(); - result.Year = year; - result.HasChanged = true; + result = new CleanDateTimeResult(match.Groups[1].Value.TrimEnd(), year); + return true; } - result.Name = name; - return result; + return false; } } } diff --git a/Emby.Naming/Video/CleanDateTimeResult.cs b/Emby.Naming/Video/CleanDateTimeResult.cs index a7581972e4..73a445612b 100644 --- a/Emby.Naming/Video/CleanDateTimeResult.cs +++ b/Emby.Naming/Video/CleanDateTimeResult.cs @@ -1,26 +1,33 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable namespace Emby.Naming.Video { - public class CleanDateTimeResult + public readonly struct CleanDateTimeResult { + public CleanDateTimeResult(string name, int? year) + { + Name = name; + Year = year; + } + + public CleanDateTimeResult(string name) + { + Name = name; + Year = null; + } + /// - /// Gets or sets the name. + /// Gets the name. /// /// The name. - public string Name { get; set; } + public string Name { get; } /// - /// Gets or sets the year. + /// Gets the year. /// /// The year. - public int? Year { get; set; } - - /// - /// Gets or sets a value indicating whether this instance has changed. - /// - /// true if this instance has changed; otherwise, false. - public bool HasChanged { get; set; } + public int? Year { get; } } } diff --git a/Emby.Naming/Video/CleanStringParser.cs b/Emby.Naming/Video/CleanStringParser.cs index fe2a91bdb9..b7b65d8228 100644 --- a/Emby.Naming/Video/CleanStringParser.cs +++ b/Emby.Naming/Video/CleanStringParser.cs @@ -1,6 +1,8 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 +#nullable enable +using System; using System.Collections.Generic; using System.Text.RegularExpressions; @@ -11,42 +13,33 @@ namespace Emby.Naming.Video /// public static class CleanStringParser { - public static CleanStringResult Clean(string name, IEnumerable expressions) + public static bool TryClean(string name, IReadOnlyList expressions, out ReadOnlySpan newName) { - var hasChanged = false; - - foreach (var exp in expressions) + var len = expressions.Count; + for (int i = 0; i < len; i++) { - var result = Clean(name, exp); - - if (!string.IsNullOrEmpty(result.Name)) + if (TryClean(name, expressions[i], out newName)) { - name = result.Name; - hasChanged = hasChanged || result.HasChanged; + return true; } } - return new CleanStringResult - { - Name = name, - HasChanged = hasChanged - }; + newName = ReadOnlySpan.Empty; + return false; } - private static CleanStringResult Clean(string name, Regex expression) + private static bool TryClean(string name, Regex expression, out ReadOnlySpan newName) { - var result = new CleanStringResult(); - var match = expression.Match(name); - - if (match.Success) + int index = match.Index; + if (match.Success && index != 0) { - result.HasChanged = true; - name = name.Substring(0, match.Index); + newName = name.AsSpan().Slice(0, match.Index); + return true; } - result.Name = name; - return result; + newName = string.Empty; + return false; } } } diff --git a/Emby.Naming/Video/CleanStringResult.cs b/Emby.Naming/Video/CleanStringResult.cs deleted file mode 100644 index 786fe9e028..0000000000 --- a/Emby.Naming/Video/CleanStringResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - -namespace Emby.Naming.Video -{ - public class CleanStringResult - { - /// - /// Gets or sets the name. - /// - /// The name. - public string Name { get; set; } - - /// - /// Gets or sets a value indicating whether this instance has changed. - /// - /// true if this instance has changed; otherwise, false. - public bool HasChanged { get; set; } - } -} diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 97f59121b1..76e0a5a0ec 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -94,9 +94,10 @@ namespace Emby.Naming.Video { var cleanDateTimeResult = CleanDateTime(name); - if (extraResult.ExtraType == null) + if (extraResult.ExtraType == null + && TryCleanString(cleanDateTimeResult.Name, out ReadOnlySpan newName)) { - name = CleanString(cleanDateTimeResult.Name).Name; + name = newName.ToString(); } year = cleanDateTimeResult.Year; @@ -130,9 +131,9 @@ namespace Emby.Naming.Video return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); } - public CleanStringResult CleanString(string name) + public bool TryCleanString(string name, out ReadOnlySpan newName) { - return CleanStringParser.Clean(name, _options.CleanStringRegexes); + return CleanStringParser.TryClean(name, _options.CleanStringRegexes, out newName); } public CleanDateTimeResult CleanDateTime(string name) diff --git a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs index 7c3270ebcc..fde06c5a17 100644 --- a/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/CleanStringTests.cs @@ -1,4 +1,5 @@ -using Emby.Naming.Common; +using System; +using Emby.Naming.Common; using Emby.Naming.Video; using Xunit; @@ -30,8 +31,15 @@ namespace Jellyfin.Naming.Tests.Video // FIXME: [InlineData("After The Sunset - [0004].mkv", "After The Sunset")] public void CleanStringTest(string input, string expectedName) { - var result = new VideoResolver(_namingOptions).CleanString(input); - Assert.Equal(expectedName, result.Name); + if (new VideoResolver(_namingOptions).TryCleanString(input, out ReadOnlySpan newName)) + { + // TODO: compare spans when XUnit supports it + Assert.Equal(expectedName, newName.ToString()); + } + else + { + Assert.Equal(expectedName, input); + } } } } From ec0ef2a2c5a20b7e6aaa5959b52bf399b0b6dde2 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 21:18:30 +0100 Subject: [PATCH 156/252] Remove useless statement --- Emby.Naming/Video/CleanDateTimeParser.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index 8638dddd42..84a14a54f5 100644 --- a/Emby.Naming/Video/CleanDateTimeParser.cs +++ b/Emby.Naming/Video/CleanDateTimeParser.cs @@ -24,12 +24,8 @@ namespace Emby.Naming.Video { var regexes = _options.CleanDateTimeRegexes; var len = regexes.Length; - CleanDateTimeResult result = new CleanDateTimeResult(name); - if (len == 0) - { - return result; - } + CleanDateTimeResult result = new CleanDateTimeResult(name); for (int i = 0; i < len; i++) { if (TryClean(name, regexes[i], ref result)) From abf03f7d3ad14f026fd439f522403353a7ecec86 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 21:31:35 +0100 Subject: [PATCH 157/252] 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(-) diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index 84a14a54f5..6c74c07d55 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 76e0a5a0ec..f93db24866 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 ae3cdece91..6fb6235541 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 }; } From 9f5bbb126e55b23243d22bcfd85fbbb17e10ec17 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 11 Jan 2020 22:29:46 +0100 Subject: [PATCH 158/252] Fix tests --- Emby.Naming/Common/NamingOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 41060426fa..9ce503b8e4 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -177,7 +177,7 @@ namespace Emby.Naming.Common CleanDateTimes = new[] { - @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](\d{4})([ _\,\.\(\)\[\]\-][^\d]|).*(\d{4})*" + @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19\d{2}|20\d{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19\d{2}|20\d{2})*" }; CleanStrings = new[] From 08c4d3797fd2bb54936d4cba1b71de915432ebd1 Mon Sep 17 00:00:00 2001 From: Nyanmisaka <799610810@qq.com> Date: Sun, 12 Jan 2020 21:38:28 +0800 Subject: [PATCH 159/252] add support for AMF hardware encoding on Linux. 1) h264_amf is now supported on linux with 'amdgpu-pro' installed and '--enable-amf' when compiling ffmpeg. 2) Using vaapi decode and h264_amf encode on linux platform can avoid some weird transcoding errors in h264_vaapi with amd gpu. --- .../MediaEncoding/EncodingHelper.cs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 020f0553ed..a099087628 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2642,22 +2642,11 @@ namespace MediaBrowser.Controller.MediaEncoding else return "-hwaccel dxva2"; } - - switch (videoStream.Codec.ToLowerInvariant()) + else { - case "avc": - case "h264": - if (_mediaEncoder.SupportsDecoder("h264_amf") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) - { - return "-c:v h264_amf"; - } - break; - case "mpeg2video": - if (_mediaEncoder.SupportsDecoder("hevc_amf") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) - { - return "-c:v mpeg2_mmal"; - } - break; + //h264_amf is now supported on linux with 'amdgpu-pro' installed and '--enable-amf' when compiling ffmpeg + //using vaapi decode and h264_amf encode on linux platform can avoid some weird transcoding errors in h264_vaapi with amd gpu + return "-hwaccel vaapi"; } } } From 2a09f05ff35d7ae9ab4bb0d216a93698814b6821 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 12 Jan 2020 16:30:31 +0100 Subject: [PATCH 160/252] Fix build --- jellyfin.ruleset | 2 -- 1 file changed, 2 deletions(-) diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 9846367d83..27d8a7cd92 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -33,8 +33,6 @@ - - From ded9857f45388ce5499d09b537c56157c48ca759 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Sun, 12 Jan 2020 18:59:10 +0100 Subject: [PATCH 161/252] Clean up server discovery code --- .../EntryPoints/UdpServerEntryPoint.cs | 58 ++-- .../Net/SocketFactory.cs | 29 -- Emby.Server.Implementations/Net/UdpSocket.cs | 9 - Emby.Server.Implementations/Udp/UdpServer.cs | 273 ++++-------------- MediaBrowser.Model/Net/ISocket.cs | 2 - MediaBrowser.Model/Net/ISocketFactory.cs | 9 - 6 files changed, 78 insertions(+), 302 deletions(-) diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs index 9ee219854d..a83817cb96 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() + public async 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; + _udpServer = new UdpServer(_logger, _appHost); + _udpServer.Start(PortNumber, _cancellationTokenSource.Token); } /// 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 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 0870db003f..4e04cde78c 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 dde4a2a34c..211ca67841 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 185a282ac4..19fc1e316d 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; + + /// + /// The _udp client. + /// + private Socket _udpSocket; + private IPEndPoint _endpoint; + private readonly byte[] _receiveBuffer = new byte[8192]; + + private bool _disposed = false; /// /// Initializes a new instance of the class. /// - public UdpServer(ILogger logger, IServerApplicationHost appHost, IJsonSerializer json, ISocketFactory socketFactory) + public UdpServer(ILogger logger, IServerApplicationHost appHost) { _logger = logger; _appHost = appHost; - _json = json; - _socketFactory = socketFactory; - - AddMessageResponder("who is JellyfinServer?", true, RespondToV2Message); } - private void AddMessageResponder(string message, bool isSubstring, Func responder) + private async Task RespondToV2Message(string messageText, EndPoint endpoint, CancellationToken cancellationToken) { - _responders.Add(new Tuple>(message, isSubstring, responder)); - } - - /// - /// Raises the event. - /// - 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) - { - 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); - } - - private async Task RespondToV2Message(string messageText, IPEndPoint endpoint, Encoding encoding, 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) + /// + public void Start(int port, CancellationToken cancellationToken) { - _udpClient = _socketFactory.CreateUdpSocket(port); + _endpoint = new IPEndPoint(IPAddress.Any, port); - Task.Run(() => BeginReceive()); + _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + _udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + _udpSocket.Bind(_endpoint); + + _ = Task.Run(async () => await BeginReceiveAsync(cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); } - private readonly byte[] _receiveBuffer = new byte[8192]; - - private void BeginReceive() + private async Task BeginReceiveAsync(CancellationToken cancellationToken) { - if (_isDisposed) + while (!cancellationToken.IsCancellationRequested) { - return; - } - - try - { - var result = _udpClient.BeginReceive(_receiveBuffer, 0, _receiveBuffer.Length, OnReceiveResult); - - if (result.CompletedSynchronously) + try { - OnReceiveResult(result); + var result = await _udpSocket.ReceiveFromAsync(_receiveBuffer, SocketFlags.None, _endpoint).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + 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) + { + _logger.LogError(ex, "Failed to receive data drom socket"); + } + catch (OperationCanceledException) + { + // Don't throw } } - 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(); - } - - /// - /// Called when [message received]. - /// - /// The message. - private void OnMessageReceived(SocketReceiveResult message) - { - if (_isDisposed) - { - return; - } - - if (message.RemoteEndPoint.Port == 0) - { - return; - } - - try - { - OnMessageReceived(new GenericEventArgs - { - 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); - } - - /// - /// 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) + if (_disposed) { - _isDisposed = true; - - if (_udpClient != null) - { - _udpClient.Dispose(); - } - } - } - - public async Task SendAsync(byte[] bytes, IPEndPoint remoteEndPoint, CancellationToken cancellationToken) - { - if (_isDisposed) - { - throw new ObjectDisposedException(GetType().Name); + return; } - if (bytes == null) - { - throw new ArgumentNullException(nameof(bytes)); - } + _udpSocket?.Dispose(); - if (remoteEndPoint == null) - { - throw new ArgumentNullException(nameof(remoteEndPoint)); - } - - try - { - await _udpClient.SendToAsync(bytes, 0, bytes.Length, remoteEndPoint, cancellationToken).ConfigureAwait(false); - - _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 f80de5524c..3fdc40bbe2 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 2f857f1af0..dc69b1fb25 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); } } From ea075c1b4805cfabff58739fc4e3de7813a33298 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sun, 12 Jan 2020 13:22:20 -0500 Subject: [PATCH 162/252] Add reminder to remove the workaround This will be no longer needed when ffmpeg is ready Co-Authored-By: dkanada --- MediaBrowser.Api/Playback/UniversalAudioService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs index c9d29f8ef6..625ea9ac1f 100644 --- a/MediaBrowser.Api/Playback/UniversalAudioService.cs +++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs @@ -301,6 +301,7 @@ namespace MediaBrowser.Api.Playback var transcodingProfile = deviceProfile.TranscodingProfiles[0]; // hls segment container can only be mpegts or fmp4 per ffmpeg documentation + // TODO: remove this when we switch back to the segment muxer var supportedHLSContainers = new string[] { "mpegts", "fmp4" }; var newRequest = new GetMasterHlsAudioPlaylist From da5893b0f14c6d1efec77e105a6240b241a1c412 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 12 Jan 2020 21:21:20 +0100 Subject: [PATCH 163/252] Try to fix nullreff --- MediaBrowser.Api/Playback/MediaInfoService.cs | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 4b9bb80103..4fa0e31406 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -266,8 +266,7 @@ namespace MediaBrowser.Api.Playback private T Clone(T obj) { - // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it - // Should we move this directly into MediaSourceManager? + var json = JsonSerializer.SerializeToUtf8Bytes(obj); return JsonSerializer.Deserialize(json); } @@ -278,27 +277,20 @@ namespace MediaBrowser.Api.Playback var item = _libraryManager.GetItemById(id); var result = new PlaybackInfoResponse(); + MediaSourceInfo[] mediaSources; if (string.IsNullOrWhiteSpace(liveStreamId)) { - IEnumerable mediaSources; - try - { - // TODO handle supportedLiveMediaTypes ? - mediaSources = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - mediaSources = new List(); - Logger.LogError(ex, "Could not find media sources for item id {id}", id); - // TODO PlaybackException ?? - //result.ErrorCode = ex.ErrorCode; - } - result.MediaSources = mediaSources.ToArray(); + // TODO handle supportedLiveMediaTypes ? + var mediaSourcesList = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false); - if (!string.IsNullOrWhiteSpace(mediaSourceId)) + if (string.IsNullOrWhiteSpace(mediaSourceId)) { - result.MediaSources = result.MediaSources + mediaSources = mediaSourcesList.ToArray(); + } + else + { + mediaSources = mediaSourcesList .Where(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) .ToArray(); } @@ -307,11 +299,13 @@ namespace MediaBrowser.Api.Playback { var mediaSource = await _mediaSourceManager.GetLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false); - result.MediaSources = new MediaSourceInfo[] { mediaSource }; + mediaSources = new MediaSourceInfo[] { mediaSource }; } - if (result.MediaSources.Count == 0) + if (mediaSources.Length == 0) { + result.MediaSources = Array.Empty(); + if (!result.ErrorCode.HasValue) { result.ErrorCode = PlaybackErrorCode.NoCompatibleStream; @@ -319,7 +313,9 @@ namespace MediaBrowser.Api.Playback } else { - result.MediaSources = Clone(result.MediaSources); + // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it + // Should we move this directly into MediaSourceManager? + result.MediaSources = JsonSerializer.Deserialize(JsonSerializer.SerializeToUtf8Bytes(mediaSources)); result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); } From 84d1b12530b4c53910b300b040fe5f316523427e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 12 Jan 2020 21:55:04 +0100 Subject: [PATCH 164/252] Attempt #2 --- MediaBrowser.Api/Playback/MediaInfoService.cs | 7 ------- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 4fa0e31406..53823943f3 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -264,13 +264,6 @@ namespace MediaBrowser.Api.Playback return ToOptimizedResult(result); } - private T Clone(T obj) - { - - var json = JsonSerializer.SerializeToUtf8Bytes(obj); - return JsonSerializer.Deserialize(json); - } - private async Task GetPlaybackInfo(Guid id, Guid userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null) { var user = _userManager.GetUserById(userId); diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 5cb0565666..16a212a81b 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -76,6 +76,7 @@ namespace MediaBrowser.Model.Dto { Formats = Array.Empty(); MediaStreams = new List(); + MediaAttachments = Array.Empty(); RequiredHttpHeaders = new Dictionary(); SupportsTranscoding = true; SupportsDirectStream = true; From 93ab829df523c36a44ffa2f9caaf96505a2b2e5c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 12 Jan 2020 23:25:57 +0100 Subject: [PATCH 165/252] Attempt #3 --- MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index 7fa7afa5b3..0b2f1d231e 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -225,7 +225,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// /// The start_pts. [JsonPropertyName("start_pts")] - public int StartPts { get; set; } + public long StartPts { get; set; } /// /// Gets or sets the is_avc. From 56f580cdf6eb1a84275a582c4dc7dfb8245cc347 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 12 Jan 2020 23:57:13 +0100 Subject: [PATCH 166/252] Add test to prevent regressions --- .../EncoderValidatorTestsData.cs | 6 +- .../FFprobeParserTests.cs | 22 ++++ .../Jellyfin.MediaEncoding.Tests.csproj | 6 + .../Test Data/ffprobe1.json | 105 ++++++++++++++++++ 4 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs create mode 100644 tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs index 12fde07705..c46c9578b9 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTestsData.cs @@ -26,7 +26,7 @@ libswscale 5. 5.100 / 5. 5.100 libswresample 3. 5.100 / 3. 5.100 libpostproc 55. 5.100 / 55. 5.100"; - public const string FFmpegV414Output = @"ffmpeg version 4.1.4-1~deb10u1 Copyright (c) 2000-2019 the FFmpeg developers + public const string FFmpegV414Output = @"ffmpeg version 4.1.4-1~deb10u1 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 8 (Raspbian 8.3.0-6+rpi1) configuration: --prefix=/usr --extra-version='1~deb10u1' --toolchain=hardened --libdir=/usr/lib/arm-linux-gnueabihf --incdir=/usr/include/arm-linux-gnueabihf --arch=arm --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared libavutil 56. 22.100 / 56. 22.100 @@ -39,7 +39,7 @@ libswscale 5. 3.100 / 5. 3.100 libswresample 3. 3.100 / 3. 3.100 libpostproc 55. 3.100 / 55. 3.100"; - public const string FFmpegV404Output = @"ffmpeg version 4.0.4 Copyright (c) 2000-2019 the FFmpeg developers + public const string FFmpegV404Output = @"ffmpeg version 4.0.4 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 8 (Debian 8.3.0-6) configuration: --toolchain=hardened --prefix=/usr --target-os=linux --enable-cross-compile --extra-cflags=--static --enable-gpl --enable-static --disable-doc --disable-ffplay --disable-shared --disable-libxcb --disable-sdl2 --disable-xlib --enable-libfontconfig --enable-fontconfig --enable-gmp --enable-gnutls --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libwebp --enable-libx264 --enable-libx265 --enable-libzvbi --enable-omx --enable-omx-rpi --enable-version3 --enable-vaapi --enable-vdpau --arch=amd64 --enable-nvenc --enable-nvdec libavutil 56. 14.100 / 56. 14.100 @@ -51,7 +51,7 @@ libswscale 5. 1.100 / 5. 1.100 libswresample 3. 1.100 / 3. 1.100 libpostproc 55. 1.100 / 55. 1.100"; - public const string FFmpegGitUnknownOutput = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers + public const string FFmpegGitUnknownOutput = @"ffmpeg version N-94303-g7cb4f8c962 Copyright (c) 2000-2019 the FFmpeg developers built with gcc 9.1.1 (GCC) 20190716 configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt libavutil 56. 30.100 / 56. 30.100 diff --git a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs new file mode 100644 index 0000000000..2032f6cecd --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs @@ -0,0 +1,22 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; +using MediaBrowser.MediaEncoding.Probing; +using Xunit; + +namespace Jellyfin.MediaEncoding.Tests +{ + public class FFprobeParserTests + { + [Theory] + [InlineData("ffprobe1.json")] + public async Task Test(string fileName) + { + var path = Path.Join("Test Data", fileName); + using (var stream = File.OpenRead(path)) + { + await JsonSerializer.DeserializeAsync(stream).ConfigureAwait(false); + } + } + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index 7f6b905332..5d9b32086c 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -5,6 +5,12 @@ false + + + PreserveNewest + + + diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json new file mode 100644 index 0000000000..cdad5df502 --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json @@ -0,0 +1,105 @@ +{ + "streams": [ + { + "index": 0, + "codec_name": "h264", + "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", + "profile": "Main", + "codec_type": "video", + "codec_time_base": "1/50", + "codec_tag_string": "[27][0][0][0]", + "codec_tag": "0x001b", + "width": 1920, + "height": 1080, + "coded_width": 1920, + "coded_height": 1080, + "has_b_frames": 0, + "sample_aspect_ratio": "0:1", + "display_aspect_ratio": "0:1", + "pix_fmt": "yuvj420p", + "level": 42, + "color_range": "pc", + "color_space": "bt709", + "color_transfer": "bt709", + "color_primaries": "bt709", + "chroma_location": "left", + "field_order": "progressive", + "refs": 1, + "is_avc": "false", + "nal_length_size": "0", + "id": "0x1", + "r_frame_rate": "25/1", + "avg_frame_rate": "25/1", + "time_base": "1/90000", + "start_pts": 8570867078, + "start_time": "95231.856422", + "duration_ts": 31694552, + "duration": "352.161689", + "bits_per_raw_sample": "8", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0 + } + }, + { + "index": 1, + "codec_name": "aac", + "codec_long_name": "AAC (Advanced Audio Coding)", + "profile": "LC", + "codec_type": "audio", + "codec_time_base": "1/44100", + "codec_tag_string": "[15][0][0][0]", + "codec_tag": "0x000f", + "sample_fmt": "fltp", + "sample_rate": "44100", + "channels": 2, + "channel_layout": "stereo", + "bits_per_sample": 0, + "id": "0x2", + "r_frame_rate": "0/0", + "avg_frame_rate": "0/0", + "time_base": "1/90000", + "start_pts": 8570867697, + "start_time": "95231.863300", + "duration_ts": 31695687, + "duration": "352.174300", + "bit_rate": "98191", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0 + } + } + ], + "format": { + "filename": "TS Test record.ts", + "nb_streams": 2, + "nb_programs": 1, + "format_name": "mpegts", + "format_long_name": "MPEG-TS (MPEG-2 Transport Stream)", + "start_time": "95231.856422", + "duration": "352.181178", + "size": "179003772", + "bit_rate": "4066174", + "probe_score": 50 + } +} From a8cd963d468ba4fb4caa003d9eb927117a3afa77 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 13 Jan 2020 15:25:54 +0900 Subject: [PATCH 167/252] fix tests for absolute paths --- .ci/azure-pipelines.yml | 2 +- .../IO/ManagedFileSystem.cs | 35 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 7bcaed70cd..d69cc4943e 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 e27081c45a..bf2173d79b 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; } From 65e9a705d303ebfadbc58035f9763966141ee437 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 13 Jan 2020 15:34:50 +0900 Subject: [PATCH 168/252] 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(-) diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index d69cc4943e..7bcaed70cd 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 bf2173d79b..1fd8ddc4d4 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 f2a8e447fa..b2aa01e655 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); + } } } } From d00fd7ca82d33e90c87a5a6c02a1eb8505babfcf Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 13 Jan 2020 15:43:06 +0900 Subject: [PATCH 169/252] switch two incorrectly used variables --- .../IO/ManagedFileSystemTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index b2aa01e655..182c709982 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -31,8 +31,8 @@ namespace Jellyfin.Server.Implementations.Tests.IO if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) { - var windowsPath = "d:" + generatedPath.Replace('/', '\\'); - Assert.Equal(expectedAbsolutePath, windowsPath); + var expectedWindowsPath = "d:" + expectedAbsolutePath.Replace('/', '\\'); + Assert.Equal(expectedWindowsPath, generatedPath); } else { From 665ca5cf1fcd6c757f214998d121e3f6a50fb8f3 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 13 Jan 2020 09:00:30 +0100 Subject: [PATCH 170/252] Update UdpServer.cs --- Emby.Server.Implementations/Udp/UdpServer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index 19fc1e316d..c91d137a72 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]; From cf13e89ad6d086cc20acd877a76df23900b222a9 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 13 Jan 2020 09:01:14 +0100 Subject: [PATCH 171/252] Update MediaBrowser.Api/Playback/MediaInfoService.cs Co-Authored-By: dkanada --- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 53823943f3..10f10a15ec 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -274,7 +274,7 @@ namespace MediaBrowser.Api.Playback if (string.IsNullOrWhiteSpace(liveStreamId)) { - // TODO handle supportedLiveMediaTypes ? + // TODO handle supportedLiveMediaTypes? var mediaSourcesList = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false); if (string.IsNullOrWhiteSpace(mediaSourceId)) From 9eac19c75ad9c69c0c30ae90c0772856e7b0ce9e Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 13 Jan 2020 17:09:22 +0900 Subject: [PATCH 172/252] change invariant culture to ordinal --- Emby.Server.Implementations/IO/ManagedFileSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 1fd8ddc4d4..68417876cf 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; } From d461f46befb06dae742ab523a3b28dadbff853f3 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 13 Jan 2020 17:31:14 +0900 Subject: [PATCH 173/252] ignore drive name in windows environments --- .../IO/ManagedFileSystemTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index 182c709982..de227cd859 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -31,7 +31,7 @@ namespace Jellyfin.Server.Implementations.Tests.IO if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) { - var expectedWindowsPath = "d:" + expectedAbsolutePath.Replace('/', '\\'); + var expectedWindowsPath = expectedAbsolutePath.Replace('/', '\\').Split(':')[1]; Assert.Equal(expectedWindowsPath, generatedPath); } else From 536237c35dc74205e25593667add97695a27f0f2 Mon Sep 17 00:00:00 2001 From: dkanada Date: Mon, 13 Jan 2020 17:37:07 +0900 Subject: [PATCH 174/252] move the split usage to the proper location --- .../IO/ManagedFileSystemTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index de227cd859..e324002f0c 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -31,8 +31,8 @@ namespace Jellyfin.Server.Implementations.Tests.IO if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) { - var expectedWindowsPath = expectedAbsolutePath.Replace('/', '\\').Split(':')[1]; - Assert.Equal(expectedWindowsPath, generatedPath); + var expectedWindowsPath = expectedAbsolutePath.Replace('/', '\\'); + Assert.Equal(expectedWindowsPath, generatedPath.Split(':')[1]); } else { From 9c2c31b0c63b12ff303790f1294eeccb6adea252 Mon Sep 17 00:00:00 2001 From: punkch Date: Fri, 10 Jan 2020 23:42:06 +0000 Subject: [PATCH 175/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json index 46c10d912e..9441da5915 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": "Смесено съдържание", From 31347de792cec994ac9796628e161eaac4bbaf4a Mon Sep 17 00:00:00 2001 From: Philmo67 Date: Fri, 10 Jan 2020 15:17:30 +0000 Subject: [PATCH 176/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 6b25e3df0f..ff70733359 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", From faf898ea0d8fc5d0dcd6d98a0d4cfc95d4126a6d Mon Sep 17 00:00:00 2001 From: hbo Date: Fri, 10 Jan 2020 15:18:30 +0000 Subject: [PATCH 177/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index ff70733359..90754e4155 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", From 7b91df199401e299463264b3b6bbae729bf51499 Mon Sep 17 00:00:00 2001 From: Alexander Brissman Date: Fri, 10 Jan 2020 21:46:39 +0000 Subject: [PATCH 178/252] =?UTF-8?q?Translated=20using=20Weblate=20(Norwegi?= =?UTF-8?q?an=20Bokm=C3=A5l)=20Translation:=20Jellyfin/Jellyfin=20Translat?= =?UTF-8?q?e-URL:=20https://translate.jellyfin.org/projects/jellyfin/jelly?= =?UTF-8?q?fin-core/nb=5FNO/?= 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index 7d4b2bdac1..f9fa1b68cd 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", From ab0130e9e288c92d8e11abc8067c69c6bb13e468 Mon Sep 17 00:00:00 2001 From: Z Yang Date: Sat, 11 Jan 2020 13:18:35 +0000 Subject: [PATCH 179/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index a4d53c57e9..a567e6e2d3 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": "最爱的节目", From 138bff43270279f46358c39cc03666fb3ee331b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slobodan=20Simi=C4=87?= Date: Fri, 10 Jan 2020 20:27:47 +0000 Subject: [PATCH 180/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json index 0967ef424b..da0088991b 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": "Албуми" +} From 65fe243afbcc4b596cf8726708c1965cd34b5f68 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 13 Jan 2020 19:44:17 +0100 Subject: [PATCH 181/252] Add thread ID and source to logging --- Jellyfin.Server/Jellyfin.Server.csproj | 1 + Jellyfin.Server/Program.cs | 6 ++++-- Jellyfin.Server/Resources/Configuration/logging.json | 7 ++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 166e22909d..62bf5b0fbd 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -42,6 +42,7 @@ + diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 712990a1ee..8f44327971 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -475,17 +475,19 @@ namespace Jellyfin.Server Serilog.Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration) .Enrich.FromLogContext() + .Enrich.WithThreadId() .CreateLogger(); } catch (Exception ex) { Serilog.Log.Logger = new LoggerConfiguration() - .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}") + .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] {ThreadId} {SourceContext}: {Message:lj} {NewLine}{Exception}") .WriteTo.Async(x => x.File( Path.Combine(appPaths.LogDirectoryPath, "log_.log"), rollingInterval: RollingInterval.Day, - outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}")) + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {ThreadId} {SourceContext}:{Message} {NewLine}{Exception}")) .Enrich.FromLogContext() + .Enrich.WithThreadId() .CreateLogger(); Serilog.Log.Logger.Fatal(ex, "Failed to create/read logger configuration"); diff --git a/Jellyfin.Server/Resources/Configuration/logging.json b/Jellyfin.Server/Resources/Configuration/logging.json index e85ef05afd..6bc2ed2ee4 100644 --- a/Jellyfin.Server/Resources/Configuration/logging.json +++ b/Jellyfin.Server/Resources/Configuration/logging.json @@ -5,7 +5,7 @@ { "Name": "Console", "Args": { - "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}" + "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {ThreadId} {SourceContext}: {Message:lj} {NewLine}{Exception}" } }, { @@ -20,12 +20,13 @@ "retainedFileCountLimit": 3, "rollOnFileSizeLimit": true, "fileSizeLimitBytes": 100000000, - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}" + "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {ThreadId} {SourceContext}:{Message} {NewLine}{Exception}" } } ] } } - ] + ], + "Enrich": [ "FromLogContext", "WithThreadId" ] } } From 402691b5e1e1b753ba88fda64ffc877b96c78ecf Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 13 Jan 2020 17:16:43 -0500 Subject: [PATCH 182/252] Explicitly ask for ffmpeg logs gimme yo ffmpeg logs, yo --- .github/ISSUE_TEMPLATE/media_playback.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/media_playback.md b/.github/ISSUE_TEMPLATE/media_playback.md index 93af33fbfd..5b3cb834e2 100644 --- a/.github/ISSUE_TEMPLATE/media_playback.md +++ b/.github/ISSUE_TEMPLATE/media_playback.md @@ -11,7 +11,10 @@ assignees: '' **Logs** - + + +**FFmpeg Logs** + **Stats for Nerds Screenshots** From cd5e5cda61fe1da1501bc0e5956684ac851eefd1 Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 13 Jan 2020 21:25:21 -0500 Subject: [PATCH 183/252] Update .github/ISSUE_TEMPLATE/media_playback.md Co-Authored-By: dkanada --- .github/ISSUE_TEMPLATE/media_playback.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/media_playback.md b/.github/ISSUE_TEMPLATE/media_playback.md index 5b3cb834e2..b2bd717b04 100644 --- a/.github/ISSUE_TEMPLATE/media_playback.md +++ b/.github/ISSUE_TEMPLATE/media_playback.md @@ -11,7 +11,7 @@ assignees: '' **Logs** - + **FFmpeg Logs** @@ -32,4 +32,3 @@ assignees: '' - Client: [e.g. Web/Browser, webOS, Android, Android TV, Electron] - Browser (if Web client): [e.g. Firefox, Chrome, Safari] - Client and Browser Version: [e.g. 10.3.4 and 68.0] - From c1350f30d20029932c1e6734c7fb9e85c6a0b75a Mon Sep 17 00:00:00 2001 From: Artiume Date: Mon, 13 Jan 2020 21:25:41 -0500 Subject: [PATCH 184/252] Update .github/ISSUE_TEMPLATE/media_playback.md Co-Authored-By: dkanada --- .github/ISSUE_TEMPLATE/media_playback.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/media_playback.md b/.github/ISSUE_TEMPLATE/media_playback.md index b2bd717b04..97e3785910 100644 --- a/.github/ISSUE_TEMPLATE/media_playback.md +++ b/.github/ISSUE_TEMPLATE/media_playback.md @@ -14,7 +14,7 @@ assignees: '' **FFmpeg Logs** - + **Stats for Nerds Screenshots** From da91b4fa4c10e6d2579291895bc32cd3a41f7bbd Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Mon, 13 Jan 2020 16:22:52 +0100 Subject: [PATCH 185/252] Split CI testing files Switch to cobertura for code coverage Switch to dotnet test for tests Add matrix run for different platforms Add extra variables for easier maintenance --- .ci/azure-pipelines-compat.yml | 96 ++++++++++ .ci/azure-pipelines-main.yml | 101 ++++++++++ .ci/azure-pipelines-test.yml | 68 +++++++ .ci/azure-pipelines-windows.yml | 82 ++++++++ .ci/azure-pipelines.yml | 320 +++----------------------------- .ci/publish-nightly.yml | 46 ----- .ci/publish-release.yml | 48 ----- tests/coverletArgs.runsettings | 17 ++ 8 files changed, 386 insertions(+), 392 deletions(-) create mode 100644 .ci/azure-pipelines-compat.yml create mode 100644 .ci/azure-pipelines-main.yml create mode 100644 .ci/azure-pipelines-test.yml create mode 100644 .ci/azure-pipelines-windows.yml delete mode 100644 .ci/publish-nightly.yml delete mode 100644 .ci/publish-release.yml create mode 100644 tests/coverletArgs.runsettings diff --git a/.ci/azure-pipelines-compat.yml b/.ci/azure-pipelines-compat.yml new file mode 100644 index 0000000000..ac23e0b6db --- /dev/null +++ b/.ci/azure-pipelines-compat.yml @@ -0,0 +1,96 @@ +parameters: + - name: Packages + type: object + default: {} + - name: LinuxImage + type: string + default: "ubuntu-latest" + - name: DotNetSdkVersion + type: string + default: 3.1.100 + +jobs: + - job: CompatibilityCheck + displayName: Compatibility Check + pool: + vmImage: "${{ parameters.LinuxImage}}" + # only execute for pull requests + condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber']) + strategy: + matrix: + ${{ each Package in parameters.Packages }}: + ${{ Package.key }}: + NugetPackageName: ${{ Package.value.NugetPackageName }} + AssemblyFileName: ${{ Package.value.AssemblyFileName }} + maxParallel: 2 + dependsOn: MainBuild + steps: + - checkout: none + + - task: UseDotNet@2 + displayName: "Update DotNet" + inputs: + packageType: sdk + version: ${{ parameters.DotNetSdkVersion }} + + - task: DownloadPipelineArtifact@2 + displayName: "Download New Assembly Build Artifact" + inputs: + source: "current" # Options: current, specific + artifact: "$(NugetPackageName)" # Optional + path: "$(System.ArtifactsDirectory)/new-artifacts" + runVersion: "latest" # Required when source == Specific. Options: latest, latestFromBranch, specific + + - task: CopyFiles@2 + displayName: "Copy New Assembly Build Artifact" + inputs: + sourceFolder: $(System.ArtifactsDirectory)/new-artifacts # Optional + contents: "**/*.dll" + targetFolder: $(System.ArtifactsDirectory)/new-release + cleanTargetFolder: true # Optional + overWrite: true # Optional + flattenFolders: true # Optional + + - task: DownloadPipelineArtifact@2 + displayName: "Download Reference Assembly Build Artifact" + inputs: + source: "specific" # Options: current, specific + artifact: "$(NugetPackageName)" # Optional + path: "$(System.ArtifactsDirectory)/current-artifacts" + project: "$(System.TeamProjectId)" # Required when source == Specific + pipeline: "$(System.DefinitionId)" # Required when source == Specific + runVersion: "latestFromBranch" # Required when source == Specific. Options: latest, latestFromBranch, specific + runBranch: "refs/heads/$(System.PullRequest.TargetBranch)" # Required when source == Specific && runVersion == LatestFromBranch + + - task: CopyFiles@2 + displayName: "Copy Reference Assembly Build Artifact" + inputs: + sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional + contents: "**/*.dll" + targetFolder: $(System.ArtifactsDirectory)/current-release + cleanTargetFolder: true # Optional + overWrite: true # Optional + flattenFolders: true # Optional + + - task: DownloadGitHubRelease@0 + displayName: "Download ABI Compatibility Check Tool" + inputs: + connection: Jellyfin Release Download + userRepository: EraYaN/dotnet-compatibility + defaultVersionType: "latest" # Options: latest, specificVersion, specificTag + itemPattern: "**-ci.zip" # Optional + downloadPath: "$(System.ArtifactsDirectory)" + + - task: ExtractFiles@1 + displayName: "Extract ABI Compatibility Check Tool" + inputs: + archiveFilePatterns: "$(System.ArtifactsDirectory)/*-ci.zip" + destinationFolder: $(System.ArtifactsDirectory)/tools + cleanDestinationFolder: true + + # The `--warnings-only` switch will swallow the return code and not emit any errors. + - task: CmdLine@2 + displayName: "Execute ABI Compatibility Check Tool" + inputs: + script: "dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only" + workingDirectory: $(System.ArtifactsDirectory) # Optional \ No newline at end of file diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml new file mode 100644 index 0000000000..fd263e20f9 --- /dev/null +++ b/.ci/azure-pipelines-main.yml @@ -0,0 +1,101 @@ +parameters: + LinuxImage: "ubuntu-latest" + RestoreBuildProjects: "Jellyfin.Server/Jellyfin.Server.csproj" + DotNetSdkVersion: 3.1.100 + +jobs: + - job: MainBuild + displayName: Main Build + strategy: + matrix: + Release: + BuildConfiguration: Release + Debug: + BuildConfiguration: Debug + maxParallel: 2 + pool: + vmImage: "${{ parameters.LinuxImage }}" + steps: + - checkout: self + clean: true + submodules: true + persistCredentials: true + + - task: CmdLine@2 + displayName: "Clone Web Client (Master, Release, or Tag)" + condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + inputs: + script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" + + - task: CmdLine@2 + displayName: "Clone Web Client (PR)" + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest')) + inputs: + script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" + + - task: NodeTool@0 + displayName: "Install Node" + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + inputs: + versionSpec: "10.x" + + - task: CmdLine@2 + displayName: "Build Web Client" + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + inputs: + script: yarn install + workingDirectory: $(Agent.TempDirectory)/jellyfin-web + + - task: CopyFiles@2 + displayName: "Copy Web Client" + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + inputs: + sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional + contents: "**" + targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web + cleanTargetFolder: true # Optional + overWrite: true # Optional + flattenFolders: false # Optional + + - task: UseDotNet@2 + displayName: "Update DotNet" + inputs: + packageType: sdk + version: ${{ parameters.DotNetSdkVersion }} + + - task: DotNetCoreCLI@2 + displayName: "Publish Server" + inputs: + command: publish + publishWebProjects: false + projects: "${{ parameters.RestoreBuildProjects }}" + arguments: "--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)" + zipAfterPublish: false + + - task: PublishPipelineArtifact@0 + displayName: "Publish Artifact Naming" + condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) + inputs: + targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll" + artifactName: "Jellyfin.Naming" + + - task: PublishPipelineArtifact@0 + displayName: "Publish Artifact Controller" + condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) + inputs: + targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll" + artifactName: "Jellyfin.Controller" + + - task: PublishPipelineArtifact@0 + displayName: "Publish Artifact Model" + condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) + inputs: + targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll" + artifactName: "Jellyfin.Model" + + - task: PublishPipelineArtifact@0 + displayName: "Publish Artifact Common" + condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) + inputs: + targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll" + artifactName: "Jellyfin.Common" \ No newline at end of file diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml new file mode 100644 index 0000000000..eec02ff3be --- /dev/null +++ b/.ci/azure-pipelines-test.yml @@ -0,0 +1,68 @@ +parameters: + - name: ImageNames + type: object + default: + Linux: "ubuntu-latest" + Windows: "windows-latest" + macOS: "macos-latest" + - name: TestProjects + type: string + default: "tests/**/*Tests.csproj" + - name: DotNetSdkVersion + type: string + default: 3.1.100 + +jobs: + - job: MainTest + displayName: Main Test + strategy: + matrix: + ${{ each imageName in parameters.ImageNames }}: + ${{ imageName.key }}: + ImageName: ${{ imageName.value }} + maxParallel: 3 + pool: + vmImage: "$(ImageName)" + steps: + - checkout: self + clean: true + submodules: true + persistCredentials: false + + - task: UseDotNet@2 + displayName: "Update DotNet" + inputs: + packageType: sdk + version: ${{ parameters.DotNetSdkVersion }} + + - task: DotNetCoreCLI@2 + displayName: Run .NET Core CLI tests + inputs: + command: "test" + projects: ${{ parameters.TestProjects }} + arguments: "--configuration Release --collect:\"XPlat Code Coverage\" --settings tests/coverletArgs.runsettings --verbosity minimal \"-p:GenerateDocumentationFile=False\"" + publishTestResults: true + testRunTitle: $(Agent.JobName) + workingDirectory: "$(Build.SourcesDirectory)" + + + - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4 + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging + displayName: ReportGenerator (merge) + inputs: + reports: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' + targetdir: '$(Agent.TempDirectory)/merged/' + reporttypes: 'Cobertura' + + ## V2 is already in the repository but it does not work "wrong number of segments" YAML error. + - task: PublishCodeCoverageResults@1 + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging + displayName: Publish Code Coverage + inputs: + codeCoverageTool: 'cobertura' + #summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2 + summaryFileLocation: '$(Agent.TempDirectory)/merged/**.xml' + pathToSources: $(Build.SourcesDirectory) # Optional + #reportDirectory: # Optional + #additionalCodeCoverageFiles: # Optional + failIfCoverageEmpty: true # Optional diff --git a/.ci/azure-pipelines-windows.yml b/.ci/azure-pipelines-windows.yml new file mode 100644 index 0000000000..c6ff3736a4 --- /dev/null +++ b/.ci/azure-pipelines-windows.yml @@ -0,0 +1,82 @@ +parameters: + WindowsImage: "windows-latest" + TestProjects: "tests/**/*Tests.csproj" + DotNetSdkVersion: 3.1.100 + +jobs: + - job: PublishWindows + displayName: Publish Windows + pool: + vmImage: ${{ parameters.WindowsImage }} + steps: + - checkout: self + clean: true + submodules: true + persistCredentials: true + + - task: CmdLine@2 + displayName: "Clone Web Client (Master, Release, or Tag)" + condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + inputs: + script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" + + - task: CmdLine@2 + displayName: "Clone Web Client (PR)" + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest')) + inputs: + script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" + + - task: NodeTool@0 + displayName: "Install Node" + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + inputs: + versionSpec: "10.x" + + - task: CmdLine@2 + displayName: "Build Web Client" + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + inputs: + script: yarn install + workingDirectory: $(Agent.TempDirectory)/jellyfin-web + + - task: CopyFiles@2 + displayName: "Copy Web Client" + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + inputs: + sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional + contents: "**" + targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web + cleanTargetFolder: true # Optional + overWrite: true # Optional + flattenFolders: false # Optional + + - task: CmdLine@2 + displayName: "Clone UX Repository" + inputs: + script: git clone --depth=1 https://github.com/jellyfin/jellyfin-ux $(Agent.TempDirectory)\jellyfin-ux + + - task: PowerShell@2 + displayName: "Build NSIS Installer" + inputs: + targetType: "filePath" # Optional. Options: filePath, inline + filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath + arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory) + errorActionPreference: "stop" # Optional. Options: stop, continue, silentlyContinue + workingDirectory: $(Build.SourcesDirectory) # Optional + + - task: CopyFiles@2 + displayName: "Copy NSIS Installer" + inputs: + sourceFolder: $(Build.SourcesDirectory)/deployment/windows/ # Optional + contents: "jellyfin*.exe" + targetFolder: $(System.ArtifactsDirectory)/setup + cleanTargetFolder: true # Optional + overWrite: true # Optional + flattenFolders: true # Optional + + - task: PublishPipelineArtifact@0 + displayName: "Publish Artifact Setup" + condition: succeeded() + inputs: + targetPath: "$(build.artifactstagingdirectory)/setup" + artifactName: "Jellyfin Server Setup" \ No newline at end of file diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 7bcaed70cd..221db162ac 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -2,9 +2,11 @@ name: $(Date:yyyyMMdd)$(Rev:.r) variables: - name: TestProjects - value: 'tests/**/*Tests.csproj' + value: "tests/**/*Tests.csproj" - name: RestoreBuildProjects - value: 'Jellyfin.Server/Jellyfin.Server.csproj' + value: "Jellyfin.Server/Jellyfin.Server.csproj" + - name: DotNetSdkVersion + value: 3.1.100 pr: autoCancel: true @@ -13,234 +15,26 @@ trigger: batch: true jobs: - - job: main_build - displayName: Main Build - pool: - vmImage: ubuntu-latest - strategy: - matrix: - Release: - BuildConfiguration: Release - Debug: - BuildConfiguration: Debug - maxParallel: 2 - steps: - - checkout: self - clean: true - submodules: true - persistCredentials: true + - template: azure-pipelines-main.yml + parameters: + LinuxImage: 'ubuntu-latest' + RestoreBuildProjects: $(RestoreBuildProjects) - - task: CmdLine@2 - displayName: "Clone Web Client (Master, Release, or Tag)" - condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' + - template: azure-pipelines-test.yml + parameters: + ImageNames: + Linux: 'ubuntu-latest' + Windows: 'windows-latest' + macOS: 'macos-latest' - - task: CmdLine@2 - displayName: "Clone Web Client (PR)" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest')) - inputs: - script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' + - template: azure-pipelines-windows.yml + parameters: + WindowsImage: 'windows-latest' + TestProjects: $(TestProjects) - - task: NodeTool@0 - displayName: 'Install Node' - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - versionSpec: '10.x' - - - task: CmdLine@2 - displayName: "Build Web Client" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - script: yarn install - workingDirectory: $(Agent.TempDirectory)/jellyfin-web - - - task: CopyFiles@2 - displayName: 'Copy Web Client' - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional - contents: '**' - targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: false # Optional - - - task: UseDotNet@2 - displayName: 'Update DotNet' - inputs: - packageType: sdk - version: 3.1.100 - - - task: DotNetCoreCLI@2 - displayName: 'Publish Server' - inputs: - command: publish - publishWebProjects: false - projects: '$(RestoreBuildProjects)' - arguments: '--configuration $(BuildConfiguration) --output $(build.artifactstagingdirectory)' - zipAfterPublish: false - - - task: PublishPipelineArtifact@0 - displayName: 'Publish Artifact Naming' - condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) - inputs: - targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll' - artifactName: 'Jellyfin.Naming' - - - task: PublishPipelineArtifact@0 - displayName: 'Publish Artifact Controller' - condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) - inputs: - targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll' - artifactName: 'Jellyfin.Controller' - - - task: PublishPipelineArtifact@0 - displayName: 'Publish Artifact Model' - condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) - inputs: - targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll' - artifactName: 'Jellyfin.Model' - - - task: PublishPipelineArtifact@0 - displayName: 'Publish Artifact Common' - condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) - inputs: - targetPath: '$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll' - artifactName: 'Jellyfin.Common' - - - job: main_test - displayName: Main Test - pool: - vmImage: windows-latest - steps: - - checkout: self - clean: true - submodules: true - persistCredentials: false - - - task: DotNetCoreCLI@2 - displayName: Build - inputs: - command: build - publishWebProjects: false - projects: '$(TestProjects)' - arguments: '--configuration $(BuildConfiguration)' - zipAfterPublish: false - - - task: VisualStudioTestPlatformInstaller@1 - inputs: - packageFeedSelector: 'nugetOrg' # Options: nugetOrg, customFeed, netShare - versionSelector: 'latestPreRelease' # Required when packageFeedSelector == NugetOrg || PackageFeedSelector == CustomFeed# Options: latestPreRelease, latestStable, specificVersion - - task: VSTest@2 - inputs: - testSelector: 'testAssemblies' # Options: testAssemblies, testPlan, testRun - testAssemblyVer2: | # Required when testSelector == TestAssemblies - **\bin\$(BuildConfiguration)\**\*tests.dll - **\bin\$(BuildConfiguration)\**\*test.dll - !**\obj\** - !**\xunit.runner.visualstudio.testadapter.dll - !**\xunit.runner.visualstudio.dotnetcore.testadapter.dll - searchFolder: '$(System.DefaultWorkingDirectory)' - runInParallel: True # Optional - runTestsInIsolation: True # Optional - codeCoverageEnabled: True # Optional - configuration: 'Debug' # Optional - publishRunAttachments: true # Optional - testRunTitle: $(Agent.JobName) - otherConsoleOptions: '/platform:x64 /Framework:.NETCoreApp,Version=v3.1 /logger:console;verbosity="normal"' - - - job: main_build_win - displayName: Publish Windows - pool: - vmImage: windows-latest - strategy: - matrix: - Release: - BuildConfiguration: Release - maxParallel: 2 - steps: - - checkout: self - clean: true - submodules: true - persistCredentials: true - - - task: CmdLine@2 - displayName: "Clone Web Client (Master, Release, or Tag)" - condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' - - - task: CmdLine@2 - displayName: "Clone Web Client (PR)" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest')) - inputs: - script: 'git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' - - - task: NodeTool@0 - displayName: 'Install Node' - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - versionSpec: '10.x' - - - task: CmdLine@2 - displayName: "Build Web Client" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - script: yarn install - workingDirectory: $(Agent.TempDirectory)/jellyfin-web - - - task: CopyFiles@2 - displayName: 'Copy Web Client' - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) - inputs: - sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional - contents: '**' - targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: false # Optional - - - task: CmdLine@2 - displayName: 'Clone UX Repository' - inputs: - script: git clone --depth=1 https://github.com/jellyfin/jellyfin-ux $(Agent.TempDirectory)\jellyfin-ux - - - task: PowerShell@2 - displayName: 'Build NSIS Installer' - inputs: - targetType: 'filePath' # Optional. Options: filePath, inline - filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath - arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory) - errorActionPreference: 'stop' # Optional. Options: stop, continue, silentlyContinue - workingDirectory: $(Build.SourcesDirectory) # Optional - - - task: CopyFiles@2 - displayName: 'Copy NSIS Installer' - inputs: - sourceFolder: $(Build.SourcesDirectory)/deployment/windows/ # Optional - contents: 'jellyfin*.exe' - targetFolder: $(System.ArtifactsDirectory)/setup - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: true # Optional - - - task: PublishPipelineArtifact@0 - displayName: 'Publish Artifact Setup' - condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) - inputs: - targetPath: '$(build.artifactstagingdirectory)/setup' - artifactName: 'Jellyfin Server Setup' - - - job: dotnet_compat - displayName: Compatibility Check - pool: - vmImage: ubuntu-latest - dependsOn: main_build - # only execute for pull requests - condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber']) - strategy: - matrix: + - template: azure-pipelines-compat.yml + parameters: + Packages: Naming: NugetPackageName: Jellyfin.Naming AssemblyFileName: Emby.Naming.dll @@ -253,74 +47,4 @@ jobs: Common: NugetPackageName: Jellyfin.Common AssemblyFileName: MediaBrowser.Common.dll - maxParallel: 2 - steps: - - checkout: none - - - task: UseDotNet@2 - displayName: 'Update DotNet' - inputs: - packageType: sdk - version: 3.1.100 - - - task: DownloadPipelineArtifact@2 - displayName: 'Download New Assembly Build Artifact' - inputs: - source: 'current' # Options: current, specific - artifact: '$(NugetPackageName)' # Optional - path: '$(System.ArtifactsDirectory)/new-artifacts' - runVersion: 'latest' # Required when source == Specific. Options: latest, latestFromBranch, specific - - - task: CopyFiles@2 - displayName: 'Copy New Assembly Build Artifact' - inputs: - sourceFolder: $(System.ArtifactsDirectory)/new-artifacts # Optional - contents: '**/*.dll' - targetFolder: $(System.ArtifactsDirectory)/new-release - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: true # Optional - - - task: DownloadPipelineArtifact@2 - displayName: 'Download Reference Assembly Build Artifact' - inputs: - source: 'specific' # Options: current, specific - artifact: '$(NugetPackageName)' # Optional - path: '$(System.ArtifactsDirectory)/current-artifacts' - project: '$(System.TeamProjectId)' # Required when source == Specific - pipeline: '$(System.DefinitionId)' # Required when source == Specific - runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific - runBranch: 'refs/heads/$(System.PullRequest.TargetBranch)' # Required when source == Specific && runVersion == LatestFromBranch - - - task: CopyFiles@2 - displayName: 'Copy Reference Assembly Build Artifact' - inputs: - sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional - contents: '**/*.dll' - targetFolder: $(System.ArtifactsDirectory)/current-release - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: true # Optional - - - task: DownloadGitHubRelease@0 - displayName: 'Download ABI Compatibility Check Tool' - inputs: - connection: Jellyfin Release Download - userRepository: EraYaN/dotnet-compatibility - defaultVersionType: 'latest' # Options: latest, specificVersion, specificTag - itemPattern: '**-ci.zip' # Optional - downloadPath: '$(System.ArtifactsDirectory)' - - - task: ExtractFiles@1 - displayName: 'Extract ABI Compatibility Check Tool' - inputs: - archiveFilePatterns: '$(System.ArtifactsDirectory)/*-ci.zip' - destinationFolder: $(System.ArtifactsDirectory)/tools - cleanDestinationFolder: true - - # The `--warnings-only` switch will swallow the return code and not emit any errors. - - task: CmdLine@2 - displayName: 'Execute ABI Compatibility Check Tool' - inputs: - script: 'dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only' - workingDirectory: $(System.ArtifactsDirectory) # Optional + LinuxImage: 'ubuntu-latest' \ No newline at end of file diff --git a/.ci/publish-nightly.yml b/.ci/publish-nightly.yml deleted file mode 100644 index a693e10f6c..0000000000 --- a/.ci/publish-nightly.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Nightly-$(date:yyyyMMdd).$(rev:r) - -variables: - - name: Version - value: '1.0.0' - -trigger: none -pr: none - -jobs: - - job: publish_artifacts_nightly - displayName: Publish Artifacts Nightly - pool: - vmImage: ubuntu-latest - steps: - - checkout: none - - task: DownloadPipelineArtifact@2 - displayName: Download the Windows Setup Artifact - inputs: - source: 'specific' # Options: current, specific - artifact: 'Jellyfin Server Setup' # Optional - path: '$(System.ArtifactsDirectory)/win-installer' - project: '$(System.TeamProjectId)' # Required when source == Specific - pipelineId: 1 # Required when source == Specific - runVersion: 'latestFromBranch' # Required when source == Specific. Options: latest, latestFromBranch, specific - runBranch: 'refs/heads/master' # Required when source == Specific && runVersion == LatestFromBranch - - - task: SSH@0 - displayName: 'Create Drop directory' - inputs: - sshEndpoint: 'Jellyfin Build Server' - commands: 'mkdir -p /srv/incoming/jellyfin_$(Version)/win-installer && ln -s /srv/incoming/jellyfin_$(Version) /srv/incoming/jellyfin_nightly_azure_upload' - - - task: CopyFilesOverSSH@0 - displayName: 'Copy the Windows Setup to the Repo' - inputs: - sshEndpoint: 'Jellyfin Build Server' - sourceFolder: '$(System.ArtifactsDirectory)/win-installer' - contents: 'jellyfin_*.exe' - targetFolder: '/srv/incoming/jellyfin_nightly_azure_upload/win-installer' - - - task: SSH@0 - displayName: 'Clean up SCP symlink' - inputs: - sshEndpoint: 'Jellyfin Build Server' - commands: 'rm -f /srv/incoming/jellyfin_nightly_azure_upload' diff --git a/.ci/publish-release.yml b/.ci/publish-release.yml deleted file mode 100644 index 57e77ae5aa..0000000000 --- a/.ci/publish-release.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Release-$(Version)-$(date:yyyyMMdd).$(rev:r) - -variables: - - name: Version - value: '1.0.0' - - name: UsedRunId - value: 0 - -trigger: none -pr: none - -jobs: - - job: publish_artifacts_release - displayName: Publish Artifacts Release - pool: - vmImage: ubuntu-latest - steps: - - checkout: none - - task: DownloadPipelineArtifact@2 - displayName: Download the Windows Setup Artifact - inputs: - source: 'specific' # Options: current, specific - artifact: 'Jellyfin Server Setup' # Optional - path: '$(System.ArtifactsDirectory)/win-installer' - project: '$(System.TeamProjectId)' # Required when source == Specific - pipelineId: 1 # Required when source == Specific - runVersion: 'specific' # Required when source == Specific. Options: latest, latestFromBranch, specific - runId: $(UsedRunId) - - - task: SSH@0 - displayName: 'Create Drop directory' - inputs: - sshEndpoint: 'Jellyfin Build Server' - commands: 'mkdir -p /srv/incoming/jellyfin_$(Version)/win-installer && ln -s /srv/incoming/jellyfin_$(Version) /srv/incoming/jellyfin_release_azure_upload' - - - task: CopyFilesOverSSH@0 - displayName: 'Copy the Windows Setup to the Repo' - inputs: - sshEndpoint: 'Jellyfin Build Server' - sourceFolder: '$(System.ArtifactsDirectory)/win-installer' - contents: 'jellyfin_*.exe' - targetFolder: '/srv/incoming/jellyfin_release_azure_upload/win-installer' - - - task: SSH@0 - displayName: 'Clean up SCP symlink' - inputs: - sshEndpoint: 'Jellyfin Build Server' - commands: 'rm -f /srv/incoming/jellyfin_release_azure_upload' diff --git a/tests/coverletArgs.runsettings b/tests/coverletArgs.runsettings new file mode 100644 index 0000000000..3113957e0a --- /dev/null +++ b/tests/coverletArgs.runsettings @@ -0,0 +1,17 @@ + + + + + + + cobertura + [coverlet.*.tests?]*,[*]Coverlet.Core*,[*]Moq* + Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute + false + true + false + + + + + \ No newline at end of file From 22f408201a1ba827c7dd92f1d10b6f75656f0b13 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Wed, 15 Jan 2020 11:20:01 +0100 Subject: [PATCH 186/252] Less output and lesser compression. --- deployment/windows/jellyfin.nsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi index 5666d30f06..d3b75482cc 100644 --- a/deployment/windows/jellyfin.nsi +++ b/deployment/windows/jellyfin.nsi @@ -1,7 +1,7 @@ ; Shows a lot of debug information while compiling ; This can be removed once stable. -!verbose 4 -SetCompressor lzma +!verbose 2 +SetCompressor bzip2 ShowInstDetails show ShowUninstDetails show ;-------------------------------- From d3bd22d7a27949d1053c8be75d160adadf3c767b Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Wed, 15 Jan 2020 11:36:22 +0100 Subject: [PATCH 187/252] Enable Unicode ANSI targets are deprecated --- deployment/windows/jellyfin.nsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi index d3b75482cc..dbce29e84c 100644 --- a/deployment/windows/jellyfin.nsi +++ b/deployment/windows/jellyfin.nsi @@ -4,6 +4,8 @@ SetCompressor bzip2 ShowInstDetails show ShowUninstDetails show +Unicode True + ;-------------------------------- !define SF_USELECTED 0 ; used to check selected options status, rest are inherited from Sections.nsh From 9aa870cf246916abe985aba1da4a63daf4137b92 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Wed, 15 Jan 2020 11:36:54 +0100 Subject: [PATCH 188/252] Enable /SOLID switch for compression --- deployment/windows/jellyfin.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi index dbce29e84c..5629fcf12e 100644 --- a/deployment/windows/jellyfin.nsi +++ b/deployment/windows/jellyfin.nsi @@ -1,7 +1,7 @@ ; Shows a lot of debug information while compiling ; This can be removed once stable. !verbose 2 -SetCompressor bzip2 +SetCompressor bzip2 /SOLID ShowInstDetails show ShowUninstDetails show Unicode True From 1ad6f016172cb094b0ead5ff3517568d027708e7 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Wed, 15 Jan 2020 11:37:37 +0100 Subject: [PATCH 189/252] Switch to slightly more verbose logging. --- deployment/windows/jellyfin.nsi | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi index 5629fcf12e..3e8d687e79 100644 --- a/deployment/windows/jellyfin.nsi +++ b/deployment/windows/jellyfin.nsi @@ -1,6 +1,4 @@ -; Shows a lot of debug information while compiling -; This can be removed once stable. -!verbose 2 +!verbose 3 SetCompressor bzip2 /SOLID ShowInstDetails show ShowUninstDetails show From 67cae3730897318233179bd32945125d676e74a8 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Wed, 15 Jan 2020 11:27:38 +0100 Subject: [PATCH 190/252] Formatting and comment removal. --- .ci/azure-pipelines-compat.yml | 44 ++++++++++++++++----------------- .ci/azure-pipelines-main.yml | 10 ++++---- .ci/azure-pipelines-test.yml | 21 +++++++--------- .ci/azure-pipelines-windows.yml | 26 +++++++++---------- .ci/azure-pipelines.yml | 16 ++++++------ 5 files changed, 57 insertions(+), 60 deletions(-) diff --git a/.ci/azure-pipelines-compat.yml b/.ci/azure-pipelines-compat.yml index ac23e0b6db..6af956e1a7 100644 --- a/.ci/azure-pipelines-compat.yml +++ b/.ci/azure-pipelines-compat.yml @@ -21,8 +21,8 @@ jobs: ${{ each Package in parameters.Packages }}: ${{ Package.key }}: NugetPackageName: ${{ Package.value.NugetPackageName }} - AssemblyFileName: ${{ Package.value.AssemblyFileName }} - maxParallel: 2 + AssemblyFileName: ${{ Package.value.AssemblyFileName }} + maxParallel: 2 dependsOn: MainBuild steps: - checkout: none @@ -36,49 +36,49 @@ jobs: - task: DownloadPipelineArtifact@2 displayName: "Download New Assembly Build Artifact" inputs: - source: "current" # Options: current, specific - artifact: "$(NugetPackageName)" # Optional + source: "current" + artifact: "$(NugetPackageName)" path: "$(System.ArtifactsDirectory)/new-artifacts" - runVersion: "latest" # Required when source == Specific. Options: latest, latestFromBranch, specific + runVersion: "latest" - task: CopyFiles@2 displayName: "Copy New Assembly Build Artifact" inputs: - sourceFolder: $(System.ArtifactsDirectory)/new-artifacts # Optional + sourceFolder: $(System.ArtifactsDirectory)/new-artifacts contents: "**/*.dll" targetFolder: $(System.ArtifactsDirectory)/new-release - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: true # Optional + cleanTargetFolder: true + overWrite: true + flattenFolders: true - task: DownloadPipelineArtifact@2 displayName: "Download Reference Assembly Build Artifact" inputs: - source: "specific" # Options: current, specific - artifact: "$(NugetPackageName)" # Optional + source: "specific" + artifact: "$(NugetPackageName)" path: "$(System.ArtifactsDirectory)/current-artifacts" - project: "$(System.TeamProjectId)" # Required when source == Specific - pipeline: "$(System.DefinitionId)" # Required when source == Specific - runVersion: "latestFromBranch" # Required when source == Specific. Options: latest, latestFromBranch, specific - runBranch: "refs/heads/$(System.PullRequest.TargetBranch)" # Required when source == Specific && runVersion == LatestFromBranch + project: "$(System.TeamProjectId)" + pipeline: "$(System.DefinitionId)" + runVersion: "latestFromBranch" + runBranch: "refs/heads/$(System.PullRequest.TargetBranch)" - task: CopyFiles@2 displayName: "Copy Reference Assembly Build Artifact" inputs: - sourceFolder: $(System.ArtifactsDirectory)/current-artifacts # Optional + sourceFolder: $(System.ArtifactsDirectory)/current-artifacts contents: "**/*.dll" targetFolder: $(System.ArtifactsDirectory)/current-release - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: true # Optional + cleanTargetFolder: true + overWrite: true + flattenFolders: true - task: DownloadGitHubRelease@0 displayName: "Download ABI Compatibility Check Tool" inputs: connection: Jellyfin Release Download userRepository: EraYaN/dotnet-compatibility - defaultVersionType: "latest" # Options: latest, specificVersion, specificTag - itemPattern: "**-ci.zip" # Optional + defaultVersionType: "latest" + itemPattern: "**-ci.zip" downloadPath: "$(System.ArtifactsDirectory)" - task: ExtractFiles@1 @@ -93,4 +93,4 @@ jobs: displayName: "Execute ABI Compatibility Check Tool" inputs: script: "dotnet tools/CompatibilityCheckerCLI.dll current-release/$(AssemblyFileName) new-release/$(AssemblyFileName) --azure-pipelines --warnings-only" - workingDirectory: $(System.ArtifactsDirectory) # Optional \ No newline at end of file + workingDirectory: $(System.ArtifactsDirectory) diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml index fd263e20f9..9880bbfaa2 100644 --- a/.ci/azure-pipelines-main.yml +++ b/.ci/azure-pipelines-main.yml @@ -50,12 +50,12 @@ jobs: displayName: "Copy Web Client" condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: - sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional + sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist contents: "**" targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: false # Optional + cleanTargetFolder: true + overWrite: true + flattenFolders: false - task: UseDotNet@2 displayName: "Update DotNet" @@ -98,4 +98,4 @@ jobs: condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) inputs: targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll" - artifactName: "Jellyfin.Common" \ No newline at end of file + artifactName: "Jellyfin.Common" diff --git a/.ci/azure-pipelines-test.yml b/.ci/azure-pipelines-test.yml index eec02ff3be..4455632e15 100644 --- a/.ci/azure-pipelines-test.yml +++ b/.ci/azure-pipelines-test.yml @@ -1,7 +1,7 @@ parameters: - name: ImageNames type: object - default: + default: Linux: "ubuntu-latest" Windows: "windows-latest" macOS: "macos-latest" @@ -40,29 +40,26 @@ jobs: inputs: command: "test" projects: ${{ parameters.TestProjects }} - arguments: "--configuration Release --collect:\"XPlat Code Coverage\" --settings tests/coverletArgs.runsettings --verbosity minimal \"-p:GenerateDocumentationFile=False\"" + arguments: '--configuration Release --collect:"XPlat Code Coverage" --settings tests/coverletArgs.runsettings --verbosity minimal "-p:GenerateDocumentationFile=False"' publishTestResults: true testRunTitle: $(Agent.JobName) workingDirectory: "$(Build.SourcesDirectory)" - - task: Palmmedia.reportgenerator.reportgenerator-build-release-task.reportgenerator@4 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging displayName: ReportGenerator (merge) inputs: - reports: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' - targetdir: '$(Agent.TempDirectory)/merged/' - reporttypes: 'Cobertura' + reports: "$(Agent.TempDirectory)/**/coverage.cobertura.xml" + targetdir: "$(Agent.TempDirectory)/merged/" + reporttypes: "Cobertura" ## V2 is already in the repository but it does not work "wrong number of segments" YAML error. - task: PublishCodeCoverageResults@1 condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux')) # !! THIS is for V1 only V2 will/should support merging displayName: Publish Code Coverage inputs: - codeCoverageTool: 'cobertura' + codeCoverageTool: "cobertura" #summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml' # !!THIS IS FOR V2 - summaryFileLocation: '$(Agent.TempDirectory)/merged/**.xml' - pathToSources: $(Build.SourcesDirectory) # Optional - #reportDirectory: # Optional - #additionalCodeCoverageFiles: # Optional - failIfCoverageEmpty: true # Optional + summaryFileLocation: "$(Agent.TempDirectory)/merged/**.xml" + pathToSources: $(Build.SourcesDirectory) + failIfCoverageEmpty: true diff --git a/.ci/azure-pipelines-windows.yml b/.ci/azure-pipelines-windows.yml index c6ff3736a4..32d1d1382d 100644 --- a/.ci/azure-pipelines-windows.yml +++ b/.ci/azure-pipelines-windows.yml @@ -43,12 +43,12 @@ jobs: displayName: "Copy Web Client" condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: - sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist # Optional + sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist contents: "**" targetFolder: $(Build.SourcesDirectory)/MediaBrowser.WebDashboard/jellyfin-web - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: false # Optional + cleanTargetFolder: true + overWrite: true + flattenFolders: false - task: CmdLine@2 displayName: "Clone UX Repository" @@ -58,25 +58,25 @@ jobs: - task: PowerShell@2 displayName: "Build NSIS Installer" inputs: - targetType: "filePath" # Optional. Options: filePath, inline - filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath + targetType: "filePath" + filePath: ./deployment/windows/build-jellyfin.ps1 arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory) - errorActionPreference: "stop" # Optional. Options: stop, continue, silentlyContinue - workingDirectory: $(Build.SourcesDirectory) # Optional + errorActionPreference: "stop" + workingDirectory: $(Build.SourcesDirectory) - task: CopyFiles@2 displayName: "Copy NSIS Installer" inputs: - sourceFolder: $(Build.SourcesDirectory)/deployment/windows/ # Optional + sourceFolder: $(Build.SourcesDirectory)/deployment/windows/ contents: "jellyfin*.exe" targetFolder: $(System.ArtifactsDirectory)/setup - cleanTargetFolder: true # Optional - overWrite: true # Optional - flattenFolders: true # Optional + cleanTargetFolder: true + overWrite: true + flattenFolders: true - task: PublishPipelineArtifact@0 displayName: "Publish Artifact Setup" condition: succeeded() inputs: targetPath: "$(build.artifactstagingdirectory)/setup" - artifactName: "Jellyfin Server Setup" \ No newline at end of file + artifactName: "Jellyfin Server Setup" diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 221db162ac..f79a85b210 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -17,24 +17,24 @@ trigger: jobs: - template: azure-pipelines-main.yml parameters: - LinuxImage: 'ubuntu-latest' + LinuxImage: "ubuntu-latest" RestoreBuildProjects: $(RestoreBuildProjects) - template: azure-pipelines-test.yml parameters: - ImageNames: - Linux: 'ubuntu-latest' - Windows: 'windows-latest' - macOS: 'macos-latest' + ImageNames: + Linux: "ubuntu-latest" + Windows: "windows-latest" + macOS: "macos-latest" - template: azure-pipelines-windows.yml parameters: - WindowsImage: 'windows-latest' + WindowsImage: "windows-latest" TestProjects: $(TestProjects) - template: azure-pipelines-compat.yml parameters: - Packages: + Packages: Naming: NugetPackageName: Jellyfin.Naming AssemblyFileName: Emby.Naming.dll @@ -47,4 +47,4 @@ jobs: Common: NugetPackageName: Jellyfin.Common AssemblyFileName: MediaBrowser.Common.dll - LinuxImage: 'ubuntu-latest' \ No newline at end of file + LinuxImage: "ubuntu-latest" From 8868ff2ffa7d50d19779e5e8c1f661ece9fd0321 Mon Sep 17 00:00:00 2001 From: Nyanmisaka <799610810@qq.com> Date: Wed, 15 Jan 2020 18:40:58 +0800 Subject: [PATCH 191/252] remove useless comment --- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index a099087628..3b8cb93fcc 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2644,8 +2644,6 @@ namespace MediaBrowser.Controller.MediaEncoding } else { - //h264_amf is now supported on linux with 'amdgpu-pro' installed and '--enable-amf' when compiling ffmpeg - //using vaapi decode and h264_amf encode on linux platform can avoid some weird transcoding errors in h264_vaapi with amd gpu return "-hwaccel vaapi"; } } From 2c10891b66bf5963d2bfca34b704607a3bc0de85 Mon Sep 17 00:00:00 2001 From: Nyanmisaka <799610810@qq.com> Date: Wed, 15 Jan 2020 18:45:28 +0800 Subject: [PATCH 192/252] turn on indentation. --- MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 993cd3f747..f5decdc0db 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -56,8 +56,8 @@ namespace MediaBrowser.MediaEncoding.Encoder "hevc_vaapi", "h264_v4l2m2m", "ac3", - "h264_amf", - "hevc_amf" + "h264_amf", + "hevc_amf" }; // Try and use the individual library versions to determine a FFmpeg version From ec8baaf48d28526e888d2301c5eaf207455c3dfd Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Wed, 15 Jan 2020 12:17:42 +0100 Subject: [PATCH 193/252] Switch around SetCompressor arguments. Hide progress bars for powershell. --- deployment/windows/build-jellyfin.ps1 | 2 ++ deployment/windows/jellyfin.nsi | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1 index dde6eb8fcf..bb2f509192 100644 --- a/deployment/windows/build-jellyfin.ps1 +++ b/deployment/windows/build-jellyfin.ps1 @@ -15,6 +15,8 @@ param( [ValidateSet('x64','x86', 'arm', 'arm64')][string]$Architecture = 'x64' ) +$ProgressPreference = 'SilentlyContinue' # Speedup all downloads by hiding progress bars. + #PowershellCore and *nix check to make determine which temp dir to use. if(($PSVersionTable.PSEdition -eq 'Core') -and (-not $IsWindows)){ $TempDir = mktemp -d diff --git a/deployment/windows/jellyfin.nsi b/deployment/windows/jellyfin.nsi index 3e8d687e79..86724b8f49 100644 --- a/deployment/windows/jellyfin.nsi +++ b/deployment/windows/jellyfin.nsi @@ -1,5 +1,5 @@ !verbose 3 -SetCompressor bzip2 /SOLID +SetCompressor /SOLID bzip2 ShowInstDetails show ShowUninstDetails show Unicode True From 2cad58e5cd4c16dfe9e3ad2267d4f5734dc9d7a1 Mon Sep 17 00:00:00 2001 From: artiume Date: Wed, 15 Jan 2020 10:02:01 -0500 Subject: [PATCH 194/252] Update .github/ISSUE_TEMPLATE/media_playback.md Co-Authored-By: dkanada --- .github/ISSUE_TEMPLATE/media_playback.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/media_playback.md b/.github/ISSUE_TEMPLATE/media_playback.md index 97e3785910..b51500f870 100644 --- a/.github/ISSUE_TEMPLATE/media_playback.md +++ b/.github/ISSUE_TEMPLATE/media_playback.md @@ -11,7 +11,7 @@ assignees: '' **Logs** - + **FFmpeg Logs** From d4745957b3dc0b1cc5f5baac299883a89df4cfb6 Mon Sep 17 00:00:00 2001 From: nextlooper42 Date: Tue, 14 Jan 2020 10:29:23 +0000 Subject: [PATCH 195/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 9bac305a27..1988bda526 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}" } From 42213d94e5126a9966c5a555d4d9cce87451c481 Mon Sep 17 00:00:00 2001 From: 4d1m Date: Wed, 15 Jan 2020 06:58:17 +0000 Subject: [PATCH 196/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index b871626f0d..1a4756e3de 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", From 15c52867eae01487a3f32a94adad9042f17b9101 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 16 Jan 2020 16:32:04 +0800 Subject: [PATCH 197/252] New upstream ffmpeg version 4.2.1 on windows --- deployment/win-x64/docker-build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/win-x64/docker-build.sh b/deployment/win-x64/docker-build.sh index 3f1ad78b50..77372a66f7 100755 --- a/deployment/win-x64/docker-build.sh +++ b/deployment/win-x64/docker-build.sh @@ -8,7 +8,7 @@ set -o xtrace # Version variables NSSM_VERSION="nssm-2.24-101-g897c7ad" NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" -FFMPEG_VERSION="ffmpeg-4.0.2-win64-static" +FFMPEG_VERSION="ffmpeg-4.2.1-win64-static" FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win64/static/${FFMPEG_VERSION}.zip" # Move to source directory From ac0b30285e22eff5e9ab36c08fe8cd24a6c3ef4a Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 16 Jan 2020 16:32:07 +0800 Subject: [PATCH 198/252] New upstream ffmpeg version 4.2.1 on windows --- deployment/win-x86/docker-build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/win-x86/docker-build.sh b/deployment/win-x86/docker-build.sh index 7d79ba4958..dd411756a8 100755 --- a/deployment/win-x86/docker-build.sh +++ b/deployment/win-x86/docker-build.sh @@ -8,7 +8,7 @@ set -o xtrace # Version variables NSSM_VERSION="nssm-2.24-101-g897c7ad" NSSM_URL="http://files.evilt.win/nssm/${NSSM_VERSION}.zip" -FFMPEG_VERSION="ffmpeg-4.0.2-win32-static" +FFMPEG_VERSION="ffmpeg-4.2.1-win32-static" FFMPEG_URL="https://ffmpeg.zeranoe.com/builds/win32/static/${FFMPEG_VERSION}.zip" # Move to source directory From e92e105c420660f59ad3671852a4a57bb04b7c67 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 16 Jan 2020 17:57:19 +0800 Subject: [PATCH 199/252] New upstream ffmpeg version 4.2.1 on windows --- deployment/windows/build-jellyfin.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1 index dde6eb8fcf..0be832f373 100644 --- a/deployment/windows/build-jellyfin.ps1 +++ b/deployment/windows/build-jellyfin.ps1 @@ -44,7 +44,8 @@ function Build-JellyFin { function Install-FFMPEG { param( [string]$ResolvedInstallLocation, - [string]$Architecture + [string]$Architecture, + [string]$FFMPEGVersionX86 = "ffmpeg-4.2.1-win32-shared", ) Write-Verbose "Checking Architecture" if($Architecture -notin @('x86','x64')){ @@ -55,7 +56,7 @@ function Install-FFMPEG { Invoke-WebRequest -Uri https://repo.jellyfin.org/releases/server/windows/ffmpeg/jellyfin-ffmpeg.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose }else{ Write-Verbose "Downloading 32 bit FFMPEG" - Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.0.2-win32-shared.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose + Invoke-WebRequest -Uri https://ffmpeg.zeranoe.com/builds/win32/shared/$FFMPEGVersionX86.zip -UseBasicParsing -OutFile "$tempdir/ffmpeg.zip" | Write-Verbose } Expand-Archive "$tempdir/ffmpeg.zip" -DestinationPath "$tempdir/ffmpeg/" -Force | Write-Verbose @@ -66,7 +67,7 @@ function Install-FFMPEG { } }else{ Write-Verbose "Copying Binaries to Jellyfin location" - Get-ChildItem "$tempdir/ffmpeg/ffmpeg-4.0.2-win32-shared/bin" | ForEach-Object { + Get-ChildItem "$tempdir/ffmpeg/$FFMPEGVersionX86/bin" | ForEach-Object { Copy-Item $_.FullName -Destination $installLocation | Write-Verbose } } From 0a7ea36c68e8f7b013dd9148d1356c031bd53e1e Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 16 Jan 2020 18:10:14 +0800 Subject: [PATCH 200/252] Update build-jellyfin.ps1 --- deployment/windows/build-jellyfin.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1 index 0be832f373..9fd86e84d8 100644 --- a/deployment/windows/build-jellyfin.ps1 +++ b/deployment/windows/build-jellyfin.ps1 @@ -45,7 +45,7 @@ function Install-FFMPEG { param( [string]$ResolvedInstallLocation, [string]$Architecture, - [string]$FFMPEGVersionX86 = "ffmpeg-4.2.1-win32-shared", + [string]$FFMPEGVersionX86 = "ffmpeg-4.2.1-win32-shared" ) Write-Verbose "Checking Architecture" if($Architecture -notin @('x86','x64')){ From 2ce16d4bb5d98677ac972739983c39b67518038f Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Thu, 16 Jan 2020 18:47:54 +0800 Subject: [PATCH 201/252] fix indentation --- deployment/windows/build-jellyfin.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deployment/windows/build-jellyfin.ps1 b/deployment/windows/build-jellyfin.ps1 index 9fd86e84d8..7afc385eac 100644 --- a/deployment/windows/build-jellyfin.ps1 +++ b/deployment/windows/build-jellyfin.ps1 @@ -44,8 +44,8 @@ function Build-JellyFin { function Install-FFMPEG { param( [string]$ResolvedInstallLocation, - [string]$Architecture, - [string]$FFMPEGVersionX86 = "ffmpeg-4.2.1-win32-shared" + [string]$Architecture, + [string]$FFMPEGVersionX86 = "ffmpeg-4.2.1-win32-shared" ) Write-Verbose "Checking Architecture" if($Architecture -notin @('x86','x64')){ From 96c9af590494aa8137d5a061aaf1e68feee60b67 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 16 Jan 2020 13:20:01 +0100 Subject: [PATCH 202/252] Add brackets around thread id --- Jellyfin.Server/Program.cs | 4 ++-- Jellyfin.Server/Resources/Configuration/logging.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 8f44327971..fa9b626744 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -481,11 +481,11 @@ namespace Jellyfin.Server catch (Exception ex) { Serilog.Log.Logger = new LoggerConfiguration() - .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] {ThreadId} {SourceContext}: {Message:lj} {NewLine}{Exception}") + .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}") .WriteTo.Async(x => x.File( Path.Combine(appPaths.LogDirectoryPath, "log_.log"), rollingInterval: RollingInterval.Day, - outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {ThreadId} {SourceContext}:{Message} {NewLine}{Exception}")) + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}")) .Enrich.FromLogContext() .Enrich.WithThreadId() .CreateLogger(); diff --git a/Jellyfin.Server/Resources/Configuration/logging.json b/Jellyfin.Server/Resources/Configuration/logging.json index 6bc2ed2ee4..e9f71afd49 100644 --- a/Jellyfin.Server/Resources/Configuration/logging.json +++ b/Jellyfin.Server/Resources/Configuration/logging.json @@ -5,7 +5,7 @@ { "Name": "Console", "Args": { - "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] {ThreadId} {SourceContext}: {Message:lj} {NewLine}{Exception}" + "outputTemplate": "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}" } }, { @@ -20,7 +20,7 @@ "retainedFileCountLimit": 3, "rollOnFileSizeLimit": true, "fileSizeLimitBytes": 100000000, - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {ThreadId} {SourceContext}:{Message} {NewLine}{Exception}" + "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}" } } ] From e882b03e817715bd14223cb2b9495e983ff5ac2f Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 16 Jan 2020 13:30:38 +0100 Subject: [PATCH 203/252] Add back support for DVDs copied as folders --- .../Encoder/MediaEncoder.cs | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index e0f7b992ce..deab0715cf 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -849,14 +849,89 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + /// public Task ConvertImage(string inputPath, string outputPath) { throw new NotImplementedException(); } + /// public IEnumerable GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber) { - throw new NotImplementedException(); + // min size 300 mb + const long MinPlayableSize = 314572800; + + var root = isoMount != null ? isoMount.MountedPath : path; + + // Try to eliminate menus and intros by skipping all files at the front of the list that are less than the minimum size + // Once we reach a file that is at least the minimum, return all subsequent ones + var allVobs = _fileSystem.GetFiles(root, true) + .Where(file => string.Equals(file.Extension, ".vob", StringComparison.OrdinalIgnoreCase)) + .OrderBy(i => i.FullName) + .ToList(); + + // If we didn't find any satisfying the min length, just take them all + if (allVobs.Count == 0) + { + _logger.LogWarning("No vobs found in dvd structure."); + return Enumerable.Empty(); + } + + if (titleNumber.HasValue) + { + var prefix = string.Format("VTS_0{0}_", titleNumber.Value.ToString(CultureInfo.InvariantCulture)); + var vobs = allVobs.Where(i => i.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList(); + + if (vobs.Count > 0) + { + var minSizeVobs = vobs + .SkipWhile(f => f.Length < MinPlayableSize) + .ToList(); + + return minSizeVobs.Count == 0 ? vobs.Select(i => i.FullName) : minSizeVobs.Select(i => i.FullName); + } + + _logger.LogWarning("Could not determine vob file list for {0} using DvdLib. Will scan using file sizes.", video.Path); + } + + var files = allVobs + .SkipWhile(f => f.Length < MinPlayableSize) + .ToList(); + + // If we didn't find any satisfying the min length, just take them all + if (files.Count == 0) + { + _logger.LogWarning("Vob size filter resulted in zero matches. Taking all vobs."); + files = allVobs; + } + + // Assuming they're named "vts_05_01", take all files whose second part matches that of the first file + if (files.Count > 0) + { + var parts = _fileSystem.GetFileNameWithoutExtension(files[0]).Split('_'); + + if (parts.Length == 3) + { + var title = parts[1]; + + files = files.TakeWhile(f => + { + var fileParts = _fileSystem.GetFileNameWithoutExtension(f).Split('_'); + + return fileParts.Length == 3 && string.Equals(title, fileParts[1], StringComparison.OrdinalIgnoreCase); + + }).ToList(); + + // If this resulted in not getting any vobs, just take them all + if (files.Count == 0) + { + _logger.LogWarning("Vob filename filter resulted in zero matches. Taking all vobs."); + files = allVobs; + } + } + } + + return files.Select(i => i.FullName); } public bool CanExtractSubtitles(string codec) From f3a1729964f8ff582aaa9f31eb415114340035ce Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 16 Jan 2020 15:02:50 +0100 Subject: [PATCH 204/252] Address comments --- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index deab0715cf..4123f0203a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -879,7 +879,10 @@ namespace MediaBrowser.MediaEncoding.Encoder if (titleNumber.HasValue) { - var prefix = string.Format("VTS_0{0}_", titleNumber.Value.ToString(CultureInfo.InvariantCulture)); + var prefix = string.Format( + CultureInfo.InvariantCulture, + titleNumber.Value >= 10 ? "VTS_{0}_" : "VTS_0{0}_", + titleNumber.Value); var vobs = allVobs.Where(i => i.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList(); if (vobs.Count > 0) @@ -891,7 +894,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return minSizeVobs.Count == 0 ? vobs.Select(i => i.FullName) : minSizeVobs.Select(i => i.FullName); } - _logger.LogWarning("Could not determine vob file list for {0} using DvdLib. Will scan using file sizes.", video.Path); + _logger.LogWarning("Could not determine vob file list for {0} using DvdLib. Will scan using file sizes.", path); } var files = allVobs From dcd6ceb9a2a971ec10fe22c94880d643910a21ab Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Thu, 16 Jan 2020 09:31:20 -0500 Subject: [PATCH 205/252] Update SQLitePCL to new version --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 03cbe00b7a..77333a03d0 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -35,7 +35,7 @@ - + From 47d3337124ca83c72133b28b0a745971c797f3ae Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 16 Jan 2020 23:22:42 +0100 Subject: [PATCH 206/252] Update StringHelper.cs --- MediaBrowser.Model/Extensions/StringHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs index 9ce1cf79ff..a4ba4b0693 100644 --- a/MediaBrowser.Model/Extensions/StringHelper.cs +++ b/MediaBrowser.Model/Extensions/StringHelper.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Model.Extensions public static class StringHelper { /// - /// Returns the string with the first character inheritdoc uppercase. + /// Returns the string with the first character as uppercase. /// /// The input string. /// The string with the first character inheritdoc uppercase. From 686d1ff9fb68fbb4b05022bdd141d17f2f720515 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Thu, 16 Jan 2020 23:23:06 +0100 Subject: [PATCH 207/252] Update StringHelper.cs --- MediaBrowser.Model/Extensions/StringHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs index a4ba4b0693..5864120544 100644 --- a/MediaBrowser.Model/Extensions/StringHelper.cs +++ b/MediaBrowser.Model/Extensions/StringHelper.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Model.Extensions /// Returns the string with the first character as uppercase. /// /// The input string. - /// The string with the first character inheritdoc uppercase. + /// The string with the first character as uppercase. public static string FirstToUpper(string str) { if (string.IsNullOrEmpty(str)) From 73a47e934d1699c5969167c0b614216f108defd3 Mon Sep 17 00:00:00 2001 From: artiume Date: Fri, 17 Jan 2020 00:35:41 -0500 Subject: [PATCH 208/252] Fix Docker Arm Nightly (#2286) * Fix Docker Arm Nightly Updated image base. There was an error for libssl missing. I attempted to add libssl1.0.0 but it wasn't found so I fell back to libssl-dev. Much more work needs to be done. ffmpeg is still debian based. I attempted to add jellyfin-ffmpeg but I was having too many issues so I'm saving it for another day, but it at least builds out successfully. * Fix docker arm64 nightly --- Dockerfile.arm | 3 ++- Dockerfile.arm64 | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile.arm b/Dockerfile.arm index 4d8fbb77d1..0e62366288 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -24,10 +24,11 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" FROM multiarch/qemu-user-static:x86_64-arm as qemu -FROM debian:stretch-slim-arm32v7 +FROM debian:stretch-slim COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ + libssl-dev \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index d41268f13e..796d12f980 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -24,10 +24,11 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu -FROM debian:stretch-slim-arm64v8 +FROM debian:stretch-slim COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ + libssl-dev \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media From 8e71046a3c7b2e39bbdbfda8518730df21120ef8 Mon Sep 17 00:00:00 2001 From: 4d1m Date: Thu, 16 Jan 2020 08:34:40 +0000 Subject: [PATCH 209/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index 1a4756e3de..71bffffc6b 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", From 549a2d8b6d51d9ea8c3a319e3ca37da8ea4c088c Mon Sep 17 00:00:00 2001 From: Denis Date: Fri, 17 Jan 2020 22:48:28 +0300 Subject: [PATCH 210/252] Enable path mapping in `PlaybackInfo` endpoint --- MediaBrowser.Api/Playback/MediaInfoService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 10f10a15ec..15880a9a16 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -275,7 +275,7 @@ namespace MediaBrowser.Api.Playback { // TODO handle supportedLiveMediaTypes? - var mediaSourcesList = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, false, CancellationToken.None).ConfigureAwait(false); + var mediaSourcesList = await _mediaSourceManager.GetPlaybackMediaSources(item, user, true, true, CancellationToken.None).ConfigureAwait(false); if (string.IsNullOrWhiteSpace(mediaSourceId)) { From 2610f377c0a15788badffd0720175109117af1ed Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 18 Jan 2020 00:09:25 +0100 Subject: [PATCH 211/252] Kestrel doesn't like sync IO operations --- Emby.Dlna/Service/BaseControlHandler.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 067d5fa43f..79f03fed9f 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -63,7 +63,8 @@ namespace Emby.Dlna.Service ValidationType = ValidationType.None, CheckCharacters = false, IgnoreProcessingInstructions = true, - IgnoreComments = true + IgnoreComments = true, + Async = true }; using (var reader = XmlReader.Create(streamReader, readerSettings)) @@ -79,7 +80,8 @@ namespace Emby.Dlna.Service var settings = new XmlWriterSettings { Encoding = Encoding.UTF8, - CloseOutput = false + CloseOutput = false, + Async = true }; StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8); From e700fc8a076769d8f2bc29dac232a2bf8faaa0cf Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 19 Jan 2020 00:18:55 +0900 Subject: [PATCH 212/252] fix and remove a few more tests --- Emby.Naming/Subtitles/SubtitleParser.cs | 3 +-- Emby.Naming/TV/EpisodePathParser.cs | 2 +- .../Music/MultiDiscAlbumTests.cs | 21 ++++++++++----- .../Subtitles/SubtitleParserTests.cs | 1 - .../TV/EpisodeNumberTests.cs | 27 +++---------------- ...llyfin.Server.Implementations.Tests.csproj | 2 -- 6 files changed, 21 insertions(+), 35 deletions(-) diff --git a/Emby.Naming/Subtitles/SubtitleParser.cs b/Emby.Naming/Subtitles/SubtitleParser.cs index 99680c6221..b055b1a6c2 100644 --- a/Emby.Naming/Subtitles/SubtitleParser.cs +++ b/Emby.Naming/Subtitles/SubtitleParser.cs @@ -31,7 +31,6 @@ namespace Emby.Naming.Subtitles } var flags = GetFlags(path); - var info = new SubtitleInfo { Path = path, @@ -45,7 +44,7 @@ namespace Emby.Naming.Subtitles // Should have a name, language and file extension if (parts.Count >= 3) { - info.Language = parts[parts.Count - 2]; + info.Language = parts[^2]; } return info; diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs index 4fac543f92..6b557d2e19 100644 --- a/Emby.Naming/TV/EpisodePathParser.cs +++ b/Emby.Naming/TV/EpisodePathParser.cs @@ -131,7 +131,7 @@ namespace Emby.Naming.TV var endingNumberGroup = match.Groups["endingepnumber"]; if (endingNumberGroup.Success) { - // Will only set EndingEpsiodeNumber if the captured number is not followed by additional numbers + // Will only set EndingEpisodeNumber if the captured number is not followed by additional numbers // or a 'p' or 'i' as what you would get with a pixel resolution specification. // 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; diff --git a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs index eb69d915ca..a79e2cf614 100644 --- a/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs +++ b/tests/Jellyfin.Naming.Tests/Music/MultiDiscAlbumTests.cs @@ -10,18 +10,27 @@ namespace Jellyfin.Naming.Tests.Music 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.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")); + Assert.True(IsMultiDiscAlbumFolder(@"disc18")); + Assert.True(IsMultiDiscAlbumFolder(@"disk10")); + Assert.True(IsMultiDiscAlbumFolder(@"vol7")); + Assert.True(IsMultiDiscAlbumFolder(@"volume1")); - // Add a space Assert.True(IsMultiDiscAlbumFolder(@"cd 1")); Assert.True(IsMultiDiscAlbumFolder(@"disc 1")); Assert.True(IsMultiDiscAlbumFolder(@"disk 1")); + Assert.False(IsMultiDiscAlbumFolder(@"disk")); + Assert.False(IsMultiDiscAlbumFolder(@"disk ·")); + Assert.False(IsMultiDiscAlbumFolder(@"disk a")); + + Assert.False(IsMultiDiscAlbumFolder(@"disk volume")); + Assert.False(IsMultiDiscAlbumFolder(@"disc disc")); + Assert.False(IsMultiDiscAlbumFolder(@"disk disc 6")); + Assert.True(IsMultiDiscAlbumFolder(@"cd - 1")); Assert.True(IsMultiDiscAlbumFolder(@"disc- 1")); Assert.True(IsMultiDiscAlbumFolder(@"disk - 1")); @@ -38,7 +47,7 @@ namespace Jellyfin.Naming.Tests.Music [Fact] public void TestMultiDiscAlbums1() { - Assert.False(IsMultiDiscAlbumFolder(@"[1985] Oppurtunities (Let's make lots of money) (1985)")); + Assert.False(IsMultiDiscAlbumFolder(@"[1985] Opportunities (Let's make lots of money) (1985)")); } [Fact] diff --git a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs index e8f14cdc45..41da889c28 100644 --- a/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs +++ b/tests/Jellyfin.Naming.Tests/Subtitles/SubtitleParserTests.cs @@ -22,7 +22,6 @@ namespace Jellyfin.Naming.Tests.Subtitles 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); } diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index 1ae6372817..837b23455c 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -60,21 +60,6 @@ namespace Jellyfin.Naming.Tests.TV 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() { @@ -84,29 +69,25 @@ namespace Jellyfin.Naming.Tests.TV [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] + [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")); + Assert.NotEqual(16, GetEpisodeNumberFromFile(@"Season 2/Season 3 Episode 16.avi")); } - // [Fact] + [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")); + Assert.NotEqual(16, GetEpisodeNumberFromFile(@"Season 2/Season 3 Episode 16 - Some Title.avi")); } [Fact] diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index bb2afea168..f62d3dcbc5 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -2,9 +2,7 @@ netcoreapp3.1 - false - Jellyfin.Server.Implementations.Tests From a562a4d5053edd2943f22dfb45f7ad5bb0ae8cfa Mon Sep 17 00:00:00 2001 From: dkanada Date: Sun, 19 Jan 2020 00:19:19 +0900 Subject: [PATCH 213/252] minor linting for ci files --- .ci/azure-pipelines-compat.yml | 2 +- .ci/azure-pipelines-main.yml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.ci/azure-pipelines-compat.yml b/.ci/azure-pipelines-compat.yml index 6af956e1a7..762bbdcb2e 100644 --- a/.ci/azure-pipelines-compat.yml +++ b/.ci/azure-pipelines-compat.yml @@ -13,7 +13,7 @@ jobs: - job: CompatibilityCheck displayName: Compatibility Check pool: - vmImage: "${{ parameters.LinuxImage}}" + vmImage: "${{ parameters.LinuxImage }}" # only execute for pull requests condition: and(succeeded(), variables['System.PullRequest.PullRequestNumber']) strategy: diff --git a/.ci/azure-pipelines-main.yml b/.ci/azure-pipelines-main.yml index 9880bbfaa2..09901b2a6a 100644 --- a/.ci/azure-pipelines-main.yml +++ b/.ci/azure-pipelines-main.yml @@ -23,32 +23,32 @@ jobs: - task: CmdLine@2 displayName: "Clone Web Client (Master, Release, or Tag)" - condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: script: "git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" - task: CmdLine@2 displayName: "Clone Web Client (PR)" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest')) + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest')) inputs: script: "git clone --single-branch --branch $(System.PullRequest.TargetBranch) --depth 1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web" - task: NodeTool@0 displayName: "Install Node" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: versionSpec: "10.x" - task: CmdLine@2 displayName: "Build Web Client" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: script: yarn install workingDirectory: $(Agent.TempDirectory)/jellyfin-web - task: CopyFiles@2 displayName: "Copy Web Client" - condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + condition: and(succeeded(), or(contains(variables['System.PullRequest.TargetBranch'], 'release'), contains(variables['System.PullRequest.TargetBranch'], 'master'), contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')), eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: sourceFolder: $(Agent.TempDirectory)/jellyfin-web/dist contents: "**" @@ -74,28 +74,28 @@ jobs: - task: PublishPipelineArtifact@0 displayName: "Publish Artifact Naming" - condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) + condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release')) inputs: targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/Emby.Naming.dll" artifactName: "Jellyfin.Naming" - task: PublishPipelineArtifact@0 displayName: "Publish Artifact Controller" - condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) + condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release')) inputs: targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Controller.dll" artifactName: "Jellyfin.Controller" - task: PublishPipelineArtifact@0 displayName: "Publish Artifact Model" - condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) + condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release')) inputs: targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Model.dll" artifactName: "Jellyfin.Model" - task: PublishPipelineArtifact@0 displayName: "Publish Artifact Common" - condition: and(eq(variables['BuildConfiguration'], 'Release'), succeeded()) + condition: and(succeeded(), eq(variables['BuildConfiguration'], 'Release')) inputs: targetPath: "$(build.artifactstagingdirectory)/Jellyfin.Server/MediaBrowser.Common.dll" artifactName: "Jellyfin.Common" From f2eea89ff084111a4a90bfefdb6def68165e6025 Mon Sep 17 00:00:00 2001 From: Carey Metcalfe Date: Sun, 19 Jan 2020 00:51:02 -0500 Subject: [PATCH 214/252] Fix pagination for DLNA root folder list Previously, when responding to a DLNA "Browse" request, the `StartingIndex` was not respected and all of the root items were returned each time. This caused infinite loops with in DLNA clients that ignored the `TotalMatches` data in the response and just continued asking for the next page until they got an empty response. This fix makes the root folder list respect the `StartingIndex` and `RequestedCount` parameters like all other responses. Fixes issue #2303 --- CONTRIBUTORS.md | 1 + Emby.Dlna/ContentDirectory/ControlHandler.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 458944778e..800b3d51ff 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -32,6 +32,7 @@ - [nevado](https://github.com/nevado) - [mark-monteiro](https://github.com/mark-monteiro) - [ullmie02](https://github.com/ullmie02) + - [pR0Ps](https://github.com/pR0Ps) # Emby Contributors diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 4f74bb222e..2068e1d2aa 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -771,11 +771,11 @@ namespace Emby.Dlna.ContentDirectory }) .ToArray(); - return new QueryResult + return ApplyPaging(new QueryResult { Items = folders, TotalRecordCount = folders.Length - }; + }, startIndex, limit); } private QueryResult GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit) From 075dad87208fa7fdf2bdaaa54a7be842a038d422 Mon Sep 17 00:00:00 2001 From: Andrew Rabert <6550543+nvllsvm@users.noreply.github.com> Date: Sun, 19 Jan 2020 07:51:38 -0500 Subject: [PATCH 215/252] Fix ARM images (#2302) Allow for native building of Docker images. Use buildx if cross-compilation is needed. --- Dockerfile.arm | 4 ---- Dockerfile.arm64 | 4 ---- 2 files changed, 8 deletions(-) diff --git a/Dockerfile.arm b/Dockerfile.arm index 0e62366288..97e02a17d9 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -1,5 +1,3 @@ -# Requires binfm_misc registration -# https://github.com/multiarch/qemu-user-static#binfmt_misc-register ARG DOTNET_VERSION=3.1 @@ -23,9 +21,7 @@ RUN find . -type d -name obj | xargs -r rm -r RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" -FROM multiarch/qemu-user-static:x86_64-arm as qemu FROM debian:stretch-slim -COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ libssl-dev \ diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 796d12f980..445a39add7 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -1,5 +1,3 @@ -# Requires binfm_misc registration -# https://github.com/multiarch/qemu-user-static#binfmt_misc-register ARG DOTNET_VERSION=3.1 @@ -23,9 +21,7 @@ RUN find . -type d -name obj | xargs -r rm -r RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" -FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu FROM debian:stretch-slim -COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ libssl-dev \ From da2c998e28e8428cd96951eb58ced81ba59db32f Mon Sep 17 00:00:00 2001 From: Andrew Rabert <6550543+nvllsvm@users.noreply.github.com> Date: Sun, 19 Jan 2020 12:34:33 -0500 Subject: [PATCH 216/252] Update ARM images to debian buster (#2313) Also has the effect of updating ffmpeg to 4.1 --- Dockerfile.arm | 2 +- Dockerfile.arm64 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile.arm b/Dockerfile.arm index 97e02a17d9..551aa177a2 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -21,7 +21,7 @@ RUN find . -type d -name obj | xargs -r rm -r RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" -FROM debian:stretch-slim +FROM debian:buster-slim RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ libssl-dev \ diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 445a39add7..4c2ca12a66 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -21,7 +21,7 @@ RUN find . -type d -name obj | xargs -r rm -r RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" -FROM debian:stretch-slim +FROM debian:buster-slim RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ libssl-dev \ From 11c758b6bea2e15ac14b6b6975a5d2e83f55ae55 Mon Sep 17 00:00:00 2001 From: dkanada Date: Tue, 21 Jan 2020 00:20:24 +0900 Subject: [PATCH 217/252] remove unsupported test cases --- tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs index 837b23455c..93c59c9cab 100644 --- a/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs +++ b/tests/Jellyfin.Naming.Tests/TV/EpisodeNumberTests.cs @@ -78,18 +78,6 @@ namespace Jellyfin.Naming.Tests.TV Assert.Equal(16, GetEpisodeNumberFromFile(@"Season 2/Episode 16 - Some Title.avi")); } - [Fact] - public void TestEpisodeNumber55() - { - Assert.NotEqual(16, GetEpisodeNumberFromFile(@"Season 2/Season 3 Episode 16.avi")); - } - - [Fact] - public void TestEpisodeNumber56() - { - Assert.NotEqual(16, GetEpisodeNumberFromFile(@"Season 2/Season 3 Episode 16 - Some Title.avi")); - } - [Fact] public void TestEpisodeNumber57() { From ef487441d1545a21ff4e11d40c37a9ecf200f2e5 Mon Sep 17 00:00:00 2001 From: Waldemar Tomme Date: Tue, 21 Jan 2020 06:48:25 +0100 Subject: [PATCH 218/252] Remove configuration of global_header flag --- .../MediaEncoding/EncodingHelper.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 424dd0dbe8..7022459461 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2750,8 +2750,6 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -mpegts_m2ts_mode 1"; } - var supportsGlobalHeaderFlag = state.OutputContainer != "mkv"; - if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { if (state.VideoStream != null @@ -2772,11 +2770,6 @@ namespace MediaBrowser.Controller.MediaEncoding if (!state.RunTimeTicks.HasValue) { - if(supportsGlobalHeaderFlag) - { - args += " -flags -global_header"; - } - args += " -fflags +genpts"; } } @@ -2822,11 +2815,6 @@ namespace MediaBrowser.Controller.MediaEncoding { args += " " + qualityParam.Trim(); } - - if (supportsGlobalHeaderFlag && !state.RunTimeTicks.HasValue) - { - args += " -flags -global_header"; - } } if (!string.IsNullOrEmpty(state.OutputVideoSync)) From ac3b958c6791a48217cda5625a68ffeb18fbcae6 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Tue, 21 Jan 2020 17:59:41 +0100 Subject: [PATCH 219/252] Use async methods --- Emby.Dlna/Api/DlnaServerService.cs | 16 ++-- .../ConnectionManager/ConnectionManager.cs | 7 +- .../ContentDirectory/ContentDirectory.cs | 7 +- Emby.Dlna/ContentDirectory/ControlHandler.cs | 4 +- Emby.Dlna/IUpnpService.cs | 4 +- .../MediaReceiverRegistrar.cs | 7 +- Emby.Dlna/Service/BaseControlHandler.cs | 94 ++++++++----------- Emby.Dlna/Service/ControlErrorHandler.cs | 4 +- 8 files changed, 68 insertions(+), 75 deletions(-) diff --git a/Emby.Dlna/Api/DlnaServerService.cs b/Emby.Dlna/Api/DlnaServerService.cs index a451bbcf91..a7ff6e9cfd 100644 --- a/Emby.Dlna/Api/DlnaServerService.cs +++ b/Emby.Dlna/Api/DlnaServerService.cs @@ -170,32 +170,32 @@ namespace Emby.Dlna.Api return _resultFactory.GetResult(Request, xml, XMLContentType); } - public object Post(ProcessMediaReceiverRegistrarControlRequest request) + public async Task Post(ProcessMediaReceiverRegistrarControlRequest request) { - var response = PostAsync(request.RequestStream, MediaReceiverRegistrar); + var response = await PostAsync(request.RequestStream, MediaReceiverRegistrar).ConfigureAwait(false); return _resultFactory.GetResult(Request, response.Xml, XMLContentType); } - public object Post(ProcessContentDirectoryControlRequest request) + public async Task Post(ProcessContentDirectoryControlRequest request) { - var response = PostAsync(request.RequestStream, ContentDirectory); + var response = await PostAsync(request.RequestStream, ContentDirectory).ConfigureAwait(false); return _resultFactory.GetResult(Request, response.Xml, XMLContentType); } - public object Post(ProcessConnectionManagerControlRequest request) + public async Task Post(ProcessConnectionManagerControlRequest request) { - var response = PostAsync(request.RequestStream, ConnectionManager); + var response = await PostAsync(request.RequestStream, ConnectionManager).ConfigureAwait(false); return _resultFactory.GetResult(Request, response.Xml, XMLContentType); } - private ControlResponse PostAsync(Stream requestStream, IUpnpService service) + private Task PostAsync(Stream requestStream, IUpnpService service) { var id = GetPathValue(2).ToString(); - return service.ProcessControlRequest(new ControlRequest + return service.ProcessControlRequestAsync(new ControlRequest { Headers = Request.Headers, InputXml = requestStream, diff --git a/Emby.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManager.cs index 83011fbabd..934b353c28 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManager.cs +++ b/Emby.Dlna/ConnectionManager/ConnectionManager.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using Emby.Dlna.Service; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -20,17 +21,19 @@ namespace Emby.Dlna.ConnectionManager _logger = logger; } + /// public string GetServiceXml() { return new ConnectionManagerXmlBuilder().GetXml(); } - public ControlResponse ProcessControlRequest(ControlRequest request) + /// + public Task ProcessControlRequestAsync(ControlRequest request) { var profile = _dlna.GetProfile(request.Headers) ?? _dlna.GetDefaultProfile(); - return new ControlHandler(_config, _logger, profile).ProcessControlRequest(request); + return new ControlHandler(_config, _logger, profile).ProcessControlRequestAsync(request); } } } diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs index 78d69b3380..a3062b18fd 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectory.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Emby.Dlna.Service; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -66,12 +67,14 @@ namespace Emby.Dlna.ContentDirectory } } + /// public string GetServiceXml() { return new ContentDirectoryXmlBuilder().GetXml(); } - public ControlResponse ProcessControlRequest(ControlRequest request) + /// + public Task ProcessControlRequestAsync(ControlRequest request) { var profile = _dlna.GetProfile(request.Headers) ?? _dlna.GetDefaultProfile(); @@ -96,7 +99,7 @@ namespace Emby.Dlna.ContentDirectory _userViewManager, _mediaEncoder, _tvSeriesManager) - .ProcessControlRequest(request); + .ProcessControlRequestAsync(request); } private User GetUser(DeviceProfile profile) diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 4f74bb222e..7ed46984ad 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -76,7 +76,7 @@ namespace Emby.Dlna.ContentDirectory _profile = profile; _config = config; - _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, _logger, mediaEncoder); + _didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress, accessToken, userDataManager, localization, mediaSourceManager, Logger, mediaEncoder); } protected override IEnumerable> GetResult(string methodName, IDictionary methodParams) @@ -1336,7 +1336,7 @@ namespace Emby.Dlna.ContentDirectory }; } - _logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id); + Logger.LogError("Error parsing item Id: {id}. Returning user root folder.", id); return new ServerItem(_libraryManager.GetUserRootFolder()); } diff --git a/Emby.Dlna/IUpnpService.cs b/Emby.Dlna/IUpnpService.cs index ae90e95c79..0f3d327ed2 100644 --- a/Emby.Dlna/IUpnpService.cs +++ b/Emby.Dlna/IUpnpService.cs @@ -1,3 +1,5 @@ +using System.Threading.Tasks; + namespace Emby.Dlna { public interface IUpnpService @@ -13,6 +15,6 @@ namespace Emby.Dlna /// /// The request. /// ControlResponse. - ControlResponse ProcessControlRequest(ControlRequest request); + Task ProcessControlRequestAsync(ControlRequest request); } } diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs index b565cb631e..5eab6aee0c 100644 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs +++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using Emby.Dlna.Service; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -15,17 +16,19 @@ namespace Emby.Dlna.MediaReceiverRegistrar _config = config; } + /// public string GetServiceXml() { return new MediaReceiverRegistrarXmlBuilder().GetXml(); } - public ControlResponse ProcessControlRequest(ControlRequest request) + /// + public Task ProcessControlRequestAsync(ControlRequest request) { return new ControlHandler( _config, Logger) - .ProcessControlRequest(request); + .ProcessControlRequestAsync(request); } } } diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 79f03fed9f..d56f54149f 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Threading.Tasks; using System.Xml; using Emby.Dlna.Didl; using MediaBrowser.Controller.Configuration; @@ -15,44 +16,34 @@ namespace Emby.Dlna.Service { private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; - protected readonly IServerConfigurationManager Config; - protected readonly ILogger _logger; + protected IServerConfigurationManager Config { get; } + protected ILogger Logger { get; } protected BaseControlHandler(IServerConfigurationManager config, ILogger logger) { Config = config; - _logger = logger; + Logger = logger; } - public ControlResponse ProcessControlRequest(ControlRequest request) + public async Task ProcessControlRequestAsync(ControlRequest request) { try { - var enableDebugLogging = Config.GetDlnaConfiguration().EnableDebugLog; - - if (enableDebugLogging) - { - LogRequest(request); - } - - var response = ProcessControlRequestInternal(request); - - if (enableDebugLogging) - { - LogResponse(response); - } + LogRequest(request); + var response = await ProcessControlRequestInternalAsync(request).ConfigureAwait(false); + LogResponse(response); return response; } catch (Exception ex) { - _logger.LogError(ex, "Error processing control request"); + Logger.LogError(ex, "Error processing control request"); - return new ControlErrorHandler().GetResponse(ex); + return ControlErrorHandler.GetResponse(ex); } } - private ControlResponse ProcessControlRequestInternal(ControlRequest request) + private async Task ProcessControlRequestInternalAsync(ControlRequest request) { ControlRequestInfo requestInfo = null; @@ -69,19 +60,18 @@ namespace Emby.Dlna.Service using (var reader = XmlReader.Create(streamReader, readerSettings)) { - requestInfo = ParseRequest(reader); + requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false); } } - _logger.LogDebug("Received control request {0}", requestInfo.LocalName); + Logger.LogDebug("Received control request {0}", requestInfo.LocalName); var result = GetResult(requestInfo.LocalName, requestInfo.Headers); var settings = new XmlWriterSettings { Encoding = Encoding.UTF8, - CloseOutput = false, - Async = true + CloseOutput = false }; StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8); @@ -116,17 +106,15 @@ namespace Emby.Dlna.Service IsSuccessful = true }; - //logger.LogDebug(xml); - controlResponse.Headers.Add("EXT", string.Empty); return controlResponse; } - private ControlRequestInfo ParseRequest(XmlReader reader) + private async Task ParseRequestAsync(XmlReader reader) { - reader.MoveToContent(); - reader.Read(); + await reader.MoveToContentAsync().ConfigureAwait(false); + await reader.ReadAsync().ConfigureAwait(false); // Loop through each element while (!reader.EOF && reader.ReadState == ReadState.Interactive) @@ -141,37 +129,38 @@ namespace Emby.Dlna.Service { using (var subReader = reader.ReadSubtree()) { - return ParseBodyTag(subReader); + return await ParseBodyTagAsync(subReader).ConfigureAwait(false); } } else { - reader.Read(); + await reader.ReadAsync().ConfigureAwait(false); } + break; } default: { - reader.Skip(); + await reader.SkipAsync().ConfigureAwait(false); break; } } } else { - reader.Read(); + await reader.ReadAsync().ConfigureAwait(false); } } return new ControlRequestInfo(); } - private ControlRequestInfo ParseBodyTag(XmlReader reader) + private async Task ParseBodyTagAsync(XmlReader reader) { var result = new ControlRequestInfo(); - reader.MoveToContent(); - reader.Read(); + await reader.MoveToContentAsync().ConfigureAwait(false); + await reader.ReadAsync().ConfigureAwait(false); // Loop through each element while (!reader.EOF && reader.ReadState == ReadState.Interactive) @@ -185,28 +174,28 @@ namespace Emby.Dlna.Service { using (var subReader = reader.ReadSubtree()) { - ParseFirstBodyChild(subReader, result.Headers); + await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false); return result; } } else { - reader.Read(); + await reader.ReadAsync().ConfigureAwait(false); } } else { - reader.Read(); + await reader.ReadAsync().ConfigureAwait(false); } } return result; } - private void ParseFirstBodyChild(XmlReader reader, IDictionary headers) + private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary headers) { - reader.MoveToContent(); - reader.Read(); + await reader.MoveToContentAsync().ConfigureAwait(false); + await reader.ReadAsync().ConfigureAwait(false); // Loop through each element while (!reader.EOF && reader.ReadState == ReadState.Interactive) @@ -214,20 +203,20 @@ namespace Emby.Dlna.Service if (reader.NodeType == XmlNodeType.Element) { // TODO: Should we be doing this here, or should it be handled earlier when decoding the request? - headers[reader.LocalName.RemoveDiacritics()] = reader.ReadElementContentAsString(); + headers[reader.LocalName.RemoveDiacritics()] = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false); } else { - reader.Read(); + await reader.ReadAsync().ConfigureAwait(false); } } } private class ControlRequestInfo { - public string LocalName; - public string NamespaceURI; - public IDictionary Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + public string LocalName { get; set; } + public string NamespaceURI { get; set; } + public Dictionary Headers { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } protected abstract IEnumerable> GetResult(string methodName, IDictionary methodParams); @@ -239,10 +228,7 @@ namespace Emby.Dlna.Service return; } - var originalHeaders = request.Headers; - var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray()); - - _logger.LogDebug("Control request. Headers: {0}", headers); + Logger.LogDebug("Control request. Headers: {@Header}", request.Headers); } private void LogResponse(ControlResponse response) @@ -252,11 +238,7 @@ namespace Emby.Dlna.Service return; } - var originalHeaders = response.Headers; - var headers = string.Join(", ", originalHeaders.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray()); - //builder.Append(response.Xml); - - _logger.LogDebug("Control response. Headers: {0}", headers); + Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml); } } } diff --git a/Emby.Dlna/Service/ControlErrorHandler.cs b/Emby.Dlna/Service/ControlErrorHandler.cs index d5eb4a8879..bbf975f384 100644 --- a/Emby.Dlna/Service/ControlErrorHandler.cs +++ b/Emby.Dlna/Service/ControlErrorHandler.cs @@ -6,11 +6,11 @@ using Emby.Dlna.Didl; namespace Emby.Dlna.Service { - public class ControlErrorHandler + public static class ControlErrorHandler { private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; - public ControlResponse GetResponse(Exception ex) + public static ControlResponse GetResponse(Exception ex) { var settings = new XmlWriterSettings { From 5340eb9363f006e61b4574a30091bfdd43a0772b Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 22 Jan 2020 17:46:26 +0100 Subject: [PATCH 220/252] Update Emby.Dlna/Service/BaseControlHandler.cs Co-Authored-By: dkanada --- Emby.Dlna/Service/BaseControlHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index d56f54149f..49129f6ffd 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -228,7 +228,7 @@ namespace Emby.Dlna.Service return; } - Logger.LogDebug("Control request. Headers: {@Header}", request.Headers); + Logger.LogDebug("Control request. Headers: {@Headers}", request.Headers); } private void LogResponse(ControlResponse response) From 6eac7f0fa7e753731e3d65c0b6c0323eb730ccd8 Mon Sep 17 00:00:00 2001 From: Marco Date: Tue, 21 Jan 2020 16:40:09 +0000 Subject: [PATCH 221/252] 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(-) diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 8f91effb92..c1c40c18e1 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", From c8409d2ea1b4843bb89377b130733e61e6dc2e41 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Wed, 8 Jan 2020 17:52:50 +0100 Subject: [PATCH 222/252] Remove FileSystem.GetStream --- DvdLib/Ifo/Dvd.cs | 4 +- Emby.Dlna/DlnaManager.cs | 2 +- Emby.Drawing/ImageProcessor.cs | 3 +- .../ApplicationHost.cs | 2 +- .../Devices/DeviceManager.cs | 2 +- .../HttpClientManager/HttpClientManager.cs | 4 +- .../HttpServer/FileWriter.cs | 14 +- .../IO/ManagedFileSystem.cs | 81 ---------- .../LiveTv/EmbyTV/DirectRecorder.cs | 8 +- .../LiveTv/EmbyTV/EmbyTV.cs | 8 +- .../LiveTv/EmbyTV/EncodedRecorder.cs | 6 +- .../LiveTv/TunerHosts/LiveStream.cs | 4 +- .../LiveTv/TunerHosts/SharedHttpStream.cs | 4 +- .../Serialization/JsonSerializer.cs | 32 ++-- MediaBrowser.Api/Images/ImageService.cs | 2 +- MediaBrowser.Api/Images/RemoteImageService.cs | 6 +- MediaBrowser.Api/ItemLookupService.cs | 2 +- .../Playback/BaseStreamingService.cs | 2 +- .../Playback/Hls/BaseHlsService.cs | 8 +- .../Playback/Hls/DynamicHlsService.cs | 8 +- .../Playback/Hls/HlsSegmentService.cs | 4 +- .../BaseProgressiveStreamingService.cs | 2 +- .../Progressive/ProgressiveStreamWriter.cs | 12 +- MediaBrowser.Api/System/SystemService.cs | 4 +- .../MediaEncoding/EncodingHelper.cs | 28 ++-- .../Net/IHttpResultFactory.cs | 2 +- .../Net/StaticResultOptions.cs | 4 +- .../Savers/BaseXmlSaver.cs | 2 +- .../BdInfo/BdInfoDirectoryInfo.cs | 38 ++--- .../BdInfo/BdInfoFileInfo.cs | 14 +- .../Subtitles/SubtitleEncoder.cs | 2 +- MediaBrowser.Model/IO/IFileSystem.cs | 138 ------------------ .../IO/{StreamDefaults.cs => IODefaults.cs} | 8 +- MediaBrowser.Providers/Manager/ImageSaver.cs | 4 +- .../Manager/ItemImageProvider.cs | 6 +- .../Manager/MetadataService.cs | 2 +- .../Manager/ProviderManager.cs | 2 +- .../Music/AudioDbAlbumProvider.cs | 6 +- .../Music/AudioDbArtistProvider.cs | 5 +- MediaBrowser.Providers/Omdb/OmdbProvider.cs | 4 +- .../Studios/StudiosImageProvider.cs | 6 +- .../Subtitles/SubtitleManager.cs | 4 +- .../Tmdb/People/TmdbPersonProvider.cs | 2 +- .../Api/DashboardService.cs | 2 +- 44 files changed, 128 insertions(+), 375 deletions(-) rename MediaBrowser.Model/IO/{StreamDefaults.cs => IODefaults.cs} (58%) diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs index 90125fa3e3..157b2e197f 100644 --- a/DvdLib/Ifo/Dvd.cs +++ b/DvdLib/Ifo/Dvd.cs @@ -42,7 +42,7 @@ namespace DvdLib.Ifo } else { - using (var vmgFs = _fileSystem.GetFileStream(vmgPath.FullName, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + using (var vmgFs = new FileStream(vmgPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var vmgRead = new BigEndianBinaryReader(vmgFs)) { @@ -95,7 +95,7 @@ namespace DvdLib.Ifo { VTSPaths[vtsNum] = vtsPath; - using (var vtsFs = _fileSystem.GetFileStream(vtsPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + using (var vtsFs = new FileStream(vtsPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var vtsRead = new BigEndianBinaryReader(vtsFs)) { diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index d5d788021d..7e744e0aac 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -385,7 +385,7 @@ namespace Emby.Dlna { Directory.CreateDirectory(systemProfilesPath); - using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) { await stream.CopyToAsync(fileStream); } diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index ce8089e59c..4e0f9493a6 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -14,7 +14,6 @@ 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; @@ -129,7 +128,7 @@ namespace Emby.Drawing { var file = await ProcessImage(options).ConfigureAwait(false); - using (var fileStream = _fileSystem.GetFileStream(file.Item1, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true)) + using (var fileStream = new FileStream(file.Item1, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true)) { await fileStream.CopyToAsync(toStream).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0bb1d832fb..226a8f3026 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -599,7 +599,7 @@ namespace Emby.Server.Implementations HttpsPort = ServerConfiguration.DefaultHttpsPort; } - JsonSerializer = new JsonSerializer(FileSystemManager); + JsonSerializer = new JsonSerializer(); if (Plugins != null) { diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index ef73170506..2bd0b840a5 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -243,7 +243,7 @@ namespace Emby.Server.Implementations.Devices try { - using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) { await stream.CopyToAsync(fs).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index 50233ea485..8a2bc83fb0 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -197,7 +197,7 @@ namespace Emby.Server.Implementations.HttpClientManager if (File.Exists(responseCachePath) && _fileSystem.GetLastWriteTimeUtc(responseCachePath).Add(cacheLength) > DateTime.UtcNow) { - var stream = _fileSystem.GetFileStream(responseCachePath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true); + var stream = new FileStream(responseCachePath, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true); return new HttpResponseInfo { @@ -220,7 +220,7 @@ namespace Emby.Server.Implementations.HttpClientManager FileMode.Create, FileAccess.Write, FileShare.None, - StreamDefaults.DefaultFileStreamBufferSize, + IODefaults.FileStreamBufferSize, true)) { await response.Content.CopyToAsync(fileStream).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index 1795651fd7..d36f230d64 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -72,7 +72,7 @@ namespace Emby.Server.Implementations.HttpServer SetRangeValues(); } - FileShare = FileShareMode.Read; + FileShare = FileShare.Read; Cookies = new List(); } @@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.HttpServer public List Cookies { get; private set; } - public FileShareMode FileShare { get; set; } + public FileShare FileShare { get; set; } /// /// Gets the options. @@ -222,17 +222,17 @@ namespace Emby.Server.Implementations.HttpServer } } - public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) + public async Task TransmitFile(Stream stream, string path, long offset, long count, FileShare fileShare, CancellationToken cancellationToken) { - var fileOpenOptions = FileOpenOptions.SequentialScan; + var fileOptions = FileOptions.SequentialScan; // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039 if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - fileOpenOptions |= FileOpenOptions.Asynchronous; + fileOptions |= FileOptions.Asynchronous; } - using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, fileOpenOptions)) + using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, fileShare, IODefaults.FileStreamBufferSize, fileOptions)) { if (offset > 0) { @@ -245,7 +245,7 @@ namespace Emby.Server.Implementations.HttpServer } else { - await fs.CopyToAsync(stream, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); + await fs.CopyToAsync(stream, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false); } } } diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 68417876cf..da5a4d50ed 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -365,87 +365,6 @@ namespace Emby.Server.Implementations.IO return GetLastWriteTimeUtc(GetFileSystemInfo(path)); } - /// - /// Gets the file stream. - /// - /// The path. - /// The mode. - /// The access. - /// The share. - /// if set to true [is asynchronous]. - /// FileStream. - public virtual Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false) - { - if (isAsync) - { - return GetFileStream(path, mode, access, share, FileOpenOptions.Asynchronous); - } - - return GetFileStream(path, mode, access, share, FileOpenOptions.None); - } - - public virtual Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions) - => new FileStream(path, GetFileMode(mode), GetFileAccess(access), GetFileShare(share), 4096, GetFileOptions(fileOpenOptions)); - - private static FileOptions GetFileOptions(FileOpenOptions mode) - { - var val = (int)mode; - return (FileOptions)val; - } - - private static FileMode GetFileMode(FileOpenMode mode) - { - switch (mode) - { - //case FileOpenMode.Append: - // return FileMode.Append; - case FileOpenMode.Create: - return FileMode.Create; - case FileOpenMode.CreateNew: - return FileMode.CreateNew; - case FileOpenMode.Open: - return FileMode.Open; - case FileOpenMode.OpenOrCreate: - return FileMode.OpenOrCreate; - //case FileOpenMode.Truncate: - // return FileMode.Truncate; - default: - throw new Exception("Unrecognized FileOpenMode"); - } - } - - private static FileAccess GetFileAccess(FileAccessMode mode) - { - switch (mode) - { - //case FileAccessMode.ReadWrite: - // return FileAccess.ReadWrite; - case FileAccessMode.Write: - return FileAccess.Write; - case FileAccessMode.Read: - return FileAccess.Read; - default: - throw new Exception("Unrecognized FileAccessMode"); - } - } - - private static FileShare GetFileShare(FileShareMode mode) - { - switch (mode) - { - case FileShareMode.ReadWrite: - return FileShare.ReadWrite; - case FileShareMode.Write: - return FileShare.Write; - case FileShareMode.Read: - return FileShare.Read; - case FileShareMode.None: - return FileShare.None; - default: - throw new Exception("Unrecognized FileShareMode"); - } - } - public virtual void SetHidden(string path, bool isHidden) { if (OperatingSystem.Id != OperatingSystemId.Windows) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs index 84e8c31f95..161cf60516 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs @@ -15,14 +15,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { private readonly ILogger _logger; private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; private readonly IStreamHelper _streamHelper; - public DirectRecorder(ILogger logger, IHttpClient httpClient, IFileSystem fileSystem, IStreamHelper streamHelper) + public DirectRecorder(ILogger logger, IHttpClient httpClient, IStreamHelper streamHelper) { _logger = logger; _httpClient = httpClient; - _fileSystem = fileSystem; _streamHelper = streamHelper; } @@ -45,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { Directory.CreateDirectory(Path.GetDirectoryName(targetFile)); - using (var output = _fileSystem.GetFileStream(targetFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.Read)) { onStarted(); @@ -81,7 +79,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Directory.CreateDirectory(Path.GetDirectoryName(targetFile)); - using (var output = _fileSystem.GetFileStream(targetFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var output = new FileStream(targetFile, FileMode.Create, FileAccess.Write, FileShare.Read)) { onStarted(); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index f4d6cd4d3a..2fe699aabc 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1664,10 +1664,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { if (mediaSource.RequiresLooping || !(mediaSource.Container ?? string.Empty).EndsWith("ts", StringComparison.OrdinalIgnoreCase) || (mediaSource.Protocol != MediaProtocol.File && mediaSource.Protocol != MediaProtocol.Http)) { - return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _processFactory, _config); + return new EncodedRecorder(_logger, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, _processFactory, _config); } - return new DirectRecorder(_logger, _httpClient, _fileSystem, _streamHelper); + return new DirectRecorder(_logger, _httpClient, _streamHelper); } private void OnSuccessfulRecording(TimerInfo timer, string path) @@ -1888,7 +1888,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } - using (var stream = _fileSystem.GetFileStream(nfoPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var stream = new FileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { var settings = new XmlWriterSettings { @@ -1952,7 +1952,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } - using (var stream = _fileSystem.GetFileStream(nfoPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var stream = new FileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { var settings = new XmlWriterSettings { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index cc9c8e5d29..ee5086a65f 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -14,7 +14,6 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Diagnostics; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; @@ -24,7 +23,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public class EncodedRecorder : IRecorder { private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; private readonly IMediaEncoder _mediaEncoder; private readonly IServerApplicationPaths _appPaths; private bool _hasExited; @@ -38,7 +36,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public EncodedRecorder( ILogger logger, - IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, @@ -46,7 +43,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV IServerConfigurationManager config) { _logger = logger; - _fileSystem = fileSystem; _mediaEncoder = mediaEncoder; _appPaths = appPaths; _json = json; @@ -107,7 +103,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV Directory.CreateDirectory(Path.GetDirectoryName(logFilePath)); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - _logFileStream = _fileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true); + _logFileStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs index 1d55e7992e..862b9fdfed 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs @@ -96,7 +96,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts FileMode.Open, FileAccess.Read, FileShare.ReadWrite, - StreamDefaults.DefaultFileStreamBufferSize, + IODefaults.FileStreamBufferSize, allowAsyncFileRead ? FileOptions.SequentialScan | FileOptions.Asynchronous : FileOptions.SequentialScan); public Task DeleteTempFiles() @@ -199,7 +199,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts await StreamHelper.CopyToAsync( inputStream, stream, - StreamDefaults.DefaultCopyToBufferSize, + IODefaults.CopyToBufferSize, emptyReadLimit, cancellationToken).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs index 0d94f4b329..99244eb625 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/SharedHttpStream.cs @@ -127,12 +127,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Logger.LogInformation("Beginning {0} stream to {1}", GetType().Name, TempFilePath); using (response) using (var stream = response.Content) - using (var fileStream = FileSystem.GetFileStream(TempFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.None)) + using (var fileStream = new FileStream(TempFilePath, FileMode.Create, FileAccess.Write, FileShare.Read)) { await StreamHelper.CopyToAsync( stream, fileStream, - StreamDefaults.DefaultCopyToBufferSize, + IODefaults.CopyToBufferSize, () => Resolve(openTaskCompletionSource), cancellationToken).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/Serialization/JsonSerializer.cs b/Emby.Server.Implementations/Serialization/JsonSerializer.cs index 36196ee36c..bcc814daf2 100644 --- a/Emby.Server.Implementations/Serialization/JsonSerializer.cs +++ b/Emby.Server.Implementations/Serialization/JsonSerializer.cs @@ -2,7 +2,6 @@ using System; using System.Globalization; using System.IO; using System.Threading.Tasks; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; namespace Emby.Server.Implementations.Serialization @@ -12,13 +11,15 @@ namespace Emby.Server.Implementations.Serialization /// public class JsonSerializer : IJsonSerializer { - private readonly IFileSystem _fileSystem; - - public JsonSerializer( - IFileSystem fileSystem) + public JsonSerializer() { - _fileSystem = fileSystem; - Configure(); + ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601; + ServiceStack.Text.JsConfig.ExcludeTypeInfo = true; + ServiceStack.Text.JsConfig.IncludeNullValues = false; + ServiceStack.Text.JsConfig.AlwaysUseUtc = true; + ServiceStack.Text.JsConfig.AssumeUtc = true; + + ServiceStack.Text.JsConfig.SerializeFn = SerializeGuid; } /// @@ -81,7 +82,7 @@ namespace Emby.Server.Implementations.Serialization throw new ArgumentNullException(nameof(file)); } - using (var stream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var stream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) { SerializeToStream(obj, stream); } @@ -162,7 +163,6 @@ namespace Emby.Server.Implementations.Serialization throw new ArgumentNullException(nameof(stream)); } - return ServiceStack.Text.JsonSerializer.DeserializeFromStreamAsync(stream); } @@ -225,20 +225,6 @@ namespace Emby.Server.Implementations.Serialization } } - /// - /// Configures this instance. - /// - private void Configure() - { - ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.DateHandler.ISO8601; - ServiceStack.Text.JsConfig.ExcludeTypeInfo = true; - ServiceStack.Text.JsConfig.IncludeNullValues = false; - ServiceStack.Text.JsConfig.AlwaysUseUtc = true; - ServiceStack.Text.JsConfig.AssumeUtc = true; - - ServiceStack.Text.JsConfig.SerializeFn = SerializeGuid; - } - private static string SerializeGuid(Guid guid) { if (guid.Equals(Guid.Empty)) diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index e94c1321f4..c55618aa1d 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -656,7 +656,7 @@ namespace MediaBrowser.Api.Images IsHeadRequest = isHeadRequest, Path = imageResult.Item1, - FileShare = FileShareMode.Read + FileShare = FileShare.Read }).ConfigureAwait(false); } diff --git a/MediaBrowser.Api/Images/RemoteImageService.cs b/MediaBrowser.Api/Images/RemoteImageService.cs index 5a37d37302..f03f5efd8d 100644 --- a/MediaBrowser.Api/Images/RemoteImageService.cs +++ b/MediaBrowser.Api/Images/RemoteImageService.cs @@ -274,11 +274,9 @@ namespace MediaBrowser.Api.Images Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); using (var stream = result.Content) + using (var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) { - using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) - { - await stream.CopyToAsync(filestream).ConfigureAwait(false); - } + await stream.CopyToAsync(filestream).ConfigureAwait(false); } Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index ea5a99892d..a76369a157 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -305,7 +305,7 @@ namespace MediaBrowser.Api Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); using (var stream = result.Content) - using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) + using (var filestream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) { await stream.CopyToAsync(filestream).ConfigureAwait(false); } diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 1f7dc0d71f..ab74bab1c3 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -261,7 +261,7 @@ namespace MediaBrowser.Api.Playback var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt"); // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory. - Stream logStream = FileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true); + Stream logStream = new FileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(Request.AbsoluteUri + Environment.NewLine + Environment.NewLine + JsonSerializer.SerializeToString(state.MediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine); await logStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 5d0dc98ddc..0cbfe4bdfa 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -168,7 +168,7 @@ namespace MediaBrowser.Api.Playback.Hls private string GetLivePlaylistText(string path, int segmentLength) { - using (var stream = FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite)) + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (var reader = new StreamReader(stream)) { @@ -211,7 +211,7 @@ namespace MediaBrowser.Api.Playback.Hls { try { - // Need to use FileShareMode.ReadWrite because we're reading the file at the same time it's being written + // Need to use FileShare.ReadWrite because we're reading the file at the same time it's being written using (var fileStream = GetPlaylistFileStream(playlist)) { using (var reader = new StreamReader(fileStream)) @@ -252,11 +252,11 @@ namespace MediaBrowser.Api.Playback.Hls try { - return FileSystem.GetFileStream(tmpPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan); + return new FileStream(tmpPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.SequentialScan); } catch (IOException) { - return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, FileOpenOptions.SequentialScan); + return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, FileOptions.SequentialScan); } } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 0178f53af6..e85ed20505 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -537,7 +537,7 @@ namespace MediaBrowser.Api.Playback.Hls return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions { Path = segmentPath, - FileShare = FileShareMode.ReadWrite, + FileShare = FileShare.ReadWrite, OnComplete = () => { Logger.LogDebug("finished serving {0}", segmentPath); @@ -954,12 +954,12 @@ namespace MediaBrowser.Api.Playback.Hls // Unable to force key frames to h264_qsv transcode if (string.Equals(codec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { - Logger.LogInformation("Bug Workaround: Disabling force_key_frames for h264_qsv"); - } + Logger.LogInformation("Bug Workaround: Disabling force_key_frames for h264_qsv"); + } else { args += " " + keyFrameArg; - } + } //args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0"; diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index bb12ab1f0d..87ccde2e04 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -140,7 +140,7 @@ namespace MediaBrowser.Api.Playback.Hls var file = request.SegmentId + Path.GetExtension(Request.PathInfo); file = Path.Combine(ServerConfigurationManager.GetTranscodePath(), file); - return ResultFactory.GetStaticFileResult(Request, file, FileShareMode.ReadWrite); + return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite); } private Task GetFileResult(string path, string playlistPath) @@ -150,7 +150,7 @@ namespace MediaBrowser.Api.Playback.Hls return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions { Path = path, - FileShare = FileShareMode.ReadWrite, + FileShare = FileShare.ReadWrite, OnComplete = () => { if (transcodingJob != null) diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index ed30dbba61..ed68219c9f 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -248,7 +248,7 @@ namespace MediaBrowser.Api.Playback.Progressive // ContentType = contentType, // IsHeadRequest = isHeadRequest, // Path = outputPath, - // FileShare = FileShareMode.ReadWrite, + // FileShare = FileShare.ReadWrite, // OnComplete = () => // { // if (transcodingJob != null) diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs index 6609120655..a53b848f9e 100644 --- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs +++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs @@ -21,8 +21,6 @@ namespace MediaBrowser.Api.Playback.Progressive private readonly CancellationToken _cancellationToken; private readonly Dictionary _outputHeaders; - const int StreamCopyToBufferSize = 81920; - private long _bytesWritten = 0; public long StartPosition { get; set; } public bool AllowEndOfFile = true; @@ -52,14 +50,14 @@ namespace MediaBrowser.Api.Playback.Progressive private Stream GetInputStream(bool allowAsyncFileRead) { - var fileOpenOptions = FileOpenOptions.SequentialScan; + var fileOptions = FileOptions.SequentialScan; if (allowAsyncFileRead) { - fileOpenOptions |= FileOpenOptions.Asynchronous; + fileOptions |= FileOptions.Asynchronous; } - return _fileSystem.GetFileStream(_path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions); + return new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, fileOptions); } public async Task WriteToAsync(Stream outputStream, CancellationToken cancellationToken) @@ -127,7 +125,7 @@ namespace MediaBrowser.Api.Playback.Progressive private async Task CopyToInternalAsyncWithSyncRead(Stream source, Stream destination, CancellationToken cancellationToken) { - var array = new byte[StreamCopyToBufferSize]; + var array = new byte[IODefaults.CopyToBufferSize]; int bytesRead; int totalBytesRead = 0; @@ -154,7 +152,7 @@ namespace MediaBrowser.Api.Playback.Progressive private async Task CopyToInternalAsync(Stream source, Stream destination, CancellationToken cancellationToken) { - var array = new byte[StreamCopyToBufferSize]; + var array = new byte[IODefaults.CopyToBufferSize]; int bytesRead; int totalBytesRead = 0; diff --git a/MediaBrowser.Api/System/SystemService.cs b/MediaBrowser.Api/System/SystemService.cs index 3a56ba701e..3a3eeb8b8f 100644 --- a/MediaBrowser.Api/System/SystemService.cs +++ b/MediaBrowser.Api/System/SystemService.cs @@ -170,10 +170,10 @@ namespace MediaBrowser.Api.System // For older files, assume fully static if (file.LastWriteTimeUtc < DateTime.UtcNow.AddHours(-1)) { - return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShareMode.Read); + return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.Read); } - return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShareMode.ReadWrite); + return ResultFactory.GetStaticFileResult(Request, file.FullName, FileShare.ReadWrite); } /// diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index acc89e352b..342c764146 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -475,7 +475,7 @@ namespace MediaBrowser.Controller.MediaEncoding .Append(' '); } - if (state.IsVideoRequest + if (state.IsVideoRequest && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); @@ -486,11 +486,11 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)) { arg.Append("-hwaccel qsv "); - } - else + } + else { arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); - } + } } arg.Append(videoDecoder + " "); @@ -653,7 +653,7 @@ namespace MediaBrowser.Controller.MediaEncoding // _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fallbackFontPath)); // using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), GetType().Namespace + ".DroidSansFallback.ttf")) // { - // using (var fileStream = _fileSystem.GetFileStream(fallbackFontPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + // using (var fileStream = new FileStream(fallbackFontPath, FileMode.Create, FileAccess.Write, FileShare.Read)) // { // stream.CopyTo(fileStream); // } @@ -1624,22 +1624,22 @@ namespace MediaBrowser.Controller.MediaEncoding // Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference) var retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\""; - if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { /* QSV in FFMpeg can now setup hardware overlay for transcodes. For software decoding and hardware encoding option, frames must be hwuploaded into hardware - with fixed frame size. + with fixed frame size. */ if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)) { retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\""; - } - else + } + else { retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwupload=extra_hw_frames=64[v];[v][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\""; } - } + } return string.Format( CultureInfo.InvariantCulture, @@ -1731,8 +1731,8 @@ namespace MediaBrowser.Controller.MediaEncoding vaapi_or_qsv, outputWidth, outputHeight)); - } - else + } + else { filters.Add(string.Format(CultureInfo.InvariantCulture, "scale_{0}=format=nv12", vaapi_or_qsv)); } @@ -1979,8 +1979,8 @@ namespace MediaBrowser.Controller.MediaEncoding var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); - // If we are software decoding, and hardware encoding - if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) + // If we are software decoding, and hardware encoding + if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) && (string.IsNullOrEmpty(videoDecoder) || !videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase))) { filters.Add("format=nv12|qsv"); diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs index ff9ecf8af0..fb00ee0081 100644 --- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs +++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.Controller.Net /// The path. /// The file share. /// System.Object. - Task GetStaticFileResult(IRequest requestContext, string path, FileShareMode fileShare = FileShareMode.Read); + Task GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read); /// /// Gets the static file result. diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs index 7a179913ac..7267326607 100644 --- a/MediaBrowser.Controller/Net/StaticResultOptions.cs +++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs @@ -24,12 +24,12 @@ namespace MediaBrowser.Controller.Net public string Path { get; set; } public long? ContentLength { get; set; } - public FileShareMode FileShare { get; set; } + public FileShare FileShare { get; set; } public StaticResultOptions() { ResponseHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - FileShare = FileShareMode.Read; + FileShare = FileShare.Read; } } diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index bd727bcdf9..46c5317974 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.LocalMetadata.Savers // On Windows, savint the file will fail if the file is hidden or readonly FileSystem.SetAttributes(path, false, false); - using (var filestream = FileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var filestream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) { stream.CopyTo(filestream); } diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs index 91c8b27925..e040286ab9 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs @@ -1,16 +1,27 @@ using System; -using System.Collections.Generic; using System.Linq; using BDInfo.IO; using MediaBrowser.Model.IO; namespace MediaBrowser.MediaEncoding.BdInfo { - class BdInfoDirectoryInfo : BDInfo.IO.IDirectoryInfo + class BdInfoDirectoryInfo : IDirectoryInfo { - IFileSystem _fileSystem = null; + private readonly IFileSystem _fileSystem = null; - FileSystemMetadata _impl = null; + private readonly FileSystemMetadata _impl = null; + + public BdInfoDirectoryInfo(IFileSystem fileSystem, string path) + { + _fileSystem = fileSystem; + _impl = _fileSystem.GetDirectoryInfo(path); + } + + private BdInfoDirectoryInfo(IFileSystem fileSystem, FileSystemMetadata impl) + { + _fileSystem = fileSystem; + _impl = impl; + } public string Name => _impl.Name; @@ -25,22 +36,11 @@ namespace MediaBrowser.MediaEncoding.BdInfo { return new BdInfoDirectoryInfo(_fileSystem, parentFolder); } + return null; } } - public BdInfoDirectoryInfo(IFileSystem fileSystem, string path) - { - _fileSystem = fileSystem; - _impl = _fileSystem.GetDirectoryInfo(path); - } - - private BdInfoDirectoryInfo(IFileSystem fileSystem, FileSystemMetadata impl) - { - _fileSystem = fileSystem; - _impl = impl; - } - public IDirectoryInfo[] GetDirectories() { return Array.ConvertAll(_fileSystem.GetDirectories(_impl.FullName).ToArray(), @@ -50,20 +50,20 @@ namespace MediaBrowser.MediaEncoding.BdInfo public IFileInfo[] GetFiles() { return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName).ToArray(), - x => new BdInfoFileInfo(_fileSystem, x)); + x => new BdInfoFileInfo(x)); } public IFileInfo[] GetFiles(string searchPattern) { return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(), - x => new BdInfoFileInfo(_fileSystem, x)); + x => new BdInfoFileInfo(x)); } public IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption) { return Array.ConvertAll(_fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, searchOption.HasFlag(System.IO.SearchOption.AllDirectories)).ToArray(), - x => new BdInfoFileInfo(_fileSystem, x)); + x => new BdInfoFileInfo(x)); } public static IDirectoryInfo FromFileSystemPath(Model.IO.IFileSystem fs, string path) diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs index de9d7cb784..a6ff4f7678 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs @@ -1,11 +1,10 @@ +using System.IO; using MediaBrowser.Model.IO; namespace MediaBrowser.MediaEncoding.BdInfo { class BdInfoFileInfo : BDInfo.IO.IFileInfo { - IFileSystem _fileSystem = null; - FileSystemMetadata _impl = null; public string Name => _impl.Name; @@ -18,18 +17,17 @@ namespace MediaBrowser.MediaEncoding.BdInfo public bool IsDir => _impl.IsDirectory; - public BdInfoFileInfo(IFileSystem fileSystem, FileSystemMetadata impl) + public BdInfoFileInfo(FileSystemMetadata impl) { - _fileSystem = fileSystem; _impl = impl; } public System.IO.Stream OpenRead() { - return _fileSystem.GetFileStream(FullName, - FileOpenMode.Open, - FileAccessMode.Read, - FileShareMode.Read); + return new FileStream(FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); } public System.IO.StreamReader OpenText() diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 99bb368b26..a4a7595d29 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -691,7 +691,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (!string.Equals(text, newText)) { - using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var writer = new StreamWriter(fileStream, encoding)) { writer.Write(newText); diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs index ca99b28ca4..48e5eea6f6 100644 --- a/MediaBrowser.Model/IO/IFileSystem.cs +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -98,20 +98,6 @@ namespace MediaBrowser.Model.IO /// DateTime. DateTime GetLastWriteTimeUtc(string path); - /// - /// Gets the file stream. - /// - /// The path. - /// The mode. - /// The access. - /// The share. - /// if set to true [is asynchronous]. - /// FileStream. - Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false); - - Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, - FileOpenOptions fileOpenOptions); - /// /// Swaps the files. /// @@ -218,128 +204,4 @@ namespace MediaBrowser.Model.IO List GetDrives(); void SetExecutable(string path); } - - //TODO Investigate if can be replaced by the one from System.IO ? - public enum FileOpenMode - { - // - // Summary: - // Specifies that the operating system should create a new file. This requires System.Security.Permissions.FileIOPermissionAccess.Write - // permission. If the file already exists, an System.IO.IOException exception is - // thrown. - CreateNew = 1, - // - // Summary: - // Specifies that the operating system should create a new file. If the file already - // exists, it will be overwritten. This requires System.Security.Permissions.FileIOPermissionAccess.Write - // permission. FileMode.Create is equivalent to requesting that if the file does - // not exist, use System.IO.FileMode.CreateNew; otherwise, use System.IO.FileMode.Truncate. - // If the file already exists but is a hidden file, an System.UnauthorizedAccessException - // exception is thrown. - Create = 2, - // - // Summary: - // Specifies that the operating system should open an existing file. The ability - // to open the file is dependent on the value specified by the System.IO.FileAccess - // enumeration. A System.IO.FileNotFoundException exception is thrown if the file - // does not exist. - Open = 3, - // - // Summary: - // Specifies that the operating system should open a file if it exists; otherwise, - // a new file should be created. If the file is opened with FileAccess.Read, System.Security.Permissions.FileIOPermissionAccess.Read - // permission is required. If the file access is FileAccess.Write, System.Security.Permissions.FileIOPermissionAccess.Write - // permission is required. If the file is opened with FileAccess.ReadWrite, both - // System.Security.Permissions.FileIOPermissionAccess.Read and System.Security.Permissions.FileIOPermissionAccess.Write - // permissions are required. - OpenOrCreate = 4 - } - - public enum FileAccessMode - { - // - // Summary: - // Read access to the file. Data can be read from the file. Combine with Write for - // read/write access. - Read = 1, - // - // Summary: - // Write access to the file. Data can be written to the file. Combine with Read - // for read/write access. - Write = 2 - } - - public enum FileShareMode - { - // - // Summary: - // Declines sharing of the current file. Any request to open the file (by this process - // or another process) will fail until the file is closed. - None = 0, - // - // Summary: - // Allows subsequent opening of the file for reading. If this flag is not specified, - // any request to open the file for reading (by this process or another process) - // will fail until the file is closed. However, even if this flag is specified, - // additional permissions might still be needed to access the file. - Read = 1, - // - // Summary: - // Allows subsequent opening of the file for writing. If this flag is not specified, - // any request to open the file for writing (by this process or another process) - // will fail until the file is closed. However, even if this flag is specified, - // additional permissions might still be needed to access the file. - Write = 2, - // - // Summary: - // Allows subsequent opening of the file for reading or writing. If this flag is - // not specified, any request to open the file for reading or writing (by this process - // or another process) will fail until the file is closed. However, even if this - // flag is specified, additional permissions might still be needed to access the - // file. - ReadWrite = 3 - } - - // - // Summary: - // Represents advanced options for creating a System.IO.FileStream object. - [Flags] - public enum FileOpenOptions - { - // - // Summary: - // Indicates that the system should write through any intermediate cache and go - // directly to disk. - WriteThrough = int.MinValue, - // - // Summary: - // Indicates that no additional options should be used when creating a System.IO.FileStream - // object. - None = 0, - // - // Summary: - // Indicates that a file is encrypted and can be decrypted only by using the same - // user account used for encryption. - Encrypted = 16384, - // - // Summary: - // Indicates that a file is automatically deleted when it is no longer in use. - DeleteOnClose = 67108864, - // - // Summary: - // Indicates that the file is to be accessed sequentially from beginning to end. - // The system can use this as a hint to optimize file caching. If an application - // moves the file pointer for random access, optimum caching may not occur; however, - // correct operation is still guaranteed. - SequentialScan = 134217728, - // - // Summary: - // Indicates that the file is accessed randomly. The system can use this as a hint - // to optimize file caching. - RandomAccess = 268435456, - // - // Summary: - // Indicates that a file can be used for asynchronous reading and writing. - Asynchronous = 1073741824 - } } diff --git a/MediaBrowser.Model/IO/StreamDefaults.cs b/MediaBrowser.Model/IO/IODefaults.cs similarity index 58% rename from MediaBrowser.Model/IO/StreamDefaults.cs rename to MediaBrowser.Model/IO/IODefaults.cs index 4b55ce1f30..f392dbcce2 100644 --- a/MediaBrowser.Model/IO/StreamDefaults.cs +++ b/MediaBrowser.Model/IO/IODefaults.cs @@ -1,18 +1,18 @@ namespace MediaBrowser.Model.IO { /// - /// Class StreamDefaults. + /// Class IODefaults. /// - public static class StreamDefaults + public static class IODefaults { /// /// The default copy to buffer size. /// - public const int DefaultCopyToBufferSize = 81920; + public const int CopyToBufferSize = 81920; /// /// The default file stream buffer size. /// - public const int DefaultFileStreamBufferSize = 4096; + public const int FileStreamBufferSize = 4096; } } diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index ab906809f3..3ab621ba41 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -244,9 +244,9 @@ namespace MediaBrowser.Providers.Manager _fileSystem.SetAttributes(path, false, false); - using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, FileOpenOptions.Asynchronous)) + using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) { - await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); + await source.CopyToAsync(fs, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false); } if (_config.Configuration.SaveMetadataHidden) diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index e9179815e7..d83c0cc86f 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -25,14 +25,12 @@ namespace MediaBrowser.Providers.Manager { private readonly ILogger _logger; private readonly IProviderManager _providerManager; - private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; - public ItemImageProvider(ILogger logger, IProviderManager providerManager, IServerConfigurationManager config, IFileSystem fileSystem) + public ItemImageProvider(ILogger logger, IProviderManager providerManager, IFileSystem fileSystem) { _logger = logger; _providerManager = providerManager; - _config = config; _fileSystem = fileSystem; } @@ -141,7 +139,7 @@ namespace MediaBrowser.Providers.Manager { var mimeType = MimeTypes.GetMimeType(response.Path); - var stream = _fileSystem.GetFileStream(response.Path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true); + var stream = new FileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, true); await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index c3401f12b9..e6cb923e33 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -73,7 +73,7 @@ namespace MediaBrowser.Providers.Manager } } - var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem); + var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, FileSystem); var localImagesFailed = false; var allImageProviders = ((ProviderManager)ProviderManager).GetImageProviders(item, refreshOptions).ToList(); diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 631d063a54..e7b349f67b 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -182,7 +182,7 @@ namespace MediaBrowser.Providers.Manager throw new ArgumentNullException(nameof(source)); } - var fileStream = _fileSystem.GetFileStream(source, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true); + var fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, true); return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken); } diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs index e61d8792c5..939c74c01c 100644 --- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -164,11 +165,10 @@ namespace MediaBrowser.Providers.Music { Url = url, CancellationToken = cancellationToken - }, - "GET").ConfigureAwait(false)) + HttpMethod.Get).ConfigureAwait(false)) using (var response = httpResponse.Content) - using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) + using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) { await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs index 7e5893d49a..e073a295b0 100644 --- a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -152,12 +153,12 @@ namespace MediaBrowser.Providers.Music CancellationToken = cancellationToken, BufferContent = true }, - "GET").ConfigureAwait(false)) + HttpMethod.Get).ConfigureAwait(false)) using (var response = httpResponse.Content) { Directory.CreateDirectory(Path.GetDirectoryName(path)); - using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) + using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) { await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index f8b8765802..fbf6ae135b 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -209,7 +209,7 @@ namespace MediaBrowser.Providers.Omdb string resultString; - using (var stream = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var reader = new StreamReader(stream, new UTF8Encoding(false))) { @@ -228,7 +228,7 @@ namespace MediaBrowser.Providers.Omdb string resultString; - using (var stream = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var reader = new StreamReader(stream, new UTF8Encoding(false))) { diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index ef412db5ac..cbef27a091 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -78,7 +78,7 @@ namespace MediaBrowser.Providers.Studios private RemoteImageInfo GetImage(BaseItem item, string filename, ImageType type, string remoteFilename) { - var list = GetAvailableImages(filename, _fileSystem); + var list = GetAvailableImages(filename); var match = FindMatch(item, list); @@ -179,9 +179,9 @@ namespace MediaBrowser.Providers.Studios .Replace("/", string.Empty); } - public IEnumerable GetAvailableImages(string file, IFileSystem fileSystem) + public IEnumerable GetAvailableImages(string file) { - using (var fileStream = fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) + using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (var reader = new StreamReader(fileStream)) { diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 37d1230e23..583c7e8ea4 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -19,7 +19,7 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using Microsoft.Extensions.Logging; -using static MediaBrowser.Model.IO.StreamDefaults; +using static MediaBrowser.Model.IO.IODefaults; namespace MediaBrowser.Providers.Subtitles { @@ -210,7 +210,7 @@ namespace MediaBrowser.Providers.Subtitles { Directory.CreateDirectory(Path.GetDirectoryName(savePath)); - using (var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, DefaultFileStreamBufferSize, true)) + using (var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, FileStreamBufferSize, true)) { await stream.CopyToAsync(fs).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs index 130403e4d1..50af9913aa 100644 --- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs @@ -234,7 +234,7 @@ namespace MediaBrowser.Providers.Tmdb.People { Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - using (var fs = _fileSystem.GetFileStream(dataFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) + using (var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) { await json.CopyToAsync(fs).ConfigureAwait(false); } diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index fadf32b28f..96ebdf4a88 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -425,7 +425,7 @@ namespace MediaBrowser.WebDashboard.Api private async Task DumpFile(PackageCreator packageCreator, string resourceVirtualPath, string destinationFilePath, string mode, string appVersion) { using (var stream = await packageCreator.GetResource(resourceVirtualPath, mode, null, appVersion).ConfigureAwait(false)) - using (var fs = _fileSystem.GetFileStream(destinationFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read)) + using (var fs = new FileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.Read)) { await stream.CopyToAsync(fs); } From c751ba9f70ca19297406542869a08cec9fc1e558 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 22 Jan 2020 22:27:03 +0100 Subject: [PATCH 223/252] Fix build --- .../HttpServer/HttpResultFactory.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index cefcaa835f..98a4f140e3 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -440,7 +440,7 @@ namespace Emby.Server.Implementations.HttpServer public Task GetStaticFileResult(IRequest requestContext, string path, - FileShareMode fileShare = FileShareMode.Read) + FileShare fileShare = FileShare.Read) { if (string.IsNullOrEmpty(path)) { @@ -464,7 +464,7 @@ namespace Emby.Server.Implementations.HttpServer throw new ArgumentException("Path can't be empty.", nameof(options)); } - if (fileShare != FileShareMode.Read && fileShare != FileShareMode.ReadWrite) + if (fileShare != FileShare.Read && fileShare != FileShare.ReadWrite) { throw new ArgumentException("FileShare must be either Read or ReadWrite"); } @@ -492,9 +492,9 @@ namespace Emby.Server.Implementations.HttpServer /// The path. /// The file share. /// Stream. - private Stream GetFileStream(string path, FileShareMode fileShare) + private Stream GetFileStream(string path, FileShare fileShare) { - return _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShare); + return new FileStream(path, FileMode.Open, FileAccess.Read, fileShare); } public Task GetStaticResult(IRequest requestContext, From 48e9c004b4e4979a176cdc6035b0f761d97f100e Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Fri, 24 Jan 2020 00:55:14 +0800 Subject: [PATCH 224/252] fix an issue about determining 'IsSecondaryAudio' fix an issue that could not correctly determine 'IsSecondaryAudio' in some cases. --- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 5cb0565666..83da952ce3 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -209,10 +209,7 @@ namespace MediaBrowser.Model.Dto { if (currentStream.Type == MediaStreamType.Audio && currentStream.IsDefault) { - if (currentStream.Index != stream.Index) - { - return true; - } + return currentStream.Index != stream.Index; } } From d3de91dab67bbe7770bf74e97699b966f30822c9 Mon Sep 17 00:00:00 2001 From: Carey Metcalfe Date: Fri, 24 Jan 2020 00:43:28 -0500 Subject: [PATCH 225/252] Fix another issue with DLNA responses not properly paginating The `UserRootFolder.GetItemsInternal` method now applies any sorting and pagination requested by the `InternalItemsQuery` that was passed to it. Previous pagination fix: #2304 Original issue #2303 --- MediaBrowser.Controller/Entities/UserRootFolder.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 7fcf48a48f..8a68f830cc 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -60,14 +60,7 @@ namespace MediaBrowser.Controller.Entities PresetViews = query.PresetViews }); - var itemsArray = result; - var totalCount = itemsArray.Length; - - return new QueryResult - { - TotalRecordCount = totalCount, - Items = itemsArray //TODO Fix The co-variant conversion between Folder[] and BaseItem[], this can generate runtime issues. - }; + return UserViewBuilder.SortAndPage(result, null, query, LibraryManager, true); } public override int GetChildCount(User user) From 4f3bfd9146a4960dec6c45a61fca955b6f31535e Mon Sep 17 00:00:00 2001 From: ThreeFive-O Date: Fri, 24 Jan 2020 23:33:32 +0100 Subject: [PATCH 226/252] Add Transcoding framerate info --- MediaBrowser.Api/Playback/StreamState.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs | 2 +- MediaBrowser.Controller/MediaEncoding/JobLogger.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 7396b5c99b..d5d2f58c03 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Api.Playback _mediaSourceManager = mediaSourceManager; } - public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate) + public override void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate) { ApiEntryPoint.Instance.ReportTranscodingProgress(TranscodingJob, this, transcodingPosition, framerate, percentComplete, bytesTranscoded, bitRate); } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 34af3b1568..35f188bb79 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -665,7 +665,7 @@ namespace MediaBrowser.Controller.MediaEncoding } public IProgress Progress { get; set; } - public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float framerate, double? percentComplete, long bytesTranscoded, int? bitRate) + public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate) { Progress.Report(percentComplete.Value); } diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index ac989f6ba1..171aedb0e0 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -146,7 +146,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (framerate.HasValue || percent.HasValue) { - state.ReportTranscodingProgress(transcodingPosition, 0, percent, 0, bitRate); + state.ReportTranscodingProgress(transcodingPosition, framerate, percent, bytesTranscoded, bitRate); } } } From eb04126473d01eb140a20675573945a918e47a8a Mon Sep 17 00:00:00 2001 From: a-alshabib Date: Thu, 23 Jan 2020 14:25:13 +0000 Subject: [PATCH 227/252] Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- Emby.Server.Implementations/Localization/Core/ar.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index 4da3cdd3b7..8932bd0d74 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -9,7 +9,7 @@ "Channels": "القنوات", "ChapterNameValue": "الباب {0}", "Collections": "مجموعات", - "DeviceOfflineWithName": "تم قطع الاتصال بـ{0}", + "DeviceOfflineWithName": " قُطِع الاتصال بـ{0}", "DeviceOnlineWithName": "{0} متصل", "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}", "Favorites": "التفضيلات", From ad5081f2763e8d9a323c7df1f6b3d89dc8ed0466 Mon Sep 17 00:00:00 2001 From: SaddFox Date: Fri, 24 Jan 2020 14:46:31 +0000 Subject: [PATCH 228/252] Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sl/ --- Emby.Server.Implementations/Localization/Core/sl-SI.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index c141a40f6e..b43cfbb747 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -12,7 +12,7 @@ "DeviceOfflineWithName": "{0} je prekinil povezavo", "DeviceOnlineWithName": "{0} je povezan", "FailedLoginAttemptWithUserName": "Neuspešen poskus prijave z {0}", - "Favorites": "Priljubljeni", + "Favorites": "Priljubljeno", "Folders": "Mape", "Genres": "Zvrsti", "HeaderAlbumArtists": "Izvajalci albuma", From d6786c730489fc5bd9c78faf39628f9f689ce408 Mon Sep 17 00:00:00 2001 From: ferferga Date: Sat, 25 Jan 2020 23:01:48 +0100 Subject: [PATCH 229/252] Removed old 'automatic restart after update' features' --- .../EntryPoints/AutomaticRestartEntryPoint.cs | 128 ------------------ .../Configuration/ServerConfiguration.cs | 2 - 2 files changed, 130 deletions(-) delete mode 100644 Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs diff --git a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs deleted file mode 100644 index a6eb1152fb..0000000000 --- a/Emby.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs +++ /dev/null @@ -1,128 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1600 - -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.LiveTv; -using MediaBrowser.Model.Tasks; -using Microsoft.Extensions.Logging; - -namespace Emby.Server.Implementations.EntryPoints -{ - public class AutomaticRestartEntryPoint : IServerEntryPoint - { - private readonly IServerApplicationHost _appHost; - private readonly ILogger _logger; - private readonly ITaskManager _iTaskManager; - private readonly ISessionManager _sessionManager; - private readonly IServerConfigurationManager _config; - private readonly ILiveTvManager _liveTvManager; - - private Timer _timer; - - public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager) - { - _appHost = appHost; - _logger = logger; - _iTaskManager = iTaskManager; - _sessionManager = sessionManager; - _config = config; - _liveTvManager = liveTvManager; - } - - public Task RunAsync() - { - if (_appHost.CanSelfRestart) - { - _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged; - } - - return Task.CompletedTask; - } - - void _appHost_HasPendingRestartChanged(object sender, EventArgs e) - { - DisposeTimer(); - - if (_appHost.HasPendingRestart) - { - _timer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(15)); - } - } - - private async void TimerCallback(object state) - { - if (_config.Configuration.EnableAutomaticRestart) - { - var isIdle = await IsIdle().ConfigureAwait(false); - - if (isIdle) - { - DisposeTimer(); - - _logger.LogInformation("Automatically restarting the system because it is idle and a restart is required."); - - try - { - _appHost.Restart(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error restarting server"); - } - } - } - } - - private async Task IsIdle() - { - if (_iTaskManager.ScheduledTasks.Any(i => i.State != TaskState.Idle)) - { - return false; - } - - if (_liveTvManager.Services.Count == 1) - { - try - { - var timers = await _liveTvManager.GetTimers(new TimerQuery(), CancellationToken.None).ConfigureAwait(false); - if (timers.Items.Any(i => i.Status == RecordingStatus.InProgress)) - { - return false; - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error getting timers"); - } - } - - var now = DateTime.UtcNow; - - return !_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 30); - } - - public void Dispose() - { - _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged; - - DisposeTimer(); - } - - private void DisposeTimer() - { - if (_timer != null) - { - _timer.Dispose(); - _timer = null; - } - } - } -} diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index cf6d9c2f6c..598ff3a808 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -159,7 +159,6 @@ namespace MediaBrowser.Model.Configuration public MetadataOptions[] MetadataOptions { get; set; } - public bool EnableAutomaticRestart { get; set; } public bool SkipDeserializationForBasicTypes { get; set; } public string ServerName { get; set; } @@ -249,7 +248,6 @@ namespace MediaBrowser.Model.Configuration EnableDashboardResponseCaching = true; EnableCaseSensitiveItemIds = true; - EnableAutomaticRestart = true; AutoRunWebApp = true; EnableRemoteAccess = true; From b5be0c567ff95ad76e09add9cf546f0bbfba2420 Mon Sep 17 00:00:00 2001 From: mtanECECS Date: Mon, 27 Jan 2020 03:25:12 +0000 Subject: [PATCH 230/252] Added translation using Weblate (Filipino) --- Emby.Server.Implementations/Localization/Core/fil.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/fil.json diff --git a/Emby.Server.Implementations/Localization/Core/fil.json b/Emby.Server.Implementations/Localization/Core/fil.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/fil.json @@ -0,0 +1 @@ +{} From 71bdcd730705a714ee208eaad7290b7c68df3885 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 27 Jan 2020 17:24:29 +0100 Subject: [PATCH 231/252] Fix spacing logs --- Jellyfin.Server/Program.cs | 2 +- Jellyfin.Server/Resources/Configuration/logging.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index fa9b626744..2638d5bfa3 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -485,7 +485,7 @@ namespace Jellyfin.Server .WriteTo.Async(x => x.File( Path.Combine(appPaths.LogDirectoryPath, "log_.log"), rollingInterval: RollingInterval.Day, - outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}")) + outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}")) .Enrich.FromLogContext() .Enrich.WithThreadId() .CreateLogger(); diff --git a/Jellyfin.Server/Resources/Configuration/logging.json b/Jellyfin.Server/Resources/Configuration/logging.json index e9f71afd49..acbca8b856 100644 --- a/Jellyfin.Server/Resources/Configuration/logging.json +++ b/Jellyfin.Server/Resources/Configuration/logging.json @@ -20,7 +20,7 @@ "retainedFileCountLimit": 3, "rollOnFileSizeLimit": true, "fileSizeLimitBytes": 100000000, - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}:{Message}{NewLine}{Exception}" + "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}" } } ] From 84fbf64867b69129938c4a800c8605793bc11399 Mon Sep 17 00:00:00 2001 From: Matt07211 Date: Tue, 28 Jan 2020 19:37:13 +1100 Subject: [PATCH 232/252] Fix broken webserver caused by dotnet publish. This should be considered a temp fix, as it fixes the symptom and not the actual root cause of the issue. Why this works? I *feel like* this is a dependency issue and this change forces it to evalute dependencys in a different order, avoiding the bug. --- deployment/linux-x64/docker-build.sh | 2 +- deployment/macos/docker-build.sh | 2 +- deployment/portable/docker-build.sh | 2 +- deployment/win-x64/docker-build.sh | 2 +- deployment/win-x86/docker-build.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deployment/linux-x64/docker-build.sh b/deployment/linux-x64/docker-build.sh index 8860f943c9..e33328a36a 100755 --- a/deployment/linux-x64/docker-build.sh +++ b/deployment/linux-x64/docker-build.sh @@ -26,7 +26,7 @@ rm -rf ${web_build_dir} version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" # Build archives -dotnet publish --configuration Release --self-contained --runtime linux-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime linux-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} rm -rf /dist/jellyfin_${version} diff --git a/deployment/macos/docker-build.sh b/deployment/macos/docker-build.sh index 1b4a554e6d..f9417388d7 100755 --- a/deployment/macos/docker-build.sh +++ b/deployment/macos/docker-build.sh @@ -26,7 +26,7 @@ rm -rf ${web_build_dir} version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" # Build archives -dotnet publish --configuration Release --self-contained --runtime osx-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime osx-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} rm -rf /dist/jellyfin_${version} diff --git a/deployment/portable/docker-build.sh b/deployment/portable/docker-build.sh index 0cc6e84f06..094190bbf6 100755 --- a/deployment/portable/docker-build.sh +++ b/deployment/portable/docker-build.sh @@ -26,7 +26,7 @@ rm -rf ${web_build_dir} version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" # Build archives -dotnet publish --configuration Release --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" +dotnet publish Jellyfin.Server --configuration Release --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" tar -cvzf /jellyfin_${version}.portable.tar.gz -C /dist jellyfin_${version} rm -rf /dist/jellyfin_${version} diff --git a/deployment/win-x64/docker-build.sh b/deployment/win-x64/docker-build.sh index 77372a66f7..79e5fb0bcd 100755 --- a/deployment/win-x64/docker-build.sh +++ b/deployment/win-x64/docker-build.sh @@ -32,7 +32,7 @@ rm -rf ${web_build_dir} version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" # Build binary -dotnet publish --configuration Release --self-contained --runtime win-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x64 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" # Prepare addins addin_build_dir="$( mktemp -d )" diff --git a/deployment/win-x86/docker-build.sh b/deployment/win-x86/docker-build.sh index dd411756a8..977dcf78fa 100755 --- a/deployment/win-x86/docker-build.sh +++ b/deployment/win-x86/docker-build.sh @@ -32,7 +32,7 @@ rm -rf ${web_build_dir} version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" # Build binary -dotnet publish --configuration Release --self-contained --runtime win-x86 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" +dotnet publish Jellyfin.Server --configuration Release --self-contained --runtime win-x86 --output /dist/jellyfin_${version}/ "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none;UseAppHost=true" # Prepare addins addin_build_dir="$( mktemp -d )" From f7b4dfc1cb0ad3e1920a119ff4aef1b2b95e572f Mon Sep 17 00:00:00 2001 From: diegoalrey Date: Tue, 28 Jan 2020 21:09:15 +0000 Subject: [PATCH 233/252] Added translation using Weblate (Galician) --- Emby.Server.Implementations/Localization/Core/gl.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/gl.json diff --git a/Emby.Server.Implementations/Localization/Core/gl.json b/Emby.Server.Implementations/Localization/Core/gl.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/gl.json @@ -0,0 +1 @@ +{} From 1ff74dd8f0571c1474a72c6c669a4ab2bc748421 Mon Sep 17 00:00:00 2001 From: Pooka Mustard Date: Mon, 27 Jan 2020 21:42:45 +0000 Subject: [PATCH 234/252] Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- Emby.Server.Implementations/Localization/Core/ar.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index 8932bd0d74..f0f165b22a 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -9,7 +9,7 @@ "Channels": "القنوات", "ChapterNameValue": "الباب {0}", "Collections": "مجموعات", - "DeviceOfflineWithName": " قُطِع الاتصال بـ{0}", + "DeviceOfflineWithName": "تم قطع اتصال {0}", "DeviceOnlineWithName": "{0} متصل", "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}", "Favorites": "التفضيلات", @@ -75,8 +75,8 @@ "Songs": "الأغاني", "StartupEmbyServerIsLoading": "سيرفر Jellyfin قيد التشغيل . الرجاء المحاولة بعد قليل.", "SubtitleDownloadFailureForItem": "عملية إنزال الترجمة فشلت لـ{0}", - "SubtitleDownloadFailureFromForItem": "الترجمات فشلت في التحميل من {0} لـ {1}", - "SubtitlesDownloadedForItem": "تم تحميل الترجمات لـ {0}", + "SubtitleDownloadFailureFromForItem": "الترجمات فشلت في التحميل من {0} الى {1}", + "SubtitlesDownloadedForItem": "تم تحميل الترجمات الى {0}", "Sync": "مزامنة", "System": "النظام", "TvShows": "البرامج التلفزيونية", @@ -88,7 +88,7 @@ "UserOfflineFromDevice": "تم قطع اتصال {0} من {1}", "UserOnlineFromDevice": "{0} متصل عبر {1}", "UserPasswordChangedWithName": "تم تغيير كلمة السر للمستخدم {0}", - "UserPolicyUpdatedWithName": "سياسة المستخدمين تم تحديثها لـ {0}", + "UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم {0}", "UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}", "UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}", "ValueHasBeenAddedToLibrary": "{0} تم اضافتها الى مكتبة الوسائط", From 81fae5932bcd157cba559520504019fbe90451e2 Mon Sep 17 00:00:00 2001 From: mtanECECS Date: Mon, 27 Jan 2020 03:25:55 +0000 Subject: [PATCH 235/252] Translated using Weblate (Filipino) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fil/ --- .../Localization/Core/fil.json | 96 ++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/fil.json b/Emby.Server.Implementations/Localization/Core/fil.json index 0967ef424b..66db059d97 100644 --- a/Emby.Server.Implementations/Localization/Core/fil.json +++ b/Emby.Server.Implementations/Localization/Core/fil.json @@ -1 +1,95 @@ -{} +{ + "VersionNumber": "Bersyon {0}", + "ValueSpecialEpisodeName": "Espesyal - {0}", + "ValueHasBeenAddedToLibrary": "Naidagdag na ang {0} sa iyong media library", + "UserStoppedPlayingItemWithValues": "Natapos ni {0} ang {1} sa {2}", + "UserStartedPlayingItemWithValues": "Si {0} ay nagplaplay ng {1} sa {2}", + "UserPolicyUpdatedWithName": "Ang user policy ay naiupdate para kay {0}", + "UserPasswordChangedWithName": "Napalitan na ang password ni {0}", + "UserOnlineFromDevice": "Si {0} ay nakakonekta galing sa {1}", + "UserOfflineFromDevice": "Si {0} ay nadiskonekta galing sa {1}", + "UserLockedOutWithName": "Si {0} ay nalock out", + "UserDownloadingItemWithValues": "Nagdadownload si {0} ng {1}", + "UserDeletedWithName": "Natanggal na is user {0}", + "UserCreatedWithName": "Nagawa na si user {0}", + "User": "User", + "TvShows": "Pelikula", + "System": "Sistema", + "Sync": "Pag-sync", + "SubtitlesDownloadedForItem": "Naidownload na ang subtitles {0}", + "SubtitleDownloadFailureFromForItem": "Hindi naidownload ang subtitles {0} para sa {1}", + "StartupEmbyServerIsLoading": "Nagloload ang Jellyfin Server. Sandaling maghintay.", + "Songs": "Kanta", + "Shows": "Pelikula", + "ServerNameNeedsToBeRestarted": "Kailangan irestart ang {0}", + "ScheduledTaskStartedWithName": "Nagsimula na ang {0}", + "ScheduledTaskFailedWithName": "Hindi gumana and {0}", + "ProviderValue": "Ang provider ay {0}", + "PluginUpdatedWithName": "Naiupdate na ang {0}", + "PluginUninstalledWithName": "Naiuninstall na ang {0}", + "PluginInstalledWithName": "Nainstall na ang {0}", + "Plugin": "Plugin", + "Playlists": "Playlists", + "Photos": "Larawan", + "NotificationOptionVideoPlaybackStopped": "Huminto na ang pelikula", + "NotificationOptionVideoPlayback": "Nagsimula na ang pelikula", + "NotificationOptionUserLockedOut": "Nakalock out ang user", + "NotificationOptionTaskFailed": "Hindi gumana ang scheduled task", + "NotificationOptionServerRestartRequired": "Kailangan irestart ang server", + "NotificationOptionPluginUpdateInstalled": "Naiupdate na ang plugin", + "NotificationOptionPluginUninstalled": "Naiuninstall na ang plugin", + "NotificationOptionPluginInstalled": "Nainstall na ang plugin", + "NotificationOptionPluginError": "Hindi gumagana ang plugin", + "NotificationOptionNewLibraryContent": "May bagong content na naidagdag", + "NotificationOptionInstallationFailed": "Hindi nainstall ng mabuti", + "NotificationOptionCameraImageUploaded": "Naiupload na ang picture", + "NotificationOptionAudioPlaybackStopped": "Huminto na ang patugtog", + "NotificationOptionAudioPlayback": "Nagsimula na ang patugtog", + "NotificationOptionApplicationUpdateInstalled": "Naiupdate na ang aplikasyon", + "NotificationOptionApplicationUpdateAvailable": "May bagong update ang aplikasyon", + "NewVersionIsAvailable": "May bagong version ng Jellyfin Server na pwede idownload.", + "NameSeasonUnknown": "Hindi alam ang season", + "NameSeasonNumber": "Season {0}", + "NameInstallFailed": "Hindi nainstall ang {0}", + "MusicVideos": "Music video", + "Music": "Kanta", + "Movies": "Pelikula", + "MixedContent": "Halo-halong content", + "MessageServerConfigurationUpdated": "Naiupdate na ang server configuration", + "MessageNamedServerConfigurationUpdatedWithValue": "Naiupdate na ang server configuration section {0}", + "MessageApplicationUpdatedTo": "Ang Jellyfin Server ay naiupdate to {0}", + "MessageApplicationUpdated": "Naiupdate na ang Jellyfin Server", + "Latest": "Pinakabago", + "LabelRunningTimeValue": "Oras: {0}", + "LabelIpAddressValue": "Ang IP Address ay {0}", + "ItemRemovedWithName": "Naitanggal ang {0} sa library", + "ItemAddedWithName": "Naidagdag ang {0} sa library", + "Inherit": "Manahin", + "HeaderRecordingGroups": "Pagtatalang Grupo", + "HeaderNextUp": "Susunod", + "HeaderLiveTV": "Live TV", + "HeaderFavoriteSongs": "Paboritong Kanta", + "HeaderFavoriteShows": "Paboritong Pelikula", + "HeaderFavoriteEpisodes": "Paboritong Episodes", + "HeaderFavoriteArtists": "Paboritong Artista", + "HeaderFavoriteAlbums": "Paboritong Albums", + "HeaderContinueWatching": "Ituloy Manood", + "HeaderCameraUploads": "Camera Uploads", + "HeaderAlbumArtists": "Artista ng Album", + "Genres": "Kategorya", + "Folders": "Folders", + "Favorites": "Paborito", + "FailedLoginAttemptWithUserName": "maling login galing {0}", + "DeviceOnlineWithName": "nakakonekta si {0}", + "DeviceOfflineWithName": "nadiskonekta si {0}", + "Collections": "Koleksyon", + "ChapterNameValue": "Kabanata {0}", + "Channels": "Channel", + "CameraImageUploadedFrom": "May bagong larawan na naupload galing {0}", + "Books": "Libro", + "AuthenticationSucceededWithUserName": "{0} na patunayan", + "Artists": "Artista", + "Application": "Aplikasyon", + "AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}", + "Albums": "Albums" +} From 2186df8ac445b74dce86bdf2fe7ba5500da0c492 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 28 Jan 2020 22:42:07 +0100 Subject: [PATCH 236/252] Update deps --- Emby.Naming/Emby.Naming.csproj | 5 +---- Emby.Notifications/CoreNotificationTypes.cs | 1 - Emby.Photos/Emby.Photos.csproj | 5 ----- .../Emby.Server.Implementations.csproj | 8 ++++---- Jellyfin.Api/Jellyfin.Api.csproj | 4 ++-- .../Jellyfin.Drawing.Skia.csproj | 6 +++--- Jellyfin.Server/Jellyfin.Server.csproj | 11 ++++------- .../MediaBrowser.Common.csproj | 2 +- .../MediaBrowser.Controller.csproj | 2 +- .../MediaBrowser.MediaEncoding.csproj | 4 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 4 ++-- .../MediaBrowser.Providers.csproj | 6 +++--- .../Jellyfin.Api.Tests.csproj | 4 ++-- .../Jellyfin.Common.Tests.csproj | 2 +- .../Jellyfin.MediaEncoding.Tests.csproj | 2 +- .../Jellyfin.Naming.Tests.csproj | 2 +- ...llyfin.Server.Implementations.Tests.csproj | 19 +++++++++---------- 17 files changed, 37 insertions(+), 50 deletions(-) diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 900b9694c2..c6b08d372b 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -4,10 +4,7 @@ netstandard2.1 false true - - - - true + true diff --git a/Emby.Notifications/CoreNotificationTypes.cs b/Emby.Notifications/CoreNotificationTypes.cs index 0f9fc08d99..d11e01e334 100644 --- a/Emby.Notifications/CoreNotificationTypes.cs +++ b/Emby.Notifications/CoreNotificationTypes.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using MediaBrowser.Controller; using MediaBrowser.Controller.Notifications; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Notifications; diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 29ed3c5f75..ed6918dba6 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -1,9 +1,4 @@ - - - true - - diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 77333a03d0..f8560ca856 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,11 +29,11 @@ - - - + + + - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 38cdb0998d..4241d9b95e 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -8,9 +8,9 @@ - + - + diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index febb1adabc..f9ce0bbe12 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -12,8 +12,8 @@ - - + + @@ -25,7 +25,7 @@ - + diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 62bf5b0fbd..bc18f11fde 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -6,9 +6,6 @@ netcoreapp3.1 false true - - - true enable @@ -38,16 +35,16 @@ - - - + + + - + diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 567fcdda19..3da8644040 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -12,7 +12,7 @@ - + diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 60c76ef7db..f85b0949a1 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -8,7 +8,7 @@ - + diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 783457bda3..a312dcd705 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 53cd08fbd0..4029084e71 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -15,9 +15,9 @@ - + - + diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 8d373be289..5593c5036c 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -11,11 +11,11 @@ - - + + - + diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 1f83489bdf..0e8ef135ef 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -9,11 +9,11 @@ - + - + diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index bc0114d1ef..da5e6576d3 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index 5d9b32086c..c01edd9fed 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index 79d2f21441..f246d459ba 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index f62d3dcbc5..c554bc9372 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -1,19 +1,18 @@  - netcoreapp3.1 - false - Jellyfin.Server.Implementations.Tests + netcoreapp3.1 + false + Jellyfin.Server.Implementations.Tests - - - - - - - + + + + + + From a3615dec693ce8f9b0a803d98e9ba0b1f32f62a7 Mon Sep 17 00:00:00 2001 From: WNotCoder Date: Wed, 29 Jan 2020 18:10:00 +0000 Subject: [PATCH 237/252] 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index a567e6e2d3..e0daac8a59 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -79,7 +79,7 @@ "SubtitlesDownloadedForItem": "已为 {0} 下载了字幕", "Sync": "同步", "System": "系统", - "TvShows": "电视节目", + "TvShows": "电视剧", "User": "用户", "UserCreatedWithName": "用户 {0} 已创建", "UserDeletedWithName": "用户 {0} 已删除", From 6cd9c84ddf4940a5db1c3d84f1f61a89e5b4e168 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Fri, 31 Jan 2020 18:10:15 +0100 Subject: [PATCH 238/252] Update MediaBrowser.Model/Extensions/StringHelper.cs Co-Authored-By: dkanada --- MediaBrowser.Model/Extensions/StringHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs index 5864120544..f97a07096c 100644 --- a/MediaBrowser.Model/Extensions/StringHelper.cs +++ b/MediaBrowser.Model/Extensions/StringHelper.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Model.Extensions (chars, buf) => { chars[0] = char.ToUpperInvariant(buf[0]); - for (int i = 1; i Date: Fri, 31 Jan 2020 22:23:46 +0100 Subject: [PATCH 239/252] Fix warnings --- Emby.Dlna/Didl/DidlBuilder.cs | 1 - Emby.Dlna/PlayTo/PlayToController.cs | 1 - Emby.Dlna/PlayTo/PlaylistItemFactory.cs | 1 - Emby.Dlna/Server/DescriptionXmlBuilder.cs | 1 - Emby.Dlna/Service/BaseControlHandler.cs | 1 - .../Activity/ActivityManager.cs | 1 - .../ApplicationHost.cs | 25 +-------------- .../Channels/ChannelPostScanTask.cs | 32 ++++++++----------- .../Channels/RefreshChannelsScheduledTask.cs | 24 ++++++++++---- .../Collections/CollectionImageProvider.cs | 1 - .../ServerConfigurationManager.cs | 1 - .../EntryPoints/LibraryChangedNotifier.cs | 1 - .../EntryPoints/UdpServerEntryPoint.cs | 1 - .../LiveTv/EmbyTV/EncodedRecorder.cs | 1 - .../LiveTv/TunerHosts/M3uParser.cs | 1 - .../Net/SocketFactory.cs | 1 - .../Net/WebSocketConnectEventArgs.cs | 2 -- .../Playlists/PlaylistImageProvider.cs | 1 - .../SocketSharp/HttpPostedFile.cs | 6 ---- .../SocketSharp/WebSocketSharpListener.cs | 2 -- .../WebSockets/WebSocketManager.cs | 2 -- Jellyfin.Drawing.Skia/SkiaCodecException.cs | 1 - Jellyfin.Server/Program.cs | 25 +++++++-------- Jellyfin.Server/Startup.cs | 1 - MediaBrowser.Api/ApiEntryPoint.cs | 1 - MediaBrowser.Api/EnvironmentService.cs | 1 - MediaBrowser.Api/ItemUpdateService.cs | 1 - MediaBrowser.Api/Library/LibraryService.cs | 4 --- .../Playback/Hls/DynamicHlsService.cs | 1 - MediaBrowser.Api/Playback/MediaInfoService.cs | 2 -- MediaBrowser.Api/Playback/StreamRequest.cs | 1 - .../System/ActivityLogWebSocketListener.cs | 1 - MediaBrowser.Api/UserLibrary/GenresService.cs | 1 - .../UserLibrary/PersonsService.cs | 1 - MediaBrowser.Api/UserLibrary/YearsService.cs | 1 - MediaBrowser.Controller/Entities/Video.cs | 1 - .../MediaEncoding/EncodingJobOptions.cs | 1 - .../MediaEncoding/JobLogger.cs | 1 - .../Net/IHttpResultFactory.cs | 2 -- .../Net/IWebSocketConnection.cs | 1 - .../Net/StaticResultOptions.cs | 2 -- .../Persistence/MediaAttachmentQuery.cs | 1 - .../Parsers/BaseItemXmlParser.cs | 1 - .../Savers/BaseXmlSaver.cs | 1 - .../Subtitles/AssParser.cs | 1 - .../Subtitles/SrtParser.cs | 1 - .../Subtitles/SsaParser.cs | 1 - .../Subtitles/TtmlWriter.cs | 1 - MediaBrowser.Model/IO/IFileSystem.cs | 1 - MediaBrowser.Model/Net/ISocketFactory.cs | 1 - MediaBrowser.Model/Tasks/IScheduledTask.cs | 4 +-- .../Manager/ItemImageProvider.cs | 1 - .../MediaInfo/SubtitleResolver.cs | 1 - .../TV/DummySeasonProvider.cs | 1 - .../TV/TheTVDB/TvdbSeriesProvider.cs | 1 - .../Tmdb/BoxSets/TmdbBoxSetImageProvider.cs | 1 - .../Models/Search/ExternalIdLookupResult.cs | 1 - .../Tmdb/Movies/GenericTmdbMovieInfo.cs | 2 -- .../Tmdb/Movies/TmdbImageProvider.cs | 1 - .../Tmdb/Movies/TmdbMovieProvider.cs | 1 - .../Tmdb/Music/TmdbMusicVideoProvider.cs | 1 - .../Tmdb/People/TmdbPersonImageProvider.cs | 1 - .../Tmdb/TV/TmdbEpisodeImageProvider.cs | 1 - .../Tmdb/TV/TmdbEpisodeProviderBase.cs | 1 - .../Tmdb/TV/TmdbSeasonImageProvider.cs | 2 -- .../Tmdb/TV/TmdbSeasonProvider.cs | 1 - .../Tmdb/TV/TmdbSeriesImageProvider.cs | 1 - .../Tmdb/TV/TmdbSeriesProvider.cs | 1 - .../Tmdb/Trailers/TmdbTrailerProvider.cs | 1 - .../Api/DashboardService.cs | 2 -- .../Parsers/BaseNfoParser.cs | 1 - RSSDP/DeviceEventArgs.cs | 2 -- RSSDP/DeviceUnavailableEventArgs.cs | 3 -- RSSDP/DiscoveredSsdpDevice.cs | 4 --- RSSDP/DisposableManagedObjectBase.cs | 1 - RSSDP/HttpParserBase.cs | 2 -- RSSDP/HttpRequestParser.cs | 4 --- RSSDP/HttpResponseParser.cs | 3 -- RSSDP/IEnumerableExtensions.cs | 1 - RSSDP/ISsdpDevicePublisher.cs | 1 - RSSDP/SsdpConstants.cs | 5 --- RSSDP/SsdpEmbeddedDevice.cs | 4 --- .../Jellyfin.Naming.Tests/Video/StubTests.cs | 4 +-- .../IO/ManagedFileSystemTests.cs | 1 - 84 files changed, 46 insertions(+), 182 deletions(-) diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 85ef9d4829..a5e46df78d 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -18,7 +18,6 @@ using MediaBrowser.Controller.Playlists; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index c58f16438b..d378c2c304 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Emby.Dlna.Didl; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; diff --git a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs index 446d8e1e6e..3b1cbab628 100644 --- a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs +++ b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs @@ -1,4 +1,3 @@ -using System.Globalization; using System.IO; using System.Linq; using MediaBrowser.Controller.Entities; diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs index 03d8f80abb..1b53e92424 100644 --- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs +++ b/Emby.Dlna/Server/DescriptionXmlBuilder.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Text; using Emby.Dlna.Common; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Extensions; namespace Emby.Dlna.Server { diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 49129f6ffd..a8da7aecd2 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index b03c4d1824..6712c47828 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -2,7 +2,6 @@ #pragma warning disable SA1600 using System; -using System.Linq; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Events; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 226a8f3026..e2df8877ab 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1007,7 +1007,7 @@ namespace Emby.Server.Implementations { string dir = Path.Combine(ApplicationPaths.PluginsPath, args.Argument.name); var types = Directory.EnumerateFiles(dir, "*.dll", SearchOption.AllDirectories) - .Select(x => Assembly.LoadFrom(x)) + .Select(Assembly.LoadFrom) .SelectMany(x => x.ExportedTypes) .Where(x => x.IsClass && !x.IsAbstract && !x.IsInterface && !x.IsGenericType) .ToArray(); @@ -1707,29 +1707,6 @@ namespace Emby.Server.Implementations _plugins = list.ToArray(); } - /// - /// This returns localhost in the case of no external dns, and the hostname if the - /// dns is prefixed with a valid Uri prefix. - /// - /// The external dns prefix to get the hostname of. - /// The hostname in . - private static string GetHostnameFromExternalDns(string externalDns) - { - if (string.IsNullOrEmpty(externalDns)) - { - return "localhost"; - } - - try - { - return new Uri(externalDns).Host; - } - catch - { - return externalDns; - } - } - public virtual void LaunchUrl(string url) { if (!CanLaunchWebBrowser) diff --git a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs index 36e0e5e26d..6cbd04fea9 100644 --- a/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/Emby.Server.Implementations/Channels/ChannelPostScanTask.cs @@ -35,14 +35,6 @@ namespace Emby.Server.Implementations.Channels return Task.CompletedTask; } - public static string GetUserDistinctValue(User user) - { - var channels = user.Policy.EnabledChannels - .OrderBy(i => i); - - return string.Join("|", channels); - } - private void CleanDatabase(CancellationToken cancellationToken) { var installedChannelIds = ((ChannelManager)_channelManager).GetInstalledChannelIds(); @@ -75,19 +67,23 @@ namespace Emby.Server.Implementations.Channels { cancellationToken.ThrowIfCancellationRequested(); - _libraryManager.DeleteItem(item, new DeleteOptions - { - DeleteFileLocation = false - - }, false); + _libraryManager.DeleteItem( + item, + new DeleteOptions + { + DeleteFileLocation = false + }, + false); } // Finally, delete the channel itself - _libraryManager.DeleteItem(channel, new DeleteOptions - { - DeleteFileLocation = false - - }, false); + _libraryManager.DeleteItem( + channel, + new DeleteOptions + { + DeleteFileLocation = false + }, + false); } } } diff --git a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs index 039e2c1383..03e6abcfba 100644 --- a/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs @@ -28,18 +28,28 @@ namespace Emby.Server.Implementations.Channels _libraryManager = libraryManager; } + /// public string Name => "Refresh Channels"; + /// public string Description => "Refreshes internet channel information."; + /// public string Category => "Internet Channels"; + /// public bool IsHidden => ((ChannelManager)_channelManager).Channels.Length == 0; + /// public bool IsEnabled => true; + /// public bool IsLogged => true; + /// + public string Key => "RefreshInternetChannels"; + + /// public async Task Execute(CancellationToken cancellationToken, IProgress progress) { var manager = (ChannelManager)_channelManager; @@ -50,18 +60,18 @@ namespace Emby.Server.Implementations.Channels .ConfigureAwait(false); } - /// - /// Creates the triggers that define when the task will run - /// + /// public IEnumerable GetDefaultTriggers() { - return new[] { + return new[] + { // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks + } }; } - - public string Key => "RefreshInternetChannels"; } } diff --git a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs index 8006b86948..8b14079844 100644 --- a/Emby.Server.Implementations/Collections/CollectionImageProvider.cs +++ b/Emby.Server.Implementations/Collections/CollectionImageProvider.cs @@ -1,7 +1,6 @@ #pragma warning disable CS1591 #pragma warning disable SA1600 -using System; using System.Collections.Generic; using System.Linq; using Emby.Server.Implementations.Images; diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index 3d8d15d197..30b654886b 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using Emby.Server.Implementations.AppBase; diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index f85d52dbc1..06458baedc 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -16,7 +16,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; -using MediaBrowser.Model.Extensions; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints diff --git a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs index a83817cb96..529f835606 100644 --- a/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs +++ b/Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs @@ -1,4 +1,3 @@ -using System; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Udp; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index ee5086a65f..6e4ac2fecc 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -2,7 +2,6 @@ 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; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 3d2267e755..51f61bac76 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -10,7 +10,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Extensions; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.TunerHosts diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index 4e04cde78c..e42ff8496e 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Net; using System.Net.Sockets; using MediaBrowser.Model.Net; diff --git a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs b/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs index e3047d3926..6880766f9a 100644 --- a/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs +++ b/Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs @@ -1,6 +1,4 @@ using System; -using System.Net.WebSockets; -using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; namespace Emby.Server.Implementations.Net diff --git a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs index 2dfe59088d..bb56d9771b 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using Emby.Server.Implementations.Images; diff --git a/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs b/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs index 95b7912fbb..7479d81045 100644 --- a/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs +++ b/Emby.Server.Implementations/SocketSharp/HttpPostedFile.cs @@ -1,11 +1,5 @@ using System; -using System.Collections.Generic; -using System.Globalization; using System.IO; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Services; public sealed class HttpPostedFile : IDisposable { diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs index ba5ba1904c..2e12a19fd9 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpListener.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.Net; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; diff --git a/Emby.Server.Implementations/WebSockets/WebSocketManager.cs b/Emby.Server.Implementations/WebSockets/WebSocketManager.cs index efd97e4ff1..31a7468fbc 100644 --- a/Emby.Server.Implementations/WebSockets/WebSocketManager.cs +++ b/Emby.Server.Implementations/WebSockets/WebSocketManager.cs @@ -1,12 +1,10 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net.WebSockets; using System.Text; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; diff --git a/Jellyfin.Drawing.Skia/SkiaCodecException.cs b/Jellyfin.Drawing.Skia/SkiaCodecException.cs index 8158b846dd..1d2db5515f 100644 --- a/Jellyfin.Drawing.Skia/SkiaCodecException.cs +++ b/Jellyfin.Drawing.Skia/SkiaCodecException.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using System.Globalization; using SkiaSharp; diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 2638d5bfa3..1b4280d82d 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -4,7 +4,6 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; -using System.Net.Security; using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -238,7 +237,7 @@ namespace Jellyfin.Server { foreach (var address in addresses) { - _logger.LogInformation("Kestrel listening on {ipaddr}", address); + _logger.LogInformation("Kestrel listening on {IpAddress}", address); options.Listen(address, appHost.HttpPort); if (appHost.EnableHttps && appHost.Certificate != null) @@ -443,20 +442,18 @@ namespace Jellyfin.Server if (!File.Exists(configPath)) { // For some reason the csproj name is used instead of the assembly name - using (Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath)) + await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath); + if (resource == null) { - if (resource == null) - { - throw new InvalidOperationException( - string.Format( - CultureInfo.InvariantCulture, - "Invalid resource path: '{0}'", - ResourcePath)); - } - - using Stream dst = File.Open(configPath, FileMode.CreateNew); - await resource.CopyToAsync(dst).ConfigureAwait(false); + throw new InvalidOperationException( + string.Format( + CultureInfo.InvariantCulture, + "Invalid resource path: '{0}'", + ResourcePath)); } + + await using Stream dst = File.Open(configPath, FileMode.CreateNew); + await resource.CopyToAsync(dst).ConfigureAwait(false); } return new ConfigurationBuilder() diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 3ee5fb8b50..4d7d56e9d4 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -3,7 +3,6 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 1a3657c920..7d3546eb7d 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -12,7 +12,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index c6dbfb9387..322b9805ba 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/MediaBrowser.Api/EnvironmentService.cs @@ -6,7 +6,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index 1847f7fde6..c81e89ca3c 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index b1ea3e2627..3d1e4a3637 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -16,17 +16,13 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.TV; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index e85ed20505..262f517869 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -16,7 +16,6 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 15880a9a16..0eb184d148 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -5,7 +5,6 @@ using System; using System.Buffers; -using System.Collections.Generic; using System.Globalization; using System.Text.Json; using System.Linq; @@ -23,7 +22,6 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index 7626cc3785..9ba8eda91f 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -1,4 +1,3 @@ -using System; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Services; diff --git a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs index a036619b81..4b6a22b7d5 100644 --- a/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs +++ b/MediaBrowser.Api/System/ActivityLogWebSocketListener.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; diff --git a/MediaBrowser.Api/UserLibrary/GenresService.cs b/MediaBrowser.Api/UserLibrary/GenresService.cs index 13bb88ca8d..1fa272a5f7 100644 --- a/MediaBrowser.Api/UserLibrary/GenresService.cs +++ b/MediaBrowser.Api/UserLibrary/GenresService.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; diff --git a/MediaBrowser.Api/UserLibrary/PersonsService.cs b/MediaBrowser.Api/UserLibrary/PersonsService.cs index 853eada256..3204e5219f 100644 --- a/MediaBrowser.Api/UserLibrary/PersonsService.cs +++ b/MediaBrowser.Api/UserLibrary/PersonsService.cs @@ -6,7 +6,6 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; diff --git a/MediaBrowser.Api/UserLibrary/YearsService.cs b/MediaBrowser.Api/UserLibrary/YearsService.cs index 07b9aff1b8..d023ee90ab 100644 --- a/MediaBrowser.Api/UserLibrary/YearsService.cs +++ b/MediaBrowser.Api/UserLibrary/YearsService.cs @@ -6,7 +6,6 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index af4d227bc8..c3ea7f347a 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index d64feb2f7c..addc88174f 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Services; diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index 171aedb0e0..11d206df7e 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -4,7 +4,6 @@ using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; -using MediaBrowser.Model.Extensions; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.MediaEncoding diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs index fb00ee0081..25404fa78d 100644 --- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs +++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; - -using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; namespace MediaBrowser.Controller.Net diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index 566897b31f..31eb7ccb75 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -3,7 +3,6 @@ using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Net; -using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs index 7267326607..071beaed19 100644 --- a/MediaBrowser.Controller/Net/StaticResultOptions.cs +++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using MediaBrowser.Model.IO; - namespace MediaBrowser.Controller.Net { public class StaticResultOptions diff --git a/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs index 91ab34aab6..e3b2d46650 100644 --- a/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs +++ b/MediaBrowser.Controller/Persistence/MediaAttachmentQuery.cs @@ -1,5 +1,4 @@ using System; -using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Persistence { diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index 59c8f4da50..d4b98182f9 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -9,7 +9,6 @@ using System.Xml; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; namespace MediaBrowser.LocalMetadata.Parsers diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index 46c5317974..ba1d850e3c 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs index 605504418b..293cf5ea5b 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs @@ -5,7 +5,6 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.MediaEncoding.Subtitles diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs index 0606dbdb29..c98dd15024 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs @@ -4,7 +4,6 @@ using System.Globalization; using System.IO; using System.Text.RegularExpressions; using System.Threading; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs index 0d696b9064..b94d451653 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.MediaEncoding.Subtitles diff --git a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs index 4f15bac496..7d3e185784 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs @@ -1,4 +1,3 @@ -using System; using System.IO; using System.Text; using System.Text.RegularExpressions; diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs index 48e5eea6f6..6a874d0470 100644 --- a/MediaBrowser.Model/IO/IFileSystem.cs +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; namespace MediaBrowser.Model.IO { diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index dc69b1fb25..a7965463ae 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -1,4 +1,3 @@ -using System.IO; using System.Net; namespace MediaBrowser.Model.Net diff --git a/MediaBrowser.Model/Tasks/IScheduledTask.cs b/MediaBrowser.Model/Tasks/IScheduledTask.cs index a615ebb07e..71f6e15f8e 100644 --- a/MediaBrowser.Model/Tasks/IScheduledTask.cs +++ b/MediaBrowser.Model/Tasks/IScheduledTask.cs @@ -39,9 +39,9 @@ namespace MediaBrowser.Model.Tasks Task Execute(CancellationToken cancellationToken, IProgress progress); /// - /// Gets the default triggers. + /// Gets the default triggers that define when the task will run. /// - /// IEnumerable{BaseTaskTrigger}. + /// The default triggers that define when the task will run. IEnumerable GetDefaultTriggers(); } } diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index d83c0cc86f..01c950260a 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index 8195591e17..7ebbb9e237 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -5,7 +5,6 @@ using System.Linq; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index 4a6676cb99..6a1e6df8fd 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -3,7 +3,6 @@ using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 72ceadaf1d..9e791bd9d5 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index 5db0edac2a..0bdf2bce1e 100644 --- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -10,7 +10,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.Collections; using MediaBrowser.Providers.Tmdb.Models.General; using MediaBrowser.Providers.Tmdb.Movies; diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs b/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs index 6d9fe7081d..d19f4e8cbd 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs +++ b/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using MediaBrowser.Providers.Movies; namespace MediaBrowser.Providers.Tmdb.Models.Search { diff --git a/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs b/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs index b7b447b684..ad42b564c6 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs @@ -11,10 +11,8 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.Movies; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs index cdb96e6acf..039a49728b 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs @@ -13,7 +13,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.General; using MediaBrowser.Providers.Tmdb.Models.Movies; diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs index a1bea5847d..861847f71f 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs @@ -19,7 +19,6 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.Movies; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs b/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs index f3f8a92cf0..81909fa38f 100644 --- a/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs +++ b/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs @@ -6,7 +6,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Movies; namespace MediaBrowser.Providers.Tmdb.Music diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs index 44ccbf4534..e205d796ae 100644 --- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs @@ -10,7 +10,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.General; using MediaBrowser.Providers.Tmdb.Models.People; using MediaBrowser.Providers.Tmdb.Movies; diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs index 51e7891a14..558c8149e5 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -13,7 +13,6 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.General; using MediaBrowser.Providers.Tmdb.Movies; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs index 2003261c9d..e87fe9332f 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs @@ -8,7 +8,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.TV; using MediaBrowser.Providers.Tmdb.Movies; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs index 24cc8c73b2..698a436046 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -10,8 +10,6 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Tmdb.Models.General; diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs index fc0cde8b37..5ad3319717 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs @@ -14,7 +14,6 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.TV; using MediaBrowser.Providers.Tmdb.Movies; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs index 882ec7574a..0460fe9940 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -12,7 +12,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.General; using MediaBrowser.Providers.Tmdb.Models.TV; using MediaBrowser.Providers.Tmdb.Movies; diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs index 304f34c25c..7dcb272d68 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs @@ -17,7 +17,6 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Models.Search; using MediaBrowser.Providers.Tmdb.Models.TV; using MediaBrowser.Providers.Tmdb.Movies; diff --git a/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs b/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs index b0dec02450..b15de01255 100644 --- a/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs +++ b/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs @@ -5,7 +5,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Movies; using MediaBrowser.Providers.Tmdb.Movies; namespace MediaBrowser.Providers.Tmdb.Trailers diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 96ebdf4a88..a8768459ac 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -9,11 +9,9 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Plugins; -using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Plugins; -using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index b8d0e6560d..62d7a8cf4a 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -12,7 +12,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Extensions; using MediaBrowser.XbmcMetadata.Configuration; using MediaBrowser.XbmcMetadata.Savers; using Microsoft.Extensions.Logging; diff --git a/RSSDP/DeviceEventArgs.cs b/RSSDP/DeviceEventArgs.cs index 3925ba2481..05eb4a2567 100644 --- a/RSSDP/DeviceEventArgs.cs +++ b/RSSDP/DeviceEventArgs.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace Rssdp { diff --git a/RSSDP/DeviceUnavailableEventArgs.cs b/RSSDP/DeviceUnavailableEventArgs.cs index d90ddfb603..ef04904bdc 100644 --- a/RSSDP/DeviceUnavailableEventArgs.cs +++ b/RSSDP/DeviceUnavailableEventArgs.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; namespace Rssdp { diff --git a/RSSDP/DiscoveredSsdpDevice.cs b/RSSDP/DiscoveredSsdpDevice.cs index f42e7c6747..1244ce523d 100644 --- a/RSSDP/DiscoveredSsdpDevice.cs +++ b/RSSDP/DiscoveredSsdpDevice.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; using System.Net.Http.Headers; namespace Rssdp diff --git a/RSSDP/DisposableManagedObjectBase.cs b/RSSDP/DisposableManagedObjectBase.cs index 0f656fb469..bb36229c40 100644 --- a/RSSDP/DisposableManagedObjectBase.cs +++ b/RSSDP/DisposableManagedObjectBase.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Text; -using System.Threading.Tasks; namespace Rssdp.Infrastructure { diff --git a/RSSDP/HttpParserBase.cs b/RSSDP/HttpParserBase.cs index 76d816e7bd..773a06cdb6 100644 --- a/RSSDP/HttpParserBase.cs +++ b/RSSDP/HttpParserBase.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; -using System.Text; -using System.IO; namespace Rssdp.Infrastructure { diff --git a/RSSDP/HttpRequestParser.cs b/RSSDP/HttpRequestParser.cs index d4505b8ad6..279ef883ca 100644 --- a/RSSDP/HttpRequestParser.cs +++ b/RSSDP/HttpRequestParser.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Net; using System.Net.Http; -using System.Text; -using System.Threading.Tasks; namespace Rssdp.Infrastructure { diff --git a/RSSDP/HttpResponseParser.cs b/RSSDP/HttpResponseParser.cs index a77c898ffe..b96eaf625d 100644 --- a/RSSDP/HttpResponseParser.cs +++ b/RSSDP/HttpResponseParser.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; -using System.Text; -using System.Threading.Tasks; namespace Rssdp.Infrastructure { diff --git a/RSSDP/IEnumerableExtensions.cs b/RSSDP/IEnumerableExtensions.cs index c96542dca4..371454893d 100644 --- a/RSSDP/IEnumerableExtensions.cs +++ b/RSSDP/IEnumerableExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace Rssdp.Infrastructure { diff --git a/RSSDP/ISsdpDevicePublisher.cs b/RSSDP/ISsdpDevicePublisher.cs index 7e914c109d..96c15443d4 100644 --- a/RSSDP/ISsdpDevicePublisher.cs +++ b/RSSDP/ISsdpDevicePublisher.cs @@ -1,4 +1,3 @@ -using System; using System.Threading.Tasks; namespace Rssdp.Infrastructure diff --git a/RSSDP/SsdpConstants.cs b/RSSDP/SsdpConstants.cs index 8372d1cb3b..28a014fce8 100644 --- a/RSSDP/SsdpConstants.cs +++ b/RSSDP/SsdpConstants.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - namespace Rssdp.Infrastructure { /// diff --git a/RSSDP/SsdpEmbeddedDevice.cs b/RSSDP/SsdpEmbeddedDevice.cs index 6f05518a93..4810703d74 100644 --- a/RSSDP/SsdpEmbeddedDevice.cs +++ b/RSSDP/SsdpEmbeddedDevice.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; - namespace Rssdp { /// diff --git a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs index 96fa8c5a55..7b3a01bc04 100644 --- a/tests/Jellyfin.Naming.Tests/Video/StubTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/StubTests.cs @@ -1,6 +1,4 @@ -using System; -using System.Globalization; -using Emby.Naming.Common; +using Emby.Naming.Common; using Emby.Naming.Video; using Xunit; diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index e324002f0c..671c59b2ec 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -1,4 +1,3 @@ -using System; using AutoFixture; using AutoFixture.AutoMoq; using Emby.Server.Implementations.IO; From acc1846e3e40f800f3f2605c010d492e2612ee31 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 31 Jan 2020 22:56:24 +0100 Subject: [PATCH 240/252] Allow changing capitalization of usernames --- Emby.Server.Implementations/Library/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index 656eeb1459..fcf3ccedec 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -667,7 +667,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentException("Invalid username", nameof(newName)); } - if (user.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)) + if (user.Name.Equals(newName, StringComparison.Ordinal)) { throw new ArgumentException("The new and old names must be different."); } From ab2349ff3c7170bea90cffe33fce6d9f10a6c183 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sat, 1 Feb 2020 22:27:25 +0900 Subject: [PATCH 241/252] update task names and descriptions --- .../ScheduledTasks/Tasks/ChapterImagesTask.cs | 5 ++--- .../ScheduledTasks/Tasks/DeleteCacheFileTask.cs | 4 ++-- .../ScheduledTasks/Tasks/DeleteLogFileTask.cs | 10 ++++------ .../ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs | 4 ++-- .../ScheduledTasks/Tasks/PeopleValidationTask.cs | 11 +++++------ .../ScheduledTasks/Tasks/PluginUpdateTask.cs | 2 +- .../ScheduledTasks/Tasks/RefreshMediaLibraryTask.cs | 2 +- 7 files changed, 17 insertions(+), 21 deletions(-) diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index ecd5262510..5822c467b7 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -70,7 +70,7 @@ namespace Emby.Server.Implementations.ScheduledTasks } /// - /// Returns the task to be executed + /// Returns the task to be executed. /// /// The cancellation token. /// The progress. @@ -89,7 +89,6 @@ namespace Emby.Server.Implementations.ScheduledTasks SourceTypes = new SourceType[] { SourceType.Library }, HasChapterImages = false, IsVirtualItem = false - }) .OfType 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; public SessionManager( @@ -924,7 +929,6 @@ namespace Emby.Server.Implementations.Session ClientName = session.Client, DeviceId = session.DeviceId, Session = session - }, _logger); } @@ -1610,7 +1614,7 @@ namespace Emby.Server.Implementations.Session private DtoOptions _itemInfoDtoOptions; /// - /// Converts a BaseItem to a BaseItemInfo + /// Converts a BaseItem to a BaseItemInfo. /// private BaseItemDto GetItemInfo(BaseItem item, MediaSourceInfo mediaSource) { @@ -1680,7 +1684,7 @@ namespace Emby.Server.Implementations.Session } catch (Exception ex) { - _logger.LogError("Error getting {0} image info", ex, type); + _logger.LogError("Error getting image information for {0}", ex, type); return null; } } diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 700861c554..9aa7b2c88a 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -18,7 +18,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.Session { /// - /// Class GetSessions + /// Class GetSessions. /// [Route("/Sessions", "GET", Summary = "Gets a list of sessions")] [Authenticated] @@ -34,7 +34,7 @@ namespace MediaBrowser.Api.Session } /// - /// Class DisplayContent + /// Class DisplayContent. /// [Route("/Sessions/{Id}/Viewing", "POST", Summary = "Instructs a session to browse to an item or view")] [Authenticated] diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index e1b01b012c..0d63c19b5e 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -281,7 +281,6 @@ namespace MediaBrowser.Api { IsHidden = false, IsDisabled = false - }, true, true); } @@ -395,10 +394,11 @@ namespace MediaBrowser.Api throw new MethodNotAllowedException("Hashed-only passwords are not valid for this API."); } + // password should always be null return Post(new AuthenticateUserByName { Username = user.Name, - Password = null, // This should always be null + Password = null, Pw = request.Pw }); } diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index acda6a4166..6e8385cf85 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -126,12 +126,6 @@ namespace MediaBrowser.Controller.Session [JsonIgnore] public ISessionController[] SessionControllers { get; set; } - /// - /// Gets or sets the application icon URL. - /// - /// The application icon URL. - public string AppIconUrl { get; set; } - /// /// Gets or sets the supported commands. /// @@ -259,7 +253,7 @@ namespace MediaBrowser.Controller.Session foreach (var additionalUser in AdditionalUsers) { - if (userId.Equals(userId)) + if (additionalUser.UserId.Equals(userId)) { return true; } @@ -321,7 +315,7 @@ namespace MediaBrowser.Controller.Session var newPositionTicks = positionTicks + ProgressIncrement; var item = progressInfo.Item; - long? runtimeTicks = item == null ? null : item.RunTimeTicks; + long? runtimeTicks = item?.RunTimeTicks; // Don't report beyond the runtime if (runtimeTicks.HasValue && newPositionTicks >= runtimeTicks.Value) From 7df6f8d54e4d7eb8efd261b9ce5138c3efa9a364 Mon Sep 17 00:00:00 2001 From: dkanada Date: Sat, 1 Feb 2020 23:14:27 +0900 Subject: [PATCH 243/252] update error message Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 72e4e151e4..0bba19f2a3 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1684,7 +1684,7 @@ namespace Emby.Server.Implementations.Session } catch (Exception ex) { - _logger.LogError("Error getting image information for {0}", ex, type); + _logger.LogError(ex, "Error getting image information for {Type}", type); return null; } } From 692a9bfdd054b166a79e71f2a3191ac57c04a65f Mon Sep 17 00:00:00 2001 From: dkanada Date: Sat, 1 Feb 2020 23:36:40 +0900 Subject: [PATCH 244/252] update some comments Co-Authored-By: Bond-009 --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- Emby.Server.Implementations/Library/UserManager.cs | 4 ++-- MediaBrowser.Api/UserService.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 9d244b74c8..1ab376001c 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1077,7 +1077,7 @@ namespace Emby.Server.Implementations.Library innerProgress.RegisterAction(pct => progress.Report(pct * pct * 0.96)); - // validate the entire media library + // Validate the entire media library await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true).ConfigureAwait(false); progress.Report(96); diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs index a5d7153d7f..9dbd48bfc8 100644 --- a/Emby.Server.Implementations/Library/UserManager.cs +++ b/Emby.Server.Implementations/Library/UserManager.cs @@ -291,10 +291,10 @@ namespace Emby.Server.Implementations.Library && authenticationProvider != null && !(authenticationProvider is DefaultAuthenticationProvider)) { - // trust the username returned by the authentication provider + // Trust the username returned by the authentication provider username = updatedUsername; - // search the database for the user again + // Search the database for the user again // the authentication provider might have created it user = Users .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 0d63c19b5e..ac140150ba 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -394,7 +394,7 @@ namespace MediaBrowser.Api throw new MethodNotAllowedException("Hashed-only passwords are not valid for this API."); } - // password should always be null + // Password should always be null return Post(new AuthenticateUserByName { Username = user.Name, From 994481213f90ec592bdae3b7f14ccab8229a9226 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 1 Feb 2020 11:02:44 -0500 Subject: [PATCH 245/252] Add root certificates to docker image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1cd864e0b3..0bccd9d646 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ COPY --from=web-builder /dist /jellyfin/jellyfin-web # mesa-va-drivers: needed for VAAPI RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y \ - libfontconfig1 libgomp1 libva-drm2 mesa-va-drivers openssl \ + libfontconfig1 libgomp1 libva-drm2 mesa-va-drivers openssl ca-certificates \ && apt-get clean autoclean \ && apt-get autoremove \ && rm -rf /var/lib/apt/lists/* \ From 78bb45e8fa8cf4150468049bd6ff1e63539b7791 Mon Sep 17 00:00:00 2001 From: artiume Date: Sat, 1 Feb 2020 14:46:11 -0500 Subject: [PATCH 246/252] fix SSL issue in arm and arm64 verified arm build on raspbian --- Dockerfile.arm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.arm b/Dockerfile.arm index 551aa177a2..e7cbb09094 100644 --- a/Dockerfile.arm +++ b/Dockerfile.arm @@ -24,7 +24,7 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" FROM debian:buster-slim RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ - libssl-dev \ + libssl-dev ca-certificates \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media From 4302f16925cde054e8d5a578909bb01e3edc2547 Mon Sep 17 00:00:00 2001 From: artiume Date: Sat, 1 Feb 2020 14:50:45 -0500 Subject: [PATCH 247/252] Update Dockerfile.arm64 --- Dockerfile.arm64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 4c2ca12a66..a0cd0dacc2 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -24,7 +24,7 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" FROM debian:buster-slim RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ - libssl-dev \ + libssl-dev ca-certificates \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /cache /config /media \ && chmod 777 /cache /config /media From 8eced835c0c03d955aaf7b357986a709aa1b3ffa Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sun, 2 Feb 2020 15:58:29 +0800 Subject: [PATCH 248/252] Update JobLogger.cs --- MediaBrowser.Controller/MediaEncoding/JobLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index ac989f6ba1..171aedb0e0 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -146,7 +146,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (framerate.HasValue || percent.HasValue) { - state.ReportTranscodingProgress(transcodingPosition, 0, percent, 0, bitRate); + state.ReportTranscodingProgress(transcodingPosition, framerate, percent, bytesTranscoded, bitRate); } } } From 7472ae5ca92f7051b674ebe7a276895c8a028f73 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sun, 2 Feb 2020 16:27:02 +0800 Subject: [PATCH 249/252] fix fps info issue when the video is h264. Fixed an issue that when the media source is in H264 format and the frame rate info is not displayed due to missing space characters in ffmpeg log. --- MediaBrowser.Controller/MediaEncoding/JobLogger.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index 171aedb0e0..a1ae31ac54 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -90,6 +90,15 @@ namespace MediaBrowser.Controller.MediaEncoding framerate = val; } } + else if (part.Contains("fps=", StringComparison.OrdinalIgnoreCase)) + { + var rate = part.Split(new[] { '=' }, 2).Last(); + + if (float.TryParse(rate, NumberStyles.Any, _usCulture, out var val)) + { + framerate = val; + } + } else if (state.RunTimeTicks.HasValue && part.StartsWith("time=", StringComparison.OrdinalIgnoreCase)) { From 2c3b1bb8cb4ac788397929f52b824f547a6b2a18 Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Sun, 2 Feb 2020 16:46:56 +0800 Subject: [PATCH 250/252] Update JobLogger.cs --- MediaBrowser.Controller/MediaEncoding/JobLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index a1ae31ac54..b514ac699e 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -90,7 +90,7 @@ namespace MediaBrowser.Controller.MediaEncoding framerate = val; } } - else if (part.Contains("fps=", StringComparison.OrdinalIgnoreCase)) + else if (part.StartsWith("fps=", StringComparison.OrdinalIgnoreCase)) { var rate = part.Split(new[] { '=' }, 2).Last(); From c149fcb3ee4a393653603419fc988670cb31778c Mon Sep 17 00:00:00 2001 From: Nyanmisaka Date: Mon, 3 Feb 2020 00:02:24 +0800 Subject: [PATCH 251/252] Update MediaBrowser.Controller/MediaEncoding/JobLogger.cs Co-Authored-By: Bond-009 --- MediaBrowser.Controller/MediaEncoding/JobLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index b514ac699e..38fb45810b 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -92,7 +92,7 @@ namespace MediaBrowser.Controller.MediaEncoding } else if (part.StartsWith("fps=", StringComparison.OrdinalIgnoreCase)) { - var rate = part.Split(new[] { '=' }, 2).Last(); + var rate = part.Split(new[] { '=' }, 2)[^1]; if (float.TryParse(rate, NumberStyles.Any, _usCulture, out var val)) { From 176e8509739ffd3d7002c94adbd814c33b5172e2 Mon Sep 17 00:00:00 2001 From: Noitty Date: Sun, 2 Feb 2020 20:30:27 +0000 Subject: [PATCH 252/252] Translated using Weblate (Catalan) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/ --- Emby.Server.Implementations/Localization/Core/ca.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index 9961b09841..44e7cf0ce6 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -3,9 +3,9 @@ "AppDeviceValues": "Aplicació: {0}, Dispositiu: {1}", "Application": "Aplicació", "Artists": "Artistes", - "AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament", + "AuthenticationSucceededWithUserName": "{0} s'ha autentificat correctament", "Books": "Llibres", - "CameraImageUploadedFrom": "Una nova imatge de càmera ha sigut pujada des de {0}", + "CameraImageUploadedFrom": "Una nova imatge de la càmera ha sigut pujada des de {0}", "Channels": "Canals", "ChapterNameValue": "Episodi {0}", "Collections": "Col·leccions",