Ver Fonte

Merge branch 'api-migration' into api-syncplay

# Conflicts:
#	MediaBrowser.Api/SyncPlay/SyncPlayService.cs
David há 4 anos atrás
pai
commit
be8cf1e9ef
100 ficheiros alterados com 1095 adições e 572 exclusões
  1. 163 0
      .ci/azure-pipelines-package.yml
  2. 6 0
      .ci/azure-pipelines.yml
  3. 1 2
      .gitignore
  4. 6 6
      .vscode/launch.json
  5. 6 1
      .vscode/tasks.json
  6. 1 0
      DvdLib/Ifo/Cell.cs
  7. 2 0
      DvdLib/Ifo/Chapter.cs
  8. 13 3
      DvdLib/Ifo/Dvd.cs
  9. 8 2
      DvdLib/Ifo/DvdTime.cs
  10. 13 2
      DvdLib/Ifo/ProgramChain.cs
  11. 8 1
      DvdLib/Ifo/Title.cs
  12. 4 3
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  13. 12 10
      Emby.Dlna/Didl/DidlBuilder.cs
  14. 1 2
      Emby.Dlna/Didl/Filter.cs
  15. 26 6
      Emby.Dlna/DlnaManager.cs
  16. 18 10
      Emby.Dlna/Eventing/EventManager.cs
  17. 3 0
      Emby.Dlna/Eventing/EventSubscription.cs
  18. 26 20
      Emby.Dlna/Main/DlnaEntryPoint.cs
  19. 21 25
      Emby.Dlna/PlayTo/Device.cs
  20. 5 0
      Emby.Dlna/PlayTo/PlayToController.cs
  21. 10 5
      Emby.Dlna/PlayTo/PlayToManager.cs
  22. 1 0
      Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
  23. 0 1
      Emby.Dlna/PlayTo/SsdpHttpClient.cs
  24. 2 0
      Emby.Dlna/PlayTo/uBaseObject.cs
  25. 1 1
      Emby.Dlna/Profiles/DefaultProfile.cs
  26. 1 1
      Emby.Dlna/Profiles/DenonAvrProfile.cs
  27. 1 1
      Emby.Dlna/Profiles/DirectTvProfile.cs
  28. 1 1
      Emby.Dlna/Profiles/Foobar2000Profile.cs
  29. 1 1
      Emby.Dlna/Profiles/MarantzProfile.cs
  30. 2 1
      Emby.Dlna/Profiles/MediaMonkeyProfile.cs
  31. 2 1
      Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
  32. 2 1
      Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
  33. 2 1
      Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
  34. 2 1
      Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
  35. 5 0
      Emby.Dlna/Server/DescriptionXmlBuilder.cs
  36. 4 0
      Emby.Dlna/Service/BaseControlHandler.cs
  37. 1 0
      Emby.Dlna/Service/ServiceXmlBuilder.cs
  38. 1 1
      Emby.Dlna/Ssdp/DeviceDiscovery.cs
  39. 1 0
      Emby.Naming/AudioBook/AudioBookFilePathParser.cs
  40. 3 3
      Emby.Naming/Common/MediaType.cs
  41. 19 20
      Emby.Server.Implementations/ApplicationHost.cs
  42. 1 1
      Emby.Server.Implementations/Channels/ChannelManager.cs
  43. 0 56
      Emby.Server.Implementations/Collections/CollectionManager.cs
  44. 0 57
      Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
  45. 0 1
      Emby.Server.Implementations/ConfigurationOptions.cs
  46. 2 3
      Emby.Server.Implementations/Data/BaseSqliteRepository.cs
  47. 0 1
      Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
  48. 3 3
      Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
  49. 197 25
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  50. 7 4
      Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
  51. 2 1
      Emby.Server.Implementations/Devices/DeviceManager.cs
  52. 24 16
      Emby.Server.Implementations/Dto/DtoService.cs
  53. 7 7
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  54. 0 1
      Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
  55. 0 2
      Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs
  56. 11 3
      Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs
  57. 1 1
      Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs
  58. 2 2
      Emby.Server.Implementations/HttpServer/FileWriter.cs
  59. 2 1
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  60. 8 6
      Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
  61. 84 97
      Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs
  62. 2 2
      Emby.Server.Implementations/HttpServer/ResponseFilter.cs
  63. 0 2
      Emby.Server.Implementations/IO/LibraryMonitor.cs
  64. 13 1
      Emby.Server.Implementations/IO/ManagedFileSystem.cs
  65. 0 5
      Emby.Server.Implementations/IStartupOptions.cs
  66. 0 1
      Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
  67. 0 1
      Emby.Server.Implementations/Images/DynamicImageProvider.cs
  68. 12 2
      Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
  69. 2 0
      Emby.Server.Implementations/Library/ExclusiveLiveStream.cs
  70. 42 8
      Emby.Server.Implementations/Library/IgnorePatterns.cs
  71. 48 51
      Emby.Server.Implementations/Library/LibraryManager.cs
  72. 2 5
      Emby.Server.Implementations/Library/LiveStreamHelper.cs
  73. 7 7
      Emby.Server.Implementations/Library/MediaSourceManager.cs
  74. 1 1
      Emby.Server.Implementations/Library/ResolverHelper.cs
  75. 2 2
      Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
  76. 1 1
      Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
  77. 2 2
      Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
  78. 4 0
      Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
  79. 1 1
      Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
  80. 3 2
      Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
  81. 1 0
      Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
  82. 2 3
      Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
  83. 1 1
      Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
  84. 1 1
      Emby.Server.Implementations/Library/SearchEngine.cs
  85. 2 2
      Emby.Server.Implementations/Library/UserDataManager.cs
  86. 0 1
      Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
  87. 0 1
      Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
  88. 0 1
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  89. 5 5
      Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
  90. 0 1
      Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
  91. 122 5
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  92. 1 0
      Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
  93. 19 13
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  94. 1 1
      Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
  95. 1 3
      Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
  96. 30 7
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  97. 7 7
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
  98. 3 2
      Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
  99. 0 1
      Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  100. 1 2
      Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs

+ 163 - 0
.ci/azure-pipelines-package.yml

@@ -0,0 +1,163 @@
+jobs:
+- job: BuildPackage
+  displayName: 'Build Packages'
+
+  strategy:
+    matrix:
+      CentOS.amd64:
+        BuildConfiguration: centos.amd64
+      Fedora.amd64:
+        BuildConfiguration: fedora.amd64
+      Debian.amd64:
+        BuildConfiguration: debian.amd64
+      Debian.arm64:
+        BuildConfiguration: debian.arm64
+      Debian.armhf:
+        BuildConfiguration: debian.armhf
+      Ubuntu.amd64:
+        BuildConfiguration: ubuntu.amd64
+      Ubuntu.arm64:
+        BuildConfiguration: ubuntu.arm64
+      Ubuntu.armhf:
+        BuildConfiguration: ubuntu.armhf
+      Linux.amd64:
+        BuildConfiguration: linux.amd64
+      Windows.amd64:
+        BuildConfiguration: windows.amd64
+      MacOS:
+        BuildConfiguration: macos
+      Portable:
+        BuildConfiguration: portable
+
+  pool:
+    vmImage: 'ubuntu-latest'
+
+  steps:
+  - script: 'docker build -f deployment/Dockerfile.$(BuildConfiguration) -t jellyfin-server-$(BuildConfiguration) deployment'
+    displayName: 'Build Dockerfile'
+
+  - script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="yes" -e BUILD_ID=$(Build.BuildNumber) jellyfin-server-$(BuildConfiguration)'
+    displayName: 'Run Dockerfile (unstable)'
+    condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
+
+  - script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="no" -e BUILD_ID=$(Build.BuildNumber) jellyfin-server-$(BuildConfiguration)'
+    displayName: 'Run Dockerfile (stable)'
+    condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
+
+  - task: PublishPipelineArtifact@1
+    displayName: 'Publish Release'
+    inputs:
+      targetPath: '$(Build.SourcesDirectory)/deployment/dist'
+      artifactName: 'jellyfin-server-$(BuildConfiguration)'
+
+  - task: SSH@0
+    displayName: 'Create target directory on repository server'
+    inputs:
+      sshEndpoint: repository
+      runOptions: 'inline'
+      inline: 'mkdir -p /srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
+
+  - task: CopyFilesOverSSH@0
+    displayName: 'Upload artifacts to repository server'
+    inputs:
+      sshEndpoint: repository
+      sourceFolder: '$(Build.SourcesDirectory)/deployment/dist'
+      contents: '**'
+      targetFolder: '/srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
+
+- job: BuildDocker
+  displayName: 'Build Docker'
+
+  strategy:
+    matrix:
+      amd64:
+        BuildConfiguration: amd64
+      arm64:
+        BuildConfiguration: arm64
+      armhf:
+        BuildConfiguration: armhf
+
+  pool:
+    vmImage: 'ubuntu-latest'
+
+  steps:
+  - task: Docker@2
+    displayName: 'Push Unstable Image'
+    condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
+    inputs:
+      repository: 'jellyfin/jellyfin-server'
+      command: buildAndPush
+      buildContext: '.'
+      Dockerfile: 'deployment/Dockerfile.docker.$(BuildConfiguration)'
+      containerRegistry: Docker Hub
+      tags: |
+        unstable-$(Build.BuildNumber)-$(BuildConfiguration)
+        unstable-$(BuildConfiguration)
+
+  - task: Docker@2
+    displayName: 'Push Stable Image'
+    condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
+    inputs:
+      repository: 'jellyfin/jellyfin-server'
+      command: buildAndPush
+      buildContext: '.'
+      Dockerfile: 'deployment/Dockerfile.docker.$(BuildConfiguration)'
+      containerRegistry: Docker Hub
+      tags: |
+        stable-$(Build.BuildNumber)-$(BuildConfiguration)
+        stable-$(BuildConfiguration)
+
+- job: CollectArtifacts
+  displayName: 'Collect Artifacts'
+  dependsOn:
+  - BuildPackage
+  - BuildDocker
+  condition: and(succeeded('BuildPackage'), succeeded('BuildDocker'))
+
+  pool:
+    vmImage: 'ubuntu-latest'
+
+  steps:
+  - task: SSH@0
+    displayName: 'Update Unstable Repository'
+    condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
+    inputs:
+      sshEndpoint: repository
+      runOptions: 'inline'
+      inline: |
+        sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber) unstable
+        rm $0
+        exit
+
+  - task: SSH@0
+    displayName: 'Update Stable Repository'
+    condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
+    inputs:
+      sshEndpoint: repository
+      runOptions: 'inline'
+      inline: |
+        sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber)
+        rm $0
+        exit
+
+- job: PublishNuget
+  displayName: 'Publish NuGet packages'
+  dependsOn:
+  - BuildPackage
+  condition: and(succeeded('BuildPackage'), startsWith(variables['Build.SourceBranch'], 'refs/tags'))
+  
+  pool:
+    vmImage: 'ubuntu-latest'
+
+  steps:
+  - task: NuGetCommand@2
+    inputs:
+      command: 'pack'
+      packagesToPack: Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj
+      packDestination: '$(Build.ArtifactStagingDirectory)'
+
+  - task: NuGetCommand@2
+    inputs:
+      command: 'push'
+      packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
+      includeNugetOrg: 'true'

+ 6 - 0
.ci/azure-pipelines.yml

@@ -15,11 +15,13 @@ trigger:
   batch: true
 
 jobs:
+- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
   - template: azure-pipelines-main.yml
     parameters:
       LinuxImage: 'ubuntu-latest'
       RestoreBuildProjects: $(RestoreBuildProjects)
 
+- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
   - template: azure-pipelines-test.yml
     parameters:
       ImageNames:
@@ -27,6 +29,7 @@ jobs:
         Windows: 'windows-latest'
         macOS: 'macos-latest'
 
+- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
   - template: azure-pipelines-abi.yml
     parameters:
       Packages:
@@ -43,3 +46,6 @@ jobs:
           NugetPackageName: Jellyfin.Common
           AssemblyFileName: MediaBrowser.Common.dll
       LinuxImage: 'ubuntu-latest'
+
+- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}:
+  - template: azure-pipelines-package.yml

+ 1 - 2
.gitignore

@@ -39,7 +39,6 @@ ProgramData*/
 CorePlugins*/
 ProgramData-Server*/
 ProgramData-UI*/
-MediaBrowser.WebDashboard/jellyfin-web/**
 
 #################
 ## Visual Studio
@@ -276,4 +275,4 @@ BenchmarkDotNet.Artifacts
 # Ignore web artifacts from native builds
 web/
 web-src.*
-MediaBrowser.WebDashboard/jellyfin-web/
+MediaBrowser.WebDashboard/jellyfin-web

+ 6 - 6
.vscode/launch.json

@@ -1,9 +1,6 @@
 {
-   // Use IntelliSense to find out which attributes exist for C# debugging
-   // Use hover for the description of the existing attributes
-   // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
-   "version": "0.2.0",
-   "configurations": [
+    "version": "0.2.0",
+    "configurations": [
         {
             "name": ".NET Core Launch (console)",
             "type": "coreclr",
@@ -24,5 +21,8 @@
             "request": "attach",
             "processId": "${command:pickProcess}"
         }
-    ,]
+    ],
+    "env": {
+        "DOTNET_CLI_TELEMETRY_OPTOUT": "1"
+    }
 }

+ 6 - 1
.vscode/tasks.json

@@ -21,5 +21,10 @@
             ],
             "problemMatcher": "$msCompile"
         }
-    ]
+    ],
+    "options": {
+        "env": {
+            "DOTNET_CLI_TELEMETRY_OPTOUT": "1"
+        }
+    }
 }

+ 1 - 0
DvdLib/Ifo/Cell.cs

@@ -7,6 +7,7 @@ namespace DvdLib.Ifo
     public class Cell
     {
         public CellPlaybackInfo PlaybackInfo { get; private set; }
+
         public CellPositionInfo PositionInfo { get; private set; }
 
         internal void ParsePlayback(BinaryReader br)

+ 2 - 0
DvdLib/Ifo/Chapter.cs

@@ -5,7 +5,9 @@ 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)

+ 13 - 3
DvdLib/Ifo/Dvd.cs

@@ -117,12 +117,19 @@ namespace DvdLib.Ifo
                         uint chapNum = 1;
                         vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin);
                         var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1));
-                        if (t == null) continue;
+                        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;
+                            if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1]))
+                            {
+                                break;
+                            }
+
                             chapNum++;
                         }
                         while (vtsFs.Position < (baseAddr + endaddr));
@@ -147,7 +154,10 @@ namespace DvdLib.Ifo
                         uint vtsPgcOffset = vtsRead.ReadUInt32();
 
                         var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum));
-                        if (t != null) t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
+                        if (t != null)
+                        {
+                            t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum);
+                        }
                     }
                 }
             }

+ 8 - 2
DvdLib/Ifo/DvdTime.cs

@@ -15,8 +15,14 @@ namespace DvdLib.Ifo
             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;
+            if ((data[3] & 0x80) != 0)
+            {
+                FrameRate = 30;
+            }
+            else if ((data[3] & 0x40) != 0)
+            {
+                FrameRate = 25;
+            }
         }
 
         private static byte GetBCDValue(byte data)

+ 13 - 2
DvdLib/Ifo/ProgramChain.cs

@@ -22,7 +22,9 @@ namespace DvdLib.Ifo
         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
 
@@ -33,9 +35,11 @@ namespace DvdLib.Ifo
         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;
@@ -71,8 +75,15 @@ namespace DvdLib.Ifo
 
             StillTime = br.ReadByte();
             byte pbMode = br.ReadByte();
-            if (pbMode == 0) PlaybackMode = ProgramPlaybackMode.Sequential;
-            else PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
+            if (pbMode == 0)
+            {
+                PlaybackMode = ProgramPlaybackMode.Sequential;
+            }
+            else
+            {
+                PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle;
+            }
+
             ProgramCount = (uint)(pbMode & 0x7F);
 
             Palette = br.ReadBytes(64);

+ 8 - 1
DvdLib/Ifo/Title.cs

@@ -8,8 +8,11 @@ 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;
@@ -17,6 +20,7 @@ namespace DvdLib.Ifo
         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;
@@ -55,7 +59,10 @@ namespace DvdLib.Ifo
             var pgc = new ProgramChain(pgcNum);
             pgc.ParseHeader(br);
             ProgramChains.Add(pgc);
-            if (entryPgc) EntryProgramChain = pgc;
+            if (entryPgc)
+            {
+                EntryProgramChain = pgc;
+            }
 
             br.BaseStream.Seek(curPos, SeekOrigin.Begin);
         }

+ 4 - 3
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -466,12 +466,12 @@ namespace Emby.Dlna.ContentDirectory
             }
             else if (search.SearchType == SearchType.Playlist)
             {
-                //items = items.OfType<Playlist>();
+                // items = items.OfType<Playlist>();
                 isFolder = true;
             }
             else if (search.SearchType == SearchType.MusicAlbum)
             {
-                //items = items.OfType<MusicAlbum>();
+                // items = items.OfType<MusicAlbum>();
                 isFolder = true;
             }
 
@@ -926,7 +926,7 @@ namespace Emby.Dlna.ContentDirectory
         private QueryResult<ServerItem> GetMovieCollections(User user, InternalItemsQuery query)
         {
             query.Recursive = true;
-            //query.Parent = parent;
+            // query.Parent = parent;
             query.SetUser(user);
 
             query.IncludeItemTypes = new[] { typeof(BoxSet).Name };
@@ -1357,6 +1357,7 @@ namespace Emby.Dlna.ContentDirectory
     internal class ServerItem
     {
         public BaseItem Item { get; set; }
+
         public StubType? StubType { get; set; }
 
         public ServerItem(BaseItem item)

+ 12 - 10
Emby.Dlna/Didl/DidlBuilder.cs

@@ -98,21 +98,21 @@ namespace Emby.Dlna.Didl
             {
                 using (var writer = XmlWriter.Create(builder, settings))
                 {
-                    //writer.WriteStartDocument();
+                    // writer.WriteStartDocument();
 
                     writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL);
 
                     writer.WriteAttributeString("xmlns", "dc", null, NS_DC);
                     writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA);
                     writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP);
-                    //didl.SetAttribute("xmlns:sec", NS_SEC);
+                    // didl.SetAttribute("xmlns:sec", NS_SEC);
 
                     WriteXmlRootAttributes(_profile, writer);
 
                     WriteItemElement(writer, item, user, context, null, deviceId, filter, streamInfo);
 
                     writer.WriteFullEndElement();
-                    //writer.WriteEndDocument();
+                    // writer.WriteEndDocument();
                 }
 
                 return builder.ToString();
@@ -705,13 +705,13 @@ namespace Emby.Dlna.Didl
         }
 
         /// <summary>
-        /// Adds fields used by both items and folders
+        /// Adds fields used by both items and folders.
         /// </summary>
         private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter)
         {
             // Don't filter on dc:title because not all devices will include it in the filter
             // MediaMonkey for example won't display content without a title
-            //if (filter.Contains("dc:title"))
+            // if (filter.Contains("dc:title"))
             {
                 AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
             }
@@ -750,7 +750,7 @@ namespace Emby.Dlna.Didl
                         AddValue(writer, "dc", "description", desc, NS_DC);
                     }
                 }
-                //if (filter.Contains("upnp:longDescription"))
+                // if (filter.Contains("upnp:longDescription"))
                 //{
                 //    if (!string.IsNullOrWhiteSpace(item.Overview))
                 //    {
@@ -765,6 +765,7 @@ namespace Emby.Dlna.Didl
                 {
                     AddValue(writer, "dc", "rating", item.OfficialRating, NS_DC);
                 }
+
                 if (filter.Contains("upnp:rating"))
                 {
                     AddValue(writer, "upnp", "rating", item.OfficialRating, NS_UPNP);
@@ -1052,10 +1053,12 @@ namespace Emby.Dlna.Didl
             {
                 return GetImageInfo(item, ImageType.Primary);
             }
+
             if (item.HasImage(ImageType.Thumb))
             {
                 return GetImageInfo(item, ImageType.Thumb);
             }
+
             if (item.HasImage(ImageType.Backdrop))
             {
                 if (item is Channel)
@@ -1135,25 +1138,24 @@ namespace Emby.Dlna.Didl
 
             if (width == 0 || height == 0)
             {
-                //_imageProcessor.GetImageSize(item, imageInfo);
+                // _imageProcessor.GetImageSize(item, imageInfo);
                 width = null;
                 height = null;
             }
-
             else if (width == -1 || height == -1)
             {
                 width = null;
                 height = null;
             }
 
-            //try
+            // try
             //{
             //    var size = _imageProcessor.GetImageSize(imageInfo);
 
             //    width = size.Width;
             //    height = size.Height;
             //}
-            //catch
+            // catch
             //{
 
             //}

+ 1 - 2
Emby.Dlna/Didl/Filter.cs

@@ -12,7 +12,6 @@ namespace Emby.Dlna.Didl
         public Filter()
             : this("*")
         {
-
         }
 
         public Filter(string filter)
@@ -26,7 +25,7 @@ namespace Emby.Dlna.Didl
         {
             // Don't bother with this. Some clients (media monkey) use the filter and then don't display very well when very little data comes back.
             return true;
-            //return _all || ListHelper.ContainsIgnoreCase(_fields, field);
+            // return _all || ListHelper.ContainsIgnoreCase(_fields, field);
         }
     }
 }

+ 26 - 6
Emby.Dlna/DlnaManager.cs

@@ -88,7 +88,6 @@ namespace Emby.Dlna
                     .Select(i => i.Item2)
                     .ToList();
             }
-
         }
 
         public DeviceProfile GetDefaultProfile()
@@ -141,55 +140,73 @@ namespace Emby.Dlna
             if (!string.IsNullOrEmpty(profileInfo.DeviceDescription))
             {
                 if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
+                {
                     return false;
+                }
             }
 
             if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
             {
                 if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
+                {
                     return false;
+                }
             }
 
             if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
             {
                 if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
+                {
                     return false;
+                }
             }
 
             if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
             {
                 if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
+                {
                     return false;
+                }
             }
 
             if (!string.IsNullOrEmpty(profileInfo.ModelDescription))
             {
                 if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
+                {
                     return false;
+                }
             }
 
             if (!string.IsNullOrEmpty(profileInfo.ModelName))
             {
                 if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
+                {
                     return false;
+                }
             }
 
             if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
             {
                 if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
+                {
                     return false;
+                }
             }
 
             if (!string.IsNullOrEmpty(profileInfo.ModelUrl))
             {
                 if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
+                {
                     return false;
+                }
             }
 
             if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
             {
                 if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
+                {
                     return false;
+                }
             }
 
             return true;
@@ -251,7 +268,7 @@ namespace Emby.Dlna
                         return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase);
                     case HeaderMatchType.Substring:
                         var isMatch = value.ToString().IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1;
-                        //_logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
+                        // _logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
                         return isMatch;
                     case HeaderMatchType.Regex:
                         return Regex.IsMatch(value, header.Value, RegexOptions.IgnoreCase);
@@ -439,6 +456,7 @@ namespace Emby.Dlna
             {
                 throw new ArgumentException("Profile is missing Id");
             }
+
             if (string.IsNullOrEmpty(profile.Name))
             {
                 throw new ArgumentException("Profile is missing Name");
@@ -464,6 +482,7 @@ namespace Emby.Dlna
             {
                 _profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
             }
+
             SerializeToXml(profile, path);
         }
 
@@ -474,7 +493,7 @@ namespace Emby.Dlna
 
         /// <summary>
         /// Recreates the object using serialization, to ensure it's not a subclass.
-        /// If it's a subclass it may not serlialize properly to xml (different root element tag name)
+        /// If it's a subclass it may not serlialize properly to xml (different root element tag name).
         /// </summary>
         /// <param name="profile"></param>
         /// <returns></returns>
@@ -493,6 +512,7 @@ namespace Emby.Dlna
         class InternalProfileInfo
         {
             internal DeviceProfileInfo Info { get; set; }
+
             internal string Path { get; set; }
         }
 
@@ -566,9 +586,9 @@ namespace Emby.Dlna
                 new Foobar2000Profile(),
                 new SharpSmartTvProfile(),
                 new MediaMonkeyProfile(),
-                //new Windows81Profile(),
-                //new WindowsMediaCenterProfile(),
-                //new WindowsPhoneProfile(),
+                // new Windows81Profile(),
+                // new WindowsMediaCenterProfile(),
+                // new WindowsPhoneProfile(),
                 new DirectTvProfile(),
                 new DishHopperJoeyProfile(),
                 new DefaultProfile(),

+ 18 - 10
Emby.Dlna/Eventing/EventManager.cs

@@ -31,18 +31,26 @@ namespace Emby.Dlna.Eventing
         public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl)
         {
             var subscription = GetSubscription(subscriptionId, false);
+            if (subscription != null)
+            {
+                subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
+                int timeoutSeconds = subscription.TimeoutSeconds;
+                subscription.SubscriptionTime = DateTime.UtcNow;
 
-            subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300;
-            int timeoutSeconds = subscription.TimeoutSeconds;
-            subscription.SubscriptionTime = DateTime.UtcNow;
+                _logger.LogDebug(
+                    "Renewing event subscription for {0} with timeout of {1} to {2}",
+                    subscription.NotificationType,
+                    timeoutSeconds,
+                    subscription.CallbackUrl);
 
-            _logger.LogDebug(
-                "Renewing event subscription for {0} with timeout of {1} to {2}",
-                subscription.NotificationType,
-                timeoutSeconds,
-                subscription.CallbackUrl);
+                return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
+            }
 
-            return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
+            return new EventSubscriptionResponse
+            {
+                Content = string.Empty,
+                ContentType = "text/plain"
+            };
         }
 
         public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
@@ -150,6 +158,7 @@ namespace Emby.Dlna.Eventing
                 builder.Append("</" + key + ">");
                 builder.Append("</e:property>");
             }
+
             builder.Append("</e:propertyset>");
 
             var options = new HttpRequestOptions
@@ -169,7 +178,6 @@ namespace Emby.Dlna.Eventing
             {
                 using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false))
                 {
-
                 }
             }
             catch (OperationCanceledException)

+ 3 - 0
Emby.Dlna/Eventing/EventSubscription.cs

@@ -7,10 +7,13 @@ namespace Emby.Dlna.Eventing
     public class EventSubscription
     {
         public string Id { get; set; }
+
         public string CallbackUrl { get; set; }
+
         public string NotificationType { get; set; }
 
         public DateTime SubscriptionTime { get; set; }
+
         public int TimeoutSeconds { get; set; }
 
         public long TriggerCount { get; set; }

+ 26 - 20
Emby.Dlna/Main/DlnaEntryPoint.cs

@@ -35,8 +35,6 @@ namespace Emby.Dlna.Main
         private readonly IServerConfigurationManager _config;
         private readonly ILogger<DlnaEntryPoint> _logger;
         private readonly IServerApplicationHost _appHost;
-
-        private PlayToManager _manager;
         private readonly ISessionManager _sessionManager;
         private readonly IHttpClient _httpClient;
         private readonly ILibraryManager _libraryManager;
@@ -47,14 +45,13 @@ namespace Emby.Dlna.Main
         private readonly ILocalizationManager _localization;
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IMediaEncoder _mediaEncoder;
-
         private readonly IDeviceDiscovery _deviceDiscovery;
-
-        private SsdpDevicePublisher _Publisher;
-
         private readonly ISocketFactory _socketFactory;
         private readonly INetworkManager _networkManager;
+        private readonly object _syncLock = new object();
 
+        private PlayToManager _manager;
+        private SsdpDevicePublisher _publisher;
         private ISsdpCommunicationsServer _communicationsServer;
 
         internal IContentDirectory ContentDirectory { get; private set; }
@@ -181,7 +178,7 @@ namespace Emby.Dlna.Main
                     var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows ||
                                                    OperatingSystem.Id == OperatingSystemId.Linux;
 
-                    _communicationsServer = new SsdpCommunicationsServer(_config, _socketFactory, _networkManager, _logger, enableMultiSocketBinding)
+                    _communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding)
                     {
                         IsShared = true
                     };
@@ -232,20 +229,22 @@ namespace Emby.Dlna.Main
                 return;
             }
 
-            if (_Publisher != null)
+            if (_publisher != null)
             {
                 return;
             }
 
             try
             {
-                _Publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost);
-                _Publisher.LogFunction = LogMessage;
-                _Publisher.SupportPnpRootDevice = false;
+                _publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost)
+                {
+                    LogFunction = LogMessage,
+                    SupportPnpRootDevice = false
+                };
 
                 await RegisterServerEndpoints().ConfigureAwait(false);
 
-                _Publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
+                _publisher.StartBroadcastingAliveMessages(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
             }
             catch (Exception ex)
             {
@@ -267,6 +266,12 @@ namespace Emby.Dlna.Main
                     continue;
                 }
 
+                // Limit to LAN addresses only
+                if (!_networkManager.IsAddressInSubnets(address, true, true))
+                {
+                    continue;
+                }
+
                 var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
 
                 _logger.LogInformation("Registering publisher for {0} on {1}", fullService, address);
@@ -276,7 +281,7 @@ namespace Emby.Dlna.Main
 
                 var device = new SsdpRootDevice
                 {
-                    CacheLifetime = TimeSpan.FromSeconds(1800), //How long SSDP clients can cache this info.
+                    CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info.
                     Location = uri, // Must point to the URL that serves your devices UPnP description document.
                     Address = address,
                     SubnetMask = _networkManager.GetLocalIpSubnetMask(address),
@@ -288,13 +293,13 @@ namespace Emby.Dlna.Main
                 };
 
                 SetProperies(device, fullService);
-                _Publisher.AddDevice(device);
+                _publisher.AddDevice(device);
 
                 var embeddedDevices = new[]
                 {
                     "urn:schemas-upnp-org:service:ContentDirectory:1",
                     "urn:schemas-upnp-org:service:ConnectionManager:1",
-                    //"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
+                    // "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
                 };
 
                 foreach (var subDevice in embeddedDevices)
@@ -320,12 +325,13 @@ namespace Emby.Dlna.Main
             {
                 guid = text.GetMD5();
             }
+
             return guid.ToString("N", CultureInfo.InvariantCulture);
         }
 
         private void SetProperies(SsdpDevice device, string fullDeviceType)
         {
-            var service = fullDeviceType.Replace("urn:", string.Empty).Replace(":1", string.Empty);
+            var service = fullDeviceType.Replace("urn:", string.Empty, StringComparison.OrdinalIgnoreCase).Replace(":1", string.Empty, StringComparison.OrdinalIgnoreCase);
 
             var serviceParts = service.Split(':');
 
@@ -336,7 +342,6 @@ namespace Emby.Dlna.Main
             device.DeviceType = serviceParts[2];
         }
 
-        private readonly object _syncLock = new object();
         private void StartPlayToManager()
         {
             lock (_syncLock)
@@ -388,6 +393,7 @@ namespace Emby.Dlna.Main
                     {
                         _logger.LogError(ex, "Error disposing PlayTo manager");
                     }
+
                     _manager = null;
                 }
             }
@@ -414,11 +420,11 @@ namespace Emby.Dlna.Main
 
         public void DisposeDevicePublisher()
         {
-            if (_Publisher != null)
+            if (_publisher != null)
             {
                 _logger.LogInformation("Disposing SsdpDevicePublisher");
-                _Publisher.Dispose();
-                _Publisher = null;
+                _publisher.Dispose();
+                _publisher = null;
             }
         }
     }

+ 21 - 25
Emby.Dlna/PlayTo/Device.cs

@@ -19,8 +19,6 @@ namespace Emby.Dlna.PlayTo
 {
     public class Device : IDisposable
     {
-        #region Fields & Properties
-
         private Timer _timer;
 
         public DeviceInfo Properties { get; set; }
@@ -37,6 +35,7 @@ namespace Emby.Dlna.PlayTo
                 RefreshVolumeIfNeeded().GetAwaiter().GetResult();
                 return _volume;
             }
+
             set => _volume = value;
         }
 
@@ -52,10 +51,10 @@ namespace Emby.Dlna.PlayTo
 
         public bool IsStopped => TransportState == TRANSPORTSTATE.STOPPED;
 
-        #endregion
-
         private readonly IHttpClient _httpClient;
+
         private readonly ILogger _logger;
+
         private readonly IServerConfigurationManager _config;
 
         public Action OnDeviceUnavailable { get; set; }
@@ -141,8 +140,6 @@ namespace Emby.Dlna.PlayTo
             }
         }
 
-        #region Commanding
-
         public Task VolumeDown(CancellationToken cancellationToken)
         {
             var sendVolume = Math.Max(Volume - 5, 0);
@@ -211,7 +208,9 @@ namespace Emby.Dlna.PlayTo
 
             var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
             if (command == null)
+            {
                 return false;
+            }
 
             var service = GetServiceRenderingControl();
 
@@ -232,7 +231,7 @@ namespace Emby.Dlna.PlayTo
         }
 
         /// <summary>
-        /// Sets volume on a scale of 0-100
+        /// Sets volume on a scale of 0-100.
         /// </summary>
         public async Task SetVolume(int value, CancellationToken cancellationToken)
         {
@@ -240,7 +239,9 @@ namespace Emby.Dlna.PlayTo
 
             var command = rendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume");
             if (command == null)
+            {
                 return;
+            }
 
             var service = GetServiceRenderingControl();
 
@@ -263,7 +264,9 @@ namespace Emby.Dlna.PlayTo
 
             var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek");
             if (command == null)
+            {
                 return;
+            }
 
             var service = GetAvTransportService();
 
@@ -288,7 +291,9 @@ namespace Emby.Dlna.PlayTo
 
             var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
             if (command == null)
+            {
                 return;
+            }
 
             var dictionary = new Dictionary<string, string>
             {
@@ -401,11 +406,8 @@ namespace Emby.Dlna.PlayTo
             RestartTimer(true);
         }
 
-        #endregion
-
-        #region Get data
-
         private int _connectFailureCount;
+
         private async void TimerCallback(object sender)
         {
             if (_disposed)
@@ -458,7 +460,9 @@ namespace Emby.Dlna.PlayTo
                     _connectFailureCount = 0;
 
                     if (_disposed)
+                    {
                         return;
+                    }
 
                     // If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
                     if (transportState.Value == TRANSPORTSTATE.STOPPED)
@@ -478,7 +482,9 @@ namespace Emby.Dlna.PlayTo
             catch (Exception ex)
             {
                 if (_disposed)
+                {
                     return;
+                }
 
                 _logger.LogError(ex, "Error updating device info for {DeviceName}", Properties.Name);
 
@@ -494,6 +500,7 @@ namespace Emby.Dlna.PlayTo
                         return;
                     }
                 }
+
                 RestartTimerInactive();
             }
         }
@@ -578,7 +585,9 @@ namespace Emby.Dlna.PlayTo
                 cancellationToken: cancellationToken).ConfigureAwait(false);
 
             if (result == null || result.Document == null)
+            {
                 return;
+            }
 
             var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse")
                                             .Select(i => i.Element("CurrentMute"))
@@ -750,7 +759,7 @@ namespace Emby.Dlna.PlayTo
 
             if (track == null)
             {
-                //If track is null, some vendors do this, use GetMediaInfo instead
+                // If track is null, some vendors do this, use GetMediaInfo instead
                 return (true, null);
             }
 
@@ -794,7 +803,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch (XmlException)
             {
-
             }
 
             // first try to add a root node with a dlna namesapce
@@ -806,7 +814,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch (XmlException)
             {
-
             }
 
             // some devices send back invalid xml
@@ -816,7 +823,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch (XmlException)
             {
-
             }
 
             return null;
@@ -871,10 +877,6 @@ namespace Emby.Dlna.PlayTo
             return new string[4];
         }
 
-        #endregion
-
-        #region From XML
-
         private async Task<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
         {
             if (AvCommands != null)
@@ -1069,8 +1071,6 @@ namespace Emby.Dlna.PlayTo
             return new Device(deviceProperties, httpClient, logger, config);
         }
 
-        #endregion
-
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
         private static DeviceIcon CreateIcon(XElement element)
         {
@@ -1194,8 +1194,6 @@ namespace Emby.Dlna.PlayTo
             });
         }
 
-        #region IDisposable
-
         bool _disposed;
 
         public void Dispose()
@@ -1222,8 +1220,6 @@ namespace Emby.Dlna.PlayTo
             _disposed = true;
         }
 
-        #endregion
-
         public override string ToString()
         {
             return string.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);

+ 5 - 0
Emby.Dlna/PlayTo/PlayToController.cs

@@ -425,6 +425,7 @@ namespace Emby.Dlna.PlayTo
                     await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
                     return;
                 }
+
                 await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
             }
         }
@@ -713,6 +714,7 @@ namespace Emby.Dlna.PlayTo
 
                             throw new ArgumentException("Volume argument cannot be null");
                         }
+
                     default:
                         return Task.CompletedTask;
                 }
@@ -798,12 +800,15 @@ namespace Emby.Dlna.PlayTo
             public int? SubtitleStreamIndex { get; set; }
 
             public string DeviceProfileId { get; set; }
+
             public string DeviceId { get; set; }
 
             public string MediaSourceId { get; set; }
+
             public string LiveStreamId { get; set; }
 
             public BaseItem Item { get; set; }
+
             private MediaSourceInfo MediaSource;
 
             private IMediaSourceManager _mediaSourceManager;

+ 10 - 5
Emby.Dlna/PlayTo/PlayToManager.cs

@@ -78,9 +78,15 @@ namespace Emby.Dlna.PlayTo
 
             var info = e.Argument;
 
-            if (!info.Headers.TryGetValue("USN", out string usn)) usn = string.Empty;
+            if (!info.Headers.TryGetValue("USN", out string usn))
+            {
+                usn = string.Empty;
+            }
 
-            if (!info.Headers.TryGetValue("NT", out string nt)) nt = string.Empty;
+            if (!info.Headers.TryGetValue("NT", out string nt))
+            {
+                nt = string.Empty;
+            }
 
             string location = info.Location.ToString();
 
@@ -88,7 +94,7 @@ namespace Emby.Dlna.PlayTo
             if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1 &&
                      nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) == -1)
             {
-                //_logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
+                // _logger.LogDebug("Upnp device {0} does not contain a MediaRenderer device (0).", location);
                 return;
             }
 
@@ -112,7 +118,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch (OperationCanceledException)
             {
-
             }
             catch (Exception ex)
             {
@@ -133,6 +138,7 @@ namespace Emby.Dlna.PlayTo
                 usn = usn.Substring(index);
                 found = true;
             }
+
             index = usn.IndexOf("::", StringComparison.OrdinalIgnoreCase);
             if (index != -1)
             {
@@ -243,7 +249,6 @@ namespace Emby.Dlna.PlayTo
             }
             catch
             {
-
             }
 
             _sessionLock.Dispose();

+ 1 - 0
Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs

@@ -12,6 +12,7 @@ namespace Emby.Dlna.PlayTo
     public class MediaChangedEventArgs : EventArgs
     {
         public uBaseObject OldMediaInfo { get; set; }
+
         public uBaseObject NewMediaInfo { get; set; }
     }
 }

+ 0 - 1
Emby.Dlna/PlayTo/SsdpHttpClient.cs

@@ -91,7 +91,6 @@ namespace Emby.Dlna.PlayTo
 
             using (await _httpClient.SendAsync(options, new HttpMethod("SUBSCRIBE")).ConfigureAwait(false))
             {
-
             }
         }
 

+ 2 - 0
Emby.Dlna/PlayTo/uBaseObject.cs

@@ -44,10 +44,12 @@ namespace Emby.Dlna.PlayTo
                 {
                     return MediaBrowser.Model.Entities.MediaType.Audio;
                 }
+
                 if (classType.IndexOf(MediaBrowser.Model.Entities.MediaType.Video, StringComparison.Ordinal) != -1)
                 {
                     return MediaBrowser.Model.Entities.MediaType.Video;
                 }
+
                 if (classType.IndexOf("image", StringComparison.Ordinal) != -1)
                 {
                     return MediaBrowser.Model.Entities.MediaType.Photo;

+ 1 - 1
Emby.Dlna/Profiles/DefaultProfile.cs

@@ -164,7 +164,7 @@ namespace Emby.Dlna.Profiles
 
         public void AddXmlRootAttribute(string name, string value)
         {
-            var atts = XmlRootAttributes ?? new XmlAttribute[] { };
+            var atts = XmlRootAttributes ?? System.Array.Empty<XmlAttribute>();
             var list = atts.ToList();
 
             list.Add(new XmlAttribute

+ 1 - 1
Emby.Dlna/Profiles/DenonAvrProfile.cs

@@ -28,7 +28,7 @@ namespace Emby.Dlna.Profiles
                 },
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = System.Array.Empty<ResponseProfile>();
         }
     }
 }

+ 1 - 1
Emby.Dlna/Profiles/DirectTvProfile.cs

@@ -123,7 +123,7 @@ namespace Emby.Dlna.Profiles
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = System.Array.Empty<ResponseProfile>();
         }
     }
 }

+ 1 - 1
Emby.Dlna/Profiles/Foobar2000Profile.cs

@@ -72,7 +72,7 @@ namespace Emby.Dlna.Profiles
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = System.Array.Empty<ResponseProfile>();
         }
     }
 }

+ 1 - 1
Emby.Dlna/Profiles/MarantzProfile.cs

@@ -37,7 +37,7 @@ namespace Emby.Dlna.Profiles
                 },
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = System.Array.Empty<ResponseProfile>();
         }
     }
 }

+ 2 - 1
Emby.Dlna/Profiles/MediaMonkeyProfile.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 
+using System;
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles
@@ -37,7 +38,7 @@ namespace Emby.Dlna.Profiles
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = Array.Empty<ResponseProfile>();
         }
     }
 }

+ 2 - 1
Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 
+using System;
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles
@@ -223,7 +224,7 @@ namespace Emby.Dlna.Profiles
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = Array.Empty<ResponseProfile>();
         }
     }
 }

+ 2 - 1
Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 
+using System;
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles
@@ -223,7 +224,7 @@ namespace Emby.Dlna.Profiles
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = Array.Empty<ResponseProfile>();
         }
     }
 }

+ 2 - 1
Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 
+using System;
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles
@@ -211,7 +212,7 @@ namespace Emby.Dlna.Profiles
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = Array.Empty<ResponseProfile>();
         }
     }
 }

+ 2 - 1
Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs

@@ -1,5 +1,6 @@
 #pragma warning disable CS1591
 
+using System;
 using MediaBrowser.Model.Dlna;
 
 namespace Emby.Dlna.Profiles
@@ -211,7 +212,7 @@ namespace Emby.Dlna.Profiles
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = Array.Empty<ResponseProfile>();
         }
     }
 }

+ 5 - 0
Emby.Dlna/Server/DescriptionXmlBuilder.cs

@@ -134,6 +134,7 @@ namespace Emby.Dlna.Server
                     return result;
                 }
             }
+
             return c.ToString(CultureInfo.InvariantCulture);
         }
 
@@ -157,18 +158,22 @@ namespace Emby.Dlna.Server
                 {
                     break;
                 }
+
                 if (stringBuilder == null)
                 {
                     stringBuilder = new StringBuilder();
                 }
+
                 stringBuilder.Append(str, num, num2 - num);
                 stringBuilder.Append(GetEscapeSequence(str[num2]));
                 num = num2 + 1;
             }
+
             if (stringBuilder == null)
             {
                 return str;
             }
+
             stringBuilder.Append(str, num, length - num);
             return stringBuilder.ToString();
         }

+ 4 - 0
Emby.Dlna/Service/BaseControlHandler.cs

@@ -18,6 +18,7 @@ namespace Emby.Dlna.Service
         private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/";
 
         protected IServerConfigurationManager Config { get; }
+
         protected ILogger Logger { get; }
 
         protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
@@ -135,6 +136,7 @@ namespace Emby.Dlna.Service
 
                                 break;
                             }
+
                         default:
                             {
                                 await reader.SkipAsync().ConfigureAwait(false);
@@ -211,7 +213,9 @@ namespace Emby.Dlna.Service
         private class ControlRequestInfo
         {
             public string LocalName { get; set; }
+
             public string NamespaceURI { get; set; }
+
             public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         }
 

+ 1 - 0
Emby.Dlna/Service/ServiceXmlBuilder.cs

@@ -80,6 +80,7 @@ namespace Emby.Dlna.Service
                     {
                         builder.Append("<allowedValue>" + DescriptionXmlBuilder.Escape(allowedValue) + "</allowedValue>");
                     }
+
                     builder.Append("</allowedValueList>");
                 }
 

+ 1 - 1
Emby.Dlna/Ssdp/DeviceDiscovery.cs

@@ -77,7 +77,7 @@ namespace Emby.Dlna.Ssdp
                     // (Optional) Set the filter so we only see notifications for devices we care about
                     // (can be any search target value i.e device type, uuid value etc - any value that appears in the
                     // DiscoverdSsdpDevice.NotificationType property or that is used with the searchTarget parameter of the Search method).
-                    //_DeviceLocator.NotificationFilter = "upnp:rootdevice";
+                    // _DeviceLocator.NotificationFilter = "upnp:rootdevice";
 
                     // Connect our event handler so we process devices as they are found
                     _deviceLocator.DeviceAvailable += OnDeviceLocatorDeviceAvailable;

+ 1 - 0
Emby.Naming/AudioBook/AudioBookFilePathParser.cs

@@ -64,6 +64,7 @@ namespace Emby.Naming.AudioBook
                 {
                     result.ChapterNumber = int.Parse(matches[0].Groups[0].Value);
                 }
+
                 if (matches.Count > 1)
                 {
                     result.PartNumber = int.Parse(matches[matches.Count - 1].Groups[0].Value);

+ 3 - 3
Emby.Naming/Common/MediaType.cs

@@ -5,17 +5,17 @@ namespace Emby.Naming.Common
     public enum MediaType
     {
         /// <summary>
-        /// The audio
+        /// The audio.
         /// </summary>
         Audio = 0,
 
         /// <summary>
-        /// The photo
+        /// The photo.
         /// </summary>
         Photo = 1,
 
         /// <summary>
-        /// The video
+        /// The video.
         /// </summary>
         Video = 2
     }

+ 19 - 20
Emby.Server.Implementations/ApplicationHost.cs

@@ -43,9 +43,9 @@ using Emby.Server.Implementations.Security;
 using Emby.Server.Implementations.Serialization;
 using Emby.Server.Implementations.Services;
 using Emby.Server.Implementations.Session;
+using Emby.Server.Implementations.SyncPlay;
 using Emby.Server.Implementations.TV;
 using Emby.Server.Implementations.Updates;
-using Emby.Server.Implementations.SyncPlay;
 using MediaBrowser.Api;
 using MediaBrowser.Common;
 using MediaBrowser.Common.Configuration;
@@ -78,8 +78,8 @@ using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Controller.Sorting;
 using MediaBrowser.Controller.Subtitles;
-using MediaBrowser.Controller.TV;
 using MediaBrowser.Controller.SyncPlay;
+using MediaBrowser.Controller.TV;
 using MediaBrowser.LocalMetadata.Savers;
 using MediaBrowser.MediaEncoding.BdInfo;
 using MediaBrowser.Model.Configuration;
@@ -483,12 +483,10 @@ namespace Emby.Server.Implementations
 
                 foreach (var plugin in Plugins)
                 {
-                    pluginBuilder.AppendLine(
-                        string.Format(
-                            CultureInfo.InvariantCulture,
-                            "{0} {1}",
-                            plugin.Name,
-                            plugin.Version));
+                    pluginBuilder.Append(plugin.Name)
+                        .Append(' ')
+                        .Append(plugin.Version)
+                        .AppendLine();
                 }
 
                 Logger.LogInformation("Plugins: {Plugins}", pluginBuilder.ToString());
@@ -565,10 +563,8 @@ namespace Emby.Server.Implementations
             serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
 
             // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
-            // TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
             serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
-            serviceCollection.AddSingleton<IMediaEncoder>(provider =>
-                ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty));
+            serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
 
             // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
             serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
@@ -872,6 +868,11 @@ namespace Emby.Server.Implementations
                     Logger.LogError(ex, "Error getting exported types from {Assembly}", ass.FullName);
                     continue;
                 }
+                catch (TypeLoadException ex)
+                {
+                    Logger.LogError(ex, "Error loading types from {Assembly}.", ass.FullName);
+                    continue;
+                }
 
                 foreach (Type type in exportedTypes)
                 {
@@ -955,7 +956,7 @@ namespace Emby.Server.Implementations
         }
 
         /// <summary>
-        /// Notifies that the kernel that a change has been made that requires a restart
+        /// Notifies that the kernel that a change has been made that requires a restart.
         /// </summary>
         public void NotifyPendingRestart()
         {
@@ -1151,7 +1152,7 @@ namespace Emby.Server.Implementations
                     return null;
                 }
 
-                return GetLocalApiUrl(addresses.First());
+                return GetLocalApiUrl(addresses[0]);
             }
             catch (Exception ex)
             {
@@ -1224,13 +1225,13 @@ namespace Emby.Server.Implementations
             var addresses = ServerConfigurationManager
                 .Configuration
                 .LocalNetworkAddresses
-                .Select(NormalizeConfiguredLocalAddress)
+                .Select(x => NormalizeConfiguredLocalAddress(x))
                 .Where(i => i != null)
                 .ToList();
 
             if (addresses.Count == 0)
             {
-                addresses.AddRange(_networkManager.GetLocalIpAddresses(ServerConfigurationManager.Configuration.IgnoreVirtualInterfaces));
+                addresses.AddRange(_networkManager.GetLocalIpAddresses());
             }
 
             var resultList = new List<IPAddress>();
@@ -1245,8 +1246,7 @@ namespace Emby.Server.Implementations
                     }
                 }
 
-                var valid = await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false);
-                if (valid)
+                if (await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false))
                 {
                     resultList.Add(address);
 
@@ -1260,13 +1260,12 @@ namespace Emby.Server.Implementations
             return resultList;
         }
 
-        public IPAddress NormalizeConfiguredLocalAddress(string address)
+        public IPAddress NormalizeConfiguredLocalAddress(ReadOnlySpan<char> address)
         {
             var index = address.Trim('/').IndexOf('/');
-
             if (index != -1)
             {
-                address = address.Substring(index + 1);
+                address = address.Slice(index + 1);
             }
 
             if (IPAddress.TryParse(address.Trim('/'), out IPAddress result))

+ 1 - 1
Emby.Server.Implementations/Channels/ChannelManager.cs

@@ -1072,7 +1072,7 @@ namespace Emby.Server.Implementations.Channels
             }
 
             // was used for status
-            //if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
+            // if (!string.Equals(item.ExternalEtag ?? string.Empty, info.Etag ?? string.Empty, StringComparison.Ordinal))
             //{
             //    item.ExternalEtag = info.Etag;
             //    forceUpdate = true;

+ 0 - 56
Emby.Server.Implementations/Collections/CollectionManager.cs

@@ -363,60 +363,4 @@ namespace Emby.Server.Implementations.Collections
             return results.Values;
         }
     }
-
-    /// <summary>
-    /// The collection manager entry point.
-    /// </summary>
-    public sealed class CollectionManagerEntryPoint : IServerEntryPoint
-    {
-        private readonly CollectionManager _collectionManager;
-        private readonly IServerConfigurationManager _config;
-        private readonly ILogger<CollectionManagerEntryPoint> _logger;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class.
-        /// </summary>
-        /// <param name="collectionManager">The collection manager.</param>
-        /// <param name="config">The server configuration manager.</param>
-        /// <param name="logger">The logger.</param>
-        public CollectionManagerEntryPoint(
-            ICollectionManager collectionManager,
-            IServerConfigurationManager config,
-            ILogger<CollectionManagerEntryPoint> logger)
-        {
-            _collectionManager = (CollectionManager)collectionManager;
-            _config = config;
-            _logger = logger;
-        }
-
-        /// <inheritdoc />
-        public async Task RunAsync()
-        {
-            if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted)
-            {
-                var path = _collectionManager.GetCollectionsFolderPath();
-
-                if (Directory.Exists(path))
-                {
-                    try
-                    {
-                        await _collectionManager.EnsureLibraryFolder(path, true).ConfigureAwait(false);
-                    }
-                    catch (Exception ex)
-                    {
-                        _logger.LogError(ex, "Error creating camera uploads library");
-                    }
-
-                    _config.Configuration.CollectionsUpgraded = true;
-                    _config.SaveConfiguration();
-                }
-            }
-        }
-
-        /// <inheritdoc />
-        public void Dispose()
-        {
-            // Nothing to dispose
-        }
-    }
 }

+ 0 - 57
Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs

@@ -109,7 +109,6 @@ namespace Emby.Server.Implementations.Configuration
             if (!string.IsNullOrWhiteSpace(newPath)
                 && !string.Equals(Configuration.CertificatePath, newPath, StringComparison.Ordinal))
             {
-                // Validate
                 if (!File.Exists(newPath))
                 {
                     throw new FileNotFoundException(
@@ -133,7 +132,6 @@ namespace Emby.Server.Implementations.Configuration
             if (!string.IsNullOrWhiteSpace(newPath)
                 && !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal))
             {
-                // Validate
                 if (!Directory.Exists(newPath))
                 {
                     throw new DirectoryNotFoundException(
@@ -146,60 +144,5 @@ namespace Emby.Server.Implementations.Configuration
                 EnsureWriteAccess(newPath);
             }
         }
-
-        /// <summary>
-        /// Sets all configuration values to their optimal values.
-        /// </summary>
-        /// <returns>If the configuration changed.</returns>
-        public bool SetOptimalValues()
-        {
-            var config = Configuration;
-
-            var changed = false;
-
-            if (!config.EnableCaseSensitiveItemIds)
-            {
-                config.EnableCaseSensitiveItemIds = true;
-                changed = true;
-            }
-
-            if (!config.SkipDeserializationForBasicTypes)
-            {
-                config.SkipDeserializationForBasicTypes = true;
-                changed = true;
-            }
-
-            if (!config.EnableSimpleArtistDetection)
-            {
-                config.EnableSimpleArtistDetection = true;
-                changed = true;
-            }
-
-            if (!config.EnableNormalizedItemByNameIds)
-            {
-                config.EnableNormalizedItemByNameIds = true;
-                changed = true;
-            }
-
-            if (!config.DisableLiveTvChannelUserDataName)
-            {
-                config.DisableLiveTvChannelUserDataName = true;
-                changed = true;
-            }
-
-            if (!config.EnableNewOmdbSupport)
-            {
-                config.EnableNewOmdbSupport = true;
-                changed = true;
-            }
-
-            if (!config.CollectionsUpgraded)
-            {
-                config.CollectionsUpgraded = true;
-                changed = true;
-            }
-
-            return changed;
-        }
     }
 }

+ 0 - 1
Emby.Server.Implementations/ConfigurationOptions.cs

@@ -17,7 +17,6 @@ namespace Emby.Server.Implementations
         {
             { HostWebClientKey, bool.TrueString },
             { HttpListenerHost.DefaultRedirectKey, "web/index.html" },
-            { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest-stable.json" },
             { FfmpegProbeSizeKey, "1G" },
             { FfmpegAnalyzeDurationKey, "200M" },
             { PlaylistsAllowDuplicatesKey, bool.TrueString }

+ 2 - 3
Emby.Server.Implementations/Data/BaseSqliteRepository.cs

@@ -162,7 +162,6 @@ namespace Emby.Server.Implementations.Data
                 }
 
                 return false;
-
             }, ReadTransactionMode);
         }
 
@@ -248,12 +247,12 @@ namespace Emby.Server.Implementations.Data
     public enum SynchronousMode
     {
         /// <summary>
-        /// SQLite continues without syncing as soon as it has handed data off to the operating system
+        /// SQLite continues without syncing as soon as it has handed data off to the operating system.
         /// </summary>
         Off = 0,
 
         /// <summary>
-        /// SQLite database engine will still sync at the most critical moments
+        /// SQLite database engine will still sync at the most critical moments.
         /// </summary>
         Normal = 1,
 

+ 0 - 1
Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs

@@ -51,7 +51,6 @@ namespace Emby.Server.Implementations.Data
                     _libraryManager.DeleteItem(item, new DeleteOptions
                     {
                         DeleteFileLocation = false
-
                     });
                 }
 

+ 3 - 3
Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs

@@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.Data
         }
 
         /// <summary>
-        /// Opens the connection to the database
+        /// Opens the connection to the database.
         /// </summary>
         /// <returns>Task.</returns>
         private void InitializeInternal()
@@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.Data
         }
 
         /// <summary>
-        /// Save the display preferences associated with an item in the repo
+        /// Save the display preferences associated with an item in the repo.
         /// </summary>
         /// <param name="displayPreferences">The display preferences.</param>
         /// <param name="userId">The user id.</param>
@@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.Data
         }
 
         /// <summary>
-        /// Save all display preferences associated with a user in the repo
+        /// Save all display preferences associated with a user in the repo.
         /// </summary>
         /// <param name="displayPreferences">The display preferences.</param>
         /// <param name="userId">The user id.</param>

Diff do ficheiro suprimidas por serem muito extensas
+ 197 - 25
Emby.Server.Implementations/Data/SqliteItemRepository.cs


+ 7 - 4
Emby.Server.Implementations/Data/SqliteUserDataRepository.cs

@@ -135,10 +135,12 @@ namespace Emby.Server.Implementations.Data
             {
                 throw new ArgumentNullException(nameof(userData));
             }
+
             if (internalUserId <= 0)
             {
                 throw new ArgumentNullException(nameof(internalUserId));
             }
+
             if (string.IsNullOrEmpty(key))
             {
                 throw new ArgumentNullException(nameof(key));
@@ -153,6 +155,7 @@ namespace Emby.Server.Implementations.Data
             {
                 throw new ArgumentNullException(nameof(userData));
             }
+
             if (internalUserId <= 0)
             {
                 throw new ArgumentNullException(nameof(internalUserId));
@@ -235,7 +238,7 @@ namespace Emby.Server.Implementations.Data
         }
 
         /// <summary>
-        /// Persist all user data for the specified user
+        /// Persist all user data for the specified user.
         /// </summary>
         private void PersistAllUserData(long internalUserId, UserItemData[] userDataList, CancellationToken cancellationToken)
         {
@@ -309,7 +312,7 @@ namespace Emby.Server.Implementations.Data
         }
 
         /// <summary>
-        /// Return all user-data associated with the given user
+        /// Return all user-data associated with the given user.
         /// </summary>
         /// <param name="internalUserId"></param>
         /// <returns></returns>
@@ -339,7 +342,7 @@ namespace Emby.Server.Implementations.Data
         }
 
         /// <summary>
-        /// Read a row from the specified reader into the provided userData object
+        /// Read a row from the specified reader into the provided userData object.
         /// </summary>
         /// <param name="reader"></param>
         private UserItemData ReadRow(IReadOnlyList<IResultSetValue> reader)
@@ -347,7 +350,7 @@ namespace Emby.Server.Implementations.Data
             var userData = new UserItemData();
 
             userData.Key = reader[0].ToString();
-            //userData.UserId = reader[1].ReadGuidFromBlob();
+            // userData.UserId = reader[1].ReadGuidFromBlob();
 
             if (reader[2].SQLiteType != SQLiteType.Null)
             {

+ 2 - 1
Emby.Server.Implementations/Devices/DeviceManager.cs

@@ -112,7 +112,7 @@ namespace Emby.Server.Implementations.Devices
         {
             IEnumerable<AuthenticationInfo> sessions = _authRepo.Get(new AuthenticationInfoQuery
             {
-                //UserId = query.UserId
+                // UserId = query.UserId
                 HasUser = true
             }).Items;
 
@@ -169,6 +169,7 @@ namespace Emby.Server.Implementations.Devices
             {
                 throw new ArgumentException("user not found");
             }
+
             if (string.IsNullOrEmpty(deviceId))
             {
                 throw new ArgumentNullException(nameof(deviceId));

+ 24 - 16
Emby.Server.Implementations/Dto/DtoService.cs

@@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.Dto
         }
 
         /// <summary>
-        /// Converts a BaseItem to a DTOBaseItem
+        /// Converts a BaseItem to a DTOBaseItem.
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="fields">The fields.</param>
@@ -277,6 +277,7 @@ namespace Emby.Server.Implementations.Dto
                     dto.EpisodeTitle = dto.Name;
                     dto.Name = dto.SeriesName;
                 }
+
                 liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user);
             }
 
@@ -292,6 +293,7 @@ namespace Emby.Server.Implementations.Dto
                 {
                     continue;
                 }
+
                 var containers = container.Split(new[] { ',' });
                 if (containers.Length < 2)
                 {
@@ -406,7 +408,6 @@ namespace Emby.Server.Implementations.Dto
                     dto.DateLastMediaAdded = folder.DateLastMediaAdded;
                 }
             }
-
             else
             {
                 if (options.EnableUserData)
@@ -443,7 +444,7 @@ namespace Emby.Server.Implementations.Dto
         }
 
         /// <summary>
-        /// Gets client-side Id of a server-side BaseItem
+        /// Gets client-side Id of a server-side BaseItem.
         /// </summary>
         /// <param name="item">The item.</param>
         /// <returns>System.String.</returns>
@@ -457,6 +458,7 @@ namespace Emby.Server.Implementations.Dto
         {
             dto.SeriesName = item.SeriesName;
         }
+
         private static void SetPhotoProperties(BaseItemDto dto, Photo item)
         {
             dto.CameraMake = item.CameraMake;
@@ -538,7 +540,7 @@ namespace Emby.Server.Implementations.Dto
         }
 
         /// <summary>
-        /// Attaches People DTO's to a DTOBaseItem
+        /// Attaches People DTO's to a DTOBaseItem.
         /// </summary>
         /// <param name="dto">The dto.</param>
         /// <param name="item">The item.</param>
@@ -555,22 +557,27 @@ namespace Emby.Server.Implementations.Dto
                     {
                         return 0;
                     }
+
                     if (i.IsType(PersonType.GuestStar))
                     {
                         return 1;
                     }
+
                     if (i.IsType(PersonType.Director))
                     {
                         return 2;
                     }
+
                     if (i.IsType(PersonType.Writer))
                     {
                         return 3;
                     }
+
                     if (i.IsType(PersonType.Producer))
                     {
                         return 4;
                     }
+
                     if (i.IsType(PersonType.Composer))
                     {
                         return 4;
@@ -594,7 +601,6 @@ namespace Emby.Server.Implementations.Dto
                         _logger.LogError(ex, "Error getting person {Name}", c);
                         return null;
                     }
-
                 }).Where(i => i != null)
                 .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
                 .Select(x => x.First())
@@ -615,6 +621,7 @@ namespace Emby.Server.Implementations.Dto
                 {
                     baseItemPerson.PrimaryImageTag = GetTagAndFillBlurhash(dto, entity, ImageType.Primary);
                     baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
+                    baseItemPerson.ImageBlurHashes = dto.ImageBlurHashes;
                     list.Add(baseItemPerson);
                 }
             }
@@ -727,7 +734,7 @@ namespace Emby.Server.Implementations.Dto
         }
 
         /// <summary>
-        /// Sets simple property values on a DTOBaseItem
+        /// Sets simple property values on a DTOBaseItem.
         /// </summary>
         /// <param name="dto">The dto.</param>
         /// <param name="item">The item.</param>
@@ -947,7 +954,7 @@ namespace Emby.Server.Implementations.Dto
                     dto.AlbumPrimaryImageTag = GetTagAndFillBlurhash(dto, albumParent, ImageType.Primary);
                 }
 
-                //if (options.ContainsField(ItemFields.MediaSourceCount))
+                // if (options.ContainsField(ItemFields.MediaSourceCount))
                 //{
                 // Songs always have one
                 //}
@@ -957,13 +964,13 @@ namespace Emby.Server.Implementations.Dto
             {
                 dto.Artists = hasArtist.Artists;
 
-                //var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
+                // var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
                 //{
                 //    EnableTotalRecordCount = false,
                 //    ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
                 //});
 
-                //dto.ArtistItems = artistItems.Items
+                // dto.ArtistItems = artistItems.Items
                 //    .Select(i =>
                 //    {
                 //        var artist = i.Item1;
@@ -976,7 +983,7 @@ namespace Emby.Server.Implementations.Dto
                 //    .ToList();
 
                 // Include artists that are not in the database yet, e.g., just added via metadata editor
-                //var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
+                // var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
                 dto.ArtistItems = hasArtist.Artists
                     //.Except(foundArtists, new DistinctNameComparer())
                     .Select(i =>
@@ -1001,7 +1008,6 @@ namespace Emby.Server.Implementations.Dto
                         }
 
                         return null;
-
                     }).Where(i => i != null).ToArray();
             }
 
@@ -1010,13 +1016,13 @@ namespace Emby.Server.Implementations.Dto
             {
                 dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
 
-                //var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
+                // var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
                 //{
                 //    EnableTotalRecordCount = false,
                 //    ItemIds = new[] { item.Id.ToString("N", CultureInfo.InvariantCulture) }
                 //});
 
-                //dto.AlbumArtists = artistItems.Items
+                // dto.AlbumArtists = artistItems.Items
                 //    .Select(i =>
                 //    {
                 //        var artist = i.Item1;
@@ -1052,7 +1058,6 @@ namespace Emby.Server.Implementations.Dto
                         }
 
                         return null;
-
                     }).Where(i => i != null).ToArray();
             }
 
@@ -1166,7 +1171,7 @@ namespace Emby.Server.Implementations.Dto
 
                 // this block will add the series poster for episodes without a poster
                 // TODO maybe remove the if statement entirely
-                //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
+                // if (options.ContainsField(ItemFields.SeriesPrimaryImage))
                 {
                     episodeSeries = episodeSeries ?? episode.Series;
                     if (episodeSeries != null)
@@ -1212,7 +1217,7 @@ namespace Emby.Server.Implementations.Dto
 
                 // this block will add the series poster for seasons without a poster
                 // TODO maybe remove the if statement entirely
-                //if (options.ContainsField(ItemFields.SeriesPrimaryImage))
+                // if (options.ContainsField(ItemFields.SeriesPrimaryImage))
                 {
                     series = series ?? season.Series;
                     if (series != null)
@@ -1350,6 +1355,7 @@ namespace Emby.Server.Implementations.Dto
                         dto.ParentLogoImageTag = GetTagAndFillBlurhash(dto, parent, image);
                     }
                 }
+
                 if (artLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Art)) && dto.ParentArtItemId == null)
                 {
                     var image = allImages.FirstOrDefault(i => i.Type == ImageType.Art);
@@ -1360,6 +1366,7 @@ namespace Emby.Server.Implementations.Dto
                         dto.ParentArtImageTag = GetTagAndFillBlurhash(dto, parent, image);
                     }
                 }
+
                 if (thumbLimit > 0 && !(imageTags != null && imageTags.ContainsKey(ImageType.Thumb)) && (dto.ParentThumbItemId == null || parent is Series) && !(parent is ICollectionFolder) && !(parent is UserView))
                 {
                     var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
@@ -1370,6 +1377,7 @@ namespace Emby.Server.Implementations.Dto
                         dto.ParentThumbImageTag = GetTagAndFillBlurhash(dto, parent, image);
                     }
                 }
+
                 if (backdropLimit > 0 && !((dto.BackdropImageTags != null && dto.BackdropImageTags.Length > 0) || (dto.ParentBackdropImageTags != null && dto.ParentBackdropImageTags.Length > 0)))
                 {
                     var images = allImages.Where(i => i.Type == ImageType.Backdrop).Take(backdropLimit).ToList();

+ 7 - 7
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -23,8 +23,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="IPNetwork2" Version="2.4.0.126" />
-    <PackageReference Include="Jellyfin.XmlTv" Version="10.4.3" />
+    <PackageReference Include="IPNetwork2" Version="2.5.211" />
+    <PackageReference Include="Jellyfin.XmlTv" Version="10.6.0-pre1" />
     <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
     <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
@@ -33,14 +33,14 @@
     <PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.5" />
-    <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.5" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" />
-    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.5" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
+    <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.6" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
+    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
     <PackageReference Include="Mono.Nat" Version="2.0.1" />
     <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
     <PackageReference Include="ServiceStack.Text.Core" Version="5.9.0" />
-    <PackageReference Include="sharpcompress" Version="0.25.0" />
+    <PackageReference Include="sharpcompress" Version="0.25.1" />
     <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
     <PackageReference Include="DotNet.Glob" Version="3.0.9" />
   </ItemGroup>

+ 0 - 1
Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs

@@ -132,7 +132,6 @@ namespace Emby.Server.Implementations.EntryPoints
                 }
                 catch
                 {
-
                 }
             }
         }

+ 0 - 2
Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs

@@ -159,7 +159,6 @@ namespace Emby.Server.Implementations.EntryPoints
             }
             catch (Exception)
             {
-
             }
         }
 
@@ -175,7 +174,6 @@ namespace Emby.Server.Implementations.EntryPoints
             }
             catch (Exception)
             {
-
             }
         }
 

+ 11 - 3
Emby.Server.Implementations/EntryPoints/UdpServerEntryPoint.cs

@@ -1,3 +1,4 @@
+using System.Net.Sockets;
 using System.Threading;
 using System.Threading.Tasks;
 using Emby.Server.Implementations.Udp;
@@ -43,14 +44,21 @@ namespace Emby.Server.Implementations.EntryPoints
             _logger = logger;
             _appHost = appHost;
             _config = configuration;
-
         }
 
         /// <inheritdoc />
         public Task RunAsync()
         {
-            _udpServer = new UdpServer(_logger, _appHost, _config);
-            _udpServer.Start(PortNumber, _cancellationTokenSource.Token);
+            try
+            {
+                _udpServer = new UdpServer(_logger, _appHost, _config);
+                _udpServer.Start(PortNumber, _cancellationTokenSource.Token);
+            }
+            catch (SocketException ex)
+            {
+                _logger.LogWarning(ex, "Unable to start AutoDiscovery listener on UDP port {PortNumber}", PortNumber);
+            }
+
             return Task.CompletedTask;
         }
 

+ 1 - 1
Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs

@@ -140,7 +140,7 @@ namespace Emby.Server.Implementations.HttpClientManager
             => SendAsync(options, HttpMethod.Get);
 
         /// <summary>
-        /// Performs a GET request and returns the resulting stream
+        /// Performs a GET request and returns the resulting stream.
         /// </summary>
         /// <param name="options">The options.</param>
         /// <returns>Task{Stream}.</returns>

+ 2 - 2
Emby.Server.Implementations/HttpServer/FileWriter.cs

@@ -32,12 +32,12 @@ namespace Emby.Server.Implementations.HttpServer
         private readonly IFileSystem _fileSystem;
 
         /// <summary>
-        /// The _options
+        /// The _options.
         /// </summary>
         private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
 
         /// <summary>
-        /// The _requested ranges
+        /// The _requested ranges.
         /// </summary>
         private List<KeyValuePair<long, long?>> _requestedRanges;
 

+ 2 - 1
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -453,6 +453,7 @@ namespace Emby.Server.Implementations.HttpServer
                     {
                         httpRes.Headers.Add(key, value);
                     }
+
                     httpRes.ContentType = "text/plain";
                     await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
                     return;
@@ -591,7 +592,7 @@ namespace Emby.Server.Implementations.HttpServer
         }
 
         /// <summary>
-        /// Get the default CORS headers
+        /// Get the default CORS headers.
         /// </summary>
         /// <param name="req"></param>
         /// <returns></returns>

+ 8 - 6
Emby.Server.Implementations/HttpServer/HttpResultFactory.cs

@@ -426,7 +426,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// </summary>
         private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, StaticResultOptions options)
         {
-            bool noCache = (requestContext.Headers[HeaderNames.CacheControl].ToString()).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
+            bool noCache = requestContext.Headers[HeaderNames.CacheControl].ToString().IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
             AddCachingHeaders(responseHeaders, options.CacheDuration, noCache, options.DateLastModified);
 
             if (!noCache)
@@ -580,13 +580,12 @@ namespace Emby.Server.Implementations.HttpServer
                 }
                 catch (NotSupportedException)
                 {
-
                 }
             }
 
             if (!string.IsNullOrWhiteSpace(rangeHeader) && totalContentLength.HasValue)
             {
-                var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest, _logger)
+                var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest)
                 {
                     OnComplete = options.OnComplete
                 };
@@ -623,8 +622,11 @@ namespace Emby.Server.Implementations.HttpServer
         /// <summary>
         /// Adds the caching responseHeaders.
         /// </summary>
-        private void AddCachingHeaders(IDictionary<string, string> responseHeaders, TimeSpan? cacheDuration,
-            bool noCache, DateTime? lastModifiedDate)
+        private void AddCachingHeaders(
+            IDictionary<string, string> responseHeaders,
+            TimeSpan? cacheDuration,
+            bool noCache,
+            DateTime? lastModifiedDate)
         {
             if (noCache)
             {
@@ -693,7 +695,7 @@ namespace Emby.Server.Implementations.HttpServer
 
 
         /// <summary>
-        /// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that
+        /// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that.
         /// </summary>
         /// <param name="date">The date.</param>
         /// <returns>DateTime.</returns>

+ 84 - 97
Emby.Server.Implementations/HttpServer/RangeRequestWriter.cs

@@ -1,6 +1,7 @@
 #pragma warning disable CS1591
 
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
@@ -8,46 +9,17 @@ using System.Net;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
 using Microsoft.Net.Http.Headers;
 
 namespace Emby.Server.Implementations.HttpServer
 {
     public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult
     {
-        /// <summary>
-        /// Gets or sets the source stream.
-        /// </summary>
-        /// <value>The source stream.</value>
-        private Stream SourceStream { get; set; }
-        private string RangeHeader { get; set; }
-        private bool IsHeadRequest { get; set; }
-
-        private long RangeStart { get; set; }
-        private long RangeEnd { get; set; }
-        private long RangeLength { get; set; }
-        private long TotalContentLength { get; set; }
-
-        public Action OnComplete { get; set; }
-        private readonly ILogger _logger;
-
         private const int BufferSize = 81920;
 
-        /// <summary>
-        /// The _options
-        /// </summary>
         private readonly Dictionary<string, string> _options = new Dictionary<string, string>();
 
-        /// <summary>
-        /// The us culture
-        /// </summary>
-        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
-        /// <summary>
-        /// Additional HTTP Headers
-        /// </summary>
-        /// <value>The headers.</value>
-        public IDictionary<string, string> Headers => _options;
+        private List<KeyValuePair<long, long?>> _requestedRanges;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="RangeRequestWriter" /> class.
@@ -57,8 +29,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// <param name="source">The source.</param>
         /// <param name="contentType">Type of the content.</param>
         /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
-        /// <param name="logger">The logger instance.</param>
-        public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest, ILogger logger)
+        public RangeRequestWriter(string rangeHeader, long contentLength, Stream source, string contentType, bool isHeadRequest)
         {
             if (string.IsNullOrEmpty(contentType))
             {
@@ -68,7 +39,6 @@ namespace Emby.Server.Implementations.HttpServer
             RangeHeader = rangeHeader;
             SourceStream = source;
             IsHeadRequest = isHeadRequest;
-            this._logger = logger;
 
             ContentType = contentType;
             Headers[HeaderNames.ContentType] = contentType;
@@ -79,40 +49,26 @@ namespace Emby.Server.Implementations.HttpServer
         }
 
         /// <summary>
-        /// Sets the range values.
+        /// Gets or sets the source stream.
         /// </summary>
-        private void SetRangeValues(long contentLength)
-        {
-            var requestedRange = RequestedRanges[0];
-
-            TotalContentLength = contentLength;
-
-            // If the requested range is "0-", we can optimize by just doing a stream copy
-            if (!requestedRange.Value.HasValue)
-            {
-                RangeEnd = TotalContentLength - 1;
-            }
-            else
-            {
-                RangeEnd = requestedRange.Value.Value;
-            }
-
-            RangeStart = requestedRange.Key;
-            RangeLength = 1 + RangeEnd - RangeStart;
+        /// <value>The source stream.</value>
+        private Stream SourceStream { get; set; }
+        private string RangeHeader { get; set; }
+        private bool IsHeadRequest { get; set; }
 
-            Headers[HeaderNames.ContentLength] = RangeLength.ToString(CultureInfo.InvariantCulture);
-            Headers[HeaderNames.ContentRange] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
+        private long RangeStart { get; set; }
+        private long RangeEnd { get; set; }
+        private long RangeLength { get; set; }
+        private long TotalContentLength { get; set; }
 
-            if (RangeStart > 0 && SourceStream.CanSeek)
-            {
-                SourceStream.Position = RangeStart;
-            }
-        }
+        public Action OnComplete { get; set; }
 
         /// <summary>
-        /// The _requested ranges
+        /// Additional HTTP Headers
         /// </summary>
-        private List<KeyValuePair<long, long?>> _requestedRanges;
+        /// <value>The headers.</value>
+        public IDictionary<string, string> Headers => _options;
+
         /// <summary>
         /// Gets the requested ranges.
         /// </summary>
@@ -137,11 +93,12 @@ namespace Emby.Server.Implementations.HttpServer
 
                         if (!string.IsNullOrEmpty(vals[0]))
                         {
-                            start = long.Parse(vals[0], UsCulture);
+                            start = long.Parse(vals[0], CultureInfo.InvariantCulture);
                         }
+
                         if (!string.IsNullOrEmpty(vals[1]))
                         {
-                            end = long.Parse(vals[1], UsCulture);
+                            end = long.Parse(vals[1], CultureInfo.InvariantCulture);
                         }
 
                         _requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
@@ -152,6 +109,51 @@ namespace Emby.Server.Implementations.HttpServer
             }
         }
 
+        public string ContentType { get; set; }
+
+        public IRequest RequestContext { get; set; }
+
+        public object Response { get; set; }
+
+        public int Status { get; set; }
+
+        public HttpStatusCode StatusCode
+        {
+            get => (HttpStatusCode)Status;
+            set => Status = (int)value;
+        }
+
+        /// <summary>
+        /// Sets the range values.
+        /// </summary>
+        private void SetRangeValues(long contentLength)
+        {
+            var requestedRange = RequestedRanges[0];
+
+            TotalContentLength = contentLength;
+
+            // If the requested range is "0-", we can optimize by just doing a stream copy
+            if (!requestedRange.Value.HasValue)
+            {
+                RangeEnd = TotalContentLength - 1;
+            }
+            else
+            {
+                RangeEnd = requestedRange.Value.Value;
+            }
+
+            RangeStart = requestedRange.Key;
+            RangeLength = 1 + RangeEnd - RangeStart;
+
+            Headers[HeaderNames.ContentLength] = RangeLength.ToString(CultureInfo.InvariantCulture);
+            Headers[HeaderNames.ContentRange] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
+
+            if (RangeStart > 0 && SourceStream.CanSeek)
+            {
+                SourceStream.Position = RangeStart;
+            }
+        }
+
         public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken)
         {
             try
@@ -167,59 +169,44 @@ namespace Emby.Server.Implementations.HttpServer
                     // If the requested range is "0-", we can optimize by just doing a stream copy
                     if (RangeEnd >= TotalContentLength - 1)
                     {
-                        await source.CopyToAsync(responseStream, BufferSize).ConfigureAwait(false);
+                        await source.CopyToAsync(responseStream, BufferSize, cancellationToken).ConfigureAwait(false);
                     }
                     else
                     {
-                        await CopyToInternalAsync(source, responseStream, RangeLength).ConfigureAwait(false);
+                        await CopyToInternalAsync(source, responseStream, RangeLength, cancellationToken).ConfigureAwait(false);
                     }
                 }
             }
             finally
             {
-                if (OnComplete != null)
-                {
-                    OnComplete();
-                }
+                OnComplete?.Invoke();
             }
         }
 
-        private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength)
+        private static async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
         {
-            var array = new byte[BufferSize];
-            int bytesRead;
-            while ((bytesRead = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0)
+            var array = ArrayPool<byte>.Shared.Rent(BufferSize);
+            try
             {
-                if (bytesRead == 0)
+                int bytesRead;
+                while ((bytesRead = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
                 {
-                    break;
-                }
+                    var bytesToCopy = Math.Min(bytesRead, copyLength);
 
-                var bytesToCopy = Math.Min(bytesRead, copyLength);
+                    await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy), cancellationToken).ConfigureAwait(false);
 
-                await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy)).ConfigureAwait(false);
+                    copyLength -= bytesToCopy;
 
-                copyLength -= bytesToCopy;
-
-                if (copyLength <= 0)
-                {
-                    break;
+                    if (copyLength <= 0)
+                    {
+                        break;
+                    }
                 }
             }
-        }
-
-        public string ContentType { get; set; }
-
-        public IRequest RequestContext { get; set; }
-
-        public object Response { get; set; }
-
-        public int Status { get; set; }
-
-        public HttpStatusCode StatusCode
-        {
-            get => (HttpStatusCode)Status;
-            set => Status = (int)value;
+            finally
+            {
+                ArrayPool<byte>.Shared.Return(array);
+            }
         }
     }
 }

+ 2 - 2
Emby.Server.Implementations/HttpServer/ResponseFilter.cs

@@ -41,11 +41,11 @@ namespace Emby.Server.Implementations.HttpServer
                 res.Headers.Add(key, value);
             }
             // Try to prevent compatibility view
-            res.Headers["Access-Control-Allow-Headers"] = ("Accept, Accept-Language, Authorization, Cache-Control, " +
+            res.Headers["Access-Control-Allow-Headers"] = "Accept, Accept-Language, Authorization, Cache-Control, " +
                 "Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, " +
                 "Content-Type, Cookie, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, " +
                 "Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, " +
-                "X-Emby-Authorization");
+                "X-Emby-Authorization";
 
             if (dto is Exception exception)
             {

+ 0 - 2
Emby.Server.Implementations/IO/LibraryMonitor.cs

@@ -266,7 +266,6 @@ namespace Emby.Server.Implementations.IO
                     {
                         DisposeWatcher(newWatcher, false);
                     }
-
                 }
                 catch (Exception ex)
                 {
@@ -393,7 +392,6 @@ namespace Emby.Server.Implementations.IO
                 }
 
                 return false;
-
             }))
             {
                 monitorPath = false;

+ 13 - 1
Emby.Server.Implementations/IO/ManagedFileSystem.cs

@@ -237,7 +237,7 @@ namespace Emby.Server.Implementations.IO
             {
                 result.IsDirectory = info is DirectoryInfo || (info.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
 
-                //if (!result.IsDirectory)
+                // if (!result.IsDirectory)
                 //{
                 //    result.IsHidden = (info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
                 //}
@@ -245,6 +245,16 @@ namespace Emby.Server.Implementations.IO
                 if (info is FileInfo fileInfo)
                 {
                     result.Length = fileInfo.Length;
+
+                    // Issue #2354 get the size of files behind symbolic links
+                    if (fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint))
+                    {
+                        using (Stream thisFileStream = File.OpenRead(fileInfo.FullName))
+                        {
+                            result.Length = thisFileStream.Length;
+                        }
+                    }
+
                     result.DirectoryName = fileInfo.DirectoryName;
                 }
 
@@ -628,6 +638,7 @@ namespace Emby.Server.Implementations.IO
                     {
                         return false;
                     }
+
                     return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
                 });
             }
@@ -682,6 +693,7 @@ namespace Emby.Server.Implementations.IO
                     {
                         return false;
                     }
+
                     return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
                 });
             }

+ 0 - 5
Emby.Server.Implementations/IStartupOptions.cs

@@ -36,11 +36,6 @@ namespace Emby.Server.Implementations
         /// </summary>
         string RestartArgs { get; }
 
-        /// <summary>
-        /// Gets the value of the --plugin-manifest-url command line option.
-        /// </summary>
-        string PluginManifestUrl { get; }
-
         /// <summary>
         /// Gets the value of the --published-server-url command line option.
         /// </summary>

+ 0 - 1
Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs

@@ -71,7 +71,6 @@ namespace Emby.Server.Implementations.Images
                     new ValueTuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending)
                 },
                 IncludeItemTypes = includeItemTypes
-
             });
         }
 

+ 0 - 1
Emby.Server.Implementations/Images/DynamicImageProvider.cs

@@ -78,7 +78,6 @@ namespace Emby.Server.Implementations.Images
                 }
 
                 return i;
-
             }).GroupBy(x => x.Id)
             .Select(x => x.First());
 

+ 12 - 2
Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs

@@ -1,5 +1,6 @@
 using System;
 using System.IO;
+using MediaBrowser.Controller;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Resolvers;
@@ -8,24 +9,33 @@ using MediaBrowser.Model.IO;
 namespace Emby.Server.Implementations.Library
 {
     /// <summary>
-    /// Provides the core resolver ignore rules
+    /// Provides the core resolver ignore rules.
     /// </summary>
     public class CoreResolutionIgnoreRule : IResolverIgnoreRule
     {
         private readonly ILibraryManager _libraryManager;
+        private readonly IServerApplicationPaths _serverApplicationPaths;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class.
         /// </summary>
         /// <param name="libraryManager">The library manager.</param>
-        public CoreResolutionIgnoreRule(ILibraryManager libraryManager)
+        /// <param name="serverApplicationPaths">The server application paths.</param>
+        public CoreResolutionIgnoreRule(ILibraryManager libraryManager, IServerApplicationPaths serverApplicationPaths)
         {
             _libraryManager = libraryManager;
+            _serverApplicationPaths = serverApplicationPaths;
         }
 
         /// <inheritdoc />
         public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
         {
+            // Don't ignore application folders
+            if (fileInfo.FullName.Contains(_serverApplicationPaths.RootFolderPath, StringComparison.InvariantCulture))
+            {
+                return false;
+            }
+
             // Don't ignore top level folders
             if (fileInfo.IsDirectory && parent is AggregateFolder)
             {

+ 2 - 0
Emby.Server.Implementations/Library/ExclusiveLiveStream.cs

@@ -12,11 +12,13 @@ namespace Emby.Server.Implementations.Library
     public class ExclusiveLiveStream : ILiveStream
     {
         public int ConsumerCount { get; set; }
+
         public string OriginalStreamId { get; set; }
 
         public string TunerHostId => null;
 
         public bool EnableStreamSharing { get; set; }
+
         public MediaSourceInfo MediaSource { get; set; }
 
         public string UniqueId { get; private set; }

+ 42 - 8
Emby.Server.Implementations/Library/IgnorePatterns.cs

@@ -1,17 +1,20 @@
+#nullable enable
+
+using System;
 using System.Linq;
 using DotNet.Globbing;
 
 namespace Emby.Server.Implementations.Library
 {
     /// <summary>
-    /// Glob patterns for files to ignore
+    /// Glob patterns for files to ignore.
     /// </summary>
     public static class IgnorePatterns
     {
         /// <summary>
-        /// Files matching these glob patterns will be ignored
+        /// Files matching these glob patterns will be ignored.
         /// </summary>
-        public static readonly string[] Patterns = new string[]
+        private static readonly string[] _patterns =
         {
             "**/small.jpg",
             "**/albumart.jpg",
@@ -19,32 +22,51 @@ namespace Emby.Server.Implementations.Library
 
             // Directories
             "**/metadata/**",
+            "**/metadata",
             "**/ps3_update/**",
+            "**/ps3_update",
             "**/ps3_vprm/**",
+            "**/ps3_vprm",
             "**/extrafanart/**",
+            "**/extrafanart",
             "**/extrathumbs/**",
+            "**/extrathumbs",
             "**/.actors/**",
+            "**/.actors",
             "**/.wd_tv/**",
+            "**/.wd_tv",
             "**/lost+found/**",
+            "**/lost+found",
 
             // WMC temp recording directories that will constantly be written to
             "**/TempRec/**",
+            "**/TempRec",
             "**/TempSBE/**",
+            "**/TempSBE",
 
             // Synology
             "**/eaDir/**",
+            "**/eaDir",
             "**/@eaDir/**",
+            "**/@eaDir",
             "**/#recycle/**",
+            "**/#recycle",
 
             // Qnap
             "**/@Recycle/**",
+            "**/@Recycle",
             "**/.@__thumb/**",
+            "**/.@__thumb",
             "**/$RECYCLE.BIN/**",
+            "**/$RECYCLE.BIN",
             "**/System Volume Information/**",
+            "**/System Volume Information",
             "**/.grab/**",
+            "**/.grab",
 
             // Unix hidden files and directories
             "**/.*/**",
+            "**/.*",
 
             // thumbs.db
             "**/thumbs.db",
@@ -56,19 +78,31 @@ namespace Emby.Server.Implementations.Library
 
         private static readonly GlobOptions _globOptions = new GlobOptions
         {
-            Evaluation = {
+            Evaluation =
+            {
                 CaseInsensitive = true
             }
         };
 
-        private static readonly Glob[] _globs = Patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray();
+        private static readonly Glob[] _globs = _patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray();
 
         /// <summary>
-        /// Returns true if the supplied path should be ignored
+        /// Returns true if the supplied path should be ignored.
         /// </summary>
-        public static bool ShouldIgnore(string path)
+        /// <param name="path">The path to test.</param>
+        /// <returns>Whether to ignore the path.</returns>
+        public static bool ShouldIgnore(ReadOnlySpan<char> path)
         {
-            return _globs.Any(g => g.IsMatch(path));
+            int len = _globs.Length;
+            for (int i = 0; i < len; i++)
+            {
+                if (_globs[i].IsMatch(path))
+                {
+                    return true;
+                }
+            }
+
+            return false;
         }
     }
 }

+ 48 - 51
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -97,13 +97,13 @@ namespace Emby.Server.Implementations.Library
         private IIntroProvider[] IntroProviders { get; set; }
 
         /// <summary>
-        /// Gets or sets the list of entity resolution ignore rules
+        /// Gets or sets the list of entity resolution ignore rules.
         /// </summary>
         /// <value>The entity resolution ignore rules.</value>
         private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
 
         /// <summary>
-        /// Gets or sets the list of currently registered entity resolvers
+        /// Gets or sets the list of currently registered entity resolvers.
         /// </summary>
         /// <value>The entity resolvers enumerable.</value>
         private IItemResolver[] EntityResolvers { get; set; }
@@ -136,7 +136,7 @@ namespace Emby.Server.Implementations.Library
         /// <summary>
         /// Initializes a new instance of the <see cref="LibraryManager" /> class.
         /// </summary>
-        /// <param name="appHost">The application host</param>
+        /// <param name="appHost">The application host.</param>
         /// <param name="logger">The logger.</param>
         /// <param name="taskManager">The task manager.</param>
         /// <param name="userManager">The user manager.</param>
@@ -209,12 +209,12 @@ namespace Emby.Server.Implementations.Library
         }
 
         /// <summary>
-        /// The _root folder
+        /// The _root folder.
         /// </summary>
         private volatile AggregateFolder _rootFolder;
 
         /// <summary>
-        /// The _root folder sync lock
+        /// The _root folder sync lock.
         /// </summary>
         private readonly object _rootFolderSyncLock = new object();
 
@@ -341,7 +341,7 @@ namespace Emby.Server.Implementations.Library
             if (item is LiveTvProgram)
             {
                 _logger.LogDebug(
-                    "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
+                    "Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
                     item.GetType().Name,
                     item.Name ?? "Unknown name",
                     item.Path ?? string.Empty,
@@ -350,7 +350,7 @@ namespace Emby.Server.Implementations.Library
             else
             {
                 _logger.LogInformation(
-                    "Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
+                    "Removing item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
                     item.GetType().Name,
                     item.Name ?? "Unknown name",
                     item.Path ?? string.Empty,
@@ -368,7 +368,12 @@ namespace Emby.Server.Implementations.Library
                     continue;
                 }
 
-                _logger.LogDebug("Deleting path {MetadataPath}", metadataPath);
+                _logger.LogDebug(
+                    "Deleting metadata path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
+                    item.GetType().Name,
+                    item.Name ?? "Unknown name",
+                    metadataPath,
+                    item.Id);
 
                 try
                 {
@@ -392,7 +397,13 @@ namespace Emby.Server.Implementations.Library
                     {
                         try
                         {
-                            _logger.LogDebug("Deleting path {path}", fileSystemInfo.FullName);
+                            _logger.LogInformation(
+                                "Deleting item path, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
+                                item.GetType().Name,
+                                item.Name ?? "Unknown name",
+                                fileSystemInfo.FullName,
+                                item.Id);
+
                             if (fileSystemInfo.IsDirectory)
                             {
                                 Directory.Delete(fileSystemInfo.FullName, true);
@@ -514,8 +525,8 @@ namespace Emby.Server.Implementations.Library
             return key.GetMD5();
         }
 
-        public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, bool allowIgnorePath = true)
-            => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent, allowIgnorePath: allowIgnorePath);
+        public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
+            => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent);
 
         private BaseItem ResolvePath(
             FileSystemMetadata fileInfo,
@@ -523,8 +534,7 @@ namespace Emby.Server.Implementations.Library
             IItemResolver[] resolvers,
             Folder parent = null,
             string collectionType = null,
-            LibraryOptions libraryOptions = null,
-            bool allowIgnorePath = true)
+            LibraryOptions libraryOptions = null)
         {
             if (fileInfo == null)
             {
@@ -548,7 +558,7 @@ namespace Emby.Server.Implementations.Library
             };
 
             // Return null if ignore rules deem that we should do so
-            if (allowIgnorePath && IgnoreFile(args.FileInfo, args.Parent))
+            if (IgnoreFile(args.FileInfo, args.Parent))
             {
                 return null;
             }
@@ -627,7 +637,7 @@ namespace Emby.Server.Implementations.Library
         }
 
         /// <summary>
-        /// Determines whether a path should be ignored based on its contents - called after the contents have been read
+        /// Determines whether a path should be ignored based on its contents - called after the contents have been read.
         /// </summary>
         /// <param name="args">The args.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
@@ -713,7 +723,7 @@ namespace Emby.Server.Implementations.Library
             Directory.CreateDirectory(rootFolderPath);
 
             var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ??
-                             ((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath), allowIgnorePath: false))
+                             ((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)))
                              .DeepCopy<Folder, AggregateFolder>();
 
             // In case program data folder was moved
@@ -795,7 +805,7 @@ namespace Emby.Server.Implementations.Library
                         if (tmpItem == null)
                         {
                             _logger.LogDebug("Creating new userRootFolder with DeepCopy");
-                            tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath), allowIgnorePath: false)).DeepCopy<Folder, UserRootFolder>();
+                            tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>();
                         }
 
                         // In case program data folder was moved
@@ -909,7 +919,7 @@ namespace Emby.Server.Implementations.Library
         }
 
         /// <summary>
-        /// Gets a Genre
+        /// Gets a Genre.
         /// </summary>
         /// <param name="name">The name.</param>
         /// <returns>Task{Genre}.</returns>
@@ -990,7 +1000,7 @@ namespace Emby.Server.Implementations.Library
         }
 
         /// <summary>
-        /// Reloads the root media folder
+        /// Reloads the root media folder.
         /// </summary>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
@@ -1793,7 +1803,7 @@ namespace Emby.Server.Implementations.Library
         /// Creates the items.
         /// </summary>
         /// <param name="items">The items.</param>
-        /// <param name="parent">The parent item</param>
+        /// <param name="parent">The parent item.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
         {
@@ -1894,9 +1904,19 @@ namespace Emby.Server.Implementations.Library
                     }
                 }
 
-                ImageDimensions size = _imageProcessor.GetImageDimensions(item, image);
-                image.Width = size.Width;
-                image.Height = size.Height;
+                try
+                {
+                    ImageDimensions size = _imageProcessor.GetImageDimensions(item, image);
+                    image.Width = size.Width;
+                    image.Height = size.Height;
+                }
+                catch (Exception ex)
+                {
+                    _logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path);
+                    image.Width = 0;
+                    image.Height = 0;
+                    continue;
+                }
 
                 try
                 {
@@ -2595,7 +2615,7 @@ namespace Emby.Server.Implementations.Library
                     Anime series don't generally have a season in their file name, however,
                     tvdb needs a season to correctly get the metadata.
                     Hence, a null season needs to be filled with something. */
-                    //FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
+                    // FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
                     episode.ParentIndexNumber = 1;
                 }
 
@@ -2784,10 +2804,12 @@ namespace Emby.Server.Implementations.Library
             {
                 throw new ArgumentNullException(nameof(path));
             }
+
             if (string.IsNullOrWhiteSpace(from))
             {
                 throw new ArgumentNullException(nameof(from));
             }
+
             if (string.IsNullOrWhiteSpace(to))
             {
                 throw new ArgumentNullException(nameof(to));
@@ -2861,7 +2883,6 @@ namespace Emby.Server.Implementations.Library
                     _logger.LogError(ex, "Error getting person");
                     return null;
                 }
-
             }).Where(i => i != null).ToList();
         }
 
@@ -2896,7 +2917,8 @@ namespace Emby.Server.Implementations.Library
                 }
                 catch (HttpException ex)
                 {
-                    if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
+                    if (ex.StatusCode.HasValue
+                        && (ex.StatusCode.Value == HttpStatusCode.NotFound || ex.StatusCode.Value == HttpStatusCode.Forbidden))
                     {
                         continue;
                     }
@@ -2989,21 +3011,6 @@ namespace Emby.Server.Implementations.Library
             });
         }
 
-        private static bool ValidateNetworkPath(string path)
-        {
-            //if (Environment.OSVersion.Platform == PlatformID.Win32NT)
-            //{
-            //    // We can't validate protocol-based paths, so just allow them
-            //    if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
-            //    {
-            //        return Directory.Exists(path);
-            //    }
-            //}
-
-            // Without native support for unc, we cannot validate this when running under mono
-            return true;
-        }
-
         private const string ShortcutFileExtension = ".mblink";
 
         public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
@@ -3030,11 +3037,6 @@ namespace Emby.Server.Implementations.Library
                 throw new FileNotFoundException("The path does not exist.");
             }
 
-            if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
-            {
-                throw new FileNotFoundException("The network path does not exist.");
-            }
-
             var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
             var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
 
@@ -3073,11 +3075,6 @@ namespace Emby.Server.Implementations.Library
                 throw new ArgumentNullException(nameof(pathInfo));
             }
 
-            if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
-            {
-                throw new FileNotFoundException("The network path does not exist.");
-            }
-
             var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
             var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
 

+ 2 - 5
Emby.Server.Implementations/Library/LiveStreamHelper.cs

@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.Library
                 {
                     mediaInfo = _json.DeserializeFromFile<MediaInfo>(cacheFilePath);
 
-                    //_logger.LogDebug("Found cached media info");
+                    // _logger.LogDebug("Found cached media info");
                 }
                 catch
                 {
@@ -85,7 +85,7 @@ namespace Emby.Server.Implementations.Library
                     Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
                     _json.SerializeToFile(mediaInfo, cacheFilePath);
 
-                    //_logger.LogDebug("Saved media info to {0}", cacheFilePath);
+                    // _logger.LogDebug("Saved media info to {0}", cacheFilePath);
                 }
             }
 
@@ -148,17 +148,14 @@ namespace Emby.Server.Implementations.Library
                     {
                         videoStream.BitRate = 30000000;
                     }
-
                     else if (width >= 1900)
                     {
                         videoStream.BitRate = 20000000;
                     }
-
                     else if (width >= 1200)
                     {
                         videoStream.BitRate = 8000000;
                     }
-
                     else if (width >= 700)
                     {
                         videoStream.BitRate = 2000000;

+ 7 - 7
Emby.Server.Implementations/Library/MediaSourceManager.cs

@@ -205,22 +205,27 @@ namespace Emby.Server.Implementations.Library
             {
                 return MediaProtocol.Rtsp;
             }
+
             if (path.StartsWith("Rtmp", StringComparison.OrdinalIgnoreCase))
             {
                 return MediaProtocol.Rtmp;
             }
+
             if (path.StartsWith("Http", StringComparison.OrdinalIgnoreCase))
             {
                 return MediaProtocol.Http;
             }
+
             if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
             {
                 return MediaProtocol.Rtp;
             }
+
             if (path.StartsWith("ftp", StringComparison.OrdinalIgnoreCase))
             {
                 return MediaProtocol.Ftp;
             }
+
             if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
             {
                 return MediaProtocol.Udp;
@@ -436,7 +441,6 @@ namespace Emby.Server.Implementations.Library
                 }
 
                 return 1;
-
             }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
             .ThenByDescending(i =>
             {
@@ -620,7 +624,6 @@ namespace Emby.Server.Implementations.Library
                     MediaSource = mediaSource,
                     ExtractChapters = false,
                     MediaType = DlnaProfileType.Video
-
                 }, cancellationToken).ConfigureAwait(false);
 
                 mediaSource.MediaStreams = info.MediaStreams;
@@ -646,7 +649,7 @@ namespace Emby.Server.Implementations.Library
                 {
                     mediaInfo = _jsonSerializer.DeserializeFromFile<MediaInfo>(cacheFilePath);
 
-                    //_logger.LogDebug("Found cached media info");
+                    // _logger.LogDebug("Found cached media info");
                 }
                 catch (Exception ex)
                 {
@@ -682,7 +685,7 @@ namespace Emby.Server.Implementations.Library
                     Directory.CreateDirectory(Path.GetDirectoryName(cacheFilePath));
                     _jsonSerializer.SerializeToFile(mediaInfo, cacheFilePath);
 
-                    //_logger.LogDebug("Saved media info to {0}", cacheFilePath);
+                    // _logger.LogDebug("Saved media info to {0}", cacheFilePath);
                 }
             }
 
@@ -748,17 +751,14 @@ namespace Emby.Server.Implementations.Library
                     {
                         videoStream.BitRate = 30000000;
                     }
-
                     else if (width >= 1900)
                     {
                         videoStream.BitRate = 20000000;
                     }
-
                     else if (width >= 1200)
                     {
                         videoStream.BitRate = 8000000;
                     }
-
                     else if (width >= 700)
                     {
                         videoStream.BitRate = 2000000;

+ 1 - 1
Emby.Server.Implementations/Library/ResolverHelper.cs

@@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.Library
         }
 
         /// <summary>
-        /// Ensures DateCreated and DateModified have values
+        /// Ensures DateCreated and DateModified have values.
         /// </summary>
         /// <param name="fileSystem">The file system.</param>
         /// <param name="item">The item.</param>

+ 2 - 2
Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs

@@ -209,8 +209,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
                     Name = parseName ?
                         resolvedItem.Name :
                         Path.GetFileNameWithoutExtension(firstMedia.Path),
-                    //AdditionalParts = resolvedItem.Files.Skip(1).Select(i => i.Path).ToArray(),
-                    //LocalAlternateVersions = resolvedItem.AlternateVersions.Select(i => i.Path).ToArray()
+                    // AdditionalParts = resolvedItem.Files.Skip(1).Select(i => i.Path).ToArray(),
+                    // LocalAlternateVersions = resolvedItem.AlternateVersions.Select(i => i.Path).ToArray()
                 };
 
                 result.Items.Add(libraryItem);

+ 1 - 1
Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs

@@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
             // Args points to an album if parent is an Artist folder or it directly contains music
             if (args.IsDirectory)
             {
-                // if (args.Parent is MusicArtist) return true;  //saves us from testing children twice
+                // if (args.Parent is MusicArtist) return true;  // saves us from testing children twice
                 if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, _libraryManager))
                 {
                     return true;

+ 2 - 2
Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs

@@ -292,7 +292,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
             }
 
             return true;
-            //var blurayExtensions = new[]
+            // var blurayExtensions = new[]
             //{
             //    ".mts",
             //    ".m2ts",
@@ -300,7 +300,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
             //    ".mpls"
             //};
 
-            //return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
+            // return directoryService.GetFiles(fullPath).Any(i => blurayExtensions.Contains(i.Extension ?? string.Empty, StringComparer.OrdinalIgnoreCase));
         }
     }
 }

+ 4 - 0
Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs

@@ -19,7 +19,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
 
             // Only process items that are in a collection folder containing books
             if (!string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
+            {
                 return null;
+            }
 
             if (args.IsDirectory)
             {
@@ -55,7 +57,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
 
             // Don't return a Book if there is more (or less) than one document in the directory
             if (bookFiles.Count != 1)
+            {
                 return null;
+            }
 
             return new Book
             {

+ 1 - 1
Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs

@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
         public virtual ResolverPriority Priority => ResolverPriority.First;
 
         /// <summary>
-        /// Sets initial values on the newly resolved item
+        /// Sets initial values on the newly resolved item.
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="args">The args.</param>

+ 3 - 2
Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs

@@ -41,10 +41,12 @@ namespace Emby.Server.Implementations.Library.Resolvers
                 {
                     return new AggregateFolder();
                 }
+
                 if (string.Equals(args.Path, _appPaths.DefaultUserViewsPath, StringComparison.OrdinalIgnoreCase))
                 {
-                    return new UserRootFolder();  //if we got here and still a root - must be user root
+                    return new UserRootFolder();  // if we got here and still a root - must be user root
                 }
+
                 if (args.IsVf)
                 {
                     return new CollectionFolder
@@ -73,7 +75,6 @@ namespace Emby.Server.Implementations.Library.Resolvers
                     {
                         return false;
                     }
-
                 })
                 .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
                 .FirstOrDefault();

+ 1 - 0
Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs

@@ -55,6 +55,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                         episode.SeriesId = series.Id;
                         episode.SeriesName = series.Name;
                     }
+
                     if (season != null)
                     {
                         episode.SeasonId = season.Id;

+ 2 - 3
Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs

@@ -23,8 +23,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
         /// </summary>
         /// <param name="config">The config.</param>
         /// <param name="libraryManager">The library manager.</param>
-        /// <param name="localization">The localization</param>
-        /// <param name="logger">The logger</param>
+        /// <param name="localization">The localization.</param>
+        /// <param name="logger">The logger.</param>
         public SeasonResolver(
             IServerConfigurationManager config,
             ILibraryManager libraryManager,
@@ -94,7 +94,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                             _localization.GetLocalizedString("NameSeasonNumber"),
                             seasonNumber,
                             args.GetLibraryOptions().PreferredMetadataLanguage);
-
                 }
 
                 return season;

+ 1 - 1
Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -59,7 +59,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                 var collectionType = args.GetCollectionType();
                 if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                 {
-                    //if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
+                    // if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
                     //{
                     //    return new Series
                     //    {

+ 1 - 1
Emby.Server.Implementations/Library/SearchEngine.cs

@@ -194,6 +194,7 @@ namespace Emby.Server.Implementations.Library
                 {
                     searchQuery.AncestorIds = new[] { searchQuery.ParentId };
                 }
+
                 searchQuery.ParentId = Guid.Empty;
                 searchQuery.IncludeItemsByName = true;
                 searchQuery.IncludeItemTypes = Array.Empty<string>();
@@ -207,7 +208,6 @@ namespace Emby.Server.Implementations.Library
             return mediaItems.Select(i => new SearchHintInfo
             {
                 Item = i
-
             }).ToList();
         }
     }

+ 2 - 2
Emby.Server.Implementations/Library/UserDataManager.cs

@@ -103,7 +103,7 @@ namespace Emby.Server.Implementations.Library
         }
 
         /// <summary>
-        /// Retrieve all user data for the given user
+        /// Retrieve all user data for the given user.
         /// </summary>
         /// <param name="userId"></param>
         /// <returns></returns>
@@ -188,7 +188,7 @@ namespace Emby.Server.Implementations.Library
         }
 
         /// <summary>
-        /// Converts a UserItemData to a DTOUserItemData
+        /// Converts a UserItemData to a DTOUserItemData.
         /// </summary>
         /// <param name="data">The data.</param>
         /// <returns>DtoUserItemData.</returns>

+ 0 - 1
Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs

@@ -98,7 +98,6 @@ namespace Emby.Server.Implementations.Library.Validators
                 _libraryManager.DeleteItem(item, new DeleteOptions
                 {
                     DeleteFileLocation = false
-
                 }, false);
             }
 

+ 0 - 1
Emby.Server.Implementations/Library/Validators/StudiosValidator.cs

@@ -92,7 +92,6 @@ namespace Emby.Server.Implementations.Library.Validators
                 _libraryManager.DeleteItem(item, new DeleteOptions
                 {
                     DeleteFileLocation = false
-
                 }, false);
             }
 

+ 0 - 1
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -1547,7 +1547,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                         IsFolder = false,
                         Recursive = true,
                         DtoOptions = new DtoOptions(true)
-
                     })
                     .Where(i => i.IsFileProtocol && File.Exists(i.Path))
                     .Skip(seriesTimer.KeepUpTo - 1)

+ 5 - 5
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -183,7 +183,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
             var subtitleArgs = CopySubtitles ? " -codec:s copy" : " -sn";
 
-            //var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ?
+            // var outputParam = string.Equals(Path.GetExtension(targetFile), ".mp4", StringComparison.OrdinalIgnoreCase) ?
             //    " -f mp4 -movflags frag_keyframe+empty_moov" :
             //    string.Empty;
 
@@ -206,13 +206,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         {
             return "-codec:a:0 copy";
 
-            //var audioChannels = 2;
-            //var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
-            //if (audioStream != null)
+            // var audioChannels = 2;
+            // var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
+            // if (audioStream != null)
             //{
             //    audioChannels = audioStream.Channels ?? audioChannels;
             //}
-            //return "-codec:a:0 aac -strict experimental -ab 320000";
+            // return "-codec:a:0 aac -strict experimental -ab 320000";
         }
 
         private static bool EncodeVideo(MediaSourceInfo mediaSource)

+ 0 - 1
Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs

@@ -56,7 +56,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     name += " " + info.EpisodeTitle;
                 }
             }
-
             else if (info.IsMovie && info.ProductionYear != null)
             {
                 name += " (" + info.ProductionYear + ")";

+ 122 - 5
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -145,7 +145,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                     var programsInfo = new List<ProgramInfo>();
                     foreach (ScheduleDirect.Program schedule in dailySchedules.SelectMany(d => d.programs))
                     {
-                        //_logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
+                        // _logger.LogDebug("Proccesing Schedule for statio ID " + stationID +
                         //              " which corresponds to channel " + channelNumber + " and program id " +
                         //              schedule.programID + " which says it has images? " +
                         //              programDict[schedule.programID].hasImageArtwork);
@@ -178,7 +178,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
                                 programEntry.backdropImage = GetProgramImage(ApiUrl, imagesWithoutText, true, WideAspect);
 
-                                //programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
+                                // programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
                                 //    GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
                                 //    GetProgramImage(ApiUrl, data, "Banner-LO", false) ??
                                 //    GetProgramImage(ApiUrl, data, "Banner-LOT", false);
@@ -212,6 +212,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             {
                 channelNumber = map.channel;
             }
+
             if (string.IsNullOrWhiteSpace(channelNumber))
             {
                 channelNumber = map.atscMajor + "." + map.atscMinor;
@@ -276,7 +277,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 CommunityRating = null,
                 EpisodeTitle = episodeTitle,
                 Audio = audioType,
-                //IsNew = programInfo.@new ?? false,
+                // IsNew = programInfo.@new ?? false,
                 IsRepeat = programInfo.@new == null,
                 IsSeries = string.Equals(details.entityType, "episode", StringComparison.OrdinalIgnoreCase),
                 ImageUrl = details.primaryImage,
@@ -400,6 +401,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             {
                 date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
             }
+
             return date;
         }
 
@@ -622,6 +624,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                         _lastErrorResponse = DateTime.UtcNow;
                     }
                 }
+
                 throw;
             }
             finally
@@ -701,7 +704,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 CancellationToken = cancellationToken,
                 LogErrorResponseBody = true
             };
-            //_logger.LogInformation("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
+            // _logger.LogInformation("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
             // httpOptions.RequestContent);
 
             using (var response = await Post(httpOptions, false, null).ConfigureAwait(false))
@@ -805,11 +808,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 {
                     throw new ArgumentException("Username is required");
                 }
+
                 if (string.IsNullOrEmpty(info.Password))
                 {
                     throw new ArgumentException("Password is required");
                 }
             }
+
             if (validateListings)
             {
                 if (string.IsNullOrEmpty(info.ListingsId))
@@ -932,24 +937,35 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             public class Token
             {
                 public int code { get; set; }
+
                 public string message { get; set; }
+
                 public string serverID { get; set; }
+
                 public string token { get; set; }
             }
+
             public class Lineup
             {
                 public string lineup { get; set; }
+
                 public string name { get; set; }
+
                 public string transport { get; set; }
+
                 public string location { get; set; }
+
                 public string uri { get; set; }
             }
 
             public class Lineups
             {
                 public int code { get; set; }
+
                 public string serverID { get; set; }
+
                 public string datetime { get; set; }
+
                 public List<Lineup> lineups { get; set; }
             }
 
@@ -957,8 +973,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             public class Headends
             {
                 public string headend { get; set; }
+
                 public string transport { get; set; }
+
                 public string location { get; set; }
+
                 public List<Lineup> lineups { get; set; }
             }
 
@@ -967,59 +986,83 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             public class Map
             {
                 public string stationID { get; set; }
+
                 public string channel { get; set; }
+
                 public string logicalChannelNumber { get; set; }
+
                 public int uhfVhf { get; set; }
+
                 public int atscMajor { get; set; }
+
                 public int atscMinor { get; set; }
             }
 
             public class Broadcaster
             {
                 public string city { get; set; }
+
                 public string state { get; set; }
+
                 public string postalcode { get; set; }
+
                 public string country { get; set; }
             }
 
             public class Logo
             {
                 public string URL { get; set; }
+
                 public int height { get; set; }
+
                 public int width { get; set; }
+
                 public string md5 { get; set; }
             }
 
             public class Station
             {
                 public string stationID { get; set; }
+
                 public string name { get; set; }
+
                 public string callsign { get; set; }
+
                 public List<string> broadcastLanguage { get; set; }
+
                 public List<string> descriptionLanguage { get; set; }
+
                 public Broadcaster broadcaster { get; set; }
+
                 public string affiliate { get; set; }
+
                 public Logo logo { get; set; }
+
                 public bool? isCommercialFree { get; set; }
             }
 
             public class Metadata
             {
                 public string lineup { get; set; }
+
                 public string modified { get; set; }
+
                 public string transport { get; set; }
             }
 
             public class Channel
             {
                 public List<Map> map { get; set; }
+
                 public List<Station> stations { get; set; }
+
                 public Metadata metadata { get; set; }
             }
 
             public class RequestScheduleForChannel
             {
                 public string stationID { get; set; }
+
                 public List<string> date { get; set; }
             }
 
@@ -1029,29 +1072,43 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             public class Rating
             {
                 public string body { get; set; }
+
                 public string code { get; set; }
             }
 
             public class Multipart
             {
                 public int partNumber { get; set; }
+
                 public int totalParts { get; set; }
             }
 
             public class Program
             {
                 public string programID { get; set; }
+
                 public string airDateTime { get; set; }
+
                 public int duration { get; set; }
+
                 public string md5 { get; set; }
+
                 public List<string> audioProperties { get; set; }
+
                 public List<string> videoProperties { get; set; }
+
                 public List<Rating> ratings { get; set; }
+
                 public bool? @new { get; set; }
+
                 public Multipart multipart { get; set; }
+
                 public string liveTapeDelay { get; set; }
+
                 public bool premiere { get; set; }
+
                 public bool repeat { get; set; }
+
                 public string isPremiereOrFinale { get; set; }
             }
 
@@ -1060,16 +1117,22 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             public class MetadataSchedule
             {
                 public string modified { get; set; }
+
                 public string md5 { get; set; }
+
                 public string startDate { get; set; }
+
                 public string endDate { get; set; }
+
                 public int days { get; set; }
             }
 
             public class Day
             {
                 public string stationID { get; set; }
+
                 public List<Program> programs { get; set; }
+
                 public MetadataSchedule metadata { get; set; }
 
                 public Day()
@@ -1092,24 +1155,28 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             public class Description100
             {
                 public string descriptionLanguage { get; set; }
+
                 public string description { get; set; }
             }
 
             public class Description1000
             {
                 public string descriptionLanguage { get; set; }
+
                 public string description { get; set; }
             }
 
             public class DescriptionsProgram
             {
                 public List<Description100> description100 { get; set; }
+
                 public List<Description1000> description1000 { get; set; }
             }
 
             public class Gracenote
             {
                 public int season { get; set; }
+
                 public int episode { get; set; }
             }
 
@@ -1121,104 +1188,154 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             public class ContentRating
             {
                 public string body { get; set; }
+
                 public string code { get; set; }
             }
 
             public class Cast
             {
                 public string billingOrder { get; set; }
+
                 public string role { get; set; }
+
                 public string nameId { get; set; }
+
                 public string personId { get; set; }
+
                 public string name { get; set; }
+
                 public string characterName { get; set; }
             }
 
             public class Crew
             {
                 public string billingOrder { get; set; }
+
                 public string role { get; set; }
+
                 public string nameId { get; set; }
+
                 public string personId { get; set; }
+
                 public string name { get; set; }
             }
 
             public class QualityRating
             {
                 public string ratingsBody { get; set; }
+
                 public string rating { get; set; }
+
                 public string minRating { get; set; }
+
                 public string maxRating { get; set; }
+
                 public string increment { get; set; }
             }
 
             public class Movie
             {
                 public string year { get; set; }
+
                 public int duration { get; set; }
+
                 public List<QualityRating> qualityRating { get; set; }
             }
 
             public class Recommendation
             {
                 public string programID { get; set; }
+
                 public string title120 { get; set; }
             }
 
             public class ProgramDetails
             {
                 public string audience { get; set; }
+
                 public string programID { get; set; }
+
                 public List<Title> titles { get; set; }
+
                 public EventDetails eventDetails { get; set; }
+
                 public DescriptionsProgram descriptions { get; set; }
+
                 public string originalAirDate { get; set; }
+
                 public List<string> genres { get; set; }
+
                 public string episodeTitle150 { get; set; }
+
                 public List<MetadataPrograms> metadata { get; set; }
+
                 public List<ContentRating> contentRating { get; set; }
+
                 public List<Cast> cast { get; set; }
+
                 public List<Crew> crew { get; set; }
+
                 public string entityType { get; set; }
+
                 public string showType { get; set; }
+
                 public bool hasImageArtwork { get; set; }
+
                 public string primaryImage { get; set; }
+
                 public string thumbImage { get; set; }
+
                 public string backdropImage { get; set; }
+
                 public string bannerImage { get; set; }
+
                 public string imageID { get; set; }
+
                 public string md5 { get; set; }
+
                 public List<string> contentAdvisory { get; set; }
+
                 public Movie movie { get; set; }
+
                 public List<Recommendation> recommendations { get; set; }
             }
 
             public class Caption
             {
                 public string content { get; set; }
+
                 public string lang { get; set; }
             }
 
             public class ImageData
             {
                 public string width { get; set; }
+
                 public string height { get; set; }
+
                 public string uri { get; set; }
+
                 public string size { get; set; }
+
                 public string aspect { get; set; }
+
                 public string category { get; set; }
+
                 public string text { get; set; }
+
                 public string primary { get; set; }
+
                 public string tier { get; set; }
+
                 public Caption caption { get; set; }
             }
 
             public class ShowImages
             {
                 public string programID { get; set; }
+
                 public List<ImageData> data { get; set; }
             }
-
         }
     }
 }

+ 1 - 0
Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs

@@ -224,6 +224,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 {
                     uniqueString = "-" + programInfo.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture);
                 }
+
                 if (programInfo.EpisodeNumber.HasValue)
                 {
                     uniqueString = "-" + programInfo.EpisodeNumber.Value.ToString(CultureInfo.InvariantCulture);

+ 19 - 13
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -406,8 +406,8 @@ namespace Emby.Server.Implementations.LiveTv
             if (!(service is EmbyTV.EmbyTV))
             {
                 // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
-                //mediaSource.SupportsDirectPlay = false;
-                //mediaSource.SupportsDirectStream = false;
+                // mediaSource.SupportsDirectPlay = false;
+                // mediaSource.SupportsDirectStream = false;
                 mediaSource.SupportsTranscoding = true;
                 foreach (var stream in mediaSource.MediaStreams)
                 {
@@ -556,9 +556,10 @@ namespace Emby.Server.Implementations.LiveTv
             {
                 forceUpdate = true;
             }
+
             item.ParentId = channel.Id;
 
-            //item.ChannelType = channelType;
+            // item.ChannelType = channelType;
 
             item.Audio = info.Audio;
             item.ChannelId = channel.Id;
@@ -575,6 +576,7 @@ namespace Emby.Server.Implementations.LiveTv
             {
                 forceUpdate = true;
             }
+
             item.ExternalSeriesId = seriesId;
 
             var isSeries = info.IsSeries || !string.IsNullOrEmpty(info.EpisodeTitle);
@@ -589,30 +591,37 @@ namespace Emby.Server.Implementations.LiveTv
             {
                 tags.Add("Live");
             }
+
             if (info.IsPremiere)
             {
                 tags.Add("Premiere");
             }
+
             if (info.IsNews)
             {
                 tags.Add("News");
             }
+
             if (info.IsSports)
             {
                 tags.Add("Sports");
             }
+
             if (info.IsKids)
             {
                 tags.Add("Kids");
             }
+
             if (info.IsRepeat)
             {
                 tags.Add("Repeat");
             }
+
             if (info.IsMovie)
             {
                 tags.Add("Movie");
             }
+
             if (isSeries)
             {
                 tags.Add("Series");
@@ -635,6 +644,7 @@ namespace Emby.Server.Implementations.LiveTv
             {
                 forceUpdate = true;
             }
+
             item.IsSeries = isSeries;
 
             item.Name = info.Name;
@@ -652,12 +662,14 @@ namespace Emby.Server.Implementations.LiveTv
             {
                 forceUpdate = true;
             }
+
             item.StartDate = info.StartDate;
 
             if (item.EndDate != info.EndDate)
             {
                 forceUpdate = true;
             }
+
             item.EndDate = info.EndDate;
 
             item.ProductionYear = info.ProductionYear;
@@ -1168,7 +1180,6 @@ namespace Emby.Server.Implementations.LiveTv
                         IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
                         ChannelIds = new Guid[] { currentChannel.Id },
                         DtoOptions = new DtoOptions(true)
-
                     }).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
 
                     var newPrograms = new List<LiveTvProgram>();
@@ -1368,10 +1379,10 @@ namespace Emby.Server.Implementations.LiveTv
                 // limit = (query.Limit ?? 10) * 2;
                 limit = null;
 
-                //var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
-                //var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
+                // var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
+                // var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
 
-                //return new QueryResult<BaseItem>
+                // return new QueryResult<BaseItem>
                 //{
                 //    Items = items,
                 //    TotalRecordCount = items.Length
@@ -1738,7 +1749,6 @@ namespace Emby.Server.Implementations.LiveTv
             var results = await GetTimers(new TimerQuery
             {
                 Id = id
-
             }, cancellationToken).ConfigureAwait(false);
 
             return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
@@ -1790,7 +1800,6 @@ namespace Emby.Server.Implementations.LiveTv
                 .Select(i =>
                 {
                     return i.Item1;
-
                 })
                 .ToArray();
 
@@ -1845,7 +1854,6 @@ namespace Emby.Server.Implementations.LiveTv
                     }
 
                     return _tvDtoService.GetSeriesTimerInfoDto(i.Item1, i.Item2, channelName);
-
                 })
                 .ToArray();
 
@@ -1878,7 +1886,6 @@ namespace Emby.Server.Implementations.LiveTv
                 OrderBy = new[] { (ItemSortBy.StartDate, SortOrder.Ascending) },
                 TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Id },
                 DtoOptions = options
-
             }) : new List<BaseItem>();
 
             RemoveFields(options);
@@ -1956,7 +1963,7 @@ namespace Emby.Server.Implementations.LiveTv
                     OriginalAirDate = program.PremiereDate,
                     Overview = program.Overview,
                     StartDate = program.StartDate,
-                    //ImagePath = program.ExternalImagePath,
+                    // ImagePath = program.ExternalImagePath,
                     Name = program.Name,
                     OfficialRating = program.OfficialRating
                 };
@@ -2456,7 +2463,6 @@ namespace Emby.Server.Implementations.LiveTv
                 UserId = user.Id,
                 IsRecordingsFolder = true,
                 RefreshLatestChannelItems = refreshChannels
-
             }).Items);
 
             return folders.Cast<BaseItem>().ToList();

+ 1 - 1
Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs

@@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.LiveTv
         }
 
         /// <summary>
-        /// Creates the triggers that define when the task will run
+        /// Creates the triggers that define when the task will run.
         /// </summary>
         /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
         public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()

+ 1 - 3
Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs

@@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
             var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false);
             var list = result.ToList();
-            //logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
+            // logger.LogInformation("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
 
             if (!string.IsNullOrEmpty(key) && list.Count > 0)
             {
@@ -99,7 +99,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                         }
                         catch (IOException)
                         {
-
                         }
                     }
                 }
@@ -116,7 +115,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                         }
                         catch (IOException)
                         {
-
                         }
                     }
                 }

+ 30 - 7
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -111,7 +111,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 ChannelType = ChannelType.TV,
                 IsLegacyTuner = (i.URL ?? string.Empty).StartsWith("hdhomerun", StringComparison.OrdinalIgnoreCase),
                 Path = i.URL
-
             }).Cast<ChannelInfo>().ToList();
         }
 
@@ -171,6 +170,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                             _modelCache[cacheKey] = response;
                         }
                     }
+
                     return response;
                 }
 
@@ -201,7 +201,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                         var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
                         var name = line.Substring(0, index - 1);
                         var currentChannel = line.Substring(index + 7);
-                        if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; }
+                        if (currentChannel != "none")
+                        {
+                            status = LiveTvTunerStatus.LiveTv;
+                        }
+                        else
+                        {
+                            status = LiveTvTunerStatus.Available;
+                        }
+
                         tuners.Add(new LiveTvTunerInfo
                         {
                             Name = name,
@@ -230,11 +238,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     inside = true;
                     continue;
                 }
+
                 if (let == '>')
                 {
                     inside = false;
                     continue;
                 }
+
                 if (!inside)
                 {
                     buffer[bufferIndex] = let;
@@ -332,12 +342,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         private class Channels
         {
             public string GuideNumber { get; set; }
+
             public string GuideName { get; set; }
+
             public string VideoCodec { get; set; }
+
             public string AudioCodec { get; set; }
+
             public string URL { get; set; }
+
             public bool Favorite { get; set; }
+
             public bool DRM { get; set; }
+
             public int HD { get; set; }
         }
 
@@ -481,7 +498,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                                 Height = height,
                                 BitRate = videoBitrate,
                                 NalLengthSize = nal
-
                             },
                             new MediaStream
                             {
@@ -502,8 +518,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 SupportsTranscoding = true,
                 IsInfiniteStream = true,
                 IgnoreDts = true,
-                //IgnoreIndex = true,
-                //ReadAtNativeFramerate = true
+                // IgnoreIndex = true,
+                // ReadAtNativeFramerate = true
             };
 
             mediaSource.InferTotalBitrate();
@@ -659,13 +675,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         public class DiscoverResponse
         {
             public string FriendlyName { get; set; }
+
             public string ModelNumber { get; set; }
+
             public string FirmwareName { get; set; }
+
             public string FirmwareVersion { get; set; }
+
             public string DeviceID { get; set; }
+
             public string DeviceAuth { get; set; }
+
             public string BaseURL { get; set; }
+
             public string LineupURL { get; set; }
+
             public int TunerCount { get; set; }
 
             public bool SupportsTranscoding
@@ -674,7 +698,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 {
                     var model = ModelNumber ?? string.Empty;
 
-                    if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
+                    if (model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1)
                     {
                         return true;
                     }
@@ -722,7 +746,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                             }
                         }
                     }
-
                 }
                 catch (OperationCanceledException)
                 {

+ 7 - 7
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs

@@ -117,17 +117,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 taskCompletionSource,
                 LiveStreamCancellationTokenSource.Token).ConfigureAwait(false);
 
-            //OpenedMediaSource.Protocol = MediaProtocol.File;
-            //OpenedMediaSource.Path = tempFile;
-            //OpenedMediaSource.ReadAtNativeFramerate = true;
+            // OpenedMediaSource.Protocol = MediaProtocol.File;
+            // OpenedMediaSource.Path = tempFile;
+            // OpenedMediaSource.ReadAtNativeFramerate = true;
 
             MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
             MediaSource.Protocol = MediaProtocol.Http;
-            //OpenedMediaSource.SupportsDirectPlay = false;
-            //OpenedMediaSource.SupportsDirectStream = true;
-            //OpenedMediaSource.SupportsTranscoding = true;
+            // OpenedMediaSource.SupportsDirectPlay = false;
+            // OpenedMediaSource.SupportsDirectStream = true;
+            // OpenedMediaSource.SupportsTranscoding = true;
 
-            //await Task.Delay(5000).ConfigureAwait(false);
+            // await Task.Delay(5000).ConfigureAwait(false);
             await taskCompletionSource.Task.ConfigureAwait(false);
         }
 

+ 3 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs

@@ -58,12 +58,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         protected virtual int EmptyReadLimit => 1000;
 
         public MediaSourceInfo OriginalMediaSource { get; set; }
+
         public MediaSourceInfo MediaSource { get; set; }
 
         public int ConsumerCount { get; set; }
 
         public string OriginalStreamId { get; set; }
+
         public bool EnableStreamSharing { get; set; }
+
         public string UniqueId { get; }
 
         public string TunerHostId { get; }
@@ -220,11 +223,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             }
             catch (IOException)
             {
-
             }
             catch (ArgumentException)
             {
-
             }
             catch (Exception ex)
             {

+ 0 - 1
Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -127,7 +127,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         {
             using (var stream = await new M3uParser(Logger, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
             {
-
             }
         }
 

+ 1 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs

@@ -210,7 +210,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                         }
                     }
                 }
-
             }
 
             if (!IsValidChannelNumber(numberString))
@@ -284,7 +283,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
                     if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out var number))
                     {
-                        //channel.Number = number.ToString();
+                        // channel.Number = number.ToString();
                         nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' });
                     }
                 }

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff