Преглед на файлове

Merge pull request #2425 from MediaBrowser/beta

Beta
Luke преди 8 години
родител
ревизия
1cd5e20871
променени са 100 файла, в които са добавени 1281 реда и са изтрити 618 реда
  1. 0 6
      Emby.Common.Implementations/BaseApplicationHost.cs
  2. 25 17
      Emby.Common.Implementations/IO/ManagedFileSystem.cs
  3. 3 3
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  4. 17 5
      Emby.Dlna/Didl/DidlBuilder.cs
  5. 1 0
      Emby.Dlna/DlnaManager.cs
  6. 4 1
      Emby.Dlna/Emby.Dlna.csproj
  7. 26 1
      Emby.Dlna/PlayTo/Device.cs
  8. 20 0
      Emby.Dlna/PlayTo/PlayToController.cs
  9. 22 0
      Emby.Dlna/PlayTo/PlayToManager.cs
  10. 2 2
      Emby.Dlna/Profiles/DefaultProfile.cs
  11. 2 17
      Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
  12. 10 2
      Emby.Dlna/Profiles/LgTvProfile.cs
  13. 10 2
      Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
  14. 7 1
      Emby.Dlna/Profiles/PanasonicVieraProfile.cs
  15. 10 2
      Emby.Dlna/Profiles/PopcornHourProfile.cs
  16. 8 2
      Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
  17. 120 0
      Emby.Dlna/Profiles/SharpSmartTvProfile.cs
  18. 8 1
      Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
  19. 7 1
      Emby.Dlna/Profiles/SonyBravia2011Profile.cs
  20. 7 1
      Emby.Dlna/Profiles/SonyBravia2012Profile.cs
  21. 7 1
      Emby.Dlna/Profiles/SonyBravia2013Profile.cs
  22. 7 1
      Emby.Dlna/Profiles/SonyBravia2014Profile.cs
  23. 11 2
      Emby.Dlna/Profiles/SonyPs4Profile.cs
  24. 1 1
      Emby.Dlna/Profiles/WdtvLiveProfile.cs
  25. 7 1
      Emby.Dlna/Profiles/XboxOneProfile.cs
  26. 2 2
      Emby.Dlna/Profiles/Xml/Default.xml
  27. 2 4
      Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
  28. 6 2
      Emby.Dlna/Profiles/Xml/LG Smart TV.xml
  29. 6 2
      Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
  30. 4 1
      Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
  31. 6 2
      Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
  32. 5 2
      Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
  33. 29 0
      Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
  34. 4 1
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
  35. 4 1
      Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
  36. 4 1
      Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
  37. 4 1
      Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
  38. 4 1
      Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
  39. 5 2
      Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
  40. 1 1
      Emby.Dlna/Profiles/Xml/WDTV Live.xml
  41. 4 1
      Emby.Dlna/Profiles/Xml/Xbox One.xml
  42. 2 1
      Emby.Dlna/Ssdp/DeviceDiscovery.cs
  43. 8 2
      Emby.Server.Core/ApplicationHost.cs
  44. 35 4
      Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs
  45. 8 1
      Emby.Server.Core/IO/LibraryMonitor.cs
  46. 2 6
      Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
  47. 1 1
      Emby.Server.Implementations/Channels/ChannelManager.cs
  48. 23 4
      Emby.Server.Implementations/Connect/ConnectEntryPoint.cs
  49. 7 2
      Emby.Server.Implementations/Connect/ConnectManager.cs
  50. 6 2
      Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
  51. 56 64
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  52. 3 5
      Emby.Server.Implementations/Dto/DtoService.cs
  53. 1 0
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  54. 3 2
      Emby.Server.Implementations/EntryPoints/SystemEvents.cs
  55. 3 2
      Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
  56. 3 2
      Emby.Server.Implementations/EntryPoints/UsageReporter.cs
  57. 168 166
      Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  58. 18 11
      Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs
  59. 4 3
      Emby.Server.Implementations/IO/FileRefresher.cs
  60. 14 4
      Emby.Server.Implementations/Intros/DefaultIntroProvider.cs
  61. 11 30
      Emby.Server.Implementations/Library/LibraryManager.cs
  62. 2 0
      Emby.Server.Implementations/Library/MediaSourceManager.cs
  63. 1 2
      Emby.Server.Implementations/Library/MusicManager.cs
  64. 7 5
      Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
  65. 2 2
      Emby.Server.Implementations/Library/SearchEngine.cs
  66. 47 13
      Emby.Server.Implementations/Library/UserManager.cs
  67. 1 9
      Emby.Server.Implementations/Library/UserViewManager.cs
  68. 8 7
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  69. 37 2
      Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
  70. 1 4
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  71. 1 9
      Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
  72. 2 10
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  73. 1 9
      Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
  74. 0 1
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs
  75. 5 5
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  76. 0 1
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
  77. 11 3
      Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  78. 60 25
      Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
  79. 1 0
      Emby.Server.Implementations/Localization/iso6392.txt
  80. 47 0
      Emby.Server.Implementations/Migrations/GuideMigration.cs
  81. 0 2
      Emby.Server.Implementations/Migrations/LibraryScanMigration.cs
  82. 17 0
      Emby.Server.Implementations/Notifications/Notifications.cs
  83. 1 1
      Emby.Server.Implementations/Playlists/PlaylistManager.cs
  84. 1 1
      Emby.Server.Implementations/Sync/SyncHelper.cs
  85. 11 9
      Emby.Server.Implementations/Sync/SyncJobProcessor.cs
  86. 7 1
      Emby.Server.Implementations/Sync/SyncManager.cs
  87. 26 6
      Emby.Server.Implementations/Sync/TargetDataProvider.cs
  88. 3 1
      Emby.Server.Implementations/UserViews/DynamicImageProvider.cs
  89. 1 1
      MediaBrowser.Api/Library/LibraryService.cs
  90. 59 6
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  91. 26 10
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  92. 37 31
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  93. 15 4
      MediaBrowser.Api/Playback/MediaInfoService.cs
  94. 12 6
      MediaBrowser.Api/Playback/StreamState.cs
  95. 5 1
      MediaBrowser.Api/Reports/ReportsService.cs
  96. 4 0
      MediaBrowser.Api/Session/SessionsService.cs
  97. 5 1
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  98. 1 8
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  99. 0 6
      MediaBrowser.Common/IApplicationHost.cs
  100. 8 10
      MediaBrowser.Controller/Channels/ChannelMediaInfo.cs

+ 0 - 6
Emby.Common.Implementations/BaseApplicationHost.cs

@@ -161,12 +161,6 @@ namespace Emby.Common.Implementations
         /// <value>The name.</value>
         public abstract string Name { get; }
 
-        /// <summary>
-        /// Gets a value indicating whether this instance is running as service.
-        /// </summary>
-        /// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value>
-        public abstract bool IsRunningAsService { get; }
-
         protected ICryptoProvider CryptographyProvider = new CryptographyProvider();
 
         protected IEnvironmentInfo EnvironmentInfo { get; private set; }

+ 25 - 17
Emby.Common.Implementations/IO/ManagedFileSystem.cs

@@ -20,10 +20,13 @@ namespace Emby.Common.Implementations.IO
         private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
         private bool EnableFileSystemRequestConcat = true;
 
-        public ManagedFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, bool enableFileSystemRequestConcat)
+        private string _tempPath;
+
+        public ManagedFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, bool enableFileSystemRequestConcat, string tempPath)
         {
             Logger = logger;
             _supportsAsyncFileStreams = supportsAsyncFileStreams;
+            _tempPath = tempPath;
             EnableFileSystemRequestConcat = enableFileSystemRequestConcat;
             SetInvalidFileNameChars(enableManagedInvalidFileNameChars);
         }
@@ -487,18 +490,37 @@ namespace Emby.Common.Implementations.IO
                 throw new ArgumentNullException("file2");
             }
 
-            var temp1 = Path.GetTempFileName();
+            var temp1 = Path.Combine(_tempPath, Guid.NewGuid().ToString("N"));
 
             // Copying over will fail against hidden files
             SetHidden(file1, false);
             SetHidden(file2, false);
 
+            Directory.CreateDirectory(_tempPath);
             CopyFile(file1, temp1, true);
 
             CopyFile(file2, file1, true);
             CopyFile(temp1, file2, true);
         }
 
+        public bool AreEqual(string path1, string path2)
+        {
+            if (path1 == null && path2 == null)
+            {
+                return true;
+            }
+
+            if (path1 == null || path2 == null)
+            {
+                return false;
+            }
+
+            path1 = path1.TrimEnd(DirectorySeparatorChar);
+            path2 = path2.TrimEnd(DirectorySeparatorChar);
+
+            return string.Equals(path1, path2, StringComparison.OrdinalIgnoreCase);
+        }
+
         public bool ContainsSubPath(string parentPath, string path)
         {
             if (string.IsNullOrEmpty(parentPath))
@@ -656,21 +678,7 @@ namespace Emby.Common.Implementations.IO
 
         private IEnumerable<FileSystemMetadata> ToMetadata(string parentPath, IEnumerable<FileSystemInfo> infos)
         {
-            return infos.Select(i =>
-            {
-                try
-                {
-                    return GetFileSystemMetadata(i);
-                }
-                catch (PathTooLongException)
-                {
-                    // Can't log using the FullName because it will throw the PathTooLongExceptiona again
-                    //Logger.Warn("Path too long: {0}", i.FullName);
-                    Logger.Warn("File or directory path too long. Parent folder: {0}", parentPath);
-                    return null;
-                }
-
-            }).Where(i => i != null);
+            return infos.Select(GetFileSystemMetadata);
         }
 
         public string[] ReadAllLines(string path)

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

@@ -260,7 +260,7 @@ namespace Emby.Dlna.ContentDirectory
                 {
                     totalCount = 1;
 
-                    if (item.IsFolder || serverItem.StubType.HasValue)
+                    if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
                     {
                         var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requestedCount).ConfigureAwait(false));
 
@@ -285,7 +285,7 @@ namespace Emby.Dlna.ContentDirectory
                         var childItem = i.Item;
                         var displayStubType = i.StubType;
 
-                        if (childItem.IsFolder || displayStubType.HasValue)
+                        if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
                         {
                             var childCount = (await GetUserItems(childItem, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false))
                                 .TotalRecordCount;
@@ -381,7 +381,7 @@ namespace Emby.Dlna.ContentDirectory
 
                 foreach (var i in childrenResult.Items)
                 {
-                    if (i.IsFolder)
+                    if (i.IsDisplayedAsFolder)
                     {
                         var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false))
                             .TotalRecordCount;

+ 17 - 5
Emby.Dlna/Didl/DidlBuilder.cs

@@ -176,6 +176,18 @@ namespace Emby.Dlna.Didl
             return new NullLogger();
         }
 
+        private string GetMimeType(string input)
+        {
+            var mime = MimeTypes.GetMimeType(input);
+
+            if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase))
+            {
+                mime = "video/mpeg";
+            }
+
+            return mime;
+        }
+
         private void AddVideoResource(DlnaOptions options, XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null)
         {
             if (streamInfo == null)
@@ -360,7 +372,7 @@ namespace Emby.Dlna.Didl
             var filename = url.Substring(0, url.IndexOf('?'));
 
             var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
-               ? MimeTypes.GetMimeType(filename)
+               ? GetMimeType(filename)
                : mediaProfile.MimeType;
 
             writer.WriteAttributeString("protocolInfo", String.Format(
@@ -481,7 +493,7 @@ namespace Emby.Dlna.Didl
             var filename = url.Substring(0, url.IndexOf('?'));
 
             var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType)
-                ? MimeTypes.GetMimeType(filename)
+                ? GetMimeType(filename)
                 : mediaProfile.MimeType;
 
             var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
@@ -674,7 +686,7 @@ namespace Emby.Dlna.Didl
 
             writer.WriteStartElement("upnp", "class", NS_UPNP);
 
-            if (item.IsFolder || stubType.HasValue)
+            if (item.IsDisplayedAsFolder || stubType.HasValue)
             {
                 string classType = null;
 
@@ -760,7 +772,7 @@ namespace Emby.Dlna.Didl
 
             // Seeing some LG models locking up due content with large lists of people
             // The actual issue might just be due to processing a more metadata than it can handle
-            var limit = 10;
+            var limit = 6;
 
             foreach (var actor in people)
             {
@@ -1007,7 +1019,7 @@ namespace Emby.Dlna.Didl
 
             writer.WriteAttributeString("protocolInfo", String.Format(
                 "http-get:*:{0}:{1}",
-                MimeTypes.GetMimeType("file." + format),
+                GetMimeType("file." + format),
                 contentFeatures
                 ));
 

+ 1 - 0
Emby.Dlna/DlnaManager.cs

@@ -595,6 +595,7 @@ namespace Emby.Dlna
                 new LinksysDMA2100Profile(),
                 new LgTvProfile(),
                 new Foobar2000Profile(),
+                new SharpSmartTvProfile(),
                 new MediaMonkeyProfile(),
                 //new Windows81Profile(),
                 //new WindowsMediaCenterProfile(),

+ 4 - 1
Emby.Dlna/Emby.Dlna.csproj

@@ -92,6 +92,7 @@
     <Compile Include="Profiles\PanasonicVieraProfile.cs" />
     <Compile Include="Profiles\PopcornHourProfile.cs" />
     <Compile Include="Profiles\SamsungSmartTvProfile.cs" />
+    <Compile Include="Profiles\SharpSmartTvProfile.cs" />
     <Compile Include="Profiles\SonyBlurayPlayer2013.cs" />
     <Compile Include="Profiles\SonyBlurayPlayer2014.cs" />
     <Compile Include="Profiles\SonyBlurayPlayer2015.cs" />
@@ -148,7 +149,9 @@
       <Name>RSSDP</Name>
     </ProjectReference>
   </ItemGroup>
-  <ItemGroup />
+  <ItemGroup>
+    <EmbeddedResource Include="Profiles\Xml\Sharp Smart TV.xml" />
+  </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Profiles\Xml\BubbleUPnp.xml" />
     <EmbeddedResource Include="Profiles\Xml\Default.xml" />

+ 26 - 1
Emby.Dlna/PlayTo/Device.cs

@@ -112,7 +112,7 @@ namespace Emby.Dlna.PlayTo
 
         private int GetInactiveTimerIntervalMs()
         {
-            return 20000;
+            return 30000;
         }
 
         public void Start()
@@ -440,6 +440,11 @@ namespace Emby.Dlna.PlayTo
             {
                 var transportState = await GetTransportInfo().ConfigureAwait(false);
 
+                if (_disposed)
+                {
+                    return;
+                }
+
                 DateLastActivity = DateTime.UtcNow;
 
                 if (transportState.HasValue)
@@ -530,6 +535,11 @@ namespace Emby.Dlna.PlayTo
 
         private async Task GetVolume()
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
             if (command == null)
                 return;
@@ -563,6 +573,11 @@ namespace Emby.Dlna.PlayTo
 
         private async Task GetMute()
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
             if (command == null)
                 return;
@@ -793,6 +808,11 @@ namespace Emby.Dlna.PlayTo
 
         private async Task GetAVProtocolAsync()
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             var avService = GetAvTransportService();
             if (avService == null)
                 return;
@@ -807,6 +827,11 @@ namespace Emby.Dlna.PlayTo
 
         private async Task GetRenderingProtocolAsync()
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             var avService = GetServiceRenderingControl();
 
             if (avService == null)

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

@@ -149,6 +149,11 @@ namespace Emby.Dlna.PlayTo
 
         async void _device_MediaChanged(object sender, MediaChangedEventArgs e)
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             try
             {
                 var streamInfo = await StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);
@@ -176,6 +181,11 @@ namespace Emby.Dlna.PlayTo
 
         async void _device_PlaybackStopped(object sender, PlaybackStoppedEventArgs e)
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             try
             {
                 var streamInfo = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager)
@@ -239,6 +249,11 @@ namespace Emby.Dlna.PlayTo
 
         async void _device_PlaybackStart(object sender, PlaybackStartEventArgs e)
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             try
             {
                 var info = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);
@@ -258,6 +273,11 @@ namespace Emby.Dlna.PlayTo
 
         async void _device_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             try
             {
                 var info = await StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager).ConfigureAwait(false);

+ 22 - 0
Emby.Dlna/PlayTo/PlayToManager.cs

@@ -43,6 +43,7 @@ namespace Emby.Dlna.PlayTo
 
         private readonly List<string> _nonRendererUrls = new List<string>();
         private DateTime _lastRendererClear;
+        private bool _disposed;
 
         public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, IServerConfigurationManager config, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ITimerFactory timerFactory)
         {
@@ -70,6 +71,11 @@ namespace Emby.Dlna.PlayTo
 
         async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             var info = e.Argument;
 
             string usn;
@@ -121,6 +127,11 @@ namespace Emby.Dlna.PlayTo
                     }
                 }
 
+                if (_disposed)
+                {
+                    return;
+                }
+
                 _logger.Debug("Logging session activity from location {0}", location);
                 var sessionInfo = await _sessionManager.LogSessionActivity(device.Properties.ClientType, _appHost.ApplicationVersion.ToString(), device.Properties.UUID, device.Properties.Name, uri.OriginalString, null)
                     .ConfigureAwait(false);
@@ -129,6 +140,11 @@ namespace Emby.Dlna.PlayTo
 
                 if (controller == null)
                 {
+                    if (_disposed)
+                    {
+                        return;
+                    }
+
                     string serverAddress;
                     if (info.LocalIpAddress == null)
                     {
@@ -187,6 +203,11 @@ namespace Emby.Dlna.PlayTo
             catch (Exception ex)
             {
                 _logger.ErrorException("Error creating PlayTo device.", ex);
+
+                lock (_nonRendererUrls)
+                {
+                    _nonRendererUrls.Add(location);
+                }
             }
         }
 
@@ -202,6 +223,7 @@ namespace Emby.Dlna.PlayTo
 
         public void Dispose()
         {
+            _disposed = true;
             _deviceDiscovery.DeviceDiscovered -= _deviceDiscovery_DeviceDiscovered;
         }
     }

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

@@ -64,7 +64,7 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "m4v,ts,mkv,avi,mpg,mpeg,mp4",
+                    Container = "m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov",
                     VideoCodec = "h264",
                     AudioCodec = "aac,mp3,ac3",
                     Type = DlnaProfileType.Video
@@ -72,7 +72,7 @@ namespace Emby.Dlna.Profiles
 
                 new DirectPlayProfile
                 {
-                    Container = "mp3,wma,aac,wav",
+                    Container = "mp3,wma,aac,wav,flac",
                     Type = DlnaProfileType.Audio
                 }
             };

+ 2 - 17
Emby.Dlna/Profiles/DishHopperJoeyProfile.cs

@@ -23,7 +23,7 @@ namespace Emby.Dlna.Profiles
                     {
                          Match = HeaderMatchType.Substring,
                          Name = "User-Agent",
-                         Value ="XiP"
+                         Value ="Zip_"
                     }
                 }
             };
@@ -63,22 +63,7 @@ namespace Emby.Dlna.Profiles
 
                 new DirectPlayProfile
                 {
-                    Container = "mp3",
-                    AudioCodec = "mp3",
-                    Type = DlnaProfileType.Audio
-                },
-
-                new DirectPlayProfile
-                {
-                    Container = "alac",
-                    AudioCodec = "alac",
-                    Type = DlnaProfileType.Audio
-                },
-
-                new DirectPlayProfile
-                {
-                    Container = "flac",
-                    AudioCodec = "flac",
+                    Container = "mp3,alac,flac",
                     Type = DlnaProfileType.Audio
                 },
 

+ 10 - 2
Emby.Dlna/Profiles/LgTvProfile.cs

@@ -67,7 +67,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "mp4",
+                    Container = "mp4,m4v",
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "aac,ac3,mp3,dca,dts",
                     Type = DlnaProfileType.Video
@@ -203,7 +203,15 @@ namespace Emby.Dlna.Profiles
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = new ResponseProfile[]
+            {
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
+                }
+            };
         }
     }
 }

+ 10 - 2
Emby.Dlna/Profiles/LinksysDMA2100Profile.cs

@@ -26,12 +26,20 @@ namespace Emby.Dlna.Profiles
 
                 new DirectPlayProfile
                 {
-                    Container = "avi,mp4,mkv,ts",
+                    Container = "avi,mp4,mkv,ts,m4v",
                     Type = DlnaProfileType.Video
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = new ResponseProfile[]
+            {
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
+                }
+            };
         }
     }
 }

+ 7 - 1
Emby.Dlna/Profiles/PanasonicVieraProfile.cs

@@ -80,7 +80,7 @@ namespace Emby.Dlna.Profiles
 
                new DirectPlayProfile
                {
-                   Container = "mp4",
+                   Container = "mp4,m4v",
                    VideoCodec = "h264",
                    AudioCodec = "aac,ac3,mp3,pcm",
                    Type = DlnaProfileType.Video
@@ -208,6 +208,12 @@ namespace Emby.Dlna.Profiles
                     Container = "ts",
                     OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
                     MimeType = "video/vnd.dlna.mpeg-tts"
+                },
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
                 }
             };
         }

+ 10 - 2
Emby.Dlna/Profiles/PopcornHourProfile.cs

@@ -38,7 +38,7 @@ namespace Emby.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "mp4,mov",
+                    Container = "mp4,mov,m4v",
                     Type = DlnaProfileType.Video,
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "aac"
@@ -201,7 +201,15 @@ namespace Emby.Dlna.Profiles
                 }
             };
 
-            ResponseProfiles = new ResponseProfile[] { };
+            ResponseProfiles = new ResponseProfile[]
+            {
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
+                }
+            };
         }
     }
 }

+ 8 - 2
Emby.Dlna/Profiles/SamsungSmartTvProfile.cs

@@ -43,7 +43,7 @@ namespace Emby.Dlna.Profiles
                    AudioCodec = "ac3",
                    VideoCodec = "h264",
                    Type = DlnaProfileType.Video,
-                   EstimateContentLength = true
+                   EstimateContentLength = false
                },
                new TranscodingProfile
                {
@@ -77,7 +77,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "mp4",
+                    Container = "mp4,m4v",
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "mp3,aac",
                     Type = DlnaProfileType.Video
@@ -335,6 +335,12 @@ namespace Emby.Dlna.Profiles
                     Container = "flac",
                     MimeType = "audio/x-flac",
                     Type = DlnaProfileType.Audio
+                },
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
                 }
             };
 

+ 120 - 0
Emby.Dlna/Profiles/SharpSmartTvProfile.cs

@@ -0,0 +1,120 @@
+using System.Xml.Serialization;
+using MediaBrowser.Model.Dlna;
+
+namespace Emby.Dlna.Profiles
+{
+    [XmlRoot("Profile")]
+    public class SharpSmartTvProfile : DefaultProfile
+    {
+        public SharpSmartTvProfile()
+        {
+            Name = "Sharp Smart TV";
+
+            RequiresPlainFolders = true;
+            RequiresPlainVideoItems = true;
+
+            Identification = new DeviceIdentification
+            {
+                Manufacturer = "Sharp",
+
+                Headers = new[]
+               {
+                   new HttpHeaderInfo
+                   {
+                       Name = "User-Agent",
+                       Value = "Sharp",
+                       Match = HeaderMatchType.Substring
+                   }
+               }
+            };
+
+            TranscodingProfiles = new[]
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    Type = DlnaProfileType.Video,
+                    AudioCodec = "ac3,aac,mp3,dts,dca",
+                    VideoCodec = "h264",
+                    EnableMpegtsM2TsMode = true
+                },
+
+                new TranscodingProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
+
+            DirectPlayProfiles = new[]
+            {
+                new DirectPlayProfile
+                {
+                    Container = "m4v,mkv,avi,mov,mp4",
+                    VideoCodec = "h264,mpeg4",
+                    AudioCodec = "aac,mp3,ac3,dts,dca",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "asf,wmv",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mpg,mpeg",
+                    VideoCodec = "mpeg2video",
+                    AudioCodec = "mp3,aac",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "flv",
+                    VideoCodec = "h264",
+                    AudioCodec = "mp3,aac",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mp3,wav",
+                    Type = DlnaProfileType.Audio
+                }
+            };
+
+            SubtitleProfiles = new[]
+            {
+                new SubtitleProfile
+                {
+                    Format = "srt",
+                    Method = SubtitleDeliveryMethod.Embed
+                },
+                new SubtitleProfile
+                {
+                    Format = "srt",
+                    Method = SubtitleDeliveryMethod.External
+                }
+            };
+
+            ResponseProfiles = new[]
+            {
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
+                }
+            };
+        }
+    }
+}

+ 8 - 1
Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs

@@ -83,7 +83,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "avi,mp4",
+                    Container = "avi,mp4,m4v",
                     VideoCodec = "mpeg4,h264",
                     AudioCodec = "ac3,aac,mp3,pcm",
                     Type = DlnaProfileType.Video
@@ -248,6 +248,13 @@ namespace Emby.Dlna.Profiles
                     Type = DlnaProfileType.Video
                 },
 
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    MimeType = "video/mpeg",
+                    Type = DlnaProfileType.Video
+                },
+
                 new ResponseProfile
                 {
                     Container = "mpeg",

+ 7 - 1
Emby.Dlna/Profiles/SonyBravia2011Profile.cs

@@ -80,7 +80,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "mp4",
+                    Container = "mp4,m4v",
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "ac3,aac,mp3",
                     Type = DlnaProfileType.Video
@@ -211,6 +211,12 @@ namespace Emby.Dlna.Profiles
                     MimeType = "video/mpeg",
                     OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
                     Type = DlnaProfileType.Video
+                },
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
                 }
             };
 

+ 7 - 1
Emby.Dlna/Profiles/SonyBravia2012Profile.cs

@@ -80,7 +80,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "mp4",
+                    Container = "mp4,m4v",
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "ac3,aac,mp3,mp2",
                     Type = DlnaProfileType.Video
@@ -199,6 +199,12 @@ namespace Emby.Dlna.Profiles
                     MimeType = "video/mpeg",
                     OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
                     Type = DlnaProfileType.Video
+                },
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
                 }
             };
 

+ 7 - 1
Emby.Dlna/Profiles/SonyBravia2013Profile.cs

@@ -79,7 +79,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "mp4",
+                    Container = "mp4,m4v",
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "ac3,eac3,aac,mp3,mp2",
                     Type = DlnaProfileType.Video
@@ -255,6 +255,12 @@ namespace Emby.Dlna.Profiles
                     MimeType = "video/mpeg",
                     OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
                     Type = DlnaProfileType.Video
+                },
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
                 }
             };
 

+ 7 - 1
Emby.Dlna/Profiles/SonyBravia2014Profile.cs

@@ -79,7 +79,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "mp4",
+                    Container = "mp4,m4v",
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "ac3,eac3,aac,mp3,mp2",
                     Type = DlnaProfileType.Video
@@ -255,6 +255,12 @@ namespace Emby.Dlna.Profiles
                     MimeType = "video/mpeg",
                     OrgPn="MPEG_PS_NTSC,MPEG_PS_PAL",
                     Type = DlnaProfileType.Video
+                },
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
                 }
             };
 

+ 11 - 2
Emby.Dlna/Profiles/SonyPs4Profile.cs

@@ -63,7 +63,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "mp4,mkv",
+                    Container = "mp4,mkv,m4v",
                     Type = DlnaProfileType.Video,
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "aac,ac3"
@@ -86,7 +86,9 @@ namespace Emby.Dlna.Profiles
                 {
                     Container = "mp3",
                     AudioCodec = "mp3",
-                    Type = DlnaProfileType.Audio
+                    Type = DlnaProfileType.Audio,
+                    // Transcoded audio won't be playable at all without this
+                    TranscodeSeekInfo = TranscodeSeekInfo.Bytes
                 },
                 new TranscodingProfile
                 {
@@ -253,6 +255,13 @@ namespace Emby.Dlna.Profiles
                     Container = "wav",
                     MimeType = "audio/wav",
                     Type = DlnaProfileType.Audio
+                },
+
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
                 }
             };
         }

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

@@ -87,7 +87,7 @@ namespace Emby.Dlna.Profiles
 
                 new DirectPlayProfile
                 {
-                    Container = "mp4,mov",
+                    Container = "mp4,mov,m4v",
                     Type = DlnaProfileType.Video,
                     VideoCodec = "h264,mpeg4",
                     AudioCodec = "ac3,aac,mp2,mp3,dca,dts"

+ 7 - 1
Emby.Dlna/Profiles/XboxOneProfile.cs

@@ -80,7 +80,7 @@ namespace Emby.Dlna.Profiles
                 },
                 new DirectPlayProfile
                 {
-                    Container = "mp4,mov,mkv",
+                    Container = "mp4,mov,mkv,m4v",
                     VideoCodec = "h264,mpeg4,mpeg2video",
                     AudioCodec = "aac,ac3",
                     Type = DlnaProfileType.Video
@@ -349,6 +349,12 @@ namespace Emby.Dlna.Profiles
                     Container = "avi",
                     MimeType = "video/avi",
                     Type = DlnaProfileType.Video
+                },
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
                 }
             };
         }

+ 2 - 2
Emby.Dlna/Profiles/Xml/Default.xml

@@ -29,8 +29,8 @@
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
   <XmlRootAttributes />
   <DirectPlayProfiles>
-    <DirectPlayProfile container="m4v,ts,mkv,avi,mpg,mpeg,mp4" audioCodec="aac,mp3,ac3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp3,wma,aac,wav" type="Audio" />
+    <DirectPlayProfile container="m4v,ts,mpegts,mkv,avi,mpg,mpeg,mp4,mov" audioCodec="aac,mp3,ac3" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="mp3,wma,aac,wav,flac" type="Audio" />
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />

+ 2 - 4
Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml

@@ -5,7 +5,7 @@
     <Manufacturer>Echostar Technologies LLC</Manufacturer>
     <ManufacturerUrl>http://www.echostar.com</ManufacturerUrl>
     <Headers>
-      <HttpHeaderInfo name="User-Agent" value="XiP" match="Substring" />
+      <HttpHeaderInfo name="User-Agent" value="Zip_" match="Substring" />
     </Headers>
   </Identification>
   <Manufacturer>Emby</Manufacturer>
@@ -37,9 +37,7 @@
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp4,mkv,mpeg,ts" audioCodec="mp3,ac3,aac,he-aac,pcm" videoCodec="h264,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
-    <DirectPlayProfile container="alac" audioCodec="alac" type="Audio" />
-    <DirectPlayProfile container="flac" audioCodec="flac" type="Audio" />
+    <DirectPlayProfile container="mp3,alac,flac" type="Audio" />
     <DirectPlayProfile container="jpeg" type="Photo" />
   </DirectPlayProfiles>
   <TranscodingProfiles>

+ 6 - 2
Emby.Dlna/Profiles/Xml/LG Smart TV.xml

@@ -37,7 +37,7 @@
   <DirectPlayProfiles>
     <DirectPlayProfile container="ts" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp4" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mp3" type="Audio" />
     <DirectPlayProfile container="jpeg" type="Photo" />
   </DirectPlayProfiles>
@@ -79,7 +79,11 @@
       <ApplyConditions />
     </CodecProfile>
   </CodecProfiles>
-  <ResponseProfiles />
+  <ResponseProfiles>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
+  </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />
     <SubtitleProfile format="srt" method="External" />

+ 6 - 2
Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml

@@ -34,7 +34,7 @@
   <XmlRootAttributes />
   <DirectPlayProfiles>
     <DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />
-    <DirectPlayProfile container="avi,mp4,mkv,ts" type="Video" />
+    <DirectPlayProfile container="avi,mp4,mkv,ts,m4v" type="Video" />
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
@@ -43,7 +43,11 @@
   </TranscodingProfiles>
   <ContainerProfiles />
   <CodecProfiles />
-  <ResponseProfiles />
+  <ResponseProfiles>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
+  </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />
   </SubtitleProfiles>

+ 4 - 1
Emby.Dlna/Profiles/Xml/Panasonic Viera.xml

@@ -41,7 +41,7 @@
     <DirectPlayProfile container="mpeg,mpg" audioCodec="ac3,mp3,pcm_dvd" videoCodec="mpeg2video,mpeg4" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="aac,ac3,dca,mp3,mp2,pcm,dts" videoCodec="h264,mpeg2video" type="Video" />
     <DirectPlayProfile container="ts" audioCodec="aac,mp3,mp2" videoCodec="h264,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4" audioCodec="aac,ac3,mp3,pcm" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,mp3,pcm" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="mov" audioCodec="aac,pcm" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="pcm" videoCodec="mpeg4" type="Video" />
     <DirectPlayProfile container="flv" audioCodec="aac" videoCodec="h264" type="Video" />
@@ -76,6 +76,9 @@
     <ResponseProfile container="ts" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
       <Conditions />
     </ResponseProfile>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
   </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />

+ 6 - 2
Emby.Dlna/Profiles/Xml/Popcorn Hour.xml

@@ -29,7 +29,7 @@
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
   <XmlRootAttributes />
   <DirectPlayProfiles>
-    <DirectPlayProfile container="mp4,mov" audioCodec="aac" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,mov,m4v" audioCodec="aac" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="ts" audioCodec="aac,ac3,eac3,mp3,mp2,pcm" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="asf,wmv" audioCodec="wmav2,wmapro" videoCodec="wmv3,vc1" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="mp3,ac3,eac3,mp2,pcm" videoCodec="mpeg4,msmpeg4" type="Video" />
@@ -81,7 +81,11 @@
       <ApplyConditions />
     </CodecProfile>
   </CodecProfiles>
-  <ResponseProfiles />
+  <ResponseProfiles>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
+  </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />
   </SubtitleProfiles>

+ 5 - 2
Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml

@@ -40,7 +40,7 @@
     <DirectPlayProfile container="asf" audioCodec="mp3,ac3,wmav2,wmapro,wmavoice" videoCodec="h264,mpeg4,mjpeg" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="mp3,ac3,dca,dts" videoCodec="h264,mpeg4,mjpeg" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="mp3,ac3,dca,aac,dts" videoCodec="h264,mpeg4,mjpeg4" type="Video" />
-    <DirectPlayProfile container="mp4" audioCodec="mp3,aac" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,m4v" audioCodec="mp3,aac" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="3gp" audioCodec="aac,he-aac" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mpg,mpeg" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
     <DirectPlayProfile container="vro,vob" audioCodec="ac3,mp2,mp3" videoCodec="mpeg1video,mpeg2video" type="Video" />
@@ -51,7 +51,7 @@
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="true" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
     <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
   </TranscodingProfiles>
   <ContainerProfiles>
@@ -117,6 +117,9 @@
     <ResponseProfile container="flac" type="Audio" mimeType="audio/x-flac">
       <Conditions />
     </ResponseProfile>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
   </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />

Файловите разлики са ограничени, защото са твърде много
+ 29 - 0
Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml


+ 4 - 1
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml

@@ -41,7 +41,7 @@
   <DirectPlayProfiles>
     <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="ac3,mp3,pcm" videoCodec="mpeg1video,mpeg2video" type="Video" />
-    <DirectPlayProfile container="avi,mp4" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" />
+    <DirectPlayProfile container="avi,mp4,m4v" audioCodec="ac3,aac,mp3,pcm" videoCodec="mpeg4,h264" type="Video" />
     <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
     <DirectPlayProfile container="jpeg" type="Photo" />
@@ -99,6 +99,9 @@
     <ResponseProfile container="mp4" type="Video" mimeType="video/mpeg">
       <Conditions />
     </ResponseProfile>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mpeg">
+      <Conditions />
+    </ResponseProfile>
     <ResponseProfile container="mpeg" type="Video" mimeType="video/mpeg">
       <Conditions />
     </ResponseProfile>

+ 4 - 1
Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml

@@ -41,7 +41,7 @@
   <DirectPlayProfiles>
     <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="ts" audioCodec="mp3" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="mp3" videoCodec="mpeg2video,mpeg1video" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" />
     <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
@@ -129,6 +129,9 @@
     <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
       <Conditions />
     </ResponseProfile>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
   </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />

+ 4 - 1
Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml

@@ -41,7 +41,7 @@
   <DirectPlayProfiles>
     <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4" audioCodec="ac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="mp3,mp2" videoCodec="mpeg2video,mpeg1video" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" />
@@ -105,6 +105,9 @@
     <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
       <Conditions />
     </ResponseProfile>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
   </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />

+ 4 - 1
Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml

@@ -41,7 +41,7 @@
   <DirectPlayProfiles>
     <DirectPlayProfile container="ts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mov" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4,mjpeg" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,aac,mp3,mp2,pcm,vorbis" videoCodec="h264,mpeg4,vp8" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="ac3,eac3,mp3" videoCodec="mpeg4" type="Video" />
@@ -104,6 +104,9 @@
     <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
       <Conditions />
     </ResponseProfile>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
   </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />

+ 4 - 1
Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml

@@ -41,7 +41,7 @@
   <DirectPlayProfiles>
     <DirectPlayProfile container="ts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" />
     <DirectPlayProfile container="ts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mov" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4,mjpeg" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,aac,mp3,mp2,pcm,vorbis" videoCodec="h264,mpeg4,vp8" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="ac3,eac3,mp3" videoCodec="mpeg4" type="Video" />
@@ -104,6 +104,9 @@
     <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
       <Conditions />
     </ResponseProfile>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
   </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />

+ 5 - 2
Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml

@@ -40,12 +40,12 @@
     <DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" />
     <DirectPlayProfile container="ts" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
     <DirectPlayProfile container="mpeg" audioCodec="mp2" videoCodec="mpeg1video,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4,mkv" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="aac,mp3,wav" type="Audio" />
     <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" />
   </DirectPlayProfiles>
   <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
+    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Bytes" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
     <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
     <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" />
   </TranscodingProfiles>
@@ -98,6 +98,9 @@
     <ResponseProfile container="wav" type="Audio" mimeType="audio/wav">
       <Conditions />
     </ResponseProfile>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
   </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />

+ 1 - 1
Emby.Dlna/Profiles/Xml/WDTV Live.xml

@@ -40,7 +40,7 @@
     <DirectPlayProfile container="mpeg" audioCodec="ac3,dca,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video" type="Video" />
     <DirectPlayProfile container="mkv" audioCodec="ac3,dca,aac,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
     <DirectPlayProfile container="ts,m2ts" audioCodec="ac3,dca,mp2,mp3,aac,dts" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" />
-    <DirectPlayProfile container="mp4,mov" audioCodec="ac3,aac,mp2,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="mp4,mov,m4v" audioCodec="ac3,aac,mp2,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="vc1" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="mp2,ac3" videoCodec="mpeg2video" type="Video" />
     <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" />

+ 4 - 1
Emby.Dlna/Profiles/Xml/Xbox One.xml

@@ -39,7 +39,7 @@
     <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg2video" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
     <DirectPlayProfile container="avi" audioCodec="aac" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp4,mov,mkv" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video" type="Video" />
+    <DirectPlayProfile container="mp4,mov,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="wmv2,wmv3,vc1" type="Video" />
     <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
     <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
@@ -116,6 +116,9 @@
     <ResponseProfile container="avi" type="Video" mimeType="video/avi">
       <Conditions />
     </ResponseProfile>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
   </ResponseProfiles>
   <SubtitleProfiles>
     <SubtitleProfile format="srt" method="Embed" />

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

@@ -104,7 +104,8 @@ namespace Emby.Dlna.Ssdp
                 Argument = new UpnpDeviceInfo
                 {
                     Location = e.DiscoveredDevice.DescriptionLocation,
-                    Headers = headers
+                    Headers = headers,
+                    LocalIpAddress = e.LocalIpAddress
                 }
             };
 

+ 8 - 2
Emby.Server.Core/ApplicationHost.cs

@@ -326,6 +326,8 @@ namespace Emby.Server.Core
             }
         }
 
+        public abstract bool IsRunningAsService { get; }
+
         private Assembly GetAssembly(Type type)
         {
             return type.GetTypeInfo().Assembly;
@@ -489,7 +491,8 @@ namespace Emby.Server.Core
         {
             var migrations = new List<IVersionMigration>
             {
-                new LibraryScanMigration(ServerConfigurationManager, TaskManager)
+                new LibraryScanMigration(ServerConfigurationManager, TaskManager),
+                new GuideMigration(ServerConfigurationManager, TaskManager)
             };
 
             foreach (var task in migrations)
@@ -1247,7 +1250,6 @@ namespace Emby.Server.Core
                 HasUpdateAvailable = HasUpdateAvailable,
                 SupportsAutoRunAtStartup = SupportsAutoRunAtStartup,
                 TranscodingTempPath = ApplicationPaths.TranscodingTempPath,
-                IsRunningAsService = IsRunningAsService,
                 SupportsRunningAsService = SupportsRunningAsService,
                 ServerName = FriendlyName,
                 LocalAddress = localAddress,
@@ -1476,6 +1478,10 @@ namespace Emby.Server.Core
             try
             {
                 AuthorizeServer();
+            }
+            catch (NotImplementedException)
+            {
+                
             }
             catch (Exception ex)
             {

+ 35 - 4
Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs

@@ -11,6 +11,7 @@ using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Threading;
 using Mono.Nat;
+using System.Threading.Tasks;
 
 namespace Emby.Server.Core.EntryPoints
 {
@@ -106,6 +107,11 @@ namespace Emby.Server.Core.EntryPoints
 
         private async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             var info = e.Argument;
 
             string usn;
@@ -169,6 +175,11 @@ namespace Emby.Server.Core.EntryPoints
                     return;
                 }
 
+                if (_disposed)
+                {
+                    return;
+                }
+
                 _logger.Debug("Calling Nat.Handle on " + identifier);
                 NatUtility.Handle(localAddress, info, endpoint, NatProtocol.Upnp);
             }
@@ -185,6 +196,11 @@ namespace Emby.Server.Core.EntryPoints
 
         void NatUtility_DeviceFound(object sender, DeviceEventArgs e)
         {
+            if (_disposed)
+            {
+                return;
+            }
+
             try
             {
                 var device = e.Device;
@@ -208,8 +224,13 @@ namespace Emby.Server.Core.EntryPoints
 
         private List<string> _createdRules = new List<string>();
         private List<string> _usnsHandled = new List<string>();
-        private void CreateRules(INatDevice device)
+        private async void CreateRules(INatDevice device)
         {
+            if (_disposed)
+            {
+                throw new ObjectDisposedException("PortMapper");
+            }
+
             // On some systems the device discovered event seems to fire repeatedly
             // This check will help ensure we're not trying to port map the same device over and over
 
@@ -219,12 +240,16 @@ namespace Emby.Server.Core.EntryPoints
             {
                 _createdRules.Add(address);
 
-                CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort);
-                CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
+                var success = await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false);
+
+                if (success)
+                {
+                    await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false);
+                }
             }
         }
 
-        private async void CreatePortMap(INatDevice device, int privatePort, int publicPort)
+        private async Task<bool> CreatePortMap(INatDevice device, int privatePort, int publicPort)
         {
             _logger.Debug("Creating port map on port {0}", privatePort);
 
@@ -235,10 +260,14 @@ namespace Emby.Server.Core.EntryPoints
                     Description = _appHost.Name
 
                 }).ConfigureAwait(false);
+
+                return true;
             }
             catch (Exception ex)
             {
                 _logger.Error("Error creating port map: " + ex.Message);
+
+                return false;
             }
         }
 
@@ -249,8 +278,10 @@ namespace Emby.Server.Core.EntryPoints
             _logger.Debug("NAT device lost: {0}", device.LocalAddress.ToString());
         }
 
+        private bool _disposed = false;
         public void Dispose()
         {
+            _disposed = true;
             DisposeNat();
         }
 

+ 8 - 1
Emby.Server.Core/IO/LibraryMonitor.cs

@@ -87,7 +87,7 @@ namespace Emby.Server.Core.IO
         public bool IsPathLocked(string path)
         {
             var lockedPaths = _tempIgnoredPaths.Keys.ToList();
-            return lockedPaths.Any(i => string.Equals(i, path, StringComparison.OrdinalIgnoreCase) || _fileSystem.ContainsSubPath(i, path));
+            return lockedPaths.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path));
         }
 
         public async void ReportFileSystemChangeComplete(string path, bool refreshPath)
@@ -288,6 +288,13 @@ namespace Emby.Server.Core.IO
             {
                 try
                 {
+                    if (!_fileSystem.DirectoryExists(path))
+                    {
+                        // Seeing a crash in the mono runtime due to an exception being thrown on a different thread
+                        Logger.Info("Skipping realtime monitor for {0} because the path does not exist", path);
+                        return;
+                    }
+
                     var newWatcher = new FileSystemWatcher(path, "*")
                     {
                         IncludeSubdirectories = true

+ 2 - 6
Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs

@@ -138,7 +138,7 @@ namespace Emby.Server.Implementations.Activity
 
             CreateLogEntry(new ActivityLogEntry
             {
-                Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, item.Name),
+                Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, Notifications.Notifications.GetItemName(item)),
                 Type = "PlaybackStopped",
                 ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName),
                 UserId = user.Id.ToString("N")
@@ -170,7 +170,7 @@ namespace Emby.Server.Implementations.Activity
 
             CreateLogEntry(new ActivityLogEntry
             {
-                Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, item.Name),
+                Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, Notifications.Notifications.GetItemName(item)),
                 Type = "PlaybackStart",
                 ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName),
                 UserId = user.Id.ToString("N")
@@ -235,10 +235,6 @@ namespace Emby.Server.Implementations.Activity
             });
         }
 
-        void _logManager_LoggerLoaded(object sender, EventArgs e)
-        {
-        }
-
         void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
         {
             CreateLogEntry(new ActivityLogEntry

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

@@ -343,7 +343,7 @@ namespace Emby.Server.Implementations.Channels
 
         private MediaSourceInfo GetMediaSource(BaseItem item, ChannelMediaInfo info)
         {
-            var source = info.ToMediaSource();
+            var source = info.ToMediaSource(item.Id);
 
             source.RunTimeTicks = source.RunTimeTicks ?? item.RunTimeTicks;
 

+ 23 - 4
Emby.Server.Implementations/Connect/ConnectEntryPoint.cs

@@ -9,6 +9,7 @@ using System;
 using System.IO;
 using System.Text;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Security;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Threading;
 
@@ -17,6 +18,7 @@ namespace Emby.Server.Implementations.Connect
     public class ConnectEntryPoint : IServerEntryPoint
     {
         private ITimer _timer;
+        private IpAddressInfo _cachedIpAddress;
         private readonly IHttpClient _httpClient;
         private readonly IApplicationPaths _appPaths;
         private readonly ILogger _logger;
@@ -26,8 +28,9 @@ namespace Emby.Server.Implementations.Connect
         private readonly IApplicationHost _appHost;
         private readonly IFileSystem _fileSystem;
         private readonly ITimerFactory _timerFactory;
+        private readonly IEncryptionManager _encryption;
 
-        public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory)
+        public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory, IEncryptionManager encryption)
         {
             _httpClient = httpClient;
             _appPaths = appPaths;
@@ -37,6 +40,7 @@ namespace Emby.Server.Implementations.Connect
             _appHost = appHost;
             _fileSystem = fileSystem;
             _timerFactory = timerFactory;
+            _encryption = encryption;
         }
 
         public void Run()
@@ -143,17 +147,31 @@ namespace Emby.Server.Implementations.Connect
 
         private string CacheFilePath
         {
-            get { return Path.Combine(_appPaths.DataPath, "wan.txt"); }
+            get { return Path.Combine(_appPaths.DataPath, "wan.dat"); }
         }
 
         private void CacheAddress(IpAddressInfo address)
         {
+            if (_cachedIpAddress != null && _cachedIpAddress.Equals(address))
+            {
+                // no need to update the file if the address has not changed
+                return;
+            }
+
             var path = CacheFilePath;
 
             try
             {
                 _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-                _fileSystem.WriteAllText(path, address.ToString(), Encoding.UTF8);
+            }
+            catch (Exception ex)
+            {
+            }
+
+            try
+            {
+                _fileSystem.WriteAllText(path, _encryption.EncryptString(address.ToString()), Encoding.UTF8);
+                _cachedIpAddress = address;
             }
             catch (Exception ex)
             {
@@ -169,11 +187,12 @@ namespace Emby.Server.Implementations.Connect
 
             try
             {
-                var endpoint = _fileSystem.ReadAllText(path, Encoding.UTF8);
+                var endpoint = _encryption.DecryptString(_fileSystem.ReadAllText(path, Encoding.UTF8));
                 IpAddressInfo ipAddress;
 
                 if (_networkManager.TryParseIpAddress(endpoint, out ipAddress))
                 {
+                    _cachedIpAddress = ipAddress;
                     ((ConnectManager)_connectManager).OnWanAddressResolved(ipAddress);
                 }
             }

+ 7 - 2
Emby.Server.Implementations/Connect/ConnectManager.cs

@@ -925,7 +925,11 @@ namespace Emby.Server.Implementations.Connect
             }
 
             _data.PendingAuthorizations = newPendingList;
-            CacheData();
+
+            if (!newPendingList.Select(i => i.Id).SequenceEqual(currentPendingList.Select(i => i.Id), StringComparer.Ordinal))
+            {
+                CacheData();
+            }
 
             await RefreshGuestNames(list, refreshImages).ConfigureAwait(false);
         }
@@ -1118,7 +1122,7 @@ namespace Emby.Server.Implementations.Connect
             }
         }
 
-        public async Task Authenticate(string username, string passwordMd5)
+        public async Task<ConnectAuthenticationResult> Authenticate(string username, string passwordMd5)
         {
             if (string.IsNullOrWhiteSpace(username))
             {
@@ -1147,6 +1151,7 @@ namespace Emby.Server.Implementations.Connect
             // No need to examine the response
             using (var response = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content)
             {
+                return _json.DeserializeFromStream<ConnectAuthenticationResult>(response);
             }
         }
 

+ 6 - 2
Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs

@@ -137,6 +137,11 @@ namespace Emby.Server.Implementations.Data
             var numComplete = 0;
             var numItems = result.Count;
 
+            var allLibraryPaths = _libraryManager
+                .GetVirtualFolders()
+                .SelectMany(i => i.Locations)
+                .ToList();
+
             foreach (var item in result)
             {
                 cancellationToken.ThrowIfCancellationRequested();
@@ -170,9 +175,8 @@ namespace Emby.Server.Implementations.Data
                         continue;
                     }
 
-                    if (Folder.IsPathOffline(path))
+                    if (Folder.IsPathOffline(path, allLibraryPaths))
                     {
-                        await libraryItem.UpdateIsOffline(true).ConfigureAwait(false);
                         continue;
                     }
 

+ 56 - 64
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -372,7 +372,7 @@ namespace Emby.Server.Implementations.Data
 
             userDataRepo.Initialize(WriteLock, _connection);
 
-            _shrinkMemoryTimer = _timerFactory.Create(OnShrinkMemoryTimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(15));
+            _shrinkMemoryTimer = _timerFactory.Create(OnShrinkMemoryTimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(30));
         }
 
         private void OnShrinkMemoryTimerCallback(object state)
@@ -2384,8 +2384,17 @@ namespace Emby.Server.Implementations.Data
 
                 var excludeIds = query.ExcludeItemIds.ToList();
                 excludeIds.Add(item.Id.ToString("N"));
-                query.ExcludeItemIds = excludeIds.ToArray();
 
+                if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(typeof(Trailer).Name))
+                {
+                    var hasTrailers = item as IHasTrailers;
+                    if (hasTrailers != null)
+                    {
+                        excludeIds.AddRange(hasTrailers.GetTrailerIds().Select(i => i.ToString("N")));
+                    }
+                }
+
+                query.ExcludeItemIds = excludeIds.ToArray();
                 query.ExcludeProviderIds = item.ProviderIds;
             }
 
@@ -2548,57 +2557,53 @@ namespace Emby.Server.Implementations.Data
             {
                 using (var connection = CreateConnection(true))
                 {
-                    return connection.RunInTransaction(db =>
-                    {
-                        var list = new List<BaseItem>();
+                    var list = new List<BaseItem>();
 
-                        using (var statement = PrepareStatementSafe(db, commandText))
+                    using (var statement = PrepareStatementSafe(connection, commandText))
+                    {
+                        if (EnableJoinUserData(query))
                         {
-                            if (EnableJoinUserData(query))
-                            {
-                                statement.TryBind("@UserId", query.User.Id);
-                            }
+                            statement.TryBind("@UserId", query.User.Id);
+                        }
 
-                            BindSimilarParams(query, statement);
+                        BindSimilarParams(query, statement);
 
-                            // Running this again will bind the params
-                            GetWhereClauses(query, statement);
+                        // Running this again will bind the params
+                        GetWhereClauses(query, statement);
 
-                            foreach (var row in statement.ExecuteQuery())
+                        foreach (var row in statement.ExecuteQuery())
+                        {
+                            var item = GetItem(row, query);
+                            if (item != null)
                             {
-                                var item = GetItem(row, query);
-                                if (item != null)
-                                {
-                                    list.Add(item);
-                                }
+                                list.Add(item);
                             }
                         }
+                    }
 
-                        // Hack for right now since we currently don't support filtering out these duplicates within a query
-                        if (query.EnableGroupByMetadataKey)
+                    // Hack for right now since we currently don't support filtering out these duplicates within a query
+                    if (query.EnableGroupByMetadataKey)
+                    {
+                        var limit = query.Limit ?? int.MaxValue;
+                        limit -= 4;
+                        var newList = new List<BaseItem>();
+
+                        foreach (var item in list)
                         {
-                            var limit = query.Limit ?? int.MaxValue;
-                            limit -= 4;
-                            var newList = new List<BaseItem>();
+                            AddItem(newList, item);
 
-                            foreach (var item in list)
+                            if (newList.Count >= limit)
                             {
-                                AddItem(newList, item);
-
-                                if (newList.Count >= limit)
-                                {
-                                    break;
-                                }
+                                break;
                             }
-
-                            list = newList;
                         }
 
-                        LogQueryTime("GetItemList", commandText, now);
+                        list = newList;
+                    }
 
-                        return list;
+                    LogQueryTime("GetItemList", commandText, now);
 
-                    }, ReadTransactionMode);
+                    return list;
                 }
             }
         }
@@ -2652,7 +2657,7 @@ namespace Emby.Server.Implementations.Data
             {
                 //Logger.Debug("{2} query time: {0}ms. Query: {1}",
                 //    Convert.ToInt32(elapsed),
-                //    cmd.CommandText,
+                //    commandText,
                 //    methodName);
             }
         }
@@ -2825,8 +2830,9 @@ namespace Emby.Server.Implementations.Data
             {
                 if (orderBy.Count == 0)
                 {
-                    orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
                     orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
+                    orderBy.Add(new Tuple<string, SortOrder>("SimilarityScore", SortOrder.Descending));
+                    //orderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.Random, SortOrder.Ascending));
                     query.SortOrder = SortOrder.Descending;
                     enableOrderInversion = false;
                 }
@@ -3212,6 +3218,11 @@ namespace Emby.Server.Implementations.Data
 
         private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement, string paramSuffix = "")
         {
+            if (query.IsResumable ?? false)
+            {
+                query.IsVirtualItem = false;
+            }
+
             var whereClauses = new List<string>();
 
             if (EnableJoinUserData(query))
@@ -3372,9 +3383,9 @@ namespace Emby.Server.Implementations.Data
                 }
             }
 
-            if (query.SimilarTo != null)
+            if (query.SimilarTo != null && query.MinSimilarityScore > 0)
             {
-                whereClauses.Add("SimilarityScore > 0");
+                whereClauses.Add("SimilarityScore > " + (query.MinSimilarityScore - 1).ToString(CultureInfo.InvariantCulture));
             }
 
             if (query.IsFolder.HasValue)
@@ -3616,10 +3627,12 @@ namespace Emby.Server.Implementations.Data
                 var index = 0;
                 foreach (var type in query.TrailerTypes)
                 {
-                    clauses.Add("TrailerTypes like @TrailerTypes" + index);
+                    var paramName = "@TrailerTypes" + index;
+
+                    clauses.Add("TrailerTypes like " + paramName);
                     if (statement != null)
                     {
-                        statement.TryBind("@TrailerTypes" + index, "%" + type + "%");
+                        statement.TryBind(paramName, "%" + type + "%");
                     }
                     index++;
                 }
@@ -4085,27 +4098,6 @@ namespace Emby.Server.Implementations.Data
 
                 whereClauses.Add("LocationType in (" + val + ")");
             }
-            if (query.ExcludeLocationTypes.Length == 1)
-            {
-                if (query.ExcludeLocationTypes[0] == LocationType.Virtual && _config.Configuration.SchemaVersion >= 90)
-                {
-                    query.IsVirtualItem = false;
-                }
-                else
-                {
-                    whereClauses.Add("LocationType<>@ExcludeLocationTypes");
-                    if (statement != null)
-                    {
-                        statement.TryBind("@ExcludeLocationTypes", query.ExcludeLocationTypes[0].ToString());
-                    }
-                }
-            }
-            else if (query.ExcludeLocationTypes.Length > 1)
-            {
-                var val = string.Join(",", query.ExcludeLocationTypes.Select(i => "'" + i + "'").ToArray());
-
-                whereClauses.Add("LocationType not in (" + val + ")");
-            }
             if (query.IsVirtualItem.HasValue)
             {
                 whereClauses.Add("IsVirtualItem=@IsVirtualItem");
@@ -4221,7 +4213,7 @@ namespace Emby.Server.Implementations.Data
 
                     var paramName = "@ExcludeProviderId" + index;
                     //excludeIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")");
-                    excludeIds.Add("ProviderIds not like " + paramName);
+                    excludeIds.Add("(ProviderIds is null or ProviderIds not like " + paramName + ")");
                     if (statement != null)
                     {
                         statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%");

+ 3 - 5
Emby.Server.Implementations/Dto/DtoService.cs

@@ -361,10 +361,7 @@ namespace Emby.Server.Implementations.Dto
             if (collectionFolder != null)
             {
                 dto.OriginalCollectionType = collectionFolder.CollectionType;
-
-                dto.CollectionType = user == null ?
-                    collectionFolder.CollectionType :
-                    collectionFolder.GetViewType(user);
+                dto.CollectionType = collectionFolder.CollectionType;
             }
 
             if (fields.Contains(ItemFields.CanDelete))
@@ -1515,7 +1512,8 @@ namespace Emby.Server.Implementations.Dto
                     return artist;
                 }
             }
-            return item.GetParent();
+
+            return item.DisplayParent ?? item.GetParent();
         }
 
         private void AddInheritedImages(BaseItemDto dto, BaseItem item, DtoOptions options, BaseItem owner)

+ 1 - 0
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -182,6 +182,7 @@
     <Compile Include="MediaEncoder\EncodingManager.cs" />
     <Compile Include="Migrations\IVersionMigration.cs" />
     <Compile Include="Migrations\LibraryScanMigration.cs" />
+    <Compile Include="Migrations\GuideMigration.cs" />
     <Compile Include="Migrations\UpdateLevelMigration.cs" />
     <Compile Include="News\NewsEntryPoint.cs" />
     <Compile Include="News\NewsService.cs" />

+ 3 - 2
Emby.Server.Implementations/EntryPoints/SystemEvents.cs

@@ -6,15 +6,16 @@ using System.Threading.Tasks;
 using MediaBrowser.Model.System;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Common;
+using MediaBrowser.Controller;
 
 namespace Emby.Server.Implementations.EntryPoints
 {
     public class SystemEvents : IServerEntryPoint
     {
         private readonly ISystemEvents _systemEvents;
-        private readonly IApplicationHost _appHost;
+        private readonly IServerApplicationHost _appHost;
 
-        public SystemEvents(ISystemEvents systemEvents, IApplicationHost appHost)
+        public SystemEvents(ISystemEvents systemEvents, IServerApplicationHost appHost)
         {
             _systemEvents = systemEvents;
             _appHost = appHost;

+ 3 - 2
Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs

@@ -10,6 +10,7 @@ using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 
 namespace Emby.Server.Implementations.EntryPoints
@@ -19,7 +20,7 @@ namespace Emby.Server.Implementations.EntryPoints
     /// </summary>
     public class UsageEntryPoint : IServerEntryPoint
     {
-        private readonly IApplicationHost _applicationHost;
+        private readonly IServerApplicationHost _applicationHost;
         private readonly IHttpClient _httpClient;
         private readonly ILogger _logger;
         private readonly ISessionManager _sessionManager;
@@ -28,7 +29,7 @@ namespace Emby.Server.Implementations.EntryPoints
 
         private readonly ConcurrentDictionary<Guid, ClientInfo> _apps = new ConcurrentDictionary<Guid, ClientInfo>();
 
-        public UsageEntryPoint(ILogger logger, IApplicationHost applicationHost, IHttpClient httpClient, ISessionManager sessionManager, IUserManager userManager, IServerConfigurationManager config)
+        public UsageEntryPoint(ILogger logger, IServerApplicationHost applicationHost, IHttpClient httpClient, ISessionManager sessionManager, IUserManager userManager, IServerConfigurationManager config)
         {
             _logger = logger;
             _applicationHost = applicationHost;

+ 3 - 2
Emby.Server.Implementations/EntryPoints/UsageReporter.cs

@@ -8,19 +8,20 @@ using System.Globalization;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller;
 using MediaBrowser.Model.Logging;
 
 namespace Emby.Server.Implementations.EntryPoints
 {
     public class UsageReporter
     {
-        private readonly IApplicationHost _applicationHost;
+        private readonly IServerApplicationHost _applicationHost;
         private readonly IHttpClient _httpClient;
         private readonly IUserManager _userManager;
         private readonly ILogger _logger;
         private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
 
-        public UsageReporter(IApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager, ILogger logger)
+        public UsageReporter(IServerApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager, ILogger logger)
         {
             _applicationHost = applicationHost;
             _httpClient = httpClient;

+ 168 - 166
Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -61,92 +61,92 @@ namespace Emby.Server.Implementations.FileOrganization
             };
 
             try
-            { 
-            if (_libraryMonitor.IsPathLocked(path))
             {
-                result.Status = FileSortingStatus.Failure;
-                result.StatusMessage = "Path is locked by other processes. Please try again later.";
-                return result;
-            }
+                if (_libraryMonitor.IsPathLocked(path))
+                {
+                    result.Status = FileSortingStatus.Failure;
+                    result.StatusMessage = "Path is locked by other processes. Please try again later.";
+                    return result;
+                }
 
-            var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
-            var resolver = new EpisodeResolver(namingOptions, new NullLogger());
+                var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
+                var resolver = new EpisodeResolver(namingOptions, new NullLogger());
 
-            var episodeInfo = resolver.Resolve(path, false) ??
-                new MediaBrowser.Naming.TV.EpisodeInfo();
+                var episodeInfo = resolver.Resolve(path, false) ??
+                    new MediaBrowser.Naming.TV.EpisodeInfo();
 
-            var seriesName = episodeInfo.SeriesName;
+                var seriesName = episodeInfo.SeriesName;
 
-            if (!string.IsNullOrEmpty(seriesName))
-            {
-                var seasonNumber = episodeInfo.SeasonNumber;
+                if (!string.IsNullOrEmpty(seriesName))
+                {
+                    var seasonNumber = episodeInfo.SeasonNumber;
 
-                result.ExtractedSeasonNumber = seasonNumber;
+                    result.ExtractedSeasonNumber = seasonNumber;
 
-                // Passing in true will include a few extra regex's
-                var episodeNumber = episodeInfo.EpisodeNumber;
+                    // Passing in true will include a few extra regex's
+                    var episodeNumber = episodeInfo.EpisodeNumber;
 
-                result.ExtractedEpisodeNumber = episodeNumber;
+                    result.ExtractedEpisodeNumber = episodeNumber;
 
-                var premiereDate = episodeInfo.IsByDate ?
-                    new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) :
-                    (DateTime?)null;
+                    var premiereDate = episodeInfo.IsByDate ?
+                        new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) :
+                        (DateTime?)null;
 
-                if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
-                {
-                    if (episodeInfo.IsByDate)
+                    if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
                     {
-                        _logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
+                        if (episodeInfo.IsByDate)
+                        {
+                            _logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
+                        }
+                        else
+                        {
+                            _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
+                        }
+
+                        var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
+
+                        result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
+
+                        await OrganizeEpisode(path,
+                            seriesName,
+                            seasonNumber,
+                            episodeNumber,
+                            endingEpisodeNumber,
+                            premiereDate,
+                            options,
+                            overwriteExisting,
+                            false,
+                            result,
+                            cancellationToken).ConfigureAwait(false);
                     }
                     else
                     {
-                        _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
+                        var msg = string.Format("Unable to determine episode number from {0}", path);
+                        result.Status = FileSortingStatus.Failure;
+                        result.StatusMessage = msg;
+                        _logger.Warn(msg);
                     }
-
-                    var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
-
-                    result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
-
-                    await OrganizeEpisode(path,
-                        seriesName,
-                        seasonNumber,
-                        episodeNumber,
-                        endingEpisodeNumber,
-                        premiereDate,
-                        options,
-                        overwriteExisting,
-                        false,
-                        result,
-                        cancellationToken).ConfigureAwait(false);
                 }
                 else
                 {
-                    var msg = string.Format("Unable to determine episode number from {0}", path);
+                    var msg = string.Format("Unable to determine series name from {0}", path);
                     result.Status = FileSortingStatus.Failure;
                     result.StatusMessage = msg;
                     _logger.Warn(msg);
                 }
-            }
-            else
-            {
-                var msg = string.Format("Unable to determine series name from {0}", path);
-                result.Status = FileSortingStatus.Failure;
-                result.StatusMessage = msg;
-                _logger.Warn(msg);
-            }
 
-            var previousResult = _organizationService.GetResultBySourcePath(path);
+                var previousResult = _organizationService.GetResultBySourcePath(path);
 
-            if (previousResult != null)
-            {
-                // Don't keep saving the same result over and over if nothing has changed
-                if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success)
+                if (previousResult != null)
                 {
-                    return previousResult;
+                    // Don't keep saving the same result over and over if nothing has changed
+                    if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success)
+                    {
+                        return previousResult;
+                    }
                 }
-            }
 
-            await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
+                await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
             }
             catch (Exception ex)
             {
@@ -162,58 +162,60 @@ namespace Emby.Server.Implementations.FileOrganization
             var result = _organizationService.GetResult(request.ResultId);
 
             try
-            { 
-            Series series = null;
-
-            if (request.NewSeriesProviderIds.Count > 0)
             {
-                // We're having a new series here
-                SeriesInfo seriesRequest = new SeriesInfo();
-                seriesRequest.ProviderIds = request.NewSeriesProviderIds;
-
-                var refreshOptions = new MetadataRefreshOptions(_fileSystem);
-                series = new Series();
-                series.Id = Guid.NewGuid();
-                series.Name = request.NewSeriesName;
+                Series series = null;
 
-                int year;
-                if (int.TryParse(request.NewSeriesYear, out year))
+                if (request.NewSeriesProviderIds.Count > 0)
                 {
-                    series.ProductionYear = year;
-                }
+                    // We're having a new series here
+                    SeriesInfo seriesRequest = new SeriesInfo();
+                    seriesRequest.ProviderIds = request.NewSeriesProviderIds;
 
-                var seriesFolderName = series.Name;
-                if (series.ProductionYear.HasValue)
-                {
-                    seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear);
-                }
+                    var refreshOptions = new MetadataRefreshOptions(_fileSystem);
+                    series = new Series();
+                    series.Id = Guid.NewGuid();
+                    series.Name = request.NewSeriesName;
+
+                    int year;
+                    if (int.TryParse(request.NewSeriesYear, out year))
+                    {
+                        series.ProductionYear = year;
+                    }
 
-                series.Path = Path.Combine(request.TargetFolder, seriesFolderName);
+                    var seriesFolderName = series.Name;
+                    if (series.ProductionYear.HasValue)
+                    {
+                        seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear);
+                    }
 
-                series.ProviderIds = request.NewSeriesProviderIds;
+                    seriesFolderName = _fileSystem.GetValidFilename(seriesFolderName);
 
-                await series.RefreshMetadata(refreshOptions, cancellationToken);
-            }
+                    series.Path = Path.Combine(request.TargetFolder, seriesFolderName);
 
-            if (series == null)
-            {
-                // Existing Series
-                series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
-            }
+                    series.ProviderIds = request.NewSeriesProviderIds;
 
-            await OrganizeEpisode(result.OriginalPath,
-                series,
-                request.SeasonNumber,
-                request.EpisodeNumber,
-                request.EndingEpisodeNumber,
-                null,
-                options,
-                true,
-                request.RememberCorrection,
-                result,
-                cancellationToken).ConfigureAwait(false);
+                    await series.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
+                }
+
+                if (series == null)
+                {
+                    // Existing Series
+                    series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
+                }
+
+                await OrganizeEpisode(result.OriginalPath,
+                    series,
+                    request.SeasonNumber,
+                    request.EpisodeNumber,
+                    request.EndingEpisodeNumber,
+                    null,
+                    options,
+                    true,
+                    request.RememberCorrection,
+                    result,
+                    cancellationToken).ConfigureAwait(false);
 
-            await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
+                await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
             }
             catch (Exception ex)
             {
@@ -287,91 +289,91 @@ namespace Emby.Server.Implementations.FileOrganization
             {
                 throw new Exception("File is currently processed otherwise. Please try again later.");
             }
-            
-            try
-            {
-            // Proceed to sort the file
-            var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);
-
-            if (string.IsNullOrEmpty(newPath))
-            {
-                var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
-                throw new Exception(msg);
-            }
-
-            _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
-            result.TargetPath = newPath;
-
-            var fileExists = _fileSystem.FileExists(result.TargetPath);
-            var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);
 
-            if (!overwriteExisting)
+            try
             {
-                if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
-                {
-                    var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath);
-                    _logger.Info(msg);
-                    result.Status = FileSortingStatus.SkippedExisting;
-                    result.StatusMessage = msg;
-                    return;
-                }
+                // Proceed to sort the file
+                var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);
 
-                if (fileExists)
+                if (string.IsNullOrEmpty(newPath))
                 {
-                    var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath);
-                    _logger.Info(msg);
-                    result.Status = FileSortingStatus.SkippedExisting;
-                    result.StatusMessage = msg;
-                    result.TargetPath = newPath;
-                    return;
+                    var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
+                    throw new Exception(msg);
                 }
 
-                if (otherDuplicatePaths.Count > 0)
-                {
-                    var msg = string.Format("File '{0}' already exists as these:'{1}'. Stopping organization", sourcePath, string.Join("', '", otherDuplicatePaths));
-                    _logger.Info(msg);
-                    result.Status = FileSortingStatus.SkippedExisting;
-                    result.StatusMessage = msg;
-                    result.DuplicatePaths = otherDuplicatePaths;
-                    return;
-                }
-            }
+                _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
+                result.TargetPath = newPath;
 
-            PerformFileSorting(options.TvOptions, result);
-
-            if (overwriteExisting)
-            {
-                var hasRenamedFiles = false;
+                var fileExists = _fileSystem.FileExists(result.TargetPath);
+                var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);
 
-                foreach (var path in otherDuplicatePaths)
+                if (!overwriteExisting)
                 {
-                    _logger.Debug("Removing duplicate episode {0}", path);
-
-                    _libraryMonitor.ReportFileSystemChangeBeginning(path);
-
-                    var renameRelatedFiles = !hasRenamedFiles &&
-                        string.Equals(Path.GetDirectoryName(path), Path.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase);
-
-                    if (renameRelatedFiles)
+                    if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
                     {
-                        hasRenamedFiles = true;
+                        var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath);
+                        _logger.Info(msg);
+                        result.Status = FileSortingStatus.SkippedExisting;
+                        result.StatusMessage = msg;
+                        return;
                     }
 
-                    try
+                    if (fileExists)
                     {
-                        DeleteLibraryFile(path, renameRelatedFiles, result.TargetPath);
+                        var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath);
+                        _logger.Info(msg);
+                        result.Status = FileSortingStatus.SkippedExisting;
+                        result.StatusMessage = msg;
+                        result.TargetPath = newPath;
+                        return;
                     }
-                    catch (IOException ex)
+
+                    if (otherDuplicatePaths.Count > 0)
                     {
-                        _logger.ErrorException("Error removing duplicate episode", ex, path);
+                        var msg = string.Format("File '{0}' already exists as these:'{1}'. Stopping organization", sourcePath, string.Join("', '", otherDuplicatePaths));
+                        _logger.Info(msg);
+                        result.Status = FileSortingStatus.SkippedExisting;
+                        result.StatusMessage = msg;
+                        result.DuplicatePaths = otherDuplicatePaths;
+                        return;
                     }
-                    finally
+                }
+
+                PerformFileSorting(options.TvOptions, result);
+
+                if (overwriteExisting)
+                {
+                    var hasRenamedFiles = false;
+
+                    foreach (var path in otherDuplicatePaths)
                     {
-                        _libraryMonitor.ReportFileSystemChangeComplete(path, true);
+                        _logger.Debug("Removing duplicate episode {0}", path);
+
+                        _libraryMonitor.ReportFileSystemChangeBeginning(path);
+
+                        var renameRelatedFiles = !hasRenamedFiles &&
+                            string.Equals(Path.GetDirectoryName(path), Path.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase);
+
+                        if (renameRelatedFiles)
+                        {
+                            hasRenamedFiles = true;
+                        }
+
+                        try
+                        {
+                            DeleteLibraryFile(path, renameRelatedFiles, result.TargetPath);
+                        }
+                        catch (IOException ex)
+                        {
+                            _logger.ErrorException("Error removing duplicate episode", ex, path);
+                        }
+                        finally
+                        {
+                            _libraryMonitor.ReportFileSystemChangeComplete(path, true);
+                        }
                     }
                 }
             }
-            }
             catch (Exception ex)
             {
                 result.Status = FileSortingStatus.Failure;

+ 18 - 11
Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs

@@ -49,6 +49,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
             string device = null;
             string client = null;
             string version = null;
+            string token = null;
 
             if (auth != null)
             {
@@ -56,9 +57,13 @@ namespace Emby.Server.Implementations.HttpServer.Security
                 auth.TryGetValue("Device", out device);
                 auth.TryGetValue("Client", out client);
                 auth.TryGetValue("Version", out version);
+                auth.TryGetValue("Token", out token);
             }
 
-            var token = httpReq.Headers["X-Emby-Token"];
+            if (string.IsNullOrWhiteSpace(token))
+            {
+                token = httpReq.Headers["X-Emby-Token"];
+            }
 
             if (string.IsNullOrWhiteSpace(token))
             {
@@ -156,8 +161,10 @@ namespace Emby.Server.Implementations.HttpServer.Security
             // There should be at least to parts
             if (parts.Length != 2) return null;
 
+            var acceptedNames = new[] { "MediaBrowser", "Emby"};
+
             // It has to be a digest request
-            if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase))
+            if (!acceptedNames.Contains(parts[0] ?? string.Empty, StringComparer.OrdinalIgnoreCase))
             {
                 return null;
             }
@@ -174,7 +181,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
 
                 if (param.Length == 2)
                 {
-					var value = NormalizeValue (param[1].Trim(new[] { '"' }));
+                    var value = NormalizeValue(param[1].Trim(new[] { '"' }));
                     result.Add(param[0], value);
                 }
             }
@@ -182,14 +189,14 @@ namespace Emby.Server.Implementations.HttpServer.Security
             return result;
         }
 
-		private string NormalizeValue(string value)
-		{
-			if (string.IsNullOrWhiteSpace (value)) 
-			{
-				return value;
-			}
+        private string NormalizeValue(string value)
+        {
+            if (string.IsNullOrWhiteSpace(value))
+            {
+                return value;
+            }
 
-			return System.Net.WebUtility.HtmlEncode(value);
-		}
+            return System.Net.WebUtility.HtmlEncode(value);
+        }
     }
 }

+ 4 - 3
Emby.Server.Implementations/IO/FileRefresher.cs

@@ -261,10 +261,11 @@ namespace Emby.Server.Implementations.IO
             // In order to determine if the file is being written to, we have to request write access
             // But if the server only has readonly access, this is going to cause this entire algorithm to fail
             // So we'll take a best guess about our access level
-            var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta
-                ? FileAccessMode.ReadWrite
-                : FileAccessMode.Read;
+            //var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta
+            //    ? FileAccessMode.ReadWrite
+            //    : FileAccessMode.Read;
 
+            var requestedFileAccess = FileAccessMode.Read;
             try
             {
                 using (_fileSystem.GetFileStream(path, FileOpenMode.Open, requestedFileAccess, FileShareMode.ReadWrite))

+ 14 - 4
Emby.Server.Implementations/Intros/DefaultIntroProvider.cs

@@ -100,20 +100,30 @@ namespace Emby.Server.Implementations.Intros
 
             if (trailerTypes.Count > 0)
             {
+                if (trailerTypes.Count >= 5)
+                {
+                    trailerTypes.Clear();
+                }
+
+                // hack - can't filter by user library because local trailers get TopParentId =null in the db. 
+                // for now we have to use a post-query filter afterwards to solve that
                 var trailerResult = _libraryManager.GetItemList(new InternalItemsQuery
                 {
                     IncludeItemTypes = new[] { typeof(Trailer).Name },
                     TrailerTypes = trailerTypes.ToArray(),
                     SimilarTo = item,
-                    IsPlayed = config.EnableIntrosForWatchedContent ? (bool?)null : false,
+                    //IsPlayed = config.EnableIntrosForWatchedContent ? (bool?)null : false,
                     MaxParentalRating = config.EnableIntrosParentalControl ? ratingLevel : null,
                     BlockUnratedItems = config.EnableIntrosParentalControl ? new[] { UnratedItem.Trailer } : new UnratedItem[] { },
 
                     // Account for duplicates by imdb id, since the database doesn't support this yet
-                    Limit = config.TrailerLimit * 2,
+                    Limit = config.TrailerLimit * 4,
                     SourceTypes = sourceTypes.ToArray()
-
-                }).Where(i => string.IsNullOrWhiteSpace(i.GetProviderId(MetadataProviders.Imdb)) || !string.Equals(i.GetProviderId(MetadataProviders.Imdb), item.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).Take(config.TrailerLimit);
+                })
+                .Where(i => string.IsNullOrWhiteSpace(i.GetProviderId(MetadataProviders.Imdb)) || !string.Equals(i.GetProviderId(MetadataProviders.Imdb), item.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase))
+                .Where(i => i.IsVisibleStandalone(user))
+                .Where(i => config.EnableIntrosForWatchedContent || !i.IsPlayed(user))
+                .Take(config.TrailerLimit);
 
                 candidates.AddRange(trailerResult.Select(i => new ItemWithTrailer
                 {

+ 11 - 30
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -1956,30 +1956,6 @@ namespace Emby.Server.Implementations.Library
 
             var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
 
-            if (options.SchemaVersion < 3)
-            {
-                options.SaveLocalMetadata = ConfigurationManager.Configuration.SaveLocalMeta;
-                options.EnableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders;
-            }
-
-            if (options.SchemaVersion < 2)
-            {
-                var chapterOptions = ConfigurationManager.GetConfiguration<ChapterOptions>("chapters");
-                options.ExtractChapterImagesDuringLibraryScan = chapterOptions.ExtractDuringLibraryScan;
-
-                if (collectionFolder != null)
-                {
-                    if (string.Equals(collectionFolder.CollectionType, "movies", StringComparison.OrdinalIgnoreCase))
-                    {
-                        options.EnableChapterImageExtraction = chapterOptions.EnableMovieChapterImageExtraction;
-                    }
-                    else if (string.Equals(collectionFolder.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
-                    {
-                        options.EnableChapterImageExtraction = chapterOptions.EnableEpisodeChapterImageExtraction;
-                    }
-                }
-            }
-
             return options;
         }
 
@@ -2034,7 +2010,7 @@ namespace Emby.Server.Implementations.Library
 
         private string GetContentTypeOverride(string path, bool inherit)
         {
-            var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => string.Equals(i.Name, path, StringComparison.OrdinalIgnoreCase) || (inherit && !string.IsNullOrWhiteSpace(i.Name) && _fileSystem.ContainsSubPath(i.Name, path)));
+            var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => _fileSystem.AreEqual(i.Name, path) || (inherit && !string.IsNullOrWhiteSpace(i.Name) && _fileSystem.ContainsSubPath(i.Name, path)));
             if (nameValuePair != null)
             {
                 return nameValuePair.Value;
@@ -2505,6 +2481,8 @@ namespace Emby.Server.Implementations.Library
                 options.VideoFileExtensions.Remove(".zip");
             }
 
+            options.VideoFileExtensions.Add(".tp");
+
             return options;
         }
 
@@ -2615,7 +2593,7 @@ namespace Emby.Server.Implementations.Library
                 {
                     foreach (var pathInfo in libraryOptions.PathInfos)
                     {
-                        if (string.IsNullOrWhiteSpace(pathInfo.NetworkPath))
+                        if (string.IsNullOrWhiteSpace(pathInfo.Path) || string.IsNullOrWhiteSpace(pathInfo.NetworkPath))
                         {
                             continue;
                         }
@@ -2643,10 +2621,13 @@ namespace Emby.Server.Implementations.Library
 
             foreach (var map in ConfigurationManager.Configuration.PathSubstitutions)
             {
-                var substitutionResult = SubstitutePathInternal(path, map.From, map.To);
-                if (substitutionResult.Item2)
+                if (!string.IsNullOrWhiteSpace(map.From))
                 {
-                    return substitutionResult.Item1;
+                    var substitutionResult = SubstitutePathInternal(path, map.From, map.To);
+                    if (substitutionResult.Item2)
+                    {
+                        return substitutionResult.Item1;
+                    }
                 }
             }
 
@@ -3088,7 +3069,7 @@ namespace Emby.Server.Implementations.Library
                 {
                     removeList.Add(contentType);
                 }
-                else if (string.Equals(path, contentType.Name, StringComparison.OrdinalIgnoreCase)
+                else if (_fileSystem.AreEqual(path, contentType.Name)
                     || _fileSystem.ContainsSubPath(path, contentType.Name))
                 {
                     removeList.Add(contentType);

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

@@ -199,6 +199,8 @@ namespace Emby.Server.Implementations.Library
 
                 foreach (var mediaSource in list)
                 {
+                    mediaSource.InferTotalBitrate();
+
                     SetKeyProperties(provider, mediaSource);
                 }
 

+ 1 - 2
Emby.Server.Implementations/Library/MusicManager.cs

@@ -105,11 +105,10 @@ namespace Emby.Server.Implementations.Library
             return inputItems
                 .Cast<Audio>()
                 .Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey)))
-                .Where(i => i.Item2 > 0)
                 .OrderByDescending(i => i.Item2)
                 .ThenBy(i => Guid.NewGuid())
                 .Select(i => i.Item1)
-                .Take(100)
+                .Take(200)
                 .OrderBy(i => Guid.NewGuid());
         }
 

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

@@ -142,12 +142,14 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
                         }
                     }
                 }
-
-                var fullName = fileSystemInfo.FullName;
-
-                if (libraryManager.IsAudioFile(fullName, libraryOptions))
+                else
                 {
-                    return true;
+                    var fullName = fileSystemInfo.FullName;
+
+                    if (libraryManager.IsAudioFile(fullName, libraryOptions))
+                    {
+                        return true;
+                    }
                 }
             }
 

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

@@ -157,6 +157,7 @@ namespace Emby.Server.Implementations.Library
             }
 
             AddIfMissing(excludeItemTypes, typeof(CollectionFolder).Name);
+            AddIfMissing(excludeItemTypes, typeof(Folder).Name);
 
             var mediaItems = _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
@@ -164,8 +165,7 @@ namespace Emby.Server.Implementations.Library
                 ExcludeItemTypes = excludeItemTypes.ToArray(),
                 IncludeItemTypes = includeItemTypes.ToArray(),
                 Limit = query.Limit,
-                IncludeItemsByName = true,
-                IsVirtualItem = false
+                IncludeItemsByName = true
             });
 
             // Add search hints based on item name

+ 47 - 13
Emby.Server.Implementations/Library/UserManager.cs

@@ -236,29 +236,63 @@ namespace Emby.Server.Implementations.Library
             var user = Users
                 .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
 
-            if (user == null)
-            {
-                throw new SecurityException("Invalid username or password entered.");
-            }
+            var success = false;
 
-            if (user.Policy.IsDisabled)
+            if (user != null)
             {
-                throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
-            }
+                // Authenticate using local credentials if not a guest
+                if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest)
+                {
+                    success = string.Equals(GetPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
 
-            var success = false;
+                    if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword)
+                    {
+                        success = string.Equals(GetLocalPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+                    }
+                }
 
-            // Authenticate using local credentials if not a guest
-            if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest)
+                // Maybe user accidently entered connect credentials. let's be flexible
+                if (!success && user.ConnectLinkType.HasValue && !string.IsNullOrWhiteSpace(passwordMd5) && !string.IsNullOrWhiteSpace(user.ConnectUserName))
+                {
+                    try
+                    {
+                        await _connectFactory().Authenticate(user.ConnectUserName, passwordMd5).ConfigureAwait(false);
+                        success = true;
+                    }
+                    catch
+                    {
+
+                    }
+                }
+            }
+
+            // Try originally entered username
+            if (!success && (user == null || !string.Equals(user.ConnectUserName, username, StringComparison.OrdinalIgnoreCase)))
             {
-                success = string.Equals(GetPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+                try
+                {
+                    var connectAuthResult = await _connectFactory().Authenticate(username, passwordMd5).ConfigureAwait(false);
 
-                if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword)
+                    user = Users.FirstOrDefault(i => string.Equals(i.ConnectUserId, connectAuthResult.User.Id, StringComparison.OrdinalIgnoreCase));
+
+                    success = user != null;
+                }
+                catch
                 {
-                    success = string.Equals(GetLocalPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
+
                 }
             }
 
+            if (user == null)
+            {
+                throw new SecurityException("Invalid username or password entered.");
+            }
+
+            if (user.Policy.IsDisabled)
+            {
+                throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
+            }
+
             // Update LastActivityDate and LastLoginDate, then save
             if (success)
             {

+ 1 - 9
Emby.Server.Implementations/Library/UserViewManager.cs

@@ -55,8 +55,6 @@ namespace Emby.Server.Implementations.Library
                 }).ToList();
             }
 
-            var plainFolderIds = user.Configuration.PlainFolderViews.Select(i => new Guid(i)).ToList();
-
             var groupedFolders = new List<ICollectionFolder>();
 
             var list = new List<Folder>();
@@ -72,12 +70,6 @@ namespace Emby.Server.Implementations.Library
                     continue;
                 }
 
-                if (plainFolderIds.Contains(folder.Id) && UserView.IsEligibleForEnhancedView(folderViewType))
-                {
-                    list.Add(folder);
-                    continue;
-                }
-
                 if (collectionFolder != null && UserView.IsEligibleForGrouping(folder) && user.IsFolderGrouped(folder.Id))
                 {
                     groupedFolders.Add(collectionFolder);
@@ -287,7 +279,7 @@ namespace Emby.Server.Implementations.Library
                 SortBy = new[] { ItemSortBy.DateCreated },
                 IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null,
                 ExcludeItemTypes = excludeItemTypes,
-                ExcludeLocationTypes = new[] { LocationType.Virtual },
+                IsVirtualItem = false,
                 Limit = limit * 5,
                 SourceTypes = parents.Count == 0 ? new[] { SourceType.Library } : new SourceType[] { },
                 IsPlayed = request.IsPlayed

+ 8 - 7
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -150,7 +150,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             foreach (var recordingFolder in recordingFolders)
             {
                 var pathsToCreate = recordingFolder.Locations
-                    .Where(i => !allExistingPaths.Contains(i, StringComparer.OrdinalIgnoreCase))
+                    .Where(i => !allExistingPaths.Any(p => _fileSystem.AreEqual(p, i)))
                     .ToList();
 
                 if (pathsToCreate.Count == 0)
@@ -1370,13 +1370,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             ActiveRecordingInfo removed;
             _activeRecordings.TryRemove(timer.Id, out removed);
 
-            if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate)
+            if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate && timer.RetryCount < 10)
             {
                 const int retryIntervalSeconds = 60;
                 _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds);
 
                 timer.Status = RecordingStatus.New;
                 timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds);
+                timer.RetryCount++;
                 _timerProvider.AddOrUpdate(timer);
             }
             else if (_fileSystem.FileExists(recordPath))
@@ -2106,13 +2107,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 {
                     return true;
                 }
-
-                if (!seriesTimer.Days.Contains(timer.StartDate.ToLocalTime().DayOfWeek))
-                {
-                    return true;
-                }
             }
 
+            //if (!seriesTimer.Days.Contains(timer.StartDate.ToLocalTime().DayOfWeek))
+            //{
+            //    return true;
+            //}
+
             if (seriesTimer.RecordNewOnly && timer.IsRepeat)
             {
                 return true;

+ 37 - 2
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -240,14 +240,49 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             {
                 try
                 {
-                    _logger.Info("Killing ffmpeg recording process for {0}", _targetPath);
+                    _logger.Info("Stopping ffmpeg recording process for {0}", _targetPath);
 
                     //process.Kill();
                     _process.StandardInput.WriteLine("q");
                 }
                 catch (Exception ex)
                 {
-                    _logger.ErrorException("Error killing transcoding job for {0}", ex, _targetPath);
+                    _logger.ErrorException("Error stopping recording transcoding job for {0}", ex, _targetPath);
+                }
+
+                if (_hasExited)
+                {
+                    return;
+                }
+
+                try
+                {
+                    _logger.Info("Calling recording process.WaitForExit for {0}", _targetPath);
+
+                    if (_process.WaitForExit(5000))
+                    {
+                        return;
+                    }
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error waiting for recording process to exit for {0}", ex, _targetPath);
+                }
+
+                if (_hasExited)
+                {
+                    return;
+                }
+
+                try
+                {
+                    _logger.Info("Killing ffmpeg recording process for {0}", _targetPath);
+
+                    _process.Kill();
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error killing recording transcoding job for {0}", ex, _targetPath);
                 }
             }
         }

+ 1 - 4
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -182,7 +182,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
                         programsInfo.Add(GetProgram(channelNumber, schedule, programDict[schedule.programID]));
                     }
-                    _logger.Info("Finished with EPGData");
                 }
             }
 
@@ -322,8 +321,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
             {
                 var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
-                _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
-                _logger.Info("Mapping Stations to Channel");
+
                 foreach (ScheduleDirect.Map map in root.map)
                 {
                     var channelNumber = map.logicalChannelNumber;
@@ -353,7 +351,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                         });
                     }
                 }
-                _logger.Info("Added " + GetChannelPairCacheCount(listingsId) + " channels to the dictionary");
 
                 foreach (ChannelInfo channel in channels)
                 {

+ 1 - 9
Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs

@@ -96,15 +96,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
 
             // Try to estimate this
-            if (!mediaSource.Bitrate.HasValue)
-            {
-                var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum();
-
-                if (total > 0)
-                {
-                    mediaSource.Bitrate = total;
-                }
-            }
+            mediaSource.InferTotalBitrate(true);
         }
     }
 }

+ 2 - 10
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -459,15 +459,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
 
             // Set the total bitrate if not already supplied
-            if (!mediaSource.Bitrate.HasValue)
-            {
-                var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum();
-
-                if (total > 0)
-                {
-                    mediaSource.Bitrate = total;
-                }
-            }
+            mediaSource.InferTotalBitrate();
 
             if (!(service is EmbyTV.EmbyTV))
             {
@@ -1602,7 +1594,7 @@ namespace Emby.Server.Implementations.LiveTv
                 Recursive = true,
                 AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(),
                 IsFolder = false,
-                ExcludeLocationTypes = new[] { LocationType.Virtual },
+                IsVirtualItem = false,
                 Limit = query.Limit,
                 SortBy = new[] { ItemSortBy.DateCreated },
                 SortOrder = SortOrder.Descending,

+ 1 - 9
Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -200,15 +200,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
 
             // Try to estimate this
-            if (!mediaSource.Bitrate.HasValue)
-            {
-                var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum();
-
-                if (total > 0)
-                {
-                    mediaSource.Bitrate = total;
-                }
-            }
+            mediaSource.InferTotalBitrate();
         }
 
         public Task CloseMediaSource(string liveStreamId)

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

@@ -104,7 +104,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                         {
                             Type = HdHomerunHost.DeviceType,
                             Url = url,
-                            DataVersion = 1,
                             DeviceId = response.DeviceID
 
                         }).ConfigureAwait(false);

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

@@ -61,10 +61,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         {
             var id = ChannelIdPrefix + i.GuideNumber;
 
-            if (info.DataVersion >= 1)
-            {
-                id += '_' + (i.GuideName ?? string.Empty).GetMD5().ToString("N");
-            }
+            id += '_' + (i.GuideName ?? string.Empty).GetMD5().ToString("N");
 
             return id;
         }
@@ -103,7 +100,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 TunerHostId = info.Id,
                 IsHD = i.HD == 1,
                 AudioCodec = i.AudioCodec,
-                VideoCodec = i.VideoCodec
+                VideoCodec = i.VideoCodec,
+                ChannelType = ChannelType.TV
             });
         }
 
@@ -430,6 +428,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 IsInfiniteStream = true
             };
 
+            mediaSource.InferTotalBitrate();
+
             return mediaSource;
         }
 

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

@@ -25,7 +25,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
         private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
         private readonly MulticastStream _multicastStream;
 
-
         public HdHomerunLiveStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost)
             : base(mediaSource)
         {

+ 11 - 3
Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
         protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
         {
-            return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
+            return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, !info.EnableTvgId, cancellationToken).ConfigureAwait(false);
         }
 
         public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
@@ -127,6 +127,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                 {
                     protocol = MediaProtocol.Udp;
                 }
+                else if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
+                {
+                    protocol = MediaProtocol.Rtmp;
+                }
 
                 var mediaSource = new MediaSourceInfo
                 {
@@ -155,9 +159,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                     ReadAtNativeFramerate = false,
 
                     Id = channel.Path.GetMD5().ToString("N"),
-                    IsInfiniteStream = true
+                    IsInfiniteStream = true,
+                    SupportsDirectStream = false,
+                    IsRemote = true
                 };
 
+                mediaSource.InferTotalBitrate();
+
                 return new List<MediaSourceInfo> { mediaSource };
             }
             return new List<MediaSourceInfo>();
@@ -168,4 +176,4 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             return Task.FromResult(true);
         }
     }
-}
+}

+ 60 - 25
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs

@@ -14,6 +14,7 @@ using MediaBrowser.Controller;
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Extensions;
 
 namespace Emby.Server.Implementations.LiveTv.TunerHosts
 {
@@ -32,14 +33,25 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             _appHost = appHost;
         }
 
-        public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
+        public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, bool enableStreamUrlAsIdentifier, CancellationToken cancellationToken)
         {
             var urlHash = url.GetMD5().ToString("N");
 
             // Read the file and display it line by line.
             using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
             {
-                return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId);
+                return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId, enableStreamUrlAsIdentifier);
+            }
+        }
+
+        public List<M3UChannel> ParseString(string text, string channelIdPrefix, string tunerHostId)
+        {
+            var urlHash = "text".GetMD5().ToString("N");
+
+            // Read the file and display it line by line.
+            using (var reader = new StringReader(text))
+            {
+                return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId, false);
             }
         }
 
@@ -59,7 +71,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         }
 
         const string ExtInfPrefix = "#EXTINF:";
-        private List<M3UChannel> GetChannels(StreamReader reader, string urlHash, string channelIdPrefix, string tunerHostId)
+        private List<M3UChannel> GetChannels(TextReader reader, string urlHash, string channelIdPrefix, string tunerHostId, bool enableStreamUrlAsIdentifier)
         {
             var channels = new List<M3UChannel>();
             string line;
@@ -85,7 +97,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                 else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase))
                 {
                     var channel = GetChannelnfo(extInf, tunerHostId, line);
-                    channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N");
+                    if (string.IsNullOrWhiteSpace(channel.Id) || enableStreamUrlAsIdentifier)
+                    {
+                        channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N");
+                    }
+                    else
+                    {
+                        channel.Id = channelIdPrefix + urlHash + channel.Id.GetMD5().ToString("N");
+                    }
+
                     channel.Path = line;
                     channels.Add(channel);
                     extInf = "";
@@ -114,6 +134,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             channel.Name = GetChannelName(extInf, attributes);
             channel.Number = GetChannelNumber(extInf, attributes, mediaUrl);
 
+            if (attributes.TryGetValue("tvg-id", out value))
+            {
+                channel.Id = value;
+            }
+
             return channel;
         }
 
@@ -122,18 +147,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
             var nameInExtInf = nameParts.Length > 1 ? nameParts.Last().Trim() : null;
 
-            var numberString = nameParts[0];
+            string numberString = null;
 
-            //Check for channel number with the format from SatIp
-            int number;
+            // Check for channel number with the format from SatIp
+            // #EXTINF:0,84. VOX Schweiz
+            // #EXTINF:0,84.0 - VOX Schweiz
             if (!string.IsNullOrWhiteSpace(nameInExtInf))
             {
-                var numberIndex = nameInExtInf.IndexOf('.');
+                var numberIndex = nameInExtInf.IndexOf(' ');
                 if (numberIndex > 0)
                 {
-                    if (int.TryParse(nameInExtInf.Substring(0, numberIndex), out number))
+                    var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });
+
+                    double number;
+                    if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
                     {
-                        numberString = number.ToString();
+                        numberString = numberPart;
                     }
                 }
             }
@@ -150,7 +179,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                 string value;
                 if (attributes.TryGetValue("tvg-id", out value))
                 {
-                    numberString = value;
+                    double doubleValue;
+                    if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue))
+                    {
+                        numberString = value;
+                    }
                 }
             }
 
@@ -208,17 +241,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
             var nameInExtInf = nameParts.Length > 1 ? nameParts.Last().Trim() : null;
 
-            //Check for channel number with the format from SatIp
-            int number;
+            // Check for channel number with the format from SatIp
+            // #EXTINF:0,84. VOX Schweiz
+            // #EXTINF:0,84.0 - VOX Schweiz
             if (!string.IsNullOrWhiteSpace(nameInExtInf))
             {
-                var numberIndex = nameInExtInf.IndexOf('.');
+                var numberIndex = nameInExtInf.IndexOf(' ');
                 if (numberIndex > 0)
                 {
-                    if (int.TryParse(nameInExtInf.Substring(0, numberIndex), out number))
+                    var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });
+
+                    double number;
+                    if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
                     {
                         //channel.Number = number.ToString();
-                        nameInExtInf = nameInExtInf.Substring(numberIndex + 1);
+                        nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' });
                     }
                 }
             }
@@ -250,20 +287,18 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
             var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
             var matches = reg.Matches(line);
-            var minIndex = int.MaxValue;
+
+            remaining = line;
+
             foreach (Match match in matches)
             {
-                dict[match.Groups[1].Value] = match.Groups[2].Value;
-                minIndex = Math.Min(minIndex, match.Index);
-            }
+                var key = match.Groups[1].Value;
+                var value = match.Groups[2].Value;
 
-            if (minIndex > 0 && minIndex < line.Length)
-            {
-                line = line.Substring(0, minIndex);
+                dict[match.Groups[1].Value] = match.Groups[2].Value;
+                remaining = remaining.Replace(key + "=\"" + value + "\"", string.Empty, StringComparison.OrdinalIgnoreCase);
             }
 
-            remaining = line;
-
             return dict;
         }
     }

+ 1 - 0
Emby.Server.Implementations/Localization/iso6392.txt

@@ -137,6 +137,7 @@ fon|||Fon|fon
 fre|fra|fr|French|français
 frm|||French, Middle (ca.1400-1600)|français moyen (1400-1600)
 fro|||French, Old (842-ca.1400)|français ancien (842-ca.1400)
+frc||fr-ca|French (Canada)|french
 frr|||Northern Frisian|frison septentrional
 frs|||Eastern Frisian|frison oriental
 fry||fy|Western Frisian|frison occidental

+ 47 - 0
Emby.Server.Implementations/Migrations/GuideMigration.cs

@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Updates;
+using System.Linq;
+
+namespace Emby.Server.Implementations.Migrations
+{
+    public class GuideMigration : IVersionMigration
+    {
+        private readonly IServerConfigurationManager _config;
+        private readonly ITaskManager _taskManager;
+
+        public GuideMigration(IServerConfigurationManager config, ITaskManager taskManager)
+        {
+            _config = config;
+            _taskManager = taskManager;
+        }
+
+        public async Task Run()
+        {
+            var name = "GuideRefresh2";
+
+            if (!_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase))
+            {
+                Task.Run(() =>
+                {
+                    _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask)
+                            .First(i => string.Equals(i.Key, "RefreshGuide", StringComparison.OrdinalIgnoreCase)));
+                });
+
+                var list = _config.Configuration.Migrations.ToList();
+                list.Add(name);
+                _config.Configuration.Migrations = list.ToArray();
+                _config.SaveConfiguration();
+            }
+        }
+    }
+}

+ 0 - 2
Emby.Server.Implementations/Migrations/LibraryScanMigration.cs

@@ -35,8 +35,6 @@ namespace Emby.Server.Implementations.Migrations
                 {
                     _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask)
                             .First(i => string.Equals(i.Key, "RefreshLibrary", StringComparison.OrdinalIgnoreCase)));
-                    _taskManager.QueueScheduledTask(_taskManager.ScheduledTasks.Select(i => i.ScheduledTask)
-                            .First(i => string.Equals(i.Key, "RefreshGuide", StringComparison.OrdinalIgnoreCase)));
                 });
 
                 var list = _config.Configuration.Migrations.ToList();

+ 17 - 0
Emby.Server.Implementations/Notifications/Notifications.cs

@@ -430,6 +430,23 @@ namespace Emby.Server.Implementations.Notifications
             return name;
         }
 
+        public static string GetItemName(BaseItemInfo item)
+        {
+            var name = item.Name;
+
+            if (!string.IsNullOrWhiteSpace(item.SeriesName))
+            {
+                name = item.SeriesName + " - " + name;
+            }
+
+            if (item.Artists != null && item.Artists.Count > 0)
+            {
+                name = item.Artists[0] + " - " + name;
+            }
+
+            return name;
+        }
+
         async void _userManager_UserCreated(object sender, GenericEventArgs<User> e)
         {
             var notification = new NotificationRequest

+ 1 - 1
Emby.Server.Implementations/Playlists/PlaylistManager.cs

@@ -100,7 +100,7 @@ namespace Emby.Server.Implementations.Playlists
 
             if (string.IsNullOrWhiteSpace(options.MediaType))
             {
-                throw new ArgumentException("A playlist media type is required.");
+                options.MediaType = "Audio";
             }
 
             var user = _userManager.GetUserById(options.UserId);

+ 1 - 1
Emby.Server.Implementations/Sync/SyncHelper.cs

@@ -4,7 +4,7 @@ namespace Emby.Server.Implementations.Sync
 {
     public class SyncHelper
     {
-        public static int? AdjustBitrate(int? profileBitrate, string quality)
+        public static long? AdjustBitrate(long? profileBitrate, string quality)
         {
             if (profileBitrate.HasValue)
             {

+ 11 - 9
Emby.Server.Implementations/Sync/SyncJobProcessor.cs

@@ -59,15 +59,8 @@ namespace Emby.Server.Implementations.Sync
             _mediaSourceManager = mediaSourceManager;
         }
 
-        public async Task EnsureJobItems(SyncJob job)
+        public async Task EnsureJobItems(SyncJob job, User user)
         {
-            var user = _userManager.GetUserById(job.UserId);
-
-            if (user == null)
-            {
-                throw new InvalidOperationException("Cannot proceed with sync because user no longer exists.");
-            }
-
             var items = (await GetItemsForSync(job.Category, job.ParentId, job.RequestedItemIds, user, job.UnwatchedOnly).ConfigureAwait(false))
                 .ToList();
 
@@ -385,7 +378,16 @@ namespace Emby.Server.Implementations.Sync
 
                 if (job.SyncNewContent)
                 {
-                    await EnsureJobItems(job).ConfigureAwait(false);
+                    var user = _userManager.GetUserById(job.UserId);
+
+                    if (user == null)
+                    {
+                        await _syncManager.CancelJob(job.Id).ConfigureAwait(false);
+                    }
+                    else
+                    {
+                        await EnsureJobItems(job, user).ConfigureAwait(false);
+                    }
                 }
             }
         }

+ 7 - 1
Emby.Server.Implementations/Sync/SyncManager.cs

@@ -181,7 +181,7 @@ namespace Emby.Server.Implementations.Sync
 
             await _repo.Create(job).ConfigureAwait(false);
 
-            await processor.EnsureJobItems(job).ConfigureAwait(false);
+            await processor.EnsureJobItems(job, user).ConfigureAwait(false);
 
             // If it already has a converting status then is must have been aborted during conversion
             var jobItemsResult = GetJobItems(new SyncJobItemQuery
@@ -560,6 +560,12 @@ namespace Emby.Server.Implementations.Sync
         {
             var jobItem = _repo.GetJobItem(id);
 
+            if (jobItem == null)
+            {
+                _logger.Debug("ReportSyncJobItemTransferred: SyncJobItem {0} doesn't exist anymore", id);
+                return;
+            }
+
             jobItem.Status = SyncJobItemStatus.Synced;
             jobItem.Progress = 100;
 

+ 26 - 6
Emby.Server.Implementations/Sync/TargetDataProvider.cs

@@ -19,6 +19,7 @@ namespace Emby.Server.Implementations.Sync
         private readonly IServerSyncProvider _provider;
 
         private readonly SemaphoreSlim _dataLock = new SemaphoreSlim(1, 1);
+        private readonly SemaphoreSlim _remoteDataLock = new SemaphoreSlim(1, 1);
         private List<LocalItem> _items;
 
         private readonly ILogger _logger;
@@ -63,15 +64,24 @@ namespace Emby.Server.Implementations.Sync
         {
             _logger.Debug("Getting {0} from {1}", string.Join(MediaSync.PathSeparatorString, GetRemotePath().ToArray()), _provider.Name);
 
-            var fileResult = await _provider.GetFiles(GetRemotePath().ToArray(), _target, cancellationToken).ConfigureAwait(false);
+            await _remoteDataLock.WaitAsync(cancellationToken).ConfigureAwait(false);
 
-            if (fileResult.Items.Length > 0)
+            try
             {
-                using (var stream = await _provider.GetFile(fileResult.Items[0].FullName, _target, new Progress<double>(), cancellationToken))
+                var fileResult = await _provider.GetFiles(GetRemotePath().ToArray(), _target, cancellationToken).ConfigureAwait(false);
+
+                if (fileResult.Items.Length > 0)
                 {
-                    return _json.DeserializeFromStream<List<LocalItem>>(stream);
+                    using (var stream = await _provider.GetFile(fileResult.Items[0].FullName, _target, new Progress<double>(), cancellationToken))
+                    {
+                        return _json.DeserializeFromStream<List<LocalItem>>(stream);
+                    }
                 }
             }
+            finally
+            {
+                _remoteDataLock.Release();
+            }
 
             return new List<LocalItem>();
         }
@@ -93,9 +103,19 @@ namespace Emby.Server.Implementations.Sync
                 // Save to sync provider
                 stream.Position = 0;
                 var remotePath = GetRemotePath();
-                _logger.Debug("Saving data.json to {0}. Remote path: {1}", _provider.Name, string.Join("/", remotePath));
 
-                await _provider.SendFile(stream, remotePath, _target, new Progress<double>(), cancellationToken).ConfigureAwait(false);
+                await _remoteDataLock.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+                try
+                {
+                    _logger.Debug("Saving data.json to {0}. Remote path: {1}", _provider.Name, string.Join("/", remotePath));
+
+                    await _provider.SendFile(stream, remotePath, _target, new Progress<double>(), cancellationToken).ConfigureAwait(false);
+                }
+                finally
+                {
+                    _remoteDataLock.Release();
+                }
             }
         }
 

+ 3 - 1
Emby.Server.Implementations/UserViews/DynamicImageProvider.cs

@@ -151,7 +151,9 @@ namespace Emby.Server.Implementations.UserViews
             string[] collectionStripViewTypes =
             {
                 CollectionType.Movies,
-                CollectionType.TvShows
+                CollectionType.TvShows,
+                CollectionType.Playlists,
+                CollectionType.Photos
             };
 
             return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty);

+ 1 - 1
MediaBrowser.Api/Library/LibraryService.cs

@@ -666,7 +666,7 @@ namespace MediaBrowser.Api.Library
                 IncludeItemTypes = new[] { type.Name },
                 Limit = 0,
                 Recursive = true,
-                ExcludeLocationTypes = new[] { LocationType.Virtual },
+                IsVirtualItem = false,
                 SourceTypes = new[] { SourceType.Library },
                 IsFavorite = request.IsFavorite
             };

+ 59 - 6
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -205,7 +205,8 @@ namespace MediaBrowser.Api.Playback
             }
             else
             {
-                args += "-map -0:v";
+                // No known video stream
+                args += "-vn";
             }
 
             if (state.AudioStream != null)
@@ -395,8 +396,6 @@ namespace MediaBrowser.Api.Playback
                 {
                     param += " -crf 23";
                 }
-
-                param += " -tune zerolatency";
             }
 
             else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase))
@@ -536,6 +535,11 @@ namespace MediaBrowser.Api.Playback
                 }
             }
 
+            if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
+            {
+                param += " -x264opts:0 subme=0:rc_lookahead=10:me_range=4:me=dia:no_chroma_me:8x8dct=0:partitions=none";
+            }
+
             if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
                 !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) &&
                 !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
@@ -1492,8 +1496,16 @@ namespace MediaBrowser.Api.Playback
                     return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture));
                 }
 
+                if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
+                {
+                    // h264
+                    return string.Format(" -maxrate {0} -bufsize {1}",
+                        bitrate.Value.ToString(UsCulture),
+                        (bitrate.Value * 2).ToString(UsCulture));
+                }
+
                 // h264
-                return string.Format(" -maxrate {0} -bufsize {1}",
+                return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}",
                     bitrate.Value.ToString(UsCulture),
                     (bitrate.Value * 2).ToString(UsCulture));
             }
@@ -1873,7 +1885,7 @@ namespace MediaBrowser.Api.Playback
                 request.AudioCodec = InferAudioCodec(url);
             }
 
-            var state = new StreamState(MediaSourceManager, Logger)
+            var state = new StreamState(MediaSourceManager, Logger, TranscodingJobType)
             {
                 Request = request,
                 RequestedUrl = url,
@@ -1975,7 +1987,7 @@ namespace MediaBrowser.Api.Playback
                 if (state.OutputVideoBitrate.HasValue && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
                 {
                     var resolution = ResolutionNormalizer.Normalize(
-                        state.VideoStream == null ? (int?) null : state.VideoStream.BitRate,
+                        state.VideoStream == null ? (int?)null : state.VideoStream.BitRate,
                         state.OutputVideoBitrate.Value,
                         state.VideoStream == null ? null : state.VideoStream.Codec,
                         state.OutputVideoCodec,
@@ -2691,11 +2703,52 @@ namespace MediaBrowser.Api.Playback
                 {
                     //inputModifier += " -noaccurate_seek";
                 }
+
+                foreach (var stream in state.MediaSource.MediaStreams)
+                {
+                    if (!stream.IsExternal && stream.Type != MediaStreamType.Subtitle)
+                    {
+                        if (!string.IsNullOrWhiteSpace(stream.Codec) && stream.Index != -1)
+                        {
+                            var decoder = GetDecoderFromCodec(stream.Codec);
+
+                            if (!string.IsNullOrWhiteSpace(decoder))
+                            {
+                                inputModifier += " -codec:" + stream.Index.ToString(UsCulture) + " " + decoder;
+                            }
+                        }
+                    }
+                }
+                //var videoStream = state.VideoStream;
+                //if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
+                //{
+                //    inputModifier += "  -codec:0 " + videoStream.Codec;
+
+                //    var audioStream = state.AudioStream;
+                //    if (audioStream != null && !string.IsNullOrWhiteSpace(audioStream.Codec))
+                //    {
+                //        inputModifier += "  -codec:1 " + audioStream.Codec;
+                //    }
+                //}
             }
 
             return inputModifier;
         }
 
+        private string GetDecoderFromCodec(string codec)
+        {
+            if (string.Equals(codec, "mp2", StringComparison.OrdinalIgnoreCase))
+            {
+                return null;
+            }
+            if (string.Equals(codec, "aac_latm", StringComparison.OrdinalIgnoreCase))
+            {
+                return null;
+            }
+
+            return codec;
+        }
+
         /// <summary>
         /// Infers the audio codec based on the url
         /// </summary>

+ 26 - 10
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -103,7 +103,7 @@ namespace MediaBrowser.Api.Playback.Hls
                             throw;
                         }
 
-                        var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 3);
+                        var waitForSegments = state.SegmentLength >= 10 ? 2 : 3;
                         await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false);
                     }
                 }
@@ -223,6 +223,7 @@ namespace MediaBrowser.Api.Playback.Hls
         protected Stream GetPlaylistFileStream(string path)
         {
             var tmpPath = path + ".tmp";
+            tmpPath = path;
 
             try
             {
@@ -242,7 +243,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
             var threads = GetNumberOfThreads(state, false);
 
-            var inputModifier = GetInputModifier(state);
+            var inputModifier = GetInputModifier(state, true);
 
             // If isEncoding is true we're actually starting ffmpeg
             var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
@@ -255,10 +256,32 @@ namespace MediaBrowser.Api.Playback.Hls
                     "hls/" + Path.GetFileNameWithoutExtension(outputPath));
             }
 
+            var useGenericSegmenter = false;
+            if (useGenericSegmenter)
+            {
+                var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state);
+
+                var timeDeltaParam = String.Empty;
+
+                return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
+                    inputModifier,
+                    GetInputArgument(state),
+                    threads,
+                    GetMapArgs(state),
+                    GetVideoArguments(state),
+                    GetAudioArguments(state),
+                    state.SegmentLength.ToString(UsCulture),
+                    startNumberParam,
+                    outputPath,
+                    outputTsArg,
+                    timeDeltaParam
+                ).Trim();
+            }
+
             // add when stream copying? 
             // -avoid_negative_ts make_zero -fflags +genpts
 
-            var args = string.Format("{0} {1} {2} -map_metadata -1 -map_chapters -1 -threads {3} {4} {5} -fflags +genpts -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
+            var args = string.Format("{0} {1} {2} -map_metadata -1 -map_chapters -1 -threads {3} {4} {5} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero {6} -hls_time {7} -individual_header_trailer 0 -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
                 itsOffset,
                 inputModifier,
                 GetInputArgument(state),
@@ -286,13 +309,6 @@ namespace MediaBrowser.Api.Playback.Hls
             return 0;
         }
 
-        protected bool IsLiveStream(StreamState state)
-        {
-            var isLiveStream = (state.RunTimeTicks ?? 0) == 0;
-
-            return isLiveStream;
-        }
-
         public BaseHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
         {
         }

+ 37 - 31
MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs

@@ -265,9 +265,15 @@ namespace MediaBrowser.Api.Playback.Hls
             double startSeconds = 0;
             var lengths = GetSegmentLengths(state);
 
+            if (requestedIndex >= lengths.Length)
+            {
+                var msg = string.Format("Invalid segment index requested: {0} - Segment count: {1}", requestedIndex, lengths.Length);
+                throw new ArgumentException(msg);
+            }
+
             for (var i = 0; i < requestedIndex; i++)
             {
-                startSeconds += lengths[requestedIndex];
+                startSeconds += lengths[i];
             }
 
             var position = TimeSpan.FromSeconds(startSeconds).Ticks;
@@ -279,9 +285,15 @@ namespace MediaBrowser.Api.Playback.Hls
             double startSeconds = 0;
             var lengths = GetSegmentLengths(state);
 
+            if (requestedIndex >= lengths.Length)
+            {
+                var msg = string.Format("Invalid segment index requested: {0} - Segment count: {1}", requestedIndex, lengths.Length);
+                throw new ArgumentException(msg);
+            }
+
             for (var i = 0; i <= requestedIndex; i++)
             {
-                startSeconds += lengths[requestedIndex];
+                startSeconds += lengths[i];
             }
 
             var position = TimeSpan.FromSeconds(startSeconds).Ticks;
@@ -348,6 +360,8 @@ namespace MediaBrowser.Api.Playback.Hls
                 return;
             }
 
+            Logger.Debug("Deleting partial HLS file {0}", file.FullName);
+
             try
             {
                 FileSystem.DeleteFile(file.FullName);
@@ -507,7 +521,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
             builder.AppendLine("#EXTM3U");
 
-            var isLiveStream = IsLiveStream(state);
+            var isLiveStream = state.IsSegmentedLiveStream;
 
             var queryStringIndex = Request.RawUrl.IndexOf('?');
             var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
@@ -723,7 +737,7 @@ namespace MediaBrowser.Api.Playback.Hls
 
             foreach (var length in segmentLengths)
             {
-                builder.AppendLine("#EXTINF:" + length.ToString("0.0000", UsCulture) + ",");
+                builder.AppendLine("#EXTINF:" + length.ToString("0.0000", UsCulture) + ", nodesc");
 
                 builder.AppendLine(string.Format("hls1/{0}/{1}{2}{3}",
 
@@ -826,7 +840,7 @@ namespace MediaBrowser.Api.Playback.Hls
                     args += " -bsf:v h264_mp4toannexb";
                 }
 
-                args += " -flags -global_header";
+                //args += " -flags -global_header";
             }
             else
             {
@@ -851,7 +865,7 @@ namespace MediaBrowser.Api.Playback.Hls
                     args += GetGraphicalSubtitleParam(state, codec);
                 }
 
-                args += " -flags -global_header";
+                //args += " -flags -global_header";
             }
 
             if (EnableCopyTs(state) && args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1)
@@ -875,27 +889,25 @@ namespace MediaBrowser.Api.Playback.Hls
             var inputModifier = GetInputModifier(state, false);
 
             // If isEncoding is true we're actually starting ffmpeg
-            var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0";
-
-            var toTimeParam = string.Empty;
-            var timestampOffsetParam = string.Empty;
-
-            if (state.IsOutputVideo && !EnableCopyTs(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0)
-            {
-                timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0);
-            }
+            var startNumber = GetStartNumber(state);
+            var startNumberParam = isEncoding ? startNumber.ToString(UsCulture) : "0";
 
             var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty;
-            var enableSplittingOnNonKeyFrames = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && false;
+            var useGenericSegmenter = true;
 
-            // TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time
-            var hlsProtocolSupportsSplittingByTime = false;
-
-            if (enableSplittingOnNonKeyFrames && !hlsProtocolSupportsSplittingByTime)
+            if (useGenericSegmenter)
             {
                 var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state);
 
-                return string.Format("{0} {10} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} -break_non_keyframes  1 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
+                var timeDeltaParam = String.Empty;
+
+                if (isEncoding && startNumber > 0)
+                {
+                    var startTime = state.SegmentLength * startNumber;
+                    timeDeltaParam = string.Format("-segment_time_delta -{0}", startTime);
+                }
+
+                return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
                     inputModifier,
                     GetInputArgument(state),
                     threads,
@@ -906,27 +918,21 @@ namespace MediaBrowser.Api.Playback.Hls
                     startNumberParam,
                     outputPath,
                     outputTsArg,
-                            toTimeParam
-                    ).Trim();
+                    timeDeltaParam
+                ).Trim();
             }
 
-            var splitByTime = hlsProtocolSupportsSplittingByTime && enableSplittingOnNonKeyFrames;
-            var splitByTimeArg = splitByTime ? " -hls_flags split_by_time" : "";
-
-            return string.Format("{0}{12} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4}{5} {6} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {7}{8} -start_number {9} -hls_list_size {10} -y \"{11}\"",
+            return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {6} -individual_header_trailer 0 -start_number {7} -hls_list_size {8} -y \"{9}\"",
                             inputModifier,
                             GetInputArgument(state),
                             threads,
                             mapArgs,
                             GetVideoArguments(state),
-                            timestampOffsetParam,
                             GetAudioArguments(state),
                             state.SegmentLength.ToString(UsCulture),
-                            splitByTimeArg,
                             startNumberParam,
                             state.HlsListSize.ToString(UsCulture),
-                            outputPath,
-                            toTimeParam
+                            outputPath
                             ).Trim();
         }
 

+ 15 - 4
MediaBrowser.Api/Playback/MediaInfoService.cs

@@ -175,6 +175,15 @@ namespace MediaBrowser.Api.Playback
             return ToOptimizedResult(info);
         }
 
+        private T Clone<T>(T obj)
+        {
+            // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it
+            // Should we move this directly into MediaSourceManager?
+
+            var json = _json.SerializeToString(obj);
+            return _json.DeserializeFromString<T>(json);
+        }
+
         private async Task<PlaybackInfoResponse> GetPlaybackInfo(string id, string userId, string[] supportedLiveMediaTypes, string mediaSourceId = null, string liveStreamId = null)
         {
             var result = new PlaybackInfoResponse();
@@ -217,6 +226,8 @@ namespace MediaBrowser.Api.Playback
             }
             else
             {
+                result.MediaSources = Clone(result.MediaSources);
+
                 result.PlaySessionId = Guid.NewGuid().ToString("N");
             }
 
@@ -227,7 +238,7 @@ namespace MediaBrowser.Api.Playback
             PlaybackInfoResponse result,
             DeviceProfile profile,
             AuthorizationInfo auth,
-            int? maxBitrate,
+            long? maxBitrate,
             long startTimeTicks,
             string mediaSourceId,
             int? audioStreamIndex,
@@ -249,7 +260,7 @@ namespace MediaBrowser.Api.Playback
             MediaSourceInfo mediaSource,
             DeviceProfile profile,
             AuthorizationInfo auth,
-            int? maxBitrate,
+            long? maxBitrate,
             long startTimeTicks,
             string mediaSourceId,
             int? audioStreamIndex,
@@ -383,7 +394,7 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
-        private int? GetMaxBitrate(int? clientMaxBitrate)
+        private long? GetMaxBitrate(long? clientMaxBitrate)
         {
             var maxBitrate = clientMaxBitrate;
             var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit;
@@ -425,7 +436,7 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
-        private void SortMediaSources(PlaybackInfoResponse result, int? maxBitrate)
+        private void SortMediaSources(PlaybackInfoResponse result, long? maxBitrate)
         {
             var originalList = result.MediaSources.ToList();
 

+ 12 - 6
MediaBrowser.Api/Playback/StreamState.cs

@@ -89,21 +89,25 @@ namespace MediaBrowser.Api.Playback
                         return 10;
                     }
 
-                    if (!RunTimeTicks.HasValue)
+                    if (IsSegmentedLiveStream)
                     {
                         return 3;
                     }
                     return 6;
                 }
 
-                if (!RunTimeTicks.HasValue)
-                {
-                    return 3;
-                }
                 return 3;
             }
         }
 
+        public bool IsSegmentedLiveStream
+        {
+            get
+            {
+                return TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue;
+            }
+        }
+
         public int HlsListSize
         {
             get
@@ -123,8 +127,9 @@ namespace MediaBrowser.Api.Playback
         public List<string> SupportedAudioCodecs { get; set; }
         public List<string> SupportedVideoCodecs { get; set; }
         public string UserAgent { get; set; }
+        public TranscodingJobType TranscodingType { get; set; }
 
-        public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger)
+        public StreamState(IMediaSourceManager mediaSourceManager, ILogger logger, TranscodingJobType transcodingType)
         {
             _mediaSourceManager = mediaSourceManager;
             _logger = logger;
@@ -132,6 +137,7 @@ namespace MediaBrowser.Api.Playback
             SupportedVideoCodecs = new List<string>();
             PlayableStreamFileNames = new List<string>();
             RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+            TranscodingType = transcodingType;
         }
 
         public string InputAudioSync { get; set; }

+ 5 - 1
MediaBrowser.Api/Reports/ReportsService.cs

@@ -293,7 +293,11 @@ namespace MediaBrowser.Api.Reports
             // ExcludeLocationTypes
             if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
             {
-                query.ExcludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray();
+                var excludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray();
+                if (excludeLocationTypes.Contains(LocationType.Virtual))
+                {
+                    query.IsVirtualItem = false;
+                }
             }
 
             if (!string.IsNullOrEmpty(request.LocationTypes))

+ 4 - 0
MediaBrowser.Api/Session/SessionsService.cs

@@ -243,6 +243,8 @@ namespace MediaBrowser.Api.Session
         [ApiMember(Name = "SupportsPersistentIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")]
         public bool SupportsPersistentIdentifier { get; set; }
 
+        public bool SupportsContentUploading { get; set; }
+
         public PostCapabilities()
         {
             SupportsPersistentIdentifier = true;
@@ -559,6 +561,8 @@ namespace MediaBrowser.Api.Session
 
                 SupportsSync = request.SupportsSync,
 
+                SupportsContentUploading = request.SupportsContentUploading,
+
                 SupportsPersistentIdentifier = request.SupportsPersistentIdentifier
             });
         }

+ 5 - 1
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -318,7 +318,11 @@ namespace MediaBrowser.Api.UserLibrary
             // ExcludeLocationTypes
             if (!string.IsNullOrEmpty(request.ExcludeLocationTypes))
             {
-                query.ExcludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray();
+                var excludeLocationTypes = request.ExcludeLocationTypes.Split(',').Select(d => (LocationType)Enum.Parse(typeof(LocationType), d, true)).ToArray();
+                if (excludeLocationTypes.Contains(LocationType.Virtual))
+                {
+                    query.IsVirtualItem = false;
+                }
             }
 
             if (!string.IsNullOrEmpty(request.LocationTypes))

+ 1 - 8
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -404,13 +404,6 @@ namespace MediaBrowser.Api.UserLibrary
         /// <param name="request">The request.</param>
         /// <returns>System.Object.</returns>
         public object Get(GetLocalTrailers request)
-        {
-            var result = GetAsync(request);
-
-            return ToOptimizedSerializedResultUsingCache(result);
-        }
-
-        private List<BaseItemDto> GetAsync(GetLocalTrailers request)
         {
             var user = _userManager.GetUserById(request.UserId);
 
@@ -430,7 +423,7 @@ namespace MediaBrowser.Api.UserLibrary
                 .Select(_libraryManager.GetItemById)
                 .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
 
-            return dtos.ToList();
+            return ToOptimizedSerializedResultUsingCache(dtos);
         }
 
         /// <summary>

+ 0 - 6
MediaBrowser.Common/IApplicationHost.cs

@@ -36,12 +36,6 @@ namespace MediaBrowser.Common
         /// </summary>
         event EventHandler<GenericEventArgs<PackageVersionInfo>> ApplicationUpdated;
 
-        /// <summary>
-        /// Gets a value indicating whether this instance is running as service.
-        /// </summary>
-        /// <value><c>true</c> if this instance is running as service; otherwise, <c>false</c>.</value>
-        bool IsRunningAsService { get; }
-
         /// <summary>
         /// Gets or sets a value indicating whether this instance has pending kernel reload.
         /// </summary>

+ 8 - 10
MediaBrowser.Controller/Channels/ChannelMediaInfo.cs

@@ -49,9 +49,11 @@ namespace MediaBrowser.Controller.Channels
             SupportsDirectPlay = true;
         }
 
-        public MediaSourceInfo ToMediaSource()
+        public MediaSourceInfo ToMediaSource(Guid itemId)
         {
-            var id = Path.GetMD5().ToString("N");
+            var id = string.IsNullOrWhiteSpace(Path) ?
+                itemId.ToString("N") :
+                Path.GetMD5().ToString("N");
 
             var source = new MediaSourceInfo
             {
@@ -65,16 +67,12 @@ namespace MediaBrowser.Controller.Channels
                 Name = id,
                 Id = id,
                 ReadAtNativeFramerate = ReadAtNativeFramerate,
-                SupportsDirectStream = Protocol == MediaProtocol.File,
-                SupportsDirectPlay = SupportsDirectPlay
+                SupportsDirectStream = false,
+                SupportsDirectPlay = SupportsDirectPlay,
+                IsRemote = true
             };
 
-            var bitrate = (AudioBitrate ?? 0) + (VideoBitrate ?? 0);
-
-            if (bitrate > 0)
-            {
-                source.Bitrate = bitrate;
-            }
+            source.InferTotalBitrate();
 
             return source;
         }

Някои файлове не бяха показани, защото твърде много файлове са промени