Browse Source

Merge branch 'master' into network-rewrite

Shadowghost 2 years ago
parent
commit
a728f0993e
42 changed files with 393 additions and 1427 deletions
  1. 1 0
      .github/workflows/repo-stale.yaml
  2. 0 25
      DvdLib/BigEndianBinaryReader.cs
  3. 0 20
      DvdLib/DvdLib.csproj
  4. 0 23
      DvdLib/Ifo/Cell.cs
  5. 0 52
      DvdLib/Ifo/CellPlaybackInfo.cs
  6. 0 19
      DvdLib/Ifo/CellPositionInfo.cs
  7. 0 20
      DvdLib/Ifo/Chapter.cs
  8. 0 167
      DvdLib/Ifo/Dvd.cs
  9. 0 39
      DvdLib/Ifo/DvdTime.cs
  10. 0 16
      DvdLib/Ifo/Program.cs
  11. 0 121
      DvdLib/Ifo/ProgramChain.cs
  12. 0 70
      DvdLib/Ifo/Title.cs
  13. 0 37
      DvdLib/Ifo/UserOperation.cs
  14. 0 21
      DvdLib/Properties/AssemblyInfo.cs
  15. 0 4
      Emby.Server.Implementations/ApplicationHost.cs
  16. 2 1
      Emby.Server.Implementations/Localization/Core/bg-BG.json
  17. 0 6
      Jellyfin.sln
  18. 0 8
      MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
  19. 0 83
      MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs
  20. 0 194
      MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
  21. 0 41
      MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs
  22. 0 79
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  23. 0 1
      MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
  24. 1 25
      MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
  25. 0 39
      MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
  26. 0 15
      MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
  27. 0 1
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  28. 1 154
      MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
  29. 0 3
      MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
  30. 0 56
      debian/bin/restart.sh
  31. 1 4
      debian/conf/jellyfin
  32. 0 1
      debian/install
  33. 1 1
      debian/jellyfin.service
  34. 0 2
      debian/postinst
  35. 0 3
      fedora/jellyfin.env
  36. 1 1
      fedora/jellyfin.service
  37. 6 9
      fedora/jellyfin.spec
  38. 0 56
      fedora/restart.sh
  39. 93 10
      tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs
  40. 81 0
      tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_interlaced.json
  41. 133 0
      tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_progressive_no_field_order.json
  42. 72 0
      tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_progressive_no_field_order2.json

+ 1 - 0
.github/workflows/repo-stale.yaml

@@ -19,6 +19,7 @@ jobs:
           days-before-pr-stale: -1
           days-before-close: 21
           days-before-pr-close: -1
+          operations-per-run: 75
           exempt-issue-labels: regression,security,roadmap,future,feature,enhancement,confirmed
           stale-issue-label: stale
           stale-issue-message: |-

+ 0 - 25
DvdLib/BigEndianBinaryReader.cs

@@ -1,25 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Buffers.Binary;
-using System.IO;
-
-namespace DvdLib
-{
-    public class BigEndianBinaryReader : BinaryReader
-    {
-        public BigEndianBinaryReader(Stream input)
-            : base(input)
-        {
-        }
-
-        public override ushort ReadUInt16()
-        {
-            return BinaryPrimitives.ReadUInt16BigEndian(base.ReadBytes(2));
-        }
-
-        public override uint ReadUInt32()
-        {
-            return BinaryPrimitives.ReadUInt32BigEndian(base.ReadBytes(4));
-        }
-    }
-}

+ 0 - 20
DvdLib/DvdLib.csproj

@@ -1,20 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
-  <PropertyGroup>
-    <ProjectGuid>{713F42B5-878E-499D-A878-E4C652B1D5E8}</ProjectGuid>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <Compile Include="..\SharedVersion.cs" />
-  </ItemGroup>
-
-  <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
-    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
-    <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <AnalysisMode>AllDisabledByDefault</AnalysisMode>
-    <Nullable>disable</Nullable>
-  </PropertyGroup>
-
-</Project>

+ 0 - 23
DvdLib/Ifo/Cell.cs

@@ -1,23 +0,0 @@
-#pragma warning disable CS1591
-
-using System.IO;
-
-namespace DvdLib.Ifo
-{
-    public class Cell
-    {
-        public CellPlaybackInfo PlaybackInfo { get; private set; }
-
-        public CellPositionInfo PositionInfo { get; private set; }
-
-        internal void ParsePlayback(BinaryReader br)
-        {
-            PlaybackInfo = new CellPlaybackInfo(br);
-        }
-
-        internal void ParsePosition(BinaryReader br)
-        {
-            PositionInfo = new CellPositionInfo(br);
-        }
-    }
-}

+ 0 - 52
DvdLib/Ifo/CellPlaybackInfo.cs

@@ -1,52 +0,0 @@
-#pragma warning disable CS1591
-
-using System.IO;
-
-namespace DvdLib.Ifo
-{
-    public enum BlockMode
-    {
-        NotInBlock = 0,
-        FirstCell = 1,
-        InBlock = 2,
-        LastCell = 3,
-    }
-
-    public enum BlockType
-    {
-        Normal = 0,
-        Angle = 1,
-    }
-
-    public enum PlaybackMode
-    {
-        Normal = 0,
-        StillAfterEachVOBU = 1,
-    }
-
-    public class CellPlaybackInfo
-    {
-        public readonly BlockMode Mode;
-        public readonly BlockType Type;
-        public readonly bool SeamlessPlay;
-        public readonly bool Interleaved;
-        public readonly bool STCDiscontinuity;
-        public readonly bool SeamlessAngle;
-        public readonly PlaybackMode PlaybackMode;
-        public readonly bool Restricted;
-        public readonly byte StillTime;
-        public readonly byte CommandNumber;
-        public readonly DvdTime PlaybackTime;
-        public readonly uint FirstSector;
-        public readonly uint FirstILVUEndSector;
-        public readonly uint LastVOBUStartSector;
-        public readonly uint LastSector;
-
-        internal CellPlaybackInfo(BinaryReader br)
-        {
-            br.BaseStream.Seek(0x4, SeekOrigin.Current);
-            PlaybackTime = new DvdTime(br.ReadBytes(4));
-            br.BaseStream.Seek(0x10, SeekOrigin.Current);
-        }
-    }
-}

+ 0 - 19
DvdLib/Ifo/CellPositionInfo.cs

@@ -1,19 +0,0 @@
-#pragma warning disable CS1591
-
-using System.IO;
-
-namespace DvdLib.Ifo
-{
-    public class CellPositionInfo
-    {
-        public readonly ushort VOBId;
-        public readonly byte CellId;
-
-        internal CellPositionInfo(BinaryReader br)
-        {
-            VOBId = br.ReadUInt16();
-            br.ReadByte();
-            CellId = br.ReadByte();
-        }
-    }
-}

+ 0 - 20
DvdLib/Ifo/Chapter.cs

@@ -1,20 +0,0 @@
-#pragma warning disable CS1591
-
-namespace DvdLib.Ifo
-{
-    public class Chapter
-    {
-        public ushort ProgramChainNumber { get; private set; }
-
-        public ushort ProgramNumber { get; private set; }
-
-        public uint ChapterNumber { get; private set; }
-
-        public Chapter(ushort pgcNum, ushort programNum, uint chapterNum)
-        {
-            ProgramChainNumber = pgcNum;
-            ProgramNumber = programNum;
-            ChapterNumber = chapterNum;
-        }
-    }
-}

+ 0 - 167
DvdLib/Ifo/Dvd.cs

@@ -1,167 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-
-namespace DvdLib.Ifo
-{
-    public class Dvd
-    {
-        private readonly ushort _titleSetCount;
-        public readonly List<Title> Titles;
-
-        private ushort _titleCount;
-        public readonly Dictionary<ushort, string> VTSPaths = new Dictionary<ushort, string>();
-        public Dvd(string path)
-        {
-            Titles = new List<Title>();
-            var allFiles = new DirectoryInfo(path).GetFiles(path, SearchOption.AllDirectories);
-
-            var vmgPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.IFO", StringComparison.OrdinalIgnoreCase)) ??
-                allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase));
-
-            if (vmgPath == null)
-            {
-                foreach (var ifo in allFiles)
-                {
-                    if (!string.Equals(ifo.Extension, ".ifo", StringComparison.OrdinalIgnoreCase))
-                    {
-                        continue;
-                    }
-
-                    var nums = ifo.Name.Split('_', StringSplitOptions.RemoveEmptyEntries);
-                    if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber))
-                    {
-                        ReadVTS(ifoNumber, ifo.FullName);
-                    }
-                }
-            }
-            else
-            {
-                using (var vmgFs = new FileStream(vmgPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
-                {
-                    using (var vmgRead = new BigEndianBinaryReader(vmgFs))
-                    {
-                        vmgFs.Seek(0x3E, SeekOrigin.Begin);
-                        _titleSetCount = vmgRead.ReadUInt16();
-
-                        // read address of TT_SRPT
-                        vmgFs.Seek(0xC4, SeekOrigin.Begin);
-                        uint ttSectorPtr = vmgRead.ReadUInt32();
-                        vmgFs.Seek(ttSectorPtr * 2048, SeekOrigin.Begin);
-                        ReadTT_SRPT(vmgRead);
-                    }
-                }
-
-                for (ushort titleSetNum = 1; titleSetNum <= _titleSetCount; titleSetNum++)
-                {
-                    ReadVTS(titleSetNum, allFiles);
-                }
-            }
-        }
-
-        private void ReadTT_SRPT(BinaryReader read)
-        {
-            _titleCount = read.ReadUInt16();
-            read.BaseStream.Seek(6, SeekOrigin.Current);
-            for (uint titleNum = 1; titleNum <= _titleCount; titleNum++)
-            {
-                var t = new Title(titleNum);
-                t.ParseTT_SRPT(read);
-                Titles.Add(t);
-            }
-        }
-
-        private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles)
-        {
-            var filename = string.Format(CultureInfo.InvariantCulture, "VTS_{0:00}_0.IFO", vtsNum);
-
-            var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ??
-                allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase));
-
-            if (vtsPath == null)
-            {
-                throw new FileNotFoundException("Unable to find VTS IFO file");
-            }
-
-            ReadVTS(vtsNum, vtsPath.FullName);
-        }
-
-        private void ReadVTS(ushort vtsNum, string vtsPath)
-        {
-            VTSPaths[vtsNum] = vtsPath;
-
-            using (var vtsFs = new FileStream(vtsPath, FileMode.Open, FileAccess.Read, FileShare.Read))
-            {
-                using (var vtsRead = new BigEndianBinaryReader(vtsFs))
-                {
-                    // Read VTS_PTT_SRPT
-                    vtsFs.Seek(0xC8, SeekOrigin.Begin);
-                    uint vtsPttSrptSecPtr = vtsRead.ReadUInt32();
-                    uint baseAddr = (vtsPttSrptSecPtr * 2048);
-                    vtsFs.Seek(baseAddr, SeekOrigin.Begin);
-
-                    ushort numTitles = vtsRead.ReadUInt16();
-                    vtsRead.ReadUInt16();
-                    uint endaddr = vtsRead.ReadUInt32();
-                    uint[] offsets = new uint[numTitles];
-                    for (ushort titleNum = 0; titleNum < numTitles; titleNum++)
-                    {
-                        offsets[titleNum] = vtsRead.ReadUInt32();
-                    }
-
-                    for (uint titleNum = 0; titleNum < numTitles; titleNum++)
-                    {
-                        uint chapNum = 1;
-                        vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin);
-                        var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1));
-                        if (t == null)
-                        {
-                            continue;
-                        }
-
-                        do
-                        {
-                            t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum));
-                            if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1]))
-                            {
-                                break;
-                            }
-
-                            chapNum++;
-                        }
-                        while (vtsFs.Position < (baseAddr + endaddr));
-                    }
-
-                    // Read VTS_PGCI
-                    vtsFs.Seek(0xCC, SeekOrigin.Begin);
-                    uint vtsPgciSecPtr = vtsRead.ReadUInt32();
-                    vtsFs.Seek(vtsPgciSecPtr * 2048, SeekOrigin.Begin);
-
-                    long startByte = vtsFs.Position;
-
-                    ushort numPgcs = vtsRead.ReadUInt16();
-                    vtsFs.Seek(6, SeekOrigin.Current);
-                    for (ushort pgcNum = 1; pgcNum <= numPgcs; pgcNum++)
-                    {
-                        byte pgcCat = vtsRead.ReadByte();
-                        bool entryPgc = (pgcCat & 0x80) != 0;
-                        uint titleNum = (uint)(pgcCat & 0x7F);
-
-                        vtsFs.Seek(3, SeekOrigin.Current);
-                        uint vtsPgcOffset = vtsRead.ReadUInt32();
-
-                        var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum));
-                        if (t != null)
-                        {
-                            t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
-                        }
-                    }
-                }
-            }
-        }
-    }
-}

+ 0 - 39
DvdLib/Ifo/DvdTime.cs

@@ -1,39 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-
-namespace DvdLib.Ifo
-{
-    public class DvdTime
-    {
-        public readonly byte Hour, Minute, Second, Frames, FrameRate;
-
-        public DvdTime(byte[] data)
-        {
-            Hour = GetBCDValue(data[0]);
-            Minute = GetBCDValue(data[1]);
-            Second = GetBCDValue(data[2]);
-            Frames = GetBCDValue((byte)(data[3] & 0x3F));
-
-            if ((data[3] & 0x80) != 0)
-            {
-                FrameRate = 30;
-            }
-            else if ((data[3] & 0x40) != 0)
-            {
-                FrameRate = 25;
-            }
-        }
-
-        private static byte GetBCDValue(byte data)
-        {
-            return (byte)((((data & 0xF0) >> 4) * 10) + (data & 0x0F));
-        }
-
-        public static explicit operator TimeSpan(DvdTime time)
-        {
-            int ms = (int)(((1.0 / (double)time.FrameRate) * time.Frames) * 1000.0);
-            return new TimeSpan(0, time.Hour, time.Minute, time.Second, ms);
-        }
-    }
-}

+ 0 - 16
DvdLib/Ifo/Program.cs

@@ -1,16 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-
-namespace DvdLib.Ifo
-{
-    public class Program
-    {
-        public IReadOnlyList<Cell> Cells { get; }
-
-        public Program(List<Cell> cells)
-        {
-            Cells = cells;
-        }
-    }
-}

+ 0 - 121
DvdLib/Ifo/ProgramChain.cs

@@ -1,121 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace DvdLib.Ifo
-{
-    public enum ProgramPlaybackMode
-    {
-        Sequential,
-        Random,
-        Shuffle
-    }
-
-    public class ProgramChain
-    {
-        private byte _programCount;
-        public readonly List<Program> Programs;
-
-        private byte _cellCount;
-        public readonly List<Cell> Cells;
-
-        public DvdTime PlaybackTime { get; private set; }
-
-        public UserOperation ProhibitedUserOperations { get; private set; }
-
-        public byte[] AudioStreamControl { get; private set; } // 8*2 entries
-        public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries
-
-        private ushort _nextProgramNumber;
-
-        private ushort _prevProgramNumber;
-
-        private ushort _goupProgramNumber;
-
-        public ProgramPlaybackMode PlaybackMode { get; private set; }
-
-        public uint ProgramCount { get; private set; }
-
-        public byte StillTime { get; private set; }
-
-        public byte[] Palette { get; private set; } // 16*4 entries
-
-        private ushort _commandTableOffset;
-
-        private ushort _programMapOffset;
-        private ushort _cellPlaybackOffset;
-        private ushort _cellPositionOffset;
-
-        public readonly uint VideoTitleSetIndex;
-
-        internal ProgramChain(uint vtsPgcNum)
-        {
-            VideoTitleSetIndex = vtsPgcNum;
-            Cells = new List<Cell>();
-            Programs = new List<Program>();
-        }
-
-        internal void ParseHeader(BinaryReader br)
-        {
-            long startPos = br.BaseStream.Position;
-
-            br.ReadUInt16();
-            _programCount = br.ReadByte();
-            _cellCount = br.ReadByte();
-            PlaybackTime = new DvdTime(br.ReadBytes(4));
-            ProhibitedUserOperations = (UserOperation)br.ReadUInt32();
-            AudioStreamControl = br.ReadBytes(16);
-            SubpictureStreamControl = br.ReadBytes(128);
-
-            _nextProgramNumber = br.ReadUInt16();
-            _prevProgramNumber = br.ReadUInt16();
-            _goupProgramNumber = br.ReadUInt16();
-
-            StillTime = br.ReadByte();
-            byte pbMode = br.ReadByte();
-            if (pbMode == 0)
-            {
-                PlaybackMode = ProgramPlaybackMode.Sequential;
-            }
-            else
-            {
-                PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
-            }
-
-            ProgramCount = (uint)(pbMode & 0x7F);
-
-            Palette = br.ReadBytes(64);
-            _commandTableOffset = br.ReadUInt16();
-            _programMapOffset = br.ReadUInt16();
-            _cellPlaybackOffset = br.ReadUInt16();
-            _cellPositionOffset = br.ReadUInt16();
-
-            // read position info
-            br.BaseStream.Seek(startPos + _cellPositionOffset, SeekOrigin.Begin);
-            for (int cellNum = 0; cellNum < _cellCount; cellNum++)
-            {
-                var c = new Cell();
-                c.ParsePosition(br);
-                Cells.Add(c);
-            }
-
-            br.BaseStream.Seek(startPos + _cellPlaybackOffset, SeekOrigin.Begin);
-            for (int cellNum = 0; cellNum < _cellCount; cellNum++)
-            {
-                Cells[cellNum].ParsePlayback(br);
-            }
-
-            br.BaseStream.Seek(startPos + _programMapOffset, SeekOrigin.Begin);
-            var cellNumbers = new List<int>();
-            for (int progNum = 0; progNum < _programCount; progNum++) cellNumbers.Add(br.ReadByte() - 1);
-
-            for (int i = 0; i < cellNumbers.Count; i++)
-            {
-                int max = (i + 1 == cellNumbers.Count) ? _cellCount : cellNumbers[i + 1];
-                Programs.Add(new Program(Cells.Where((c, idx) => idx >= cellNumbers[i] && idx < max).ToList()));
-            }
-        }
-    }
-}

+ 0 - 70
DvdLib/Ifo/Title.cs

@@ -1,70 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using System.IO;
-
-namespace DvdLib.Ifo
-{
-    public class Title
-    {
-        public uint TitleNumber { get; private set; }
-
-        public uint AngleCount { get; private set; }
-
-        public ushort ChapterCount { get; private set; }
-
-        public byte VideoTitleSetNumber { get; private set; }
-
-        private ushort _parentalManagementMask;
-        private byte _titleNumberInVTS;
-        private uint _vtsStartSector; // relative to start of entire disk
-
-        public ProgramChain EntryProgramChain { get; private set; }
-
-        public readonly List<ProgramChain> ProgramChains;
-
-        public readonly List<Chapter> Chapters;
-
-        public Title(uint titleNum)
-        {
-            ProgramChains = new List<ProgramChain>();
-            Chapters = new List<Chapter>();
-            Chapters = new List<Chapter>();
-            TitleNumber = titleNum;
-        }
-
-        public bool IsVTSTitle(uint vtsNum, uint vtsTitleNum)
-        {
-            return (vtsNum == VideoTitleSetNumber && vtsTitleNum == _titleNumberInVTS);
-        }
-
-        internal void ParseTT_SRPT(BinaryReader br)
-        {
-            byte titleType = br.ReadByte();
-            // TODO parse Title Type
-
-            AngleCount = br.ReadByte();
-            ChapterCount = br.ReadUInt16();
-            _parentalManagementMask = br.ReadUInt16();
-            VideoTitleSetNumber = br.ReadByte();
-            _titleNumberInVTS = br.ReadByte();
-            _vtsStartSector = br.ReadUInt32();
-        }
-
-        internal void AddPgc(BinaryReader br, long startByte, bool entryPgc, uint pgcNum)
-        {
-            long curPos = br.BaseStream.Position;
-            br.BaseStream.Seek(startByte, SeekOrigin.Begin);
-
-            var pgc = new ProgramChain(pgcNum);
-            pgc.ParseHeader(br);
-            ProgramChains.Add(pgc);
-            if (entryPgc)
-            {
-                EntryProgramChain = pgc;
-            }
-
-            br.BaseStream.Seek(curPos, SeekOrigin.Begin);
-        }
-    }
-}

+ 0 - 37
DvdLib/Ifo/UserOperation.cs

@@ -1,37 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-
-namespace DvdLib.Ifo
-{
-    [Flags]
-    public enum UserOperation
-    {
-        None = 0,
-        TitleOrTimePlay = 1,
-        ChapterSearchOrPlay = 2,
-        TitlePlay = 4,
-        Stop = 8,
-        GoUp = 16,
-        TimeOrChapterSearch = 32,
-        PrevOrTopProgramSearch = 64,
-        NextProgramSearch = 128,
-        ForwardScan = 256,
-        BackwardScan = 512,
-        TitleMenuCall = 1024,
-        RootMenuCall = 2048,
-        SubpictureMenuCall = 4096,
-        AudioMenuCall = 8192,
-        AngleMenuCall = 16384,
-        ChapterMenuCall = 32768,
-        Resume = 65536,
-        ButtonSelectOrActive = 131072,
-        StillOff = 262144,
-        PauseOn = 524288,
-        AudioStreamChange = 1048576,
-        SubpictureStreamChange = 2097152,
-        AngleChange = 4194304,
-        KaraokeAudioPresentationModeChange = 8388608,
-        VideoPresentationModeChange = 16777216,
-    }
-}

+ 0 - 21
DvdLib/Properties/AssemblyInfo.cs

@@ -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("DvdLib")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Jellyfin Project")]
-[assembly: AssemblyProduct("Jellyfin Server")]
-[assembly: AssemblyCopyright("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)]

+ 0 - 4
Emby.Server.Implementations/ApplicationHost.cs

@@ -83,13 +83,11 @@ using MediaBrowser.Controller.Subtitles;
 using MediaBrowser.Controller.SyncPlay;
 using MediaBrowser.Controller.TV;
 using MediaBrowser.LocalMetadata.Savers;
-using MediaBrowser.MediaEncoding.BdInfo;
 using MediaBrowser.MediaEncoding.Subtitles;
 using MediaBrowser.Model.Cryptography;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.System;
@@ -562,8 +560,6 @@ namespace Emby.Server.Implementations
 
             serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
 
-            serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
-
             serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
             serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
 

+ 2 - 1
Emby.Server.Implementations/Localization/Core/bg-BG.json

@@ -123,5 +123,6 @@
     "TaskOptimizeDatabase": "Оптимизирай базата данни",
     "TaskKeyframeExtractorDescription": "Извличат се ключови кадри от видеофайловете ,за да се създаде по точен ХЛС списък . Задачата може да отнеме много време.",
     "TaskKeyframeExtractor": "Извличане на ключови кадри",
-    "External": "Външен"
+    "External": "Външен",
+    "HearingImpaired": "Увреден слух"
 }

+ 0 - 6
Jellyfin.sln

@@ -21,8 +21,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Drawing", "src\Jel
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DvdLib", "DvdLib\DvdLib.csproj", "{713F42B5-878E-499D-A878-E4C652B1D5E8}"
-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}"
@@ -139,10 +137,6 @@ Global
 		{89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.Build.0 = Release|Any CPU
-		{713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.Build.0 = Release|Any CPU
 		{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{E383961B-9356-4D5D-8233-9A1079D03055}.Release|Any CPU.ActiveCfg = Release|Any CPU

+ 0 - 8
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -187,13 +187,5 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <param name="path">The path.</param>
         /// <param name="pathType">The type of path.</param>
         void UpdateEncoderPath(string path, string pathType);
-
-        /// <summary>
-        /// Gets the primary playlist of .vob files.
-        /// </summary>
-        /// <param name="path">The to the .vob files.</param>
-        /// <param name="titleNumber">The title number to start with.</param>
-        /// <returns>A playlist.</returns>
-        IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber);
     }
 }

+ 0 - 83
MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs

@@ -1,83 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Linq;
-using BDInfo.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.MediaEncoding.BdInfo
-{
-    public class BdInfoDirectoryInfo : IDirectoryInfo
-    {
-        private readonly IFileSystem _fileSystem;
-
-        private readonly FileSystemMetadata _impl;
-
-        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;
-
-        public string FullName => _impl.FullName;
-
-        public IDirectoryInfo? Parent
-        {
-            get
-            {
-                var parentFolder = System.IO.Path.GetDirectoryName(_impl.FullName);
-                if (parentFolder is not null)
-                {
-                    return new BdInfoDirectoryInfo(_fileSystem, parentFolder);
-                }
-
-                return null;
-            }
-        }
-
-        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(x));
-        }
-
-        public IFileInfo[] GetFiles(string searchPattern)
-        {
-            return Array.ConvertAll(
-                _fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(),
-                x => new BdInfoFileInfo(x));
-        }
-
-        public IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption)
-        {
-            return Array.ConvertAll(
-                _fileSystem.GetFiles(
-                    _impl.FullName,
-                    new[] { searchPattern },
-                    false,
-                    (searchOption & System.IO.SearchOption.AllDirectories) == System.IO.SearchOption.AllDirectories).ToArray(),
-                x => new BdInfoFileInfo(x));
-        }
-
-        public static IDirectoryInfo FromFileSystemPath(IFileSystem fs, string path)
-        {
-            return new BdInfoDirectoryInfo(fs, path);
-        }
-    }
-}

+ 0 - 194
MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs

@@ -1,194 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using BDInfo;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.MediaEncoding.BdInfo
-{
-    /// <summary>
-    /// Class BdInfoExaminer.
-    /// </summary>
-    public class BdInfoExaminer : IBlurayExaminer
-    {
-        private readonly IFileSystem _fileSystem;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="BdInfoExaminer" /> class.
-        /// </summary>
-        /// <param name="fileSystem">The filesystem.</param>
-        public BdInfoExaminer(IFileSystem fileSystem)
-        {
-            _fileSystem = fileSystem;
-        }
-
-        /// <summary>
-        /// Gets the disc info.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <returns>BlurayDiscInfo.</returns>
-        public BlurayDiscInfo GetDiscInfo(string path)
-        {
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
-
-            var bdrom = new BDROM(BdInfoDirectoryInfo.FromFileSystemPath(_fileSystem, path));
-
-            bdrom.Scan();
-
-            // Get the longest playlist
-            var playlist = bdrom.PlaylistFiles.Values.OrderByDescending(p => p.TotalLength).FirstOrDefault(p => p.IsValid);
-
-            var outputStream = new BlurayDiscInfo
-            {
-                MediaStreams = Array.Empty<MediaStream>()
-            };
-
-            if (playlist is null)
-            {
-                return outputStream;
-            }
-
-            outputStream.Chapters = playlist.Chapters.ToArray();
-
-            outputStream.RunTimeTicks = TimeSpan.FromSeconds(playlist.TotalLength).Ticks;
-
-            var mediaStreams = new List<MediaStream>();
-
-            foreach (var stream in playlist.SortedStreams)
-            {
-                if (stream is TSVideoStream videoStream)
-                {
-                    AddVideoStream(mediaStreams, videoStream);
-                    continue;
-                }
-
-                if (stream is TSAudioStream audioStream)
-                {
-                    AddAudioStream(mediaStreams, audioStream);
-                    continue;
-                }
-
-                if (stream is TSTextStream textStream)
-                {
-                    AddSubtitleStream(mediaStreams, textStream);
-                    continue;
-                }
-
-                if (stream is TSGraphicsStream graphicsStream)
-                {
-                    AddSubtitleStream(mediaStreams, graphicsStream);
-                }
-            }
-
-            outputStream.MediaStreams = mediaStreams.ToArray();
-
-            outputStream.PlaylistName = playlist.Name;
-
-            if (playlist.StreamClips is not null && playlist.StreamClips.Any())
-            {
-                // Get the files in the playlist
-                outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.Name).ToArray();
-            }
-
-            return outputStream;
-        }
-
-        /// <summary>
-        /// Adds the video stream.
-        /// </summary>
-        /// <param name="streams">The streams.</param>
-        /// <param name="videoStream">The video stream.</param>
-        private void AddVideoStream(List<MediaStream> streams, TSVideoStream videoStream)
-        {
-            var mediaStream = new MediaStream
-            {
-                BitRate = Convert.ToInt32(videoStream.BitRate),
-                Width = videoStream.Width,
-                Height = videoStream.Height,
-                Codec = videoStream.CodecShortName,
-                IsInterlaced = videoStream.IsInterlaced,
-                Type = MediaStreamType.Video,
-                Index = streams.Count
-            };
-
-            if (videoStream.FrameRateDenominator > 0)
-            {
-                float frameRateEnumerator = videoStream.FrameRateEnumerator;
-                float frameRateDenominator = videoStream.FrameRateDenominator;
-
-                mediaStream.AverageFrameRate = mediaStream.RealFrameRate = frameRateEnumerator / frameRateDenominator;
-            }
-
-            streams.Add(mediaStream);
-        }
-
-        /// <summary>
-        /// Adds the audio stream.
-        /// </summary>
-        /// <param name="streams">The streams.</param>
-        /// <param name="audioStream">The audio stream.</param>
-        private void AddAudioStream(List<MediaStream> streams, TSAudioStream audioStream)
-        {
-            var stream = new MediaStream
-            {
-                Codec = audioStream.CodecShortName,
-                Language = audioStream.LanguageCode,
-                Channels = audioStream.ChannelCount,
-                SampleRate = audioStream.SampleRate,
-                Type = MediaStreamType.Audio,
-                Index = streams.Count
-            };
-
-            var bitrate = Convert.ToInt32(audioStream.BitRate);
-
-            if (bitrate > 0)
-            {
-                stream.BitRate = bitrate;
-            }
-
-            if (audioStream.LFE > 0)
-            {
-                stream.Channels = audioStream.ChannelCount + 1;
-            }
-
-            streams.Add(stream);
-        }
-
-        /// <summary>
-        /// Adds the subtitle stream.
-        /// </summary>
-        /// <param name="streams">The streams.</param>
-        /// <param name="textStream">The text stream.</param>
-        private void AddSubtitleStream(List<MediaStream> streams, TSTextStream textStream)
-        {
-            streams.Add(new MediaStream
-            {
-                Language = textStream.LanguageCode,
-                Codec = textStream.CodecShortName,
-                Type = MediaStreamType.Subtitle,
-                Index = streams.Count
-            });
-        }
-
-        /// <summary>
-        /// Adds the subtitle stream.
-        /// </summary>
-        /// <param name="streams">The streams.</param>
-        /// <param name="textStream">The text stream.</param>
-        private void AddSubtitleStream(List<MediaStream> streams, TSGraphicsStream textStream)
-        {
-            streams.Add(new MediaStream
-            {
-                Language = textStream.LanguageCode,
-                Codec = textStream.CodecShortName,
-                Type = MediaStreamType.Subtitle,
-                Index = streams.Count
-            });
-        }
-    }
-}

+ 0 - 41
MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs

@@ -1,41 +0,0 @@
-#pragma warning disable CS1591
-
-using System.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.MediaEncoding.BdInfo
-{
-    public class BdInfoFileInfo : BDInfo.IO.IFileInfo
-    {
-        private FileSystemMetadata _impl;
-
-        public BdInfoFileInfo(FileSystemMetadata impl)
-        {
-            _impl = impl;
-        }
-
-        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 Stream OpenRead()
-        {
-            return new FileStream(
-                FullName,
-                FileMode.Open,
-                FileAccess.Read,
-                FileShare.Read);
-        }
-
-        public StreamReader OpenText()
-        {
-            return new StreamReader(OpenRead());
-        }
-    }
-}

+ 0 - 79
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -862,85 +862,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
             throw new NotImplementedException();
         }
 
-        /// <inheritdoc />
-        public IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber)
-        {
-            // min size 300 mb
-            const long MinPlayableSize = 314572800;
-
-            // 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(path, 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<string>();
-            }
-
-            if (titleNumber.HasValue)
-            {
-                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)
-                {
-                    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 {Path} using DvdLib. Will scan using file sizes.", 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)
         {
             // TODO is there ever a case when a subtitle can't be extracted??

+ 0 - 1
MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj

@@ -22,7 +22,6 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="BDInfo" Version="0.7.6.2" />
     <PackageReference Include="libse" Version="3.6.10" />
     <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
     <PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />

+ 1 - 25
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs

@@ -625,17 +625,6 @@ namespace MediaBrowser.MediaEncoding.Probing
             return attachment;
         }
 
-        /// <summary>
-        /// Determines whether a stream code time base is double the frame rate.
-        /// </summary>
-        /// <param name="averageFrameRate">average frame rate.</param>
-        /// <param name="codecTimeBase">codec time base string.</param>
-        /// <returns>true if the codec time base is double the frame rate.</returns>
-        internal static bool IsCodecTimeBaseDoubleTheFrameRate(float? averageFrameRate, string codecTimeBase)
-        {
-            return MathF.Abs(((averageFrameRate ?? 0) * (GetFrameRate(codecTimeBase) ?? 0)) - 0.5f) <= float.Epsilon;
-        }
-
         /// <summary>
         /// Converts ffprobe stream info to our MediaStream class.
         /// </summary>
@@ -748,22 +737,9 @@ namespace MediaBrowser.MediaEncoding.Probing
                 stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate);
                 stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate);
 
-                bool videoInterlaced = !string.IsNullOrWhiteSpace(streamInfo.FieldOrder)
+                stream.IsInterlaced = !string.IsNullOrWhiteSpace(streamInfo.FieldOrder)
                     && !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase);
 
-                // Some interlaced H.264 files in mp4 containers using MBAFF coding aren't flagged as being interlaced by FFprobe,
-                // so for H.264 files we also calculate the frame rate from the codec time base and check if it is double the reported
-                // frame rate to determine if the file is interlaced
-
-                bool h264MbaffCoded = string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase)
-                    && string.IsNullOrWhiteSpace(streamInfo.FieldOrder)
-                    && IsCodecTimeBaseDoubleTheFrameRate(stream.AverageFrameRate, stream.CodecTimeBase);
-
-                if (videoInterlaced || h264MbaffCoded)
-                {
-                    stream.IsInterlaced = true;
-                }
-
                 if (isAudio
                     || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)
                     || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase)

+ 0 - 39
MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs

@@ -1,39 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.MediaInfo
-{
-    /// <summary>
-    /// Represents the result of BDInfo output.
-    /// </summary>
-    public class BlurayDiscInfo
-    {
-        /// <summary>
-        /// Gets or sets the media streams.
-        /// </summary>
-        /// <value>The media streams.</value>
-        public MediaStream[] MediaStreams { get; set; }
-
-        /// <summary>
-        /// Gets or sets the run time ticks.
-        /// </summary>
-        /// <value>The run time ticks.</value>
-        public long? RunTimeTicks { get; set; }
-
-        /// <summary>
-        /// Gets or sets the files.
-        /// </summary>
-        /// <value>The files.</value>
-        public string[] Files { get; set; }
-
-        public string PlaylistName { get; set; }
-
-        /// <summary>
-        /// Gets or sets the chapters.
-        /// </summary>
-        /// <value>The chapters.</value>
-        public double[] Chapters { get; set; }
-    }
-}

+ 0 - 15
MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs

@@ -1,15 +0,0 @@
-namespace MediaBrowser.Model.MediaInfo
-{
-    /// <summary>
-    /// Interface IBlurayExaminer.
-    /// </summary>
-    public interface IBlurayExaminer
-    {
-        /// <summary>
-        /// Gets the disc info.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <returns>BlurayDiscInfo.</returns>
-        BlurayDiscInfo GetDiscInfo(string path);
-    }
-}

+ 0 - 1
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -8,7 +8,6 @@
   <ItemGroup>
     <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
     <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
-    <ProjectReference Include="..\DvdLib\DvdLib.csproj" />
   </ItemGroup>
 
   <ItemGroup>

+ 1 - 154
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs

@@ -9,7 +9,6 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using DvdLib.Ifo;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Chapters;
 using MediaBrowser.Controller.Configuration;
@@ -37,7 +36,6 @@ namespace MediaBrowser.Providers.MediaInfo
         private readonly ILogger<FFProbeVideoInfo> _logger;
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IItemRepository _itemRepo;
-        private readonly IBlurayExaminer _blurayExaminer;
         private readonly ILocalizationManager _localization;
         private readonly IEncodingManager _encodingManager;
         private readonly IServerConfigurationManager _config;
@@ -53,7 +51,6 @@ namespace MediaBrowser.Providers.MediaInfo
             IMediaSourceManager mediaSourceManager,
             IMediaEncoder mediaEncoder,
             IItemRepository itemRepo,
-            IBlurayExaminer blurayExaminer,
             ILocalizationManager localization,
             IEncodingManager encodingManager,
             IServerConfigurationManager config,
@@ -67,7 +64,6 @@ namespace MediaBrowser.Providers.MediaInfo
             _mediaSourceManager = mediaSourceManager;
             _mediaEncoder = mediaEncoder;
             _itemRepo = itemRepo;
-            _blurayExaminer = blurayExaminer;
             _localization = localization;
             _encodingManager = encodingManager;
             _config = config;
@@ -84,47 +80,16 @@ namespace MediaBrowser.Providers.MediaInfo
             CancellationToken cancellationToken)
             where T : Video
         {
-            BlurayDiscInfo blurayDiscInfo = null;
-
             Model.MediaInfo.MediaInfo mediaInfoResult = null;
 
             if (!item.IsShortcut || options.EnableRemoteContentProbe)
             {
-                string[] streamFileNames = null;
-
-                if (item.VideoType == VideoType.Dvd)
-                {
-                    streamFileNames = FetchFromDvdLib(item);
-
-                    if (streamFileNames.Length == 0)
-                    {
-                        _logger.LogError("No playable vobs found in dvd structure, skipping ffprobe.");
-                        return ItemUpdateType.MetadataImport;
-                    }
-                }
-                else if (item.VideoType == VideoType.BluRay)
-                {
-                    var inputPath = item.Path;
-
-                    blurayDiscInfo = GetBDInfo(inputPath);
-
-                    streamFileNames = blurayDiscInfo.Files;
-
-                    if (streamFileNames.Length == 0)
-                    {
-                        _logger.LogError("No playable vobs found in bluray structure, skipping ffprobe.");
-                        return ItemUpdateType.MetadataImport;
-                    }
-                }
-
-                streamFileNames ??= Array.Empty<string>();
-
                 mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false);
 
                 cancellationToken.ThrowIfCancellationRequested();
             }
 
-            await Fetch(item, cancellationToken, mediaInfoResult, blurayDiscInfo, options).ConfigureAwait(false);
+            await Fetch(item, cancellationToken, mediaInfoResult, options).ConfigureAwait(false);
 
             return ItemUpdateType.MetadataImport;
         }
@@ -164,7 +129,6 @@ namespace MediaBrowser.Providers.MediaInfo
             Video video,
             CancellationToken cancellationToken,
             Model.MediaInfo.MediaInfo mediaInfo,
-            BlurayDiscInfo blurayInfo,
             MetadataRefreshOptions options)
         {
             List<MediaStream> mediaStreams;
@@ -218,10 +182,6 @@ namespace MediaBrowser.Providers.MediaInfo
                 video.Container = mediaInfo.Container;
 
                 chapters = mediaInfo.Chapters ?? Array.Empty<ChapterInfo>();
-                if (blurayInfo is not null)
-                {
-                    FetchBdInfo(video, ref chapters, mediaStreams, blurayInfo);
-                }
             }
             else
             {
@@ -317,91 +277,6 @@ namespace MediaBrowser.Providers.MediaInfo
             }
         }
 
-        private void FetchBdInfo(BaseItem item, ref ChapterInfo[] chapters, List<MediaStream> mediaStreams, BlurayDiscInfo blurayInfo)
-        {
-            var video = (Video)item;
-
-            // video.PlayableStreamFileNames = blurayInfo.Files.ToList();
-
-            // Use BD Info if it has multiple m2ts. Otherwise, treat it like a video file and rely more on ffprobe output
-            if (blurayInfo.Files.Length > 1)
-            {
-                int? currentHeight = null;
-                int? currentWidth = null;
-                int? currentBitRate = null;
-
-                var videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
-
-                // Grab the values that ffprobe recorded
-                if (videoStream is not null)
-                {
-                    currentBitRate = videoStream.BitRate;
-                    currentWidth = videoStream.Width;
-                    currentHeight = videoStream.Height;
-                }
-
-                // Fill video properties from the BDInfo result
-                mediaStreams.Clear();
-                mediaStreams.AddRange(blurayInfo.MediaStreams);
-
-                if (blurayInfo.RunTimeTicks.HasValue && blurayInfo.RunTimeTicks.Value > 0)
-                {
-                    video.RunTimeTicks = blurayInfo.RunTimeTicks;
-                }
-
-                if (blurayInfo.Chapters is not null)
-                {
-                    double[] brChapter = blurayInfo.Chapters;
-                    chapters = new ChapterInfo[brChapter.Length];
-                    for (int i = 0; i < brChapter.Length; i++)
-                    {
-                        chapters[i] = new ChapterInfo
-                        {
-                            StartPositionTicks = TimeSpan.FromSeconds(brChapter[i]).Ticks
-                        };
-                    }
-                }
-
-                videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
-
-                // Use the ffprobe values if these are empty
-                if (videoStream is not null)
-                {
-                    videoStream.BitRate = IsEmpty(videoStream.BitRate) ? currentBitRate : videoStream.BitRate;
-                    videoStream.Width = IsEmpty(videoStream.Width) ? currentWidth : videoStream.Width;
-                    videoStream.Height = IsEmpty(videoStream.Height) ? currentHeight : videoStream.Height;
-                }
-            }
-        }
-
-        private bool IsEmpty(int? num)
-        {
-            return !num.HasValue || num.Value == 0;
-        }
-
-        /// <summary>
-        /// Gets information about the longest playlist on a bdrom.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <returns>VideoStream.</returns>
-        private BlurayDiscInfo GetBDInfo(string path)
-        {
-            if (string.IsNullOrWhiteSpace(path))
-            {
-                throw new ArgumentNullException(nameof(path));
-            }
-
-            try
-            {
-                return _blurayExaminer.GetDiscInfo(path);
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error getting BDInfo");
-                return null;
-            }
-        }
-
         private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions refreshOptions, LibraryOptions libraryOptions)
         {
             var replaceData = refreshOptions.ReplaceAllMetadata;
@@ -683,33 +558,5 @@ namespace MediaBrowser.Providers.MediaInfo
 
             return chapters;
         }
-
-        private string[] FetchFromDvdLib(Video item)
-        {
-            var path = item.Path;
-            var dvd = new Dvd(path);
-
-            var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault();
-
-            byte? titleNumber = null;
-
-            if (primaryTitle is not null)
-            {
-                titleNumber = primaryTitle.VideoTitleSetNumber;
-                item.RunTimeTicks = GetRuntime(primaryTitle);
-            }
-
-            return _mediaEncoder.GetPrimaryPlaylistVobFiles(item.Path, titleNumber)
-                .Select(Path.GetFileName)
-                .ToArray();
-        }
-
-        private long GetRuntime(Title title)
-        {
-            return title.ProgramChains
-                    .Select(i => (TimeSpan)i.PlaybackTime)
-                    .Select(i => i.Ticks)
-                    .Sum();
-        }
     }
 }

+ 0 - 3
MediaBrowser.Providers/MediaInfo/ProbeProvider.cs

@@ -53,7 +53,6 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
         /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
         /// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param>
-        /// <param name="blurayExaminer">Instance of the <see cref="IBlurayExaminer"/> interface.</param>
         /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
         /// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param>
         /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
@@ -67,7 +66,6 @@ namespace MediaBrowser.Providers.MediaInfo
             IMediaSourceManager mediaSourceManager,
             IMediaEncoder mediaEncoder,
             IItemRepository itemRepo,
-            IBlurayExaminer blurayExaminer,
             ILocalizationManager localization,
             IEncodingManager encodingManager,
             IServerConfigurationManager config,
@@ -87,7 +85,6 @@ namespace MediaBrowser.Providers.MediaInfo
                 mediaSourceManager,
                 mediaEncoder,
                 itemRepo,
-                blurayExaminer,
                 localization,
                 encodingManager,
                 config,

+ 0 - 56
debian/bin/restart.sh

@@ -1,56 +0,0 @@
-#!/bin/bash
-
-# restart.sh - Jellyfin server restart script
-# Part of the Jellyfin project (https://github.com/jellyfin)
-#
-# This script restarts the Jellyfin daemon on Linux when using
-# the Restart button on the admin dashboard. It supports the
-# systemctl, service, and traditional /etc/init.d (sysv) restart
-# methods, chosen automatically by which one is found first (in
-# that order).
-#
-# This script is used by the Debian/Ubuntu/Fedora/CentOS packages.
-
-# This is the Right Way(tm) to check if we are booted with
-# systemd, according to sd_booted(3)
-if [ -d /run/systemd/system ]; then
-    cmd=systemctl
-else
-    # Everything else is really hard to figure out, so we just use
-    # service(8) if it's available - that works with most init
-    # systems/distributions I know of, including FreeBSD
-    if type service >/dev/null 2>&1; then
-        cmd=service
-    else
-        # If even service(8) isn't available, we just try /etc/init.d
-        # and hope for the best
-        if [ -d /etc/init.d ]; then
-            cmd=sysv
-        else
-            echo "Unable to detect a way to restart Jellyfin; bailing out" 1>&2
-            echo "Please report this bug to https://github.com/jellyfin/jellyfin/issues" 1>&2
-            exit 1
-        fi
-    fi
-fi
-
-if type sudo >/dev/null 2>&1; then
-    sudo_command=sudo
-else
-    sudo_command=
-fi
-
-echo "Detected service control platform '$cmd'; using it to restart Jellyfin..."
-case $cmd in
-    'systemctl')
-        # Without systemd-run here, `jellyfin.service`'s shutdown terminates this process too
-        $sudo_command systemd-run systemctl restart jellyfin
-        ;;
-    'service')
-        echo "sleep 0.5; $sudo_command service jellyfin start" | at now
-        ;;
-    'sysv')
-        echo "sleep 0.5; /usr/bin/sudo /etc/init.d/jellyfin start" | at now 
-        ;;
-esac
-exit 0

+ 1 - 4
debian/conf/jellyfin

@@ -21,9 +21,6 @@ JELLYFIN_CACHE_DIR="/var/cache/jellyfin"
 # web client path, installed by the jellyfin-web package
 JELLYFIN_WEB_OPT="--webdir=/usr/share/jellyfin/web"
 
-# Restart script for in-app server control
-JELLYFIN_RESTART_OPT="--restartpath=/usr/lib/jellyfin/restart.sh"
-
 # ffmpeg binary paths, overriding the system values
 JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/lib/jellyfin-ffmpeg/ffmpeg"
 
@@ -50,4 +47,4 @@ JELLYFIN_ADDITIONAL_OPTS=""
 # Application username
 JELLYFIN_USER="jellyfin"
 # Full application command
-JELLYFIN_ARGS="$JELLYFIN_WEB_OPT $JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLYFIN_NOWEBAPP_OPT $JELLFIN_ADDITIONAL_OPTS"
+JELLYFIN_ARGS="$JELLYFIN_WEB_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLYFIN_NOWEBAPP_OPT $JELLFIN_ADDITIONAL_OPTS"

+ 0 - 1
debian/install

@@ -3,4 +3,3 @@ debian/conf/jellyfin etc/default/
 debian/conf/logging.json etc/jellyfin/
 debian/conf/jellyfin.service.conf etc/systemd/system/jellyfin.service.d/
 debian/conf/jellyfin-sudoers etc/sudoers.d/
-debian/bin/restart.sh usr/lib/jellyfin/

+ 1 - 1
debian/jellyfin.service

@@ -8,7 +8,7 @@ EnvironmentFile = /etc/default/jellyfin
 User = jellyfin
 Group = jellyfin
 WorkingDirectory = /var/lib/jellyfin
-ExecStart = /usr/bin/jellyfin $JELLYFIN_WEB_OPT $JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLYFIN_NOWEBAPP_OPT $JELLYFIN_ADDITIONAL_OPTS
+ExecStart = /usr/bin/jellyfin $JELLYFIN_WEB_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLYFIN_NOWEBAPP_OPT $JELLYFIN_ADDITIONAL_OPTS
 Restart = on-failure
 TimeoutSec = 15
 SuccessExitStatus=0 143

+ 0 - 2
debian/postinst

@@ -59,8 +59,6 @@ case "$1" in
     chgrp adm $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA
     chmod 0750 $PROGRAMDATA $CONFIGDATA $LOGDATA $CACHEDATA
 
-    chmod +x /usr/lib/jellyfin/restart.sh > /dev/null 2>&1 || true
-
     # Install jellyfin symlink into /usr/bin
     ln -sf /usr/lib/jellyfin/bin/jellyfin /usr/bin/jellyfin
 

+ 0 - 3
fedora/jellyfin.env

@@ -23,9 +23,6 @@ JELLYFIN_CACHE_DIR="/var/cache/jellyfin"
 # web client path, installed by the jellyfin-web package
 # JELLYFIN_WEB_OPT="--webdir=/usr/share/jellyfin-web"
 
-# In-App service control
-JELLYFIN_RESTART_OPT="--restartpath=/usr/libexec/jellyfin/restart.sh"
-
 # [OPTIONAL] ffmpeg binary paths, overriding the UI-configured values
 #JELLYFIN_FFMPEG_OPT="--ffmpeg=/usr/bin/ffmpeg"
 

+ 1 - 1
fedora/jellyfin.service

@@ -8,7 +8,7 @@ EnvironmentFile = /etc/sysconfig/jellyfin
 User = jellyfin
 Group = jellyfin
 WorkingDirectory = /var/lib/jellyfin
-ExecStart = /usr/bin/jellyfin $JELLYFIN_WEB_OPT $JELLYFIN_RESTART_OPT $JELLYFIN_FFMPEG_OPT $JELLYFIN_SERVICE_OPT $JELLYFIN_NOWEBAPP_OPT $JELLYFIN_ADDITIONAL_OPTS
+ExecStart = /usr/bin/jellyfin $JELLYFIN_WEB_OPT $JELLYFIN_SERVICE_OPT $JELLYFIN_NOWEBAPP_OPT $JELLYFIN_ADDITIONAL_OPTS
 Restart = on-failure
 TimeoutSec = 15
 SuccessExitStatus=0 143

+ 6 - 9
fedora/jellyfin.spec

@@ -17,10 +17,9 @@ Source0:        jellyfin-server-%{version}.tar.gz
 Source11:       jellyfin.service
 Source12:       jellyfin.env
 Source13:       jellyfin.sudoers
-Source14:       restart.sh
-Source15:       jellyfin.override.conf
-Source16:       jellyfin-firewalld.xml
-Source17:       jellyfin-server-lowports.conf
+Source14:       jellyfin.override.conf
+Source15:       jellyfin-firewalld.xml
+Source16:       jellyfin-server-lowports.conf
 
 %{?systemd_requires}
 BuildRequires:  systemd
@@ -76,16 +75,15 @@ dotnet publish --configuration Release --self-contained --runtime %{dotnet_runti
 %{__mkdir} -p %{buildroot}%{_libdir}/jellyfin %{buildroot}%{_bindir}
 %{__cp} -r Jellyfin.Server/bin/Release/net7.0/%{dotnet_runtime}/publish/* %{buildroot}%{_libdir}/jellyfin
 ln -srf %{_libdir}/jellyfin/jellyfin %{buildroot}%{_bindir}/jellyfin
-%{__install} -D %{SOURCE14} %{buildroot}%{_libexecdir}/jellyfin/restart.sh
 
 # Jellyfin config
 %{__install} -D Jellyfin.Server/Resources/Configuration/logging.json %{buildroot}%{_sysconfdir}/jellyfin/logging.json
 %{__install} -D %{SOURCE12} %{buildroot}%{_sysconfdir}/sysconfig/jellyfin
 
 # system config
-%{__install} -D %{SOURCE16} %{buildroot}%{_prefix}/lib/firewalld/services/jellyfin.xml
+%{__install} -D %{SOURCE15} %{buildroot}%{_prefix}/lib/firewalld/services/jellyfin.xml
 %{__install} -D %{SOURCE13} %{buildroot}%{_sysconfdir}/sudoers.d/jellyfin-sudoers
-%{__install} -D %{SOURCE15} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf
+%{__install} -D %{SOURCE14} %{buildroot}%{_sysconfdir}/systemd/system/jellyfin.service.d/override.conf
 %{__install} -D %{SOURCE11} %{buildroot}%{_unitdir}/jellyfin.service
 
 # empty directories
@@ -95,7 +93,7 @@ ln -srf %{_libdir}/jellyfin/jellyfin %{buildroot}%{_bindir}/jellyfin
 %{__mkdir} -p %{buildroot}%{_var}/log/jellyfin
 
 # jellyfin-server-lowports subpackage
-%{__install} -D -m 0644 %{SOURCE17} %{buildroot}%{_unitdir}/jellyfin.service.d/jellyfin-server-lowports.conf
+%{__install} -D -m 0644 %{SOURCE16} %{buildroot}%{_unitdir}/jellyfin.service.d/jellyfin-server-lowports.conf
 
 
 %files
@@ -110,7 +108,6 @@ ln -srf %{_libdir}/jellyfin/jellyfin %{buildroot}%{_bindir}/jellyfin
 %attr(755,root,root) %{_libdir}/jellyfin/createdump
 %attr(755,root,root) %{_libdir}/jellyfin/jellyfin
 %{_libdir}/jellyfin/*
-%attr(755,root,root) %{_libexecdir}/jellyfin/restart.sh
 
 # Jellyfin config
 %config(noreplace) %attr(644,jellyfin,jellyfin) %{_sysconfdir}/jellyfin/logging.json

+ 0 - 56
fedora/restart.sh

@@ -1,56 +0,0 @@
-#!/bin/bash
-
-# restart.sh - Jellyfin server restart script
-# Part of the Jellyfin project (https://github.com/jellyfin)
-#
-# This script restarts the Jellyfin daemon on Linux when using
-# the Restart button on the admin dashboard. It supports the
-# systemctl, service, and traditional /etc/init.d (sysv) restart
-# methods, chosen automatically by which one is found first (in
-# that order).
-#
-# This script is used by the Debian/Ubuntu/Fedora/CentOS packages.
-
-# This is the Right Way(tm) to check if we are booted with
-# systemd, according to sd_booted(3)
-if [ -d /run/systemd/system ]; then
-    cmd=systemctl
-else
-    # Everything else is really hard to figure out, so we just use
-    # service(8) if it's available - that works with most init
-    # systems/distributions I know of, including FreeBSD
-    if type service >/dev/null 2>&1; then
-        cmd=service
-    else
-        # If even service(8) isn't available, we just try /etc/init.d
-        # and hope for the best
-        if [ -d /etc/init.d ]; then
-            cmd=sysv
-        else
-            echo "Unable to detect a way to restart Jellyfin; bailing out" 1>&2
-            echo "Please report this bug to https://github.com/jellyfin/jellyfin/issues" 1>&2
-            exit 1
-        fi
-    fi
-fi
-
-if type sudo >/dev/null 2>&1; then
-    sudo_command=sudo
-else
-    sudo_command=
-fi
-
-echo "Detected service control platform '$cmd'; using it to restart Jellyfin..."
-case $cmd in
-    'systemctl')
-        # Without systemd-run here, `jellyfin.service`'s shutdown terminates this process too
-        $sudo_command systemd-run systemctl restart jellyfin
-        ;;
-    'service')
-        echo "sleep 0.5; $sudo_command service jellyfin start" | at now
-        ;;
-    'sysv')
-        echo "sleep 0.5; /usr/bin/sudo /etc/init.d/jellyfin start" | at now 
-        ;;
-esac
-exit 0

+ 93 - 10
tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs

@@ -31,16 +31,6 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
         public void GetFrameRate_Success(string value, float? expected)
             => Assert.Equal(expected, ProbeResultNormalizer.GetFrameRate(value));
 
-        [Theory]
-        [InlineData(0.5f, "0/1", false)]
-        [InlineData(24.5f, "8/196", false)]
-        [InlineData(63.5f, "1/127", true)]
-        [InlineData(null, "1/60", false)]
-        [InlineData(30f, "2/120", true)]
-        [InlineData(59.999996f, "1563/187560", true)]
-        public void IsCodecTimeBaseDoubleTheFrameRate_Success(float? frameRate, string codecTimeBase, bool expected)
-            => Assert.Equal(expected, ProbeResultNormalizer.IsCodecTimeBaseDoubleTheFrameRate(frameRate, codecTimeBase));
-
         [Fact]
         public void GetMediaInfo_MetaData_Success()
         {
@@ -158,6 +148,99 @@ namespace Jellyfin.MediaEncoding.Tests.Probing
             Assert.False(res.MediaStreams[5].IsHearingImpaired);
         }
 
+        [Fact]
+        public void GetMediaInfo_ProgressiveVideoNoFieldOrder_Success()
+        {
+            var bytes = File.ReadAllBytes("Test Data/Probing/video_progressive_no_field_order.json");
+
+            var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
+            MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_progressive_no_field_order.mp4", MediaProtocol.File);
+
+            Assert.Equal(2, res.MediaStreams.Count);
+
+            Assert.NotNull(res.VideoStream);
+            Assert.Equal(res.MediaStreams[0], res.VideoStream);
+            Assert.Equal(0, res.VideoStream.Index);
+            Assert.Equal("h264", res.VideoStream.Codec);
+            Assert.Equal("Main", res.VideoStream.Profile);
+            Assert.Equal(MediaStreamType.Video, res.VideoStream.Type);
+            Assert.Equal(1080, res.VideoStream.Height);
+            Assert.Equal(1920, res.VideoStream.Width);
+            Assert.False(res.VideoStream.IsInterlaced);
+            Assert.Equal("16:9", res.VideoStream.AspectRatio);
+            Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
+            Assert.Equal(41d, res.VideoStream.Level);
+            Assert.Equal(1, res.VideoStream.RefFrames);
+            Assert.True(res.VideoStream.IsAVC);
+            Assert.Equal(23.9760246f, res.VideoStream.RealFrameRate);
+            Assert.Equal("1/24000", res.VideoStream.TimeBase);
+            Assert.Equal(3948341, res.VideoStream.BitRate);
+            Assert.Equal(8, res.VideoStream.BitDepth);
+            Assert.True(res.VideoStream.IsDefault);
+        }
+
+        [Fact]
+        public void GetMediaInfo_ProgressiveVideoNoFieldOrder2_Success()
+        {
+            var bytes = File.ReadAllBytes("Test Data/Probing/video_progressive_no_field_order2.json");
+
+            var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
+            MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_progressive_no_field_order2.mp4", MediaProtocol.File);
+
+            Assert.Single(res.MediaStreams);
+
+            Assert.NotNull(res.VideoStream);
+            Assert.Equal(res.MediaStreams[0], res.VideoStream);
+            Assert.Equal(0, res.VideoStream.Index);
+            Assert.Equal("h264", res.VideoStream.Codec);
+            Assert.Equal("High", res.VideoStream.Profile);
+            Assert.Equal(MediaStreamType.Video, res.VideoStream.Type);
+            Assert.Equal(720, res.VideoStream.Height);
+            Assert.Equal(1280, res.VideoStream.Width);
+            Assert.False(res.VideoStream.IsInterlaced);
+            Assert.Equal("16:9", res.VideoStream.AspectRatio);
+            Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
+            Assert.Equal(31d, res.VideoStream.Level);
+            Assert.Equal(1, res.VideoStream.RefFrames);
+            Assert.True(res.VideoStream.IsAVC);
+            Assert.Equal(25f, res.VideoStream.RealFrameRate);
+            Assert.Equal("1/12800", res.VideoStream.TimeBase);
+            Assert.Equal(53288, res.VideoStream.BitRate);
+            Assert.Equal(8, res.VideoStream.BitDepth);
+            Assert.True(res.VideoStream.IsDefault);
+        }
+
+        [Fact]
+        public void GetMediaInfo_InterlacedVideo_Success()
+        {
+            var bytes = File.ReadAllBytes("Test Data/Probing/video_interlaced.json");
+
+            var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions);
+            MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_interlaced.mp4", MediaProtocol.File);
+
+            Assert.Single(res.MediaStreams);
+
+            Assert.NotNull(res.VideoStream);
+            Assert.Equal(res.MediaStreams[0], res.VideoStream);
+            Assert.Equal(0, res.VideoStream.Index);
+            Assert.Equal("h264", res.VideoStream.Codec);
+            Assert.Equal("High", res.VideoStream.Profile);
+            Assert.Equal(MediaStreamType.Video, res.VideoStream.Type);
+            Assert.Equal(720, res.VideoStream.Height);
+            Assert.Equal(1280, res.VideoStream.Width);
+            Assert.True(res.VideoStream.IsInterlaced);
+            Assert.Equal("16:9", res.VideoStream.AspectRatio);
+            Assert.Equal("yuv420p", res.VideoStream.PixelFormat);
+            Assert.Equal(40d, res.VideoStream.Level);
+            Assert.Equal(1, res.VideoStream.RefFrames);
+            Assert.True(res.VideoStream.IsAVC);
+            Assert.Equal(25f, res.VideoStream.RealFrameRate);
+            Assert.Equal("1/12800", res.VideoStream.TimeBase);
+            Assert.Equal(56945, res.VideoStream.BitRate);
+            Assert.Equal(8, res.VideoStream.BitDepth);
+            Assert.True(res.VideoStream.IsDefault);
+        }
+
         [Fact]
         public void GetMediaInfo_MusicVideo_Success()
         {

+ 81 - 0
tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_interlaced.json

@@ -0,0 +1,81 @@
+{
+    "streams": [
+        {
+            "index": 0,
+            "codec_name": "h264",
+            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
+            "profile": "High",
+            "codec_type": "video",
+            "codec_tag_string": "avc1",
+            "codec_tag": "0x31637661",
+            "width": 1280,
+            "height": 720,
+            "coded_width": 1280,
+            "coded_height": 720,
+            "closed_captions": 0,
+            "film_grain": 0,
+            "has_b_frames": 2,
+            "pix_fmt": "yuv420p",
+            "level": 40,
+            "chroma_location": "left",
+            "field_order": "tt",
+            "refs": 1,
+            "is_avc": "true",
+            "nal_length_size": "4",
+            "id": "0x1",
+            "r_frame_rate": "25/1",
+            "avg_frame_rate": "25/1",
+            "time_base": "1/12800",
+            "start_pts": 0,
+            "start_time": "0.000000",
+            "duration_ts": 3840000,
+            "duration": "300.000000",
+            "bit_rate": "56945",
+            "bits_per_raw_sample": "8",
+            "nb_frames": "7500",
+            "extradata_size": 42,
+            "disposition": {
+                "default": 1,
+                "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,
+                "captions": 0,
+                "descriptions": 0,
+                "metadata": 0,
+                "dependent": 0,
+                "still_image": 0
+            },
+            "tags": {
+                "language": "und",
+                "handler_name": "VideoHandler",
+                "vendor_id": "[0][0][0][0]"
+            }
+        }
+    ],
+    "format": {
+        "filename": "test-gray.720i.mp4",
+        "nb_streams": 1,
+        "nb_programs": 0,
+        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
+        "format_long_name": "QuickTime / MOV",
+        "start_time": "0.000000",
+        "duration": "300.000000",
+        "size": "2223957",
+        "bit_rate": "59305",
+        "probe_score": 100,
+        "tags": {
+            "major_brand": "isom",
+            "minor_version": "512",
+            "compatible_brands": "isomiso2avc1mp41",
+            "encoder": "Lavf58.20.100"
+        }
+    }
+}

+ 133 - 0
tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_progressive_no_field_order.json

@@ -0,0 +1,133 @@
+{
+    "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": "1001/48000",
+            "codec_tag_string": "avc1",
+            "codec_tag": "0x31637661",
+            "width": 1920,
+            "height": 1080,
+            "coded_width": 1920,
+            "coded_height": 1088,
+            "closed_captions": 0,
+            "has_b_frames": 1,
+            "sample_aspect_ratio": "1:1",
+            "display_aspect_ratio": "16:9",
+            "pix_fmt": "yuv420p",
+            "level": 41,
+            "chroma_location": "left",
+            "refs": 1,
+            "is_avc": "true",
+            "nal_length_size": "4",
+            "r_frame_rate": "24000/1001",
+            "avg_frame_rate": "24000/1001",
+            "time_base": "1/24000",
+            "start_pts": 1000,
+            "start_time": "0.041667",
+            "duration_ts": 29095066,
+            "duration": "1212.294417",
+            "bit_rate": "3948341",
+            "bits_per_raw_sample": "8",
+            "nb_frames": "29066",
+            "disposition": {
+                "default": 1,
+                "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
+            },
+            "tags": {
+                "creation_time": "2020-01-20T13:56:34.000000Z",
+                "language": "eng",
+                "handler_name": "\fVideoHandler",
+                "encoder": "h264"
+            }
+        },
+        {
+            "index": 1,
+            "codec_name": "ac3",
+            "codec_long_name": "ATSC A/52A (AC-3)",
+            "codec_type": "audio",
+            "codec_time_base": "1/48000",
+            "codec_tag_string": "ac-3",
+            "codec_tag": "0x332d6361",
+            "sample_fmt": "fltp",
+            "sample_rate": "48000",
+            "channels": 2,
+            "channel_layout": "stereo",
+            "bits_per_sample": 0,
+            "dmix_mode": "-1",
+            "ltrt_cmixlev": "-1.000000",
+            "ltrt_surmixlev": "-1.000000",
+            "loro_cmixlev": "-1.000000",
+            "loro_surmixlev": "-1.000000",
+            "r_frame_rate": "0/0",
+            "avg_frame_rate": "0/0",
+            "time_base": "1/48000",
+            "start_pts": 0,
+            "start_time": "0.000000",
+            "duration_ts": 58232832,
+            "duration": "1213.184000",
+            "bit_rate": "224000",
+            "nb_frames": "37912",
+            "disposition": {
+                "default": 1,
+                "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
+            },
+            "tags": {
+                "creation_time": "2020-01-20T13:56:34.000000Z",
+                "language": "eng",
+                "handler_name": "\fSoundHandler"
+            },
+            "side_data_list": [
+                {
+                    "side_data_type": "Audio Service Type"
+                }
+            ]
+        }
+    ],
+    "format": {
+        "filename": "The Big Bang Theory - S01E17.mp4",
+        "nb_streams": 2,
+        "nb_programs": 0,
+        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
+        "format_long_name": "QuickTime / MOV",
+        "start_time": "0.000000",
+        "duration": "1213.184000",
+        "size": "633084606",
+        "bit_rate": "4174698",
+        "probe_score": 100,
+        "tags": {
+            "major_brand": "mp42",
+            "minor_version": "512",
+            "compatible_brands": "mp42",
+            "creation_time": "2020-01-20T13:56:34.000000Z",
+            "media_type": "9",
+            "season_number": "0",
+            "episode_sort": "0",
+            "hd_video": "0",
+            "iTunMOVI": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\"  \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"><plist version=\"1.0\"><dict><key>studio</key><string>studio</string><key>cast</key><array><dict><key>name</key><string></string></dict></array><key>directors</key><array><dict><key>name</key><string></string></dict></array><key>producers</key><array><dict><key>name</key><string></string></dict></array><key>codirectors</key><array><dict><key>name</key><string>codirector</string></dict></array><key>screenwriters</key><array><dict><key>name</key><string></string></dict></array></dict></plist>"
+        }
+    }
+}

+ 72 - 0
tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_progressive_no_field_order2.json

@@ -0,0 +1,72 @@
+{
+    "streams": [
+        {
+            "index": 0,
+            "codec_name": "h264",
+            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
+            "profile": "High",
+            "codec_type": "video",
+            "codec_time_base": "1/50",
+            "codec_tag_string": "avc1",
+            "codec_tag": "0x31637661",
+            "width": 1280,
+            "height": 720,
+            "coded_width": 1280,
+            "coded_height": 720,
+            "closed_captions": 0,
+            "has_b_frames": 2,
+            "pix_fmt": "yuv420p",
+            "level": 31,
+            "chroma_location": "left",
+            "refs": 1,
+            "is_avc": "true",
+            "nal_length_size": "4",
+            "r_frame_rate": "25/1",
+            "avg_frame_rate": "25/1",
+            "time_base": "1/12800",
+            "start_pts": 0,
+            "start_time": "0.000000",
+            "duration_ts": 3840000,
+            "duration": "300.000000",
+            "bit_rate": "53288",
+            "bits_per_raw_sample": "8",
+            "nb_frames": "7500",
+            "disposition": {
+                "default": 1,
+                "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
+            },
+            "tags": {
+                "language": "und",
+                "handler_name": "VideoHandler"
+            }
+        }
+    ],
+    "format": {
+        "filename": "test-gray.720p.mp4",
+        "nb_streams": 1,
+        "nb_programs": 0,
+        "format_name": "mov,mp4,m4a,3gp,3g2,mj2",
+        "format_long_name": "QuickTime / MOV",
+        "start_time": "0.000000",
+        "duration": "300.000000",
+        "size": "2086818",
+        "bit_rate": "55648",
+        "probe_score": 100,
+        "tags": {
+            "major_brand": "isom",
+            "minor_version": "512",
+            "compatible_brands": "isomiso2avc1mp41",
+            "encoder": "Lavf58.20.100"
+        }
+    }
+}