Ver Fonte

update dlna profiles

Luke Pulverenti há 11 anos atrás
pai
commit
54eb7cb855
83 ficheiros alterados com 1161 adições e 451 exclusões
  1. 2 3
      MediaBrowser.Api/Movies/MoviesService.cs
  2. 134 26
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  3. 10 1
      MediaBrowser.Controller/Collections/ICollectionManager.cs
  4. 1 0
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  5. 1 3
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  6. 1 0
      MediaBrowser.Controller/Entities/Video.cs
  7. 5 0
      MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs
  8. 2 1
      MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
  9. 271 205
      MediaBrowser.Dlna/PlayTo/Device.cs
  10. 28 0
      MediaBrowser.Dlna/PlayTo/DeviceEventArgs.cs
  11. 85 134
      MediaBrowser.Dlna/PlayTo/DlnaController.cs
  12. 13 21
      MediaBrowser.Dlna/PlayTo/PlayToManager.cs
  13. 6 5
      MediaBrowser.Dlna/PlayTo/UpnpContainer.cs
  14. 37 1
      MediaBrowser.Dlna/PlayTo/uBaseObject.cs
  15. 3 5
      MediaBrowser.Dlna/PlayTo/uParser.cs
  16. 2 0
      MediaBrowser.Dlna/Profiles/DefaultProfile.cs
  17. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Default.xml
  18. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
  19. 1 0
      MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
  20. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
  21. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
  22. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
  23. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
  24. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
  25. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
  26. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
  27. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
  28. 1 0
      MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
  29. 1 0
      MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
  30. 1 0
      MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
  31. 10 11
      MediaBrowser.Dlna/Server/ControlHandler.cs
  32. 1 1
      MediaBrowser.Model/Dlna/DeviceProfile.cs
  33. 2 0
      MediaBrowser.Model/Entities/MediaStream.cs
  34. 2 2
      MediaBrowser.Model/Session/PlaybackReports.cs
  35. 4 3
      MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
  36. 7 5
      MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
  37. 24 0
      MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
  38. 8 2
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  39. 0 0
      MediaBrowser.Server.Implementations/Localization/JavaScript/ar.json
  40. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/ca.json
  41. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/cs.json
  42. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/de.json
  43. 0 0
      MediaBrowser.Server.Implementations/Localization/JavaScript/el.json
  44. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/en_GB.json
  45. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json
  46. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/es.json
  47. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json
  48. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json
  49. 0 0
      MediaBrowser.Server.Implementations/Localization/JavaScript/he.json
  50. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/it.json
  51. 0 0
      MediaBrowser.Server.Implementations/Localization/JavaScript/kk.json
  52. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/ms.json
  53. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/nb.json
  54. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json
  55. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json
  56. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json
  57. 0 0
      MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json
  58. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json
  59. 30 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/zh_TW.json
  60. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/ar.json
  61. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/ca.json
  62. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/cs.json
  63. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/de.json
  64. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/el.json
  65. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/en_GB.json
  66. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/en_US.json
  67. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/es.json
  68. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/es_MX.json
  69. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/fr.json
  70. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/he.json
  71. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/it.json
  72. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/kk.json
  73. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/ms.json
  74. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/nb.json
  75. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/nl.json
  76. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json
  77. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json
  78. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/ru.json
  79. 3 1
      MediaBrowser.Server.Implementations/Localization/Server/server.json
  80. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/sv.json
  81. 0 0
      MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json
  82. 2 1
      MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
  83. 3 4
      MediaBrowser.Server.Implementations/Session/SessionManager.cs

+ 2 - 3
MediaBrowser.Api/Movies/MoviesService.cs

@@ -237,7 +237,7 @@ namespace MediaBrowser.Api.Movies
             foreach (var director in directors)
             {
                 var items = allMovies
-                    .Where(i => !_userDataRepository.GetUserData(userId, i.GetUserDataKey()).Played && i.People.Any(p => string.Equals(p.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) && string.Equals(p.Name, director, StringComparison.OrdinalIgnoreCase)))
+                    .Where(i => i.People.Any(p => string.Equals(p.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) && string.Equals(p.Name, director, StringComparison.OrdinalIgnoreCase)))
                     .Take(itemLimit)
                     .ToList();
 
@@ -261,7 +261,7 @@ namespace MediaBrowser.Api.Movies
             foreach (var name in names)
             {
                 var items = allMovies
-                    .Where(i => !_userDataRepository.GetUserData(userId, i.GetUserDataKey()).Played && i.People.Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase)))
+                    .Where(i => i.People.Any(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase)))
                     .Take(itemLimit)
                     .ToList();
 
@@ -286,7 +286,6 @@ namespace MediaBrowser.Api.Movies
             {
                 var similar = SimilarItemsHelper
                     .GetSimilaritems(item, allMovies, SimilarItemsHelper.GetSimiliarityScore)
-                    .Where(i => !_userDataRepository.GetUserData(userId, i.GetUserDataKey()).Played)
                     .Take(itemLimit)
                     .ToList();
 

+ 134 - 26
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Collections;
+using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Movies;
@@ -257,26 +258,28 @@ namespace MediaBrowser.Api.UserLibrary
         /// The _library manager
         /// </summary>
         private readonly ILibraryManager _libraryManager;
-        private readonly ISearchEngine _searchEngine;
         private readonly ILocalizationManager _localization;
 
         private readonly IDtoService _dtoService;
+        private readonly ICollectionManager _collectionManager;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ItemsService" /> class.
         /// </summary>
         /// <param name="userManager">The user manager.</param>
         /// <param name="libraryManager">The library manager.</param>
-        /// <param name="searchEngine">The search engine.</param>
         /// <param name="userDataRepository">The user data repository.</param>
-        public ItemsService(IUserManager userManager, ILibraryManager libraryManager, ISearchEngine searchEngine, IUserDataManager userDataRepository, ILocalizationManager localization, IDtoService dtoService)
+        /// <param name="localization">The localization.</param>
+        /// <param name="dtoService">The dto service.</param>
+        /// <param name="collectionManager">The collection manager.</param>
+        public ItemsService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, ILocalizationManager localization, IDtoService dtoService, ICollectionManager collectionManager)
         {
             _userManager = userManager;
             _libraryManager = libraryManager;
-            _searchEngine = searchEngine;
             _userDataRepository = userDataRepository;
             _localization = localization;
             _dtoService = dtoService;
+            _collectionManager = collectionManager;
         }
 
         /// <summary>
@@ -319,7 +322,7 @@ namespace MediaBrowser.Api.UserLibrary
 
             if (CollapseBoxSetItems(request))
             {
-                items = CollapseItemsWithinBoxSets(items, user);
+                items = _collectionManager.CollapseItemsWithinBoxSets(items, user);
             }
 
             items = ApplySortOrder(request, items, user, _libraryManager);
@@ -1245,38 +1248,143 @@ namespace MediaBrowser.Api.UserLibrary
             return false;
         }
 
-        private IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user)
+        private bool AllowBoxSetCollapsing(GetItems request)
         {
-            var itemsToCollapse = new List<ISupportsBoxSetGrouping>();
-            var boxsets = new List<BaseItem>();
+            if (!string.IsNullOrWhiteSpace(request.Filters))
+            {
+                return false;
+            }
 
-            var list = items.ToList();
+            if (!string.IsNullOrWhiteSpace(request.AllGenres))
+            {
+                return false;
+            }
 
-            foreach (var item in list.OfType<ISupportsBoxSetGrouping>())
+            if (!string.IsNullOrWhiteSpace(request.Genres))
             {
-                var currentBoxSets = item.BoxSetIdList
-                    .Select(i => _libraryManager.GetItemById(i))
-                    .Where(i => i != null && i.IsVisible(user))
-                    .ToList();
+                return false;
+            }
 
-                if (currentBoxSets.Count > 0)
-                {
-                    itemsToCollapse.Add(item);
-                    boxsets.AddRange(currentBoxSets);
-                }
+            if (request.HasImdbId.HasValue)
+            {
+                return false;
             }
 
-            return list.Except(itemsToCollapse.Cast<BaseItem>()).Concat(boxsets).Distinct();
-        }
+            if (request.HasOfficialRating.HasValue)
+            {
+                return false;
+            }
 
-        private bool AllowBoxSetCollapsing(GetItems request)
-        {
-            // Only allow when using default sort order
-            if (!string.IsNullOrEmpty(request.SortBy) && !string.Equals(request.SortBy, "SortName", StringComparison.OrdinalIgnoreCase))
+            if (request.HasOverview.HasValue)
             {
                 return false;
             }
 
+            if (request.HasParentalRating.HasValue)
+            {
+                return false;
+            }
+
+            if (request.HasSpecialFeature.HasValue)
+            {
+                return false;
+            }
+
+            if (request.HasSubtitles.HasValue)
+            {
+                return false;
+            }
+
+            if (request.HasThemeSong.HasValue)
+            {
+                return false;
+            }
+
+            if (request.HasThemeVideo.HasValue)
+            {
+                return false;
+            }
+
+            if (request.HasTmdbId.HasValue)
+            {
+                return false;
+            }
+
+            if (request.HasTrailer.HasValue)
+            {
+                return false;
+            }
+
+            if (!string.IsNullOrWhiteSpace(request.Ids))
+            {
+                return false;
+            }
+
+            if (!string.IsNullOrWhiteSpace(request.ImageTypes))
+            {
+                return false;
+            }
+
+            if (request.Is3D.HasValue)
+            {
+                return false;
+            }
+
+            if (request.IsHD.HasValue)
+            {
+                return false;
+            }
+
+            if (request.IsInBoxSet.HasValue)
+            {
+                return false;
+            }
+
+            if (request.IsLocked.HasValue)
+            {
+                return false;
+            }
+
+            if (request.IsPlaceHolder.HasValue)
+            {
+                return false;
+            }
+
+            if (request.IsPlayed.HasValue)
+            {
+                return false;
+            }
+
+            if (request.IsUnidentified.HasValue)
+            {
+                return false;
+            }
+
+            if (request.IsYearMismatched.HasValue)
+            {
+                return false;
+            }
+
+            if (!string.IsNullOrWhiteSpace(request.Person))
+            {
+                return false;
+            }
+
+            if (!string.IsNullOrWhiteSpace(request.Studios))
+            {
+                return false;
+            }
+
+            if (!string.IsNullOrWhiteSpace(request.VideoTypes))
+            {
+                return false;
+            }
+
+            if (!string.IsNullOrWhiteSpace(request.Years))
+            {
+                return false;
+            }
+          
             return true;
         }
 

+ 10 - 1
MediaBrowser.Controller/Collections/ICollectionManager.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
 using System;
 using System.Collections.Generic;
 using System.Threading.Tasks;
@@ -29,5 +30,13 @@ namespace MediaBrowser.Controller.Collections
         /// <param name="itemIds">The item ids.</param>
         /// <returns>Task.</returns>
         Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds);
+
+        /// <summary>
+        /// Collapses the items within box sets.
+        /// </summary>
+        /// <param name="items">The items.</param>
+        /// <param name="user">The user.</param>
+        /// <returns>IEnumerable{BaseItem}.</returns>
+        IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user);
     }
 }

+ 1 - 0
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -15,6 +15,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         public string FormatName { get; set; }
         public long? Size { get; set; }
         public string Container { get; set; }
+        public int? TotalBitrate { get; set; }
 
         public Audio()
         {

+ 1 - 3
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -129,14 +129,13 @@ namespace MediaBrowser.Controller.Entities
             return NullTaskResult;
         }
 
-        private List<LinkedChild> _linkedChildren;
         /// <summary>
         /// Our children are actually just references to the ones in the physical root...
         /// </summary>
         /// <value>The linked children.</value>
         public override List<LinkedChild> LinkedChildren
         {
-            get { return _linkedChildren ?? (_linkedChildren = GetLinkedChildrenInternal()); }
+            get { return GetLinkedChildrenInternal(); }
             set
             {
                 base.LinkedChildren = value;
@@ -175,7 +174,6 @@ namespace MediaBrowser.Controller.Entities
         public void ResetDynamicChildren()
         {
             _actualChildren = null;
-            _linkedChildren = null;
         }
     }
 }

+ 1 - 0
MediaBrowser.Controller/Entities/Video.cs

@@ -29,6 +29,7 @@ namespace MediaBrowser.Controller.Entities
         public string FormatName { get; set; }
         public long? Size { get; set; }
         public string Container { get; set; }
+        public int? TotalBitrate { get; set; }
 
         public Video()
         {

+ 5 - 0
MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs

@@ -134,6 +134,11 @@ namespace MediaBrowser.Controller.MediaEncoding
             if (data.format != null)
             {
                 info.Format = data.format.format_name;
+
+                if (!string.IsNullOrEmpty(data.format.bit_rate))
+                {
+                    info.TotalBitrate = int.Parse(data.format.bit_rate, UsCulture);
+                }
             }
 
             return info;

+ 2 - 1
MediaBrowser.Dlna/MediaBrowser.Dlna.csproj

@@ -58,6 +58,7 @@
     <Compile Include="PlayTo\Device.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="PlayTo\DeviceEventArgs.cs" />
     <Compile Include="PlayTo\DeviceInfo.cs" />
     <Compile Include="Common\DeviceService.cs" />
     <Compile Include="PlayTo\DidlBuilder.cs" />
@@ -86,7 +87,7 @@
     <Compile Include="PlayTo\TransportCommands.cs" />
     <Compile Include="PlayTo\TransportStateEventArgs.cs" />
     <Compile Include="PlayTo\uBaseObject.cs" />
-    <Compile Include="PlayTo\uContainer.cs" />
+    <Compile Include="PlayTo\UpnpContainer.cs" />
     <Compile Include="Common\DeviceIcon.cs" />
     <Compile Include="PlayTo\uParser.cs" />
     <Compile Include="PlayTo\uPnpNamespaces.cs" />

+ 271 - 205
MediaBrowser.Dlna/PlayTo/Device.cs

@@ -20,34 +20,12 @@ namespace MediaBrowser.Dlna.PlayTo
         #region Fields & Properties
 
         private Timer _timer;
+        private Timer _volumeTimer;
 
         public DeviceInfo Properties { get; set; }
 
         private int _muteVol;
-        public bool IsMuted
-        {
-            get
-            {
-                return _muteVol > 0;
-            }
-        }
-
-        private string _currentId = String.Empty;
-        public string CurrentId
-        {
-            get
-            {
-                return _currentId;
-            }
-            set
-            {
-                if (_currentId == value)
-                    return;
-                _currentId = value;
-
-                NotifyCurrentIdChanged(value);
-            }
-        }
+        public bool IsMuted { get; set; }
 
         public int Volume { get; set; }
 
@@ -66,23 +44,7 @@ namespace MediaBrowser.Dlna.PlayTo
             }
         }
 
-        private TRANSPORTSTATE _transportState = TRANSPORTSTATE.STOPPED;
-        public TRANSPORTSTATE TransportState
-        {
-            get
-            {
-                return _transportState;
-            }
-            set
-            {
-                if (_transportState == value)
-                    return;
-
-                _transportState = value;
-
-                NotifyPlaybackChanged(value);
-            }
-        }
+        public TRANSPORTSTATE TransportState { get; private set; }
 
         public bool IsPlaying
         {
@@ -92,14 +54,6 @@ namespace MediaBrowser.Dlna.PlayTo
             }
         }
 
-        public bool IsTransitioning
-        {
-            get
-            {
-                return (TransportState == TRANSPORTSTATE.TRANSITIONING);
-            }
-        }
-
         public bool IsPaused
         {
             get
@@ -134,7 +88,12 @@ namespace MediaBrowser.Dlna.PlayTo
 
         private int GetPlaybackTimerIntervalMs()
         {
-            return 2000;
+            return 1000;
+        }
+
+        private int GetVolumeTimerIntervalMs()
+        {
+            return 5000;
         }
 
         private int GetInactiveTimerIntervalMs()
@@ -146,77 +105,135 @@ namespace MediaBrowser.Dlna.PlayTo
         {
             UpdateTime = DateTime.UtcNow;
 
-            var interval = GetPlaybackTimerIntervalMs();
+            _timer = new Timer(TimerCallback, null, GetPlaybackTimerIntervalMs(), GetInactiveTimerIntervalMs());
+
+            _volumeTimer = new Timer(VolumeTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
 
-            _timer = new Timer(TimerCallback, null, interval, interval);
+            _timerActive = false;
         }
 
+        private readonly object _timerLock = new object();
+        private bool _timerActive;
         private void RestartTimer()
         {
-            var interval = GetPlaybackTimerIntervalMs();
+            if (!_timerActive)
+            {
+                lock (_timerLock)
+                {
+                    if (!_timerActive)
+                    {
+                        _timer.Change(10, GetPlaybackTimerIntervalMs());
 
-            _timer.Change(interval, interval);
-        }
+                        _volumeTimer.Change(100, GetVolumeTimerIntervalMs());
+                    }
 
+                    _timerActive = true;
+                }
+            }
+        }
 
         /// <summary>
         /// Restarts the timer in inactive mode.
         /// </summary>
         private void RestartTimerInactive()
         {
-            var interval = GetInactiveTimerIntervalMs();
+            if (_timerActive)
+            {
+                lock (_timerLock)
+                {
+                    if (_timerActive)
+                    {
+                        var interval = GetInactiveTimerIntervalMs();
 
-            _timer.Change(interval, interval);
-        }
+                        _timer.Change(interval, interval);
+                        _volumeTimer.Change(Timeout.Infinite, Timeout.Infinite);
+                    }
 
-        private void StopTimer()
-        {
-            _timer.Change(Timeout.Infinite, Timeout.Infinite);
+                    _timerActive = false;
+                }
+            }
         }
 
         #region Commanding
 
-        public Task<bool> VolumeDown(bool mute = false)
+        public Task VolumeDown()
         {
-            var sendVolume = (Volume - 5) > 0 ? Volume - 5 : 0;
-            if (mute && _muteVol == 0)
-            {
-                sendVolume = 0;
-                _muteVol = Volume;
-            }
+            var sendVolume = Math.Max(Volume - 5, 0);
+
             return SetVolume(sendVolume);
         }
 
-        public Task<bool> VolumeUp(bool unmute = false)
+        public Task VolumeUp()
         {
-            var sendVolume = (Volume + 5) < 100 ? Volume + 5 : 100;
-            if (unmute && _muteVol > 0)
-                sendVolume = _muteVol;
-            _muteVol = 0;
+            var sendVolume = Math.Min(Volume + 5, 100);
+
             return SetVolume(sendVolume);
         }
 
         public Task ToggleMute()
         {
-            if (_muteVol == 0)
+            if (IsMuted)
             {
-                _muteVol = Volume;
-                return SetVolume(0);
+                return Unmute();
+            }
+
+            return Mute();
+        }
+
+        public async Task Mute()
+        {
+            var success = await SetMute(true).ConfigureAwait(true);
+
+            if (!success)
+            {
+                await SetVolume(0).ConfigureAwait(false);
             }
+        }
+
+        public async Task Unmute()
+        {
+            var success = await SetMute(false).ConfigureAwait(true);
+
+            if (!success)
+            {
+                var sendVolume = _muteVol <= 0 ? 20 : _muteVol;
 
-            var tmp = _muteVol;
-            _muteVol = 0;
-            return SetVolume(tmp);
+                await SetVolume(sendVolume).ConfigureAwait(false);
+            }
+        }
+
+        private async Task<bool> SetMute(bool mute)
+        {
+            var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
+            if (command == null)
+                return false;
+
+            var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
+
+            if (service == null)
+            {
+                return false;
+            }
+
+            _logger.Debug("Setting mute");
+            var value = mute ? 1 : 0;
+
+            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, value))
+                .ConfigureAwait(false);
+
+            IsMuted = mute;
+
+            return true;
         }
 
         /// <summary>
         /// Sets volume on a scale of 0-100
         /// </summary>
-        public async Task<bool> SetVolume(int value)
+        public async Task SetVolume(int value)
         {
             var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume");
             if (command == null)
-                return true;
+                return;
 
             var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
 
@@ -225,17 +242,19 @@ namespace MediaBrowser.Dlna.PlayTo
                 throw new InvalidOperationException("Unable to find service");
             }
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, value))
-                .ConfigureAwait(false);
+            // Set it early and assume it will succeed
+            // Remote control will perform better
             Volume = value;
-            return true;
+
+            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType, value))
+                .ConfigureAwait(false);
         }
 
-        public async Task<TimeSpan> Seek(TimeSpan value)
+        public async Task Seek(TimeSpan value)
         {
             var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Seek");
             if (command == null)
-                return value;
+                return;
 
             var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
 
@@ -244,22 +263,17 @@ namespace MediaBrowser.Dlna.PlayTo
                 throw new InvalidOperationException("Unable to find service");
             }
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, String.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
+            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, String.Format("{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
                 .ConfigureAwait(false);
-
-            return value;
         }
 
-        public async Task<bool> SetAvTransport(string url, string header, string metaData)
+        public async Task SetAvTransport(string url, string header, string metaData)
         {
-            StopTimer();
-
             await SetStop().ConfigureAwait(false);
-            CurrentId = null;
 
             var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
             if (command == null)
-                return false;
+                return;
 
             var dictionary = new Dictionary<string, string>
             {
@@ -274,18 +288,13 @@ namespace MediaBrowser.Dlna.PlayTo
                 throw new InvalidOperationException("Unable to find service");
             }
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, url, dictionary), header)
+            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, url, dictionary), header)
                 .ConfigureAwait(false);
 
-
             await Task.Delay(50).ConfigureAwait(false);
             await SetPlay().ConfigureAwait(false);
 
-
-            _lapsCount = GetLapsCount();
             RestartTimer();
-
-            return true;
         }
 
         private string CreateDidlMeta(string value)
@@ -300,11 +309,11 @@ namespace MediaBrowser.Dlna.PlayTo
 
         private const string BaseDidl = "&lt;DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"&gt;{0}&lt;/DIDL-Lite&gt;";
 
-        public async Task<bool> SetNextAvTransport(string value, string header, string metaData)
+        public async Task SetNextAvTransport(string value, string header, string metaData)
         {
             var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetNextAVTransportURI");
             if (command == null)
-                return false;
+                return;
 
             var dictionary = new Dictionary<string, string>
             {
@@ -319,19 +328,17 @@ namespace MediaBrowser.Dlna.PlayTo
                 throw new InvalidOperationException("Unable to find service");
             }
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, value, dictionary), header)
+            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, value, dictionary), header)
                 .ConfigureAwait(false);
 
-            await Task.Delay(100).ConfigureAwait(false);
-
-            return true;
+            RestartTimer();
         }
 
-        public async Task<bool> SetPlay()
+        public async Task SetPlay()
         {
             var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
             if (command == null)
-                return false;
+                return;
 
             var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
 
@@ -340,81 +347,73 @@ namespace MediaBrowser.Dlna.PlayTo
                 throw new InvalidOperationException("Unable to find service");
             }
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
+            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
                 .ConfigureAwait(false);
-
-            _lapsCount = GetLapsCount();
-            return true;
         }
 
-        public async Task<bool> SetStop()
+        public async Task SetStop()
         {
             var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
             if (command == null)
-                return false;
+                return;
 
             var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
+            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
                 .ConfigureAwait(false);
-            await Task.Delay(50).ConfigureAwait(false);
-            return true;
         }
 
-        public async Task<bool> SetPause()
+        public async Task SetPause()
         {
             var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
             if (command == null)
-                return false;
+                return;
 
             var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
 
-            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
+            await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1))
                 .ConfigureAwait(false);
 
-            await Task.Delay(50).ConfigureAwait(false);
-            TransportState = TRANSPORTSTATE.PAUSED_PLAYBACK;
-            return true;
+            TransportState = TRANSPORTSTATE.PAUSED;
         }
 
         #endregion
 
         #region Get data
 
-        private int GetLapsCount()
-        {
-            // No need to get all data every lap, just every X time. 
-            return 10;
-        }
-
-        int _lapsCount = 0;
-
         private async void TimerCallback(object sender)
         {
             if (_disposed)
                 return;
 
-            StopTimer();
-
             try
             {
-                await GetTransportInfo().ConfigureAwait(false);
+                var transportState = await GetTransportInfo().ConfigureAwait(false);
 
-                //If we're not playing anything no need to get additional data
-                if (TransportState != TRANSPORTSTATE.STOPPED)
+                if (transportState.HasValue)
                 {
-                    var hasTrack = await GetPositionInfo().ConfigureAwait(false);
+                    UpdateTime = DateTime.UtcNow;
 
-                    // TODO: Why make these requests if hasTrack==false?
-                    // TODO ANSWER Some vendors don't include track in GetPositionInfo, use GetMediaInfo instead.
-                    if (_lapsCount > GetLapsCount())
+                    // If we're not playing anything no need to get additional data
+                    if (transportState.Value == TRANSPORTSTATE.STOPPED)
                     {
-                        if (!hasTrack)
+                        UpdateMediaInfo(null, transportState.Value);
+                    }
+                    else
+                    {
+                        var tuple = await GetPositionInfo().ConfigureAwait(false);
+
+                        var currentObject = tuple.Item2;
+
+                        if (tuple.Item1 && currentObject == null)
+                        {
+                            currentObject = await GetMediaInfo().ConfigureAwait(false);
+                        }
+
+                        if (currentObject != null)
                         {
-                            await GetMediaInfo().ConfigureAwait(false);
+                            UpdateMediaInfo(currentObject, transportState.Value);
                         }
-                        await GetVolume().ConfigureAwait(false);
-                        _lapsCount = 0;
                     }
                 }
             }
@@ -423,16 +422,27 @@ namespace MediaBrowser.Dlna.PlayTo
                 _logger.ErrorException("Error updating device info", ex);
             }
 
-            _lapsCount++;
-
             if (_disposed)
                 return;
 
-            //If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
-            if (TransportState != TRANSPORTSTATE.STOPPED)
-                RestartTimer();
-            else
+            // If we're not playing anything make sure we don't get data more often than neccessry to keep the Session alive
+            if (TransportState == TRANSPORTSTATE.STOPPED)
                 RestartTimerInactive();
+            else
+                RestartTimer();
+        }
+
+        private async void VolumeTimerCallback(object sender)
+        {
+            try
+            {
+                await GetVolume().ConfigureAwait(false);
+                await GetMute().ConfigureAwait(false);
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error updating device info", ex);
+            }
         }
 
         private async Task GetVolume()
@@ -462,28 +472,52 @@ namespace MediaBrowser.Dlna.PlayTo
 
             Volume = int.Parse(volumeValue, UsCulture);
 
-            //Reset the Mute value if Volume is bigger than zero
-            if (Volume > 0 && _muteVol > 0)
+            if (Volume > 0)
             {
-                _muteVol = 0;
+                _muteVol = Volume;
             }
         }
 
-        private async Task GetTransportInfo()
+        private async Task GetMute()
         {
-            var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
+            var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
             if (command == null)
                 return;
 
-            var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
+            var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType);
+
             if (service == null)
+            {
+                throw new InvalidOperationException("Unable to find service");
+            }
+
+            var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, RendererCommands.BuildPost(command, service.ServiceType))
+                .ConfigureAwait(false);
+
+            if (result == null || result.Document == null)
                 return;
 
+            var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse").Select(i => i.Element("CurrentMute")).FirstOrDefault(i => i != null);
+            var value = valueNode == null ? null : valueNode.Value;
+
+            IsMuted = string.Equals(value, "1", StringComparison.OrdinalIgnoreCase);
+        }
+
+        private async Task<TRANSPORTSTATE?> GetTransportInfo()
+        {
+            var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
+            if (command == null)
+                return null;
+
+            var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
+            if (service == null)
+                return null;
+
             var result = await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType))
                 .ConfigureAwait(false);
 
             if (result == null || result.Document == null)
-                return;
+                return null;
 
             var transportState =
                 result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null);
@@ -496,18 +530,18 @@ namespace MediaBrowser.Dlna.PlayTo
 
                 if (Enum.TryParse(transportStateValue, true, out state))
                 {
-                    TransportState = state;
+                    return state;
                 }
             }
 
-            UpdateTime = DateTime.UtcNow;
+            return null;
         }
 
-        private async Task GetMediaInfo()
+        private async Task<uBaseObject> GetMediaInfo()
         {
             var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
             if (command == null)
-                return;
+                return null;
 
             var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
 
@@ -520,33 +554,25 @@ namespace MediaBrowser.Dlna.PlayTo
                 .ConfigureAwait(false);
 
             if (result == null || result.Document == null)
-                return;
+                return null;
 
             var track = result.Document.Descendants("CurrentURIMetaData").FirstOrDefault();
 
             if (track == null)
             {
-                CurrentId = null;
-                return;
+                return null;
             }
 
             var e = track.Element(uPnpNamespaces.items) ?? track;
 
-            var uTrack = uParser.CreateObjectFromXML(new uParserObject
-            {
-                Type = e.GetValue(uPnpNamespaces.uClass),
-                Element = e
-            });
-
-            if (uTrack != null)
-                CurrentId = uTrack.Id;
+            return UpnpContainer.Create(e);
         }
 
-        private async Task<bool> GetPositionInfo()
+        private async Task<Tuple<bool, uBaseObject>> GetPositionInfo()
         {
             var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
             if (command == null)
-                return true;
+                return new Tuple<bool, uBaseObject>(false, null);
 
             var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType);
 
@@ -559,7 +585,7 @@ namespace MediaBrowser.Dlna.PlayTo
                 .ConfigureAwait(false);
 
             if (result == null || result.Document == null)
-                return true;
+                return new Tuple<bool, uBaseObject>(false, null);
 
             var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null);
             var duration = durationElem == null ? null : durationElem.Value;
@@ -582,14 +608,14 @@ namespace MediaBrowser.Dlna.PlayTo
             if (track == null)
             {
                 //If track is null, some vendors do this, use GetMediaInfo instead                    
-                return false;
+                return new Tuple<bool, uBaseObject>(true, null);
             }
 
             var trackString = (string)track;
 
             if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
             {
-                return false;
+                return new Tuple<bool, uBaseObject>(true, null);
             }
 
             XElement uPnpResponse;
@@ -601,19 +627,14 @@ namespace MediaBrowser.Dlna.PlayTo
             catch
             {
                 _logger.Error("Unable to parse xml {0}", trackString);
-                return false;
+                return new Tuple<bool, uBaseObject>(true, null);
             }
 
             var e = uPnpResponse.Element(uPnpNamespaces.items);
 
             var uTrack = CreateUBaseObject(e);
 
-            if (uTrack == null)
-                return true;
-
-            CurrentId = uTrack.Id;
-
-            return true;
+            return new Tuple<bool, uBaseObject>(true, uTrack);
         }
 
         private static uBaseObject CreateUBaseObject(XElement container)
@@ -858,29 +879,64 @@ namespace MediaBrowser.Dlna.PlayTo
             };
         }
 
-        #region Events
+        public event EventHandler<PlaybackStartEventArgs> PlaybackStart;
+        public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
+        public event EventHandler<PlaybackStoppedEventArgs> PlaybackStopped;
+
+        private uBaseObject _lastMediaInfo;
+        private void UpdateMediaInfo(uBaseObject mediaInfo, TRANSPORTSTATE state)
+        {
+            TransportState = state;
+
+            var previousMediaInfo = _lastMediaInfo;
+            _lastMediaInfo = mediaInfo;
 
-        public event EventHandler<TransportStateEventArgs> PlaybackChanged;
-        public event EventHandler<CurrentIdEventArgs> CurrentIdChanged;
+            if (previousMediaInfo == null && mediaInfo != null)
+            {
+                OnPlaybackStart(mediaInfo);
+            }
+            else if (mediaInfo == null && previousMediaInfo != null)
+            {
+                OnPlaybackStop(previousMediaInfo);
+            }
+            else if (mediaInfo != null && mediaInfo.Equals(previousMediaInfo))
+            {
+                OnPlaybackProgress(mediaInfo);
+            }
+        }
 
-        private void NotifyPlaybackChanged(TRANSPORTSTATE state)
+        private void OnPlaybackStart(uBaseObject mediaInfo)
         {
-            if (PlaybackChanged != null)
+            if (PlaybackStart != null)
             {
-                PlaybackChanged.Invoke(this, new TransportStateEventArgs
+                PlaybackStart.Invoke(this, new PlaybackStartEventArgs
                 {
-                    State = state
+                    MediaInfo = mediaInfo
                 });
             }
         }
 
-        private void NotifyCurrentIdChanged(string value)
+        private void OnPlaybackProgress(uBaseObject mediaInfo)
         {
-            if (CurrentIdChanged != null)
-                CurrentIdChanged.Invoke(this, new CurrentIdEventArgs { Id = value });
+            if (PlaybackProgress != null)
+            {
+                PlaybackProgress.Invoke(this, new PlaybackProgressEventArgs
+                {
+                    MediaInfo = mediaInfo
+                });
+            }
         }
 
-        #endregion
+        private void OnPlaybackStop(uBaseObject mediaInfo)
+        {
+            if (PlaybackStopped != null)
+            {
+                PlaybackStopped.Invoke(this, new PlaybackStoppedEventArgs
+                {
+                    MediaInfo = mediaInfo
+                });
+            }
+        }
 
         #region IDisposable
 
@@ -890,7 +946,27 @@ namespace MediaBrowser.Dlna.PlayTo
             if (!_disposed)
             {
                 _disposed = true;
+
+                DisposeTimer();
+                DisposeVolumeTimer();
+            }
+        }
+
+        private void DisposeTimer()
+        {
+            if (_timer != null)
+            {
                 _timer.Dispose();
+                _timer = null;
+            }
+        }
+
+        private void DisposeVolumeTimer()
+        {
+            if (_volumeTimer != null)
+            {
+                _volumeTimer.Dispose();
+                _volumeTimer = null;
             }
         }
 
@@ -900,15 +976,5 @@ namespace MediaBrowser.Dlna.PlayTo
         {
             return String.Format("{0} - {1}", Properties.Name, Properties.BaseUrl);
         }
-
-    }
-
-    public enum TRANSPORTSTATE
-    {
-        STOPPED,
-        PLAYING,
-        TRANSITIONING,
-        PAUSED_PLAYBACK,
-        PAUSED
     }
 }

+ 28 - 0
MediaBrowser.Dlna/PlayTo/DeviceEventArgs.cs

@@ -0,0 +1,28 @@
+using System;
+
+namespace MediaBrowser.Dlna.PlayTo
+{
+    public class PlaybackStartEventArgs : EventArgs
+    {
+        public uBaseObject MediaInfo { get; set; }
+    }
+
+    public class PlaybackProgressEventArgs : EventArgs
+    {
+        public uBaseObject MediaInfo { get; set; }
+    }
+
+    public class PlaybackStoppedEventArgs : EventArgs
+    {
+        public uBaseObject MediaInfo { get; set; }
+    }
+    
+    public enum TRANSPORTSTATE
+    {
+        STOPPED,
+        PLAYING,
+        TRANSITIONING,
+        PAUSED_PLAYBACK,
+        PAUSED
+    }
+}

+ 85 - 134
MediaBrowser.Dlna/PlayTo/DlnaController.cs

@@ -24,7 +24,6 @@ namespace MediaBrowser.Dlna.PlayTo
     public class PlayToController : ISessionController, IDisposable
     {
         private Device _device;
-        private BaseItem _currentItem;
         private readonly SessionInfo _session;
         private readonly ISessionManager _sessionManager;
         private readonly IItemRepository _itemRepository;
@@ -35,9 +34,6 @@ namespace MediaBrowser.Dlna.PlayTo
         private readonly IUserManager _userManager;
         private readonly IServerApplicationHost _appHost;
         private readonly IDtoService _dtoService;
-        private bool _playbackStarted;
-
-        private const int UpdateTimerIntervalMs = 1000;
 
         public bool SupportsMediaRemoteControl
         {
@@ -72,157 +68,124 @@ namespace MediaBrowser.Dlna.PlayTo
         public void Init(Device device)
         {
             _device = device;
-            _device.PlaybackChanged += Device_PlaybackChanged;
-            _device.CurrentIdChanged += Device_CurrentIdChanged;
+            _device.PlaybackStart += _device_PlaybackStart;
+            _device.PlaybackProgress += _device_PlaybackProgress;
+            _device.PlaybackStopped += _device_PlaybackStopped;
             _device.Start();
 
-            _updateTimer = new Timer(updateTimer_Elapsed, null, UpdateTimerIntervalMs, UpdateTimerIntervalMs);
+            _updateTimer = new Timer(updateTimer_Elapsed, null, 30000, 30000);
         }
 
-        #region Device EventHandlers & Update Timer
-
-        Timer _updateTimer;
-
-        async void Device_PlaybackChanged(object sender, TransportStateEventArgs e)
+        async void _device_PlaybackStopped(object sender, PlaybackStoppedEventArgs e)
         {
-            if (_currentItem == null)
-                return;
-
-            if (e.State == TRANSPORTSTATE.STOPPED)
-                await ReportProgress().ConfigureAwait(false);
-
-            else if (e.State == TRANSPORTSTATE.STOPPED && _playbackStarted)
+            try
             {
-                _playbackStarted = false;
-
                 await _sessionManager.OnPlaybackStopped(new PlaybackStopInfo
                 {
-                    ItemId = _currentItem.Id.ToString("N"),
+                    ItemId = e.MediaInfo.Id,
                     SessionId = _session.Id,
                     PositionTicks = _device.Position.Ticks
 
                 }).ConfigureAwait(false);
-
-                await SetNext().ConfigureAwait(false);
             }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error reporting progress", ex);
+            }
+
+            await SetNext().ConfigureAwait(false);
         }
 
-        async void Device_CurrentIdChanged(object sender, CurrentIdEventArgs e)
+        async void _device_PlaybackStart(object sender, PlaybackStartEventArgs e)
         {
-            if (!string.IsNullOrWhiteSpace(e.Id))
-            {
-                Guid guid;
-
-                if (Guid.TryParse(e.Id, out guid))
-                {
-                    if (_currentItem != null && _currentItem.Id == guid)
-                    {
-                        return;
-                    }
+            var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
 
-                    var item = _libraryManager.GetItemById(guid);
+            if (playlistItem != null)
+            {
+                var streamInfo = playlistItem.StreamInfo;
 
-                    if (item != null)
-                    {
-                        _logger.Debug("{0} - CurrentId {1}", _session.DeviceName, item.Id);
-                        _currentItem = item;
-                        _playbackStarted = false;
+                var info = GetProgressInfo(streamInfo, e.MediaInfo);
 
-                        await ReportProgress().ConfigureAwait(false);
-                    }
+                try
+                {
+                    await _sessionManager.OnPlaybackStart(info).ConfigureAwait(false);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error reporting progress", ex);
                 }
             }
         }
 
-        /// <summary>
-        /// Handles the Elapsed event of the updateTimer control.
-        /// </summary>
-        /// <param name="state">The state.</param>
-        private async void updateTimer_Elapsed(object state)
+        async void _device_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
         {
-            if (_disposed)
-                return;
+            var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
 
-            if (IsSessionActive)
-            {
-                await ReportProgress().ConfigureAwait(false);
-            }
-            else
+            if (playlistItem != null)
             {
-                _updateTimer.Change(Timeout.Infinite, Timeout.Infinite);
+                var streamInfo = playlistItem.StreamInfo;
+
+                var info = GetProgressInfo(streamInfo, e.MediaInfo);
 
                 try
                 {
-                    // Session is inactive, mark it for Disposal and don't start the elapsed timer.
-                    await _sessionManager.ReportSessionEnded(_session.Id);
+                    await _sessionManager.OnPlaybackProgress(info).ConfigureAwait(false);
                 }
                 catch (Exception ex)
                 {
-                    _logger.ErrorException("Error in ReportSessionEnded", ex);
+                    _logger.ErrorException("Error reporting progress", ex);
                 }
             }
         }
 
-        /// <summary>
-        /// Reports the playback progress.
-        /// </summary>
-        /// <returns></returns>
-        private async Task ReportProgress()
+        private PlaybackStartInfo GetProgressInfo(StreamInfo streamInfo, uBaseObject mediaInfo)
         {
-            if (_currentItem == null || _device.IsStopped)
-                return;
+            var ticks = _device.Position.Ticks;
 
-            var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
+            if (!streamInfo.IsDirectStream)
+            {
+                ticks += streamInfo.StartPositionTicks;
+            }
 
-            if (playlistItem != null)
+            return new PlaybackStartInfo
             {
-                var streamInfo = playlistItem.StreamInfo;
+                ItemId = mediaInfo.Id,
+                SessionId = _session.Id,
+                PositionTicks = ticks,
+                IsMuted = _device.IsMuted,
+                IsPaused = _device.IsPaused,
+                MediaSourceId = streamInfo.MediaSourceId,
+                AudioStreamIndex = streamInfo.AudioStreamIndex,
+                SubtitleStreamIndex = streamInfo.SubtitleStreamIndex,
+                VolumeLevel = _device.Volume,
+                CanSeek = streamInfo.RunTimeTicks.HasValue,
+                PlayMethod = streamInfo.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode,
+                QueueableMediaTypes = new List<string> { mediaInfo.MediaType }
+            };
+        }
 
-                if (!_playbackStarted)
-                {
-                    await _sessionManager.OnPlaybackStart(new PlaybackStartInfo
-                    {
-                        ItemId = _currentItem.Id.ToString("N"),
-                        SessionId = _session.Id,
-                        CanSeek = streamInfo.RunTimeTicks.HasValue,
-                        QueueableMediaTypes = new List<string> { _currentItem.MediaType },
-                        MediaSourceId = streamInfo.MediaSourceId,
-                        AudioStreamIndex = streamInfo.AudioStreamIndex,
-                        SubtitleStreamIndex = streamInfo.SubtitleStreamIndex,
-                        IsMuted = _device.IsMuted,
-                        IsPaused = _device.IsPaused,
-                        VolumeLevel = _device.Volume,
-                        PlayMethod = streamInfo.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode
-
-                    }).ConfigureAwait(false);
-
-                    _playbackStarted = true;
-                }
+        #region Device EventHandlers & Update Timer
 
-                if ((_device.IsPlaying || _device.IsPaused))
-                {
-                    var ticks = _device.Position.Ticks;
+        Timer _updateTimer;
 
-                    if (!streamInfo.IsDirectStream)
-                    {
-                        ticks += streamInfo.StartPositionTicks;
-                    }
+        /// <summary>
+        /// Handles the Elapsed event of the updateTimer control.
+        /// </summary>
+        /// <param name="state">The state.</param>
+        private async void updateTimer_Elapsed(object state)
+        {
+            if (!IsSessionActive)
+            {
+                _updateTimer.Change(Timeout.Infinite, Timeout.Infinite);
 
-                    await _sessionManager.OnPlaybackProgress(new PlaybackProgressInfo
-                    {
-                        ItemId = _currentItem.Id.ToString("N"),
-                        SessionId = _session.Id,
-                        PositionTicks = ticks,
-                        IsMuted = _device.IsMuted,
-                        IsPaused = _device.IsPaused,
-                        MediaSourceId = streamInfo.MediaSourceId,
-                        AudioStreamIndex = streamInfo.AudioStreamIndex,
-                        SubtitleStreamIndex = streamInfo.SubtitleStreamIndex,
-                        VolumeLevel = _device.Volume,
-                        CanSeek = streamInfo.RunTimeTicks.HasValue,
-                        PlayMethod = streamInfo.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode
-
-                    }).ConfigureAwait(false);
+                try
+                {
+                    // Session is inactive, mark it for Disposal and don't start the elapsed timer.
+                    await _sessionManager.ReportSessionEnded(_session.Id);
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error in ReportSessionEnded", ex);
                 }
             }
         }
@@ -263,11 +226,11 @@ namespace MediaBrowser.Dlna.PlayTo
 
             if (command.PlayCommand == PlayCommand.PlayLast)
             {
-                AddItemsToPlaylist(playlist);
+                Playlist.AddRange(playlist);
             }
             if (command.PlayCommand == PlayCommand.PlayNext)
             {
-                AddItemsToPlaylist(playlist);
+                Playlist.AddRange(playlist);
             }
 
             _logger.Debug("{0} - Playing {1} items", _session.DeviceName, playlist.Count);
@@ -314,11 +277,9 @@ namespace MediaBrowser.Dlna.PlayTo
 
 
                 case PlaystateCommand.NextTrack:
-                    _currentItem = null;
                     return SetNext();
 
                 case PlaystateCommand.PreviousTrack:
-                    _currentItem = null;
                     return SetPrevious();
             }
 
@@ -374,18 +335,13 @@ namespace MediaBrowser.Dlna.PlayTo
 
         #region Playlist
 
-        private List<PlaylistItem> _playlist = new List<PlaylistItem>();
-
+        private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
         private List<PlaylistItem> Playlist
         {
             get
             {
                 return _playlist;
             }
-            set
-            {
-                _playlist = value;
-            }
         }
 
         private void AddItemFromId(Guid id, List<BaseItem> list)
@@ -564,15 +520,6 @@ namespace MediaBrowser.Dlna.PlayTo
             return true;
         }
 
-        /// <summary>
-        /// Adds the items to playlist.
-        /// </summary>
-        /// <param name="items">The items.</param>
-        private void AddItemsToPlaylist(IEnumerable<PlaylistItem> items)
-        {
-            Playlist.AddRange(items);
-        }
-
         private async Task<bool> SetNext()
         {
             if (!Playlist.Any() || Playlist.All(i => i.PlayState != 0))
@@ -608,7 +555,7 @@ namespace MediaBrowser.Dlna.PlayTo
             return true;
         }
 
-        public Task<bool> SetPrevious()
+        public Task SetPrevious()
         {
             if (!Playlist.Any() || Playlist.All(i => i.PlayState != 2))
                 return Task.FromResult(false);
@@ -638,9 +585,13 @@ namespace MediaBrowser.Dlna.PlayTo
             if (!_disposed)
             {
                 _disposed = true;
+                
+                _device.PlaybackStart -= _device_PlaybackStart;
+                _device.PlaybackProgress -= _device_PlaybackProgress;
+                _device.PlaybackStopped -= _device_PlaybackStopped;
+                
                 _updateTimer.Dispose();
                 _device.Dispose();
-                _logger.Log(LogSeverity.Debug, "Controller disposed");
             }
         }
 
@@ -659,9 +610,9 @@ namespace MediaBrowser.Dlna.PlayTo
                     case GeneralCommandType.VolumeUp:
                         return _device.VolumeUp();
                     case GeneralCommandType.Mute:
-                        return _device.VolumeDown(true);
+                        return _device.Mute();
                     case GeneralCommandType.Unmute:
-                        return _device.VolumeUp(true);
+                        return _device.Unmute();
                     case GeneralCommandType.ToggleMute:
                         return _device.ToggleMute();
                     case GeneralCommandType.SetVolume:

+ 13 - 21
MediaBrowser.Dlna/PlayTo/PlayToManager.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.Dlna.PlayTo
 {
     class PlayToManager : IDisposable
     {
-        private bool _disposed = false;
+        private bool _disposed;
         private readonly ILogger _logger;
         private readonly ISessionManager _sessionManager;
         private readonly IHttpClient _httpClient;
@@ -65,36 +65,28 @@ namespace MediaBrowser.Dlna.PlayTo
             {
                 _logger.Debug("Found interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus);
 
-                if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any())
+                if (!network.SupportsMulticast || !network.GetIPProperties().MulticastAddresses.Any())
                     continue;
 
                 var ipV4 = network.GetIPProperties().GetIPv4Properties();
                 if (null == ipV4)
                     continue;
 
-                IPAddress localIp = null;
+                var localIp = network.GetIPProperties().UnicastAddresses
+                    .Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork)
+                    .Select(i => i.Address)
+                    .FirstOrDefault();
 
-                foreach (var ipInfo in network.GetIPProperties().UnicastAddresses)
+                if (localIp != null)
                 {
-                    if (ipInfo.Address.AddressFamily == AddressFamily.InterNetwork)
+                    try
                     {
-                        localIp = ipInfo.Address;
-                        break;
+                        CreateListener(localIp);
+                    }
+                    catch (Exception e)
+                    {
+                        _logger.ErrorException("Failed to Initilize Socket", e);
                     }
-                }
-
-                if (localIp == null)
-                {
-                    continue;
-                }
-
-                try
-                {
-                    CreateListener(localIp);
-                }
-                catch (Exception e)
-                {
-                    _logger.ErrorException("Failed to Initilize Socket", e);
                 }
             }
         }

+ 6 - 5
MediaBrowser.Dlna/PlayTo/uContainer.cs → MediaBrowser.Dlna/PlayTo/UpnpContainer.cs

@@ -3,7 +3,7 @@ using System.Xml.Linq;
 
 namespace MediaBrowser.Dlna.PlayTo
 {
-    public class uContainer : uBaseObject
+    public class UpnpContainer : uBaseObject
     {
         new public static uBaseObject Create(XElement container)
         {
@@ -14,10 +14,11 @@ namespace MediaBrowser.Dlna.PlayTo
 
             return new uBaseObject
             {
-                Id = (string)container.Attribute(uPnpNamespaces.Id),
-                ParentId = (string)container.Attribute(uPnpNamespaces.ParentId),
-                Title = (string)container.Element(uPnpNamespaces.title),
-                IconUrl = container.GetValue(uPnpNamespaces.Artwork)
+                Id = container.GetAttributeValue(uPnpNamespaces.Id),
+                ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId),
+                Title = container.GetValue(uPnpNamespaces.title),
+                IconUrl = container.GetValue(uPnpNamespaces.Artwork),
+                UpnpClass = container.GetValue(uPnpNamespaces.uClass)
             };
         }
     }

+ 37 - 1
MediaBrowser.Dlna/PlayTo/uBaseObject.cs

@@ -1,4 +1,5 @@
-
+using System;
+
 namespace MediaBrowser.Dlna.PlayTo
 {
     public class uBaseObject 
@@ -18,5 +19,40 @@ namespace MediaBrowser.Dlna.PlayTo
         public string Url { get; set; }
 
         public string[] ProtocolInfo { get; set; }
+
+        public string UpnpClass { get; set; }
+
+        public bool Equals(uBaseObject obj)
+        {
+            if (obj == null)
+            {
+                throw new ArgumentNullException("obj");
+            }
+
+            return string.Equals(Id, obj.Id);
+        }
+
+        public string MediaType
+        {
+            get
+            {
+                var classType = UpnpClass ?? string.Empty;
+
+                if (classType.IndexOf(Model.Entities.MediaType.Audio, StringComparison.Ordinal) != -1)
+                {
+                    return Model.Entities.MediaType.Audio;
+                }
+                if (classType.IndexOf(Model.Entities.MediaType.Video, StringComparison.Ordinal) != -1)
+                {
+                    return Model.Entities.MediaType.Video;
+                }
+                if (classType.IndexOf("image", StringComparison.Ordinal) != -1)
+                {
+                    return Model.Entities.MediaType.Photo;
+                }
+
+                return null;
+            }
+        }
     }
 }

+ 3 - 5
MediaBrowser.Dlna/PlayTo/uParser.cs

@@ -29,10 +29,10 @@ namespace MediaBrowser.Dlna.PlayTo
             var uPnpResponse = XElement.Parse((String)item);
 
             var uObjects = from container in uPnpResponse.Elements(uPnpNamespaces.containers)
-                           select new uParserObject { Type = (string)container.Element(uPnpNamespaces.uClass), Element = container };
+                           select new uParserObject { Element = container };
 
             var uObjects2 = from container in uPnpResponse.Elements(uPnpNamespaces.items)
-                            select new uParserObject { Type = (string)container.Element(uPnpNamespaces.uClass), Element = container };
+                            select new uParserObject { Element = container };
 
             list.AddRange(uObjects.Concat(uObjects2).Select(CreateObjectFromXML).Where(uObject => uObject != null));
 
@@ -41,14 +41,12 @@ namespace MediaBrowser.Dlna.PlayTo
 
         public static uBaseObject CreateObjectFromXML(uParserObject uItem)
         {
-            return uContainer.Create(uItem.Element);
+            return UpnpContainer.Create(uItem.Element);
         }
     }
 
     public class uParserObject
     {
-        public string Type { get; set; }
-
         public XElement Element { get; set; }
     }
 }

+ 2 - 0
MediaBrowser.Dlna/Profiles/DefaultProfile.cs

@@ -12,6 +12,8 @@ namespace MediaBrowser.Dlna.Profiles
 
             ProtocolInfo = "DLNA";
 
+            XDlnaDoc = "DMS-1.50";
+
             FriendlyName = "Media Browser";
             Manufacturer = "Media Browser";
             ModelDescription = "Media Browser";

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Default.xml

@@ -17,6 +17,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml

@@ -22,6 +22,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml

@@ -23,6 +23,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>10</TimelineOffsetSeconds>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml

@@ -21,6 +21,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml

@@ -24,6 +24,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>10</TimelineOffsetSeconds>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml

@@ -21,6 +21,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>

Diff do ficheiro suprimidas por serem muito extensas
+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml


Diff do ficheiro suprimidas por serem muito extensas
+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml


+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml

@@ -24,6 +24,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <SonyAggregationFlags>10</SonyAggregationFlags>
   <ProtocolInfo>http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml

@@ -24,6 +24,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <SonyAggregationFlags>10</SonyAggregationFlags>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml

@@ -24,6 +24,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <SonyAggregationFlags>10</SonyAggregationFlags>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml

@@ -24,6 +24,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <SonyAggregationFlags>10</SonyAggregationFlags>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml

@@ -24,6 +24,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>5</TimelineOffsetSeconds>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>

+ 1 - 0
MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml

@@ -23,6 +23,7 @@
   <MaxIconWidth xsi:nil="true" />
   <MaxIconHeight xsi:nil="true" />
   <MaxBitrate xsi:nil="true" />
+  <XDlnaDoc>DMS-1.50</XDlnaDoc>
   <ProtocolInfo>DLNA</ProtocolInfo>
   <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
   <RequiresPlainVideoItems>false</RequiresPlainVideoItems>

+ 10 - 11
MediaBrowser.Dlna/Server/ControlHandler.cs

@@ -259,7 +259,7 @@ namespace MediaBrowser.Dlna.Server
             didl.SetAttribute("xmlns:dc", NS_DC);
             didl.SetAttribute("xmlns:dlna", NS_DLNA);
             didl.SetAttribute("xmlns:upnp", NS_UPNP);
-            didl.SetAttribute("xmlns:sec", NS_SEC);
+            //didl.SetAttribute("xmlns:sec", NS_SEC);
             result.AppendChild(didl);
 
             var folder = (Folder)GetItemFromObjectId(id, user);
@@ -341,7 +341,7 @@ namespace MediaBrowser.Dlna.Server
             didl.SetAttribute("xmlns:dc", NS_DC);
             didl.SetAttribute("xmlns:dlna", NS_DLNA);
             didl.SetAttribute("xmlns:upnp", NS_UPNP);
-            didl.SetAttribute("xmlns:sec", NS_SEC);
+            //didl.SetAttribute("xmlns:sec", NS_SEC);
             result.AppendChild(didl);
 
             var folder = (Folder)GetItemFromObjectId(sparams["ContainerID"], user);
@@ -498,7 +498,6 @@ namespace MediaBrowser.Dlna.Server
 
             AddCover(f, container);
 
-            container.AppendChild(CreateObjectClass(result, f));
             result.DocumentElement.AppendChild(container);
         }
 
@@ -527,9 +526,7 @@ namespace MediaBrowser.Dlna.Server
                 element.SetAttribute("parentID", item.Parent.Id.ToString("N"));
             }
 
-            element.AppendChild(CreateObjectClass(result, item));
-
-            AddBookmarkInfo(item, user, element);
+            //AddBookmarkInfo(item, user, element);
 
             AddGeneralProperties(item, element, filter);
 
@@ -812,6 +809,13 @@ namespace MediaBrowser.Dlna.Server
         /// <param name="filter">The filter.</param>
         private void AddCommonFields(BaseItem item, XmlElement element, Filter filter)
         {
+            if (filter.Contains("dc:title"))
+            {
+                AddValue(element, "dc", "title", item.Name, NS_DC);
+            }
+
+            element.AppendChild(CreateObjectClass(element.OwnerDocument, item));
+            
             if (filter.Contains("dc:date"))
             {
                 if (item.PremiereDate.HasValue)
@@ -830,11 +834,6 @@ namespace MediaBrowser.Dlna.Server
                 AddValue(element, "upnp", "publisher", studio, NS_UPNP);
             }
 
-            if (filter.Contains("dc:title"))
-            {
-                AddValue(element, "dc", "title", item.Name, NS_DC);
-            }
-
             if (filter.Contains("dc:description"))
             {
                 if (!string.IsNullOrWhiteSpace(item.Overview))

+ 1 - 1
MediaBrowser.Model/Dlna/DeviceProfile.cs

@@ -61,7 +61,7 @@ namespace MediaBrowser.Model.Dlna
         /// </summary>
         public string XDlnaCap { get; set; }
         /// <summary>
-        /// Controls the content of the aggregationFlags element in the urn:schemas-sonycom:av.
+        /// Controls the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace.
         /// </summary>
         public string SonyAggregationFlags { get; set; }
 

+ 2 - 0
MediaBrowser.Model/Entities/MediaStream.cs

@@ -173,6 +173,8 @@ namespace MediaBrowser.Model.Entities
         /// <value>The format.</value>
         public string Format { get; set; }
 
+        public int? TotalBitrate { get; set; }
+
         public MediaInfo()
         {
             MediaStreams = new List<MediaStream>();

+ 2 - 2
MediaBrowser.Model/Session/PlaybackReports.cs

@@ -1,5 +1,5 @@
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Entities;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Model.Session
 {

+ 4 - 3
MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs

@@ -93,8 +93,11 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <returns>Task.</returns>
         protected Task Fetch(Audio audio, CancellationToken cancellationToken, InternalMediaInfoResult data)
         {
-            var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;
+            var mediaInfo = MediaEncoderHelpers.GetMediaInfo(data);
+            var mediaStreams = mediaInfo.MediaStreams;
 
+            audio.FormatName = mediaInfo.Format;
+            audio.TotalBitrate = mediaInfo.TotalBitrate;
             audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.Video);
 
             if (data.streams != null)
@@ -123,8 +126,6 @@ namespace MediaBrowser.Providers.MediaInfo
 
             if (data.format != null)
             {
-                audio.FormatName = data.format.format_name;
-
                 var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.');
 
                 audio.Container = extension;

+ 7 - 5
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs

@@ -151,6 +151,13 @@ namespace MediaBrowser.Providers.MediaInfo
 
         protected async Task Fetch(Video video, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount, BlurayDiscInfo blurayInfo, IDirectoryService directoryService)
         {
+            var mediaInfo = MediaEncoderHelpers.GetMediaInfo(data);
+            var mediaStreams = mediaInfo.MediaStreams;
+
+            video.TotalBitrate = mediaInfo.TotalBitrate;
+            video.FormatName = (mediaInfo.Format ?? string.Empty)
+                .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);
+
             if (data.format != null)
             {
                 // For dvd's this may not always be accurate, so don't set the runtime if the item already has one
@@ -161,9 +168,6 @@ namespace MediaBrowser.Providers.MediaInfo
                     video.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks;
                 }
 
-                video.FormatName = (data.format.format_name ?? string.Empty)
-                    .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);
-
                 if (video.VideoType == VideoType.VideoFile)
                 {
                     var extension = (Path.GetExtension(video.Path) ?? string.Empty).TrimStart('.');
@@ -185,8 +189,6 @@ namespace MediaBrowser.Providers.MediaInfo
                 }
             }
 
-            var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;
-
             var mediaChapters = (data.Chapters ?? new MediaChapter[] { }).ToList();
             var chapters = mediaChapters.Select(GetChapterInfo).ToList();
 

+ 24 - 0
MediaBrowser.Server.Implementations/Collections/CollectionManager.cs

@@ -227,5 +227,29 @@ namespace MediaBrowser.Server.Implementations.Collections
 
             await collection.RefreshMetadata(CancellationToken.None).ConfigureAwait(false);
         }
+
+        public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user)
+        {
+            var itemsToCollapse = new List<ISupportsBoxSetGrouping>();
+            var boxsets = new List<BaseItem>();
+
+            var list = items.ToList();
+
+            foreach (var item in list.OfType<ISupportsBoxSetGrouping>())
+            {
+                var currentBoxSets = item.BoxSetIdList
+                    .Select(i => _libraryManager.GetItemById(i))
+                    .Where(i => i != null && i.IsVisible(user))
+                    .ToList();
+
+                if (currentBoxSets.Count > 0)
+                {
+                    itemsToCollapse.Add(item);
+                    boxsets.AddRange(currentBoxSets);
+                }
+            }
+
+            return list.Except(itemsToCollapse.Cast<BaseItem>()).Concat(boxsets).Distinct();
+        }
     }
 }

+ 8 - 2
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -1252,7 +1252,10 @@ namespace MediaBrowser.Server.Implementations.Dto
                 }
             }
 
-            var bitrate = info.MediaStreams.Where(m => m.Type == MediaStreamType.Audio || m.Type == MediaStreamType.Video).Select(m => m.BitRate ?? 0).Sum();
+            var bitrate = i.TotalBitrate ?? 
+                info.MediaStreams.Where(m => m.Type != MediaStreamType.Subtitle && !string.Equals(m.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase))
+                .Select(m => m.BitRate ?? 0)
+                .Sum();
 
             if (bitrate > 0)
             {
@@ -1286,7 +1289,10 @@ namespace MediaBrowser.Server.Implementations.Dto
                 }
             }
 
-            var bitrate = info.MediaStreams.Where(m => m.Type == MediaStreamType.Audio).Select(m => m.BitRate ?? 0).Sum();
+            var bitrate = i.TotalBitrate ??
+                info.MediaStreams.Where(m => m.Type == MediaStreamType.Audio)
+                .Select(m => m.BitRate ?? 0)
+                .Sum();
 
             if (bitrate > 0)
             {

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/JavaScript/ar.json


+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/ca.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Configuraci\u00f3 guardada.","AddUser":"Afegir Usuari","Users":"Usuaris","Delete":"Esborrar","Administrator":"Administrador","Password":"Contrasenya","DeleteImage":"Esborrar Imatge","DeleteImageConfirmation":"Esteu segur que voleu suprimir aquesta imatge?","FileReadCancelled":"La lectura de l'arxiu ha estat cancel\u00b7lada.","FileNotFound":"Arxiu no trobat.","FileReadError":"S'ha produ\u00eft un error en llegir el fitxer.","DeleteUser":"Esborrar Usuari","DeleteUserConfirmation":"Esteu segur que voleu suprimir {0}?","PasswordResetHeader":"Restablir contrasenya","PasswordResetComplete":"La contrasenya s'ha restablert.","PasswordResetConfirmation":"Esteu segur que voleu restablir la contrasenya?","PasswordSaved":"S'ha guardat la contrasenya.","PasswordMatchError":"Confirmaci\u00f3 de la contrasenya i la contrasenya han de coincidir.","OptionOff":"Apagat","OptionOn":"Enc\u00e8s","OptionRelease":"Versi\u00f3 Oficial","OptionBeta":"Beta","OptionDev":"Dev (Inestable)","UninstallPluginHeader":"Desinstal\u00b7lar Plugin.","UninstallPluginConfirmation":"Esteu segur que voleu desinstal\u00b7lar {0}?","NoPluginConfigurationMessage":"Aquest plugin no necessita configuraci\u00f3.","NoPluginsInstalledMessage":"No t\u00e9 cap plugin instal\u00b7lat.","BrowsePluginCatalogMessage":"Consulti el nostre cat\u00e0leg per veure els plugins disponibles."}
+{
+    "SettingsSaved": "Configuraci\u00f3 guardada.",
+    "AddUser": "Afegir Usuari",
+    "Users": "Usuaris",
+    "Delete": "Esborrar",
+    "Administrator": "Administrador",
+    "Password": "Contrasenya",
+    "DeleteImage": "Esborrar Imatge",
+    "DeleteImageConfirmation": "Esteu segur que voleu suprimir aquesta imatge?",
+    "FileReadCancelled": "La lectura de l'arxiu ha estat cancel\u00b7lada.",
+    "FileNotFound": "Arxiu no trobat.",
+    "FileReadError": "S'ha produ\u00eft un error en llegir el fitxer.",
+    "DeleteUser": "Esborrar Usuari",
+    "DeleteUserConfirmation": "Esteu segur que voleu suprimir {0}?",
+    "PasswordResetHeader": "Restablir contrasenya",
+    "PasswordResetComplete": "La contrasenya s'ha restablert.",
+    "PasswordResetConfirmation": "Esteu segur que voleu restablir la contrasenya?",
+    "PasswordSaved": "S'ha guardat la contrasenya.",
+    "PasswordMatchError": "Confirmaci\u00f3 de la contrasenya i la contrasenya han de coincidir.",
+    "OptionOff": "Apagat",
+    "OptionOn": "Enc\u00e8s",
+    "OptionRelease": "Versi\u00f3 Oficial",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev (Inestable)",
+    "UninstallPluginHeader": "Desinstal\u00b7lar Plugin.",
+    "UninstallPluginConfirmation": "Esteu segur que voleu desinstal\u00b7lar {0}?",
+    "NoPluginConfigurationMessage": "Aquest plugin no necessita configuraci\u00f3.",
+    "NoPluginsInstalledMessage": "No t\u00e9 cap plugin instal\u00b7lat.",
+    "BrowsePluginCatalogMessage": "Consulti el nostre cat\u00e0leg per veure els plugins disponibles."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/cs.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Nastaven\u00ed ulo\u017eeno.","AddUser":"P\u0159idat u\u017eivatele","Users":"U\u017eivatel\u00e9","Delete":"Odstranit","Administrator":"Administr\u00e1tor","Password":"Heslo","DeleteImage":"Odstranit obr\u00e1zek","DeleteImageConfirmation":"Jste si jisti, \u017ee chcete odstranit tento obr\u00e1zek?","FileReadCancelled":"\u010cten\u00ed souboru bylo zru\u0161eno.","FileNotFound":"Soubor nebyl nalezen.","FileReadError":"Nastala chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed souboru.","DeleteUser":"Odstranit u\u017eivatele","DeleteUserConfirmation":"Jste si jisti, \u017ee chcete odstranit {0}?","PasswordResetHeader":"Obnovit heslo","PasswordResetComplete":"Heslo bylo obnoveno.","PasswordResetConfirmation":"Jste si jisti, \u017ee chcete obnovit heslo?","PasswordSaved":"Heslo ulo\u017eeno.","PasswordMatchError":"Heslo a potvrzen\u00ed hesla mus\u00ed souhlasit.","OptionOff":"Vypnout","OptionOn":"Zapnout","OptionRelease":"Ofici\u00e1ln\u00ed vyd\u00e1n\u00ed","OptionBeta":"Betaverze","OptionDev":"Dev (Nestabiln\u00ed\/V\u00fdvoj\u00e1\u0159sk\u00e1)","UninstallPluginHeader":"Odinstalovat plugin","UninstallPluginConfirmation":"Jste si jisti, \u017ee chcete odinstalovat {0}?","NoPluginConfigurationMessage":"Tento plugin nem\u00e1 nastaven\u00ed.","NoPluginsInstalledMessage":"Nem\u00e1te nainstalov\u00e1n \u017e\u00e1dn\u00fd plugin.","BrowsePluginCatalogMessage":"Prohl\u00e9dn\u011bte si n\u00e1\u0161 katalog, kde najdete dostupn\u00e9 pluginy."}
+{
+    "SettingsSaved": "Nastaven\u00ed ulo\u017eeno.",
+    "AddUser": "P\u0159idat u\u017eivatele",
+    "Users": "U\u017eivatel\u00e9",
+    "Delete": "Odstranit",
+    "Administrator": "Administr\u00e1tor",
+    "Password": "Heslo",
+    "DeleteImage": "Odstranit obr\u00e1zek",
+    "DeleteImageConfirmation": "Jste si jisti, \u017ee chcete odstranit tento obr\u00e1zek?",
+    "FileReadCancelled": "\u010cten\u00ed souboru bylo zru\u0161eno.",
+    "FileNotFound": "Soubor nebyl nalezen.",
+    "FileReadError": "Nastala chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed souboru.",
+    "DeleteUser": "Odstranit u\u017eivatele",
+    "DeleteUserConfirmation": "Jste si jisti, \u017ee chcete odstranit {0}?",
+    "PasswordResetHeader": "Obnovit heslo",
+    "PasswordResetComplete": "Heslo bylo obnoveno.",
+    "PasswordResetConfirmation": "Jste si jisti, \u017ee chcete obnovit heslo?",
+    "PasswordSaved": "Heslo ulo\u017eeno.",
+    "PasswordMatchError": "Heslo a potvrzen\u00ed hesla mus\u00ed souhlasit.",
+    "OptionOff": "Vypnout",
+    "OptionOn": "Zapnout",
+    "OptionRelease": "Ofici\u00e1ln\u00ed vyd\u00e1n\u00ed",
+    "OptionBeta": "Betaverze",
+    "OptionDev": "Dev (Nestabiln\u00ed\/V\u00fdvoj\u00e1\u0159sk\u00e1)",
+    "UninstallPluginHeader": "Odinstalovat plugin",
+    "UninstallPluginConfirmation": "Jste si jisti, \u017ee chcete odinstalovat {0}?",
+    "NoPluginConfigurationMessage": "Tento plugin nem\u00e1 nastaven\u00ed.",
+    "NoPluginsInstalledMessage": "Nem\u00e1te nainstalov\u00e1n \u017e\u00e1dn\u00fd plugin.",
+    "BrowsePluginCatalogMessage": "Prohl\u00e9dn\u011bte si n\u00e1\u0161 katalog, kde najdete dostupn\u00e9 pluginy."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/de.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Einstellungen gespeichert","AddUser":"Benutzer hinzuf\u00fcgen","Users":"Benutzer","Delete":"L\u00f6schen","Administrator":"Administrator","Password":"Passwort","DeleteImage":"Bild l\u00f6schen","DeleteImageConfirmation":"M\u00f6chten Sie das Bild wirklich l\u00f6schen?","FileReadCancelled":"Das Einlesen der Datei wurde abgebrochen.","FileNotFound":"Datei nicht gefunden","FileReadError":"Beim Lesen der Datei ist ein Fehler aufgetreten.","DeleteUser":"Benutzer l\u00f6schen","DeleteUserConfirmation":"M\u00f6chten Sie {0} wirklich l\u00f6schen?","PasswordResetHeader":"Passwort zur\u00fccksetzen","PasswordResetComplete":"Das Passwort wurde zur\u00fcckgesetzt.","PasswordResetConfirmation":"M\u00f6chten Sie das Passwort wirklich zur\u00fccksetzen?","PasswordSaved":"Passwort gespeichert","PasswordMatchError":"Passwort und Passwortbest\u00e4tigung stimmen nicht \u00fcberein.","OptionOff":"Aus","OptionOn":"Ein","OptionRelease":"Offizielles Release","OptionBeta":"Beta","OptionDev":"Entwickler (instabil)","UninstallPluginHeader":"Deinstalliere Plugin","UninstallPluginConfirmation":"M\u00f6chten Sie {0} wirklich deinstallieren?","NoPluginConfigurationMessage":"Bei diesem Plugin kann nichts eingestellt werden.","NoPluginsInstalledMessage":"Sie haben keine Plugins installiert.","BrowsePluginCatalogMessage":"Durchsuchen Sie unsere Bibliothek um alle verf\u00fcgbaren Plugins anzuzeigen."}
+{
+    "SettingsSaved": "Einstellungen gespeichert",
+    "AddUser": "Benutzer hinzuf\u00fcgen",
+    "Users": "Benutzer",
+    "Delete": "L\u00f6schen",
+    "Administrator": "Administrator",
+    "Password": "Passwort",
+    "DeleteImage": "Bild l\u00f6schen",
+    "DeleteImageConfirmation": "M\u00f6chten Sie das Bild wirklich l\u00f6schen?",
+    "FileReadCancelled": "Das Einlesen der Datei wurde abgebrochen.",
+    "FileNotFound": "Datei nicht gefunden",
+    "FileReadError": "Beim Lesen der Datei ist ein Fehler aufgetreten.",
+    "DeleteUser": "Benutzer l\u00f6schen",
+    "DeleteUserConfirmation": "M\u00f6chten Sie {0} wirklich l\u00f6schen?",
+    "PasswordResetHeader": "Passwort zur\u00fccksetzen",
+    "PasswordResetComplete": "Das Passwort wurde zur\u00fcckgesetzt.",
+    "PasswordResetConfirmation": "M\u00f6chten Sie das Passwort wirklich zur\u00fccksetzen?",
+    "PasswordSaved": "Passwort gespeichert",
+    "PasswordMatchError": "Passwort und Passwortbest\u00e4tigung stimmen nicht \u00fcberein.",
+    "OptionOff": "Aus",
+    "OptionOn": "Ein",
+    "OptionRelease": "Offizielles Release",
+    "OptionBeta": "Beta",
+    "OptionDev": "Entwickler (instabil)",
+    "UninstallPluginHeader": "Deinstalliere Plugin",
+    "UninstallPluginConfirmation": "M\u00f6chten Sie {0} wirklich deinstallieren?",
+    "NoPluginConfigurationMessage": "Bei diesem Plugin kann nichts eingestellt werden.",
+    "NoPluginsInstalledMessage": "Sie haben keine Plugins installiert.",
+    "BrowsePluginCatalogMessage": "Durchsuchen Sie unsere Bibliothek um alle verf\u00fcgbaren Plugins anzuzeigen."
+}

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/JavaScript/el.json


+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/en_GB.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Settings saved.","AddUser":"Add User","Users":"Users","Delete":"Delete","Administrator":"Administrator","Password":"Password","DeleteImage":"Delete Image","DeleteImageConfirmation":"Are you sure you wish to delete this image?","FileReadCancelled":"The file read has been cancelled.","FileNotFound":"File not found.","FileReadError":"An error occurred while reading the file.","DeleteUser":"Delete User","DeleteUserConfirmation":"Are you sure you wish to delete {0}?","PasswordResetHeader":"Password Reset","PasswordResetComplete":"The password has been reset.","PasswordResetConfirmation":"Are you sure you wish to reset the password?","PasswordSaved":"Password saved.","PasswordMatchError":"Password and password confirmation must match.","OptionOff":"Off","OptionOn":"On","OptionRelease":"Official Release","OptionBeta":"Beta","OptionDev":"Dev (Unstable)","UninstallPluginHeader":"Uninstall Plugin","UninstallPluginConfirmation":"Are you sure you wish to uninstall {0}?","NoPluginConfigurationMessage":"This plugin has nothing to configure.","NoPluginsInstalledMessage":"You have no plugins installed.","BrowsePluginCatalogMessage":"Browse our plugin catalog to view available plugins."}
+{
+    "SettingsSaved": "Settings saved.",
+    "AddUser": "Add User",
+    "Users": "Users",
+    "Delete": "Delete",
+    "Administrator": "Administrator",
+    "Password": "Password",
+    "DeleteImage": "Delete Image",
+    "DeleteImageConfirmation": "Are you sure you wish to delete this image?",
+    "FileReadCancelled": "The file read has been cancelled.",
+    "FileNotFound": "File not found.",
+    "FileReadError": "An error occurred while reading the file.",
+    "DeleteUser": "Delete User",
+    "DeleteUserConfirmation": "Are you sure you wish to delete {0}?",
+    "PasswordResetHeader": "Password Reset",
+    "PasswordResetComplete": "The password has been reset.",
+    "PasswordResetConfirmation": "Are you sure you wish to reset the password?",
+    "PasswordSaved": "Password saved.",
+    "PasswordMatchError": "Password and password confirmation must match.",
+    "OptionOff": "Off",
+    "OptionOn": "On",
+    "OptionRelease": "Official Release",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev (Unstable)",
+    "UninstallPluginHeader": "Uninstall Plugin",
+    "UninstallPluginConfirmation": "Are you sure you wish to uninstall {0}?",
+    "NoPluginConfigurationMessage": "This plugin has nothing to configure.",
+    "NoPluginsInstalledMessage": "You have no plugins installed.",
+    "BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/en_US.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Settings saved.","AddUser":"Add User","Users":"Users","Delete":"Delete","Administrator":"Administrator","Password":"Password","DeleteImage":"Delete Image","DeleteImageConfirmation":"Are you sure you wish to delete this image?","FileReadCancelled":"The file read has been canceled.","FileNotFound":"File not found.","FileReadError":"An error occurred while reading the file.","DeleteUser":"Delete User","DeleteUserConfirmation":"Are you sure you wish to delete {0}?","PasswordResetHeader":"Password Reset","PasswordResetComplete":"The password has been reset.","PasswordResetConfirmation":"Are you sure you wish to reset the password?","PasswordSaved":"Password saved.","PasswordMatchError":"Password and password confirmation must match.","OptionOff":"Off","OptionOn":"On","OptionRelease":"Official Release","OptionBeta":"Beta","OptionDev":"Dev (Unstable)","UninstallPluginHeader":"Uninstall Plugin","UninstallPluginConfirmation":"Are you sure you wish to uninstall {0}?","NoPluginConfigurationMessage":"This plugin has nothing to configure.","NoPluginsInstalledMessage":"You have no plugins installed.","BrowsePluginCatalogMessage":"Browse our plugin catalog to view available plugins."}
+{
+    "SettingsSaved": "Settings saved.",
+    "AddUser": "Add User",
+    "Users": "Users",
+    "Delete": "Delete",
+    "Administrator": "Administrator",
+    "Password": "Password",
+    "DeleteImage": "Delete Image",
+    "DeleteImageConfirmation": "Are you sure you wish to delete this image?",
+    "FileReadCancelled": "The file read has been canceled.",
+    "FileNotFound": "File not found.",
+    "FileReadError": "An error occurred while reading the file.",
+    "DeleteUser": "Delete User",
+    "DeleteUserConfirmation": "Are you sure you wish to delete {0}?",
+    "PasswordResetHeader": "Password Reset",
+    "PasswordResetComplete": "The password has been reset.",
+    "PasswordResetConfirmation": "Are you sure you wish to reset the password?",
+    "PasswordSaved": "Password saved.",
+    "PasswordMatchError": "Password and password confirmation must match.",
+    "OptionOff": "Off",
+    "OptionOn": "On",
+    "OptionRelease": "Official Release",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev (Unstable)",
+    "UninstallPluginHeader": "Uninstall Plugin",
+    "UninstallPluginConfirmation": "Are you sure you wish to uninstall {0}?",
+    "NoPluginConfigurationMessage": "This plugin has nothing to configure.",
+    "NoPluginsInstalledMessage": "You have no plugins installed.",
+    "BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/es.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Configuraci\u00f3n guardada","AddUser":"Agregar usuario","Users":"Usuarios","Delete":"Borrar","Administrator":"Administrador","Password":"Contrase\u00f1a","DeleteImage":"Borrar Imagen","DeleteImageConfirmation":"Est\u00e1 seguro que desea borrar esta imagen?","FileReadCancelled":"La lectura del archivo se ha cancelado.","FileNotFound":"Archivo no encontrado.","FileReadError":"Se encontr\u00f3 un error al leer el archivo.","DeleteUser":"Borrar Usuario","DeleteUserConfirmation":"Esta seguro que desea eliminar a {0}?","PasswordResetHeader":"Restablecer contrase\u00f1a","PasswordResetComplete":"La contrase\u00f1a se ha restablecido.","PasswordResetConfirmation":"Esta seguro que desea restablecer la contrase\u00f1a?","PasswordSaved":"Contrase\u00f1a guardada.","PasswordMatchError":"La contrase\u00f1a y la confirmaci\u00f3n de la contrase\u00f1a deben de ser iguales.","OptionOff":"Apagado","OptionOn":"Encendido","OptionRelease":"Liberar","OptionBeta":"Beta","OptionDev":"Desarrollo","UninstallPluginHeader":"Desinstalar Plugin","UninstallPluginConfirmation":"Esta seguro que desea desinstalar {0}?","NoPluginConfigurationMessage":"El plugin no requiere configuraci\u00f3n","NoPluginsInstalledMessage":"No tiene plugins instalados.","BrowsePluginCatalogMessage":"Navegar el catalogo de plugins para ver los plugins disponibles."}
+{
+    "SettingsSaved": "Configuraci\u00f3n guardada",
+    "AddUser": "Agregar usuario",
+    "Users": "Usuarios",
+    "Delete": "Borrar",
+    "Administrator": "Administrador",
+    "Password": "Contrase\u00f1a",
+    "DeleteImage": "Borrar Imagen",
+    "DeleteImageConfirmation": "Est\u00e1 seguro que desea borrar esta imagen?",
+    "FileReadCancelled": "La lectura del archivo se ha cancelado.",
+    "FileNotFound": "Archivo no encontrado.",
+    "FileReadError": "Se encontr\u00f3 un error al leer el archivo.",
+    "DeleteUser": "Borrar Usuario",
+    "DeleteUserConfirmation": "Esta seguro que desea eliminar a {0}?",
+    "PasswordResetHeader": "Restablecer contrase\u00f1a",
+    "PasswordResetComplete": "La contrase\u00f1a se ha restablecido.",
+    "PasswordResetConfirmation": "Esta seguro que desea restablecer la contrase\u00f1a?",
+    "PasswordSaved": "Contrase\u00f1a guardada.",
+    "PasswordMatchError": "La contrase\u00f1a y la confirmaci\u00f3n de la contrase\u00f1a deben de ser iguales.",
+    "OptionOff": "Apagado",
+    "OptionOn": "Encendido",
+    "OptionRelease": "Liberar",
+    "OptionBeta": "Beta",
+    "OptionDev": "Desarrollo",
+    "UninstallPluginHeader": "Desinstalar Plugin",
+    "UninstallPluginConfirmation": "Esta seguro que desea desinstalar {0}?",
+    "NoPluginConfigurationMessage": "El plugin no requiere configuraci\u00f3n",
+    "NoPluginsInstalledMessage": "No tiene plugins instalados.",
+    "BrowsePluginCatalogMessage": "Navegar el catalogo de plugins para ver los plugins disponibles."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/es_MX.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Configuraci\u00f3n guardada.","AddUser":"Agregar usuario","Users":"Usuarios","Delete":"Eliminar","Administrator":"Administrador","Password":"Contrase\u00f1a","DeleteImage":"Eliminar imagen","DeleteImageConfirmation":"\u00bfEst\u00e1 seguro que desea eliminar esta imagen?","FileReadCancelled":"La lectura del archivo ha sido cancelada.","FileNotFound":"Archivo no encontrado.","FileReadError":"Ha ocurrido un error al leer el archivo.","DeleteUser":"Eliminar Usuario","DeleteUserConfirmation":"\u00bfEsta seguro que desea eliminar a {0}?","PasswordResetHeader":"Restablecer Contrase\u00f1a","PasswordResetComplete":"La contrase\u00f1a ha sido restablecida.","PasswordResetConfirmation":"\u00bfEst\u00e1 seguro que desea restablecer la contrase\u00f1a?","PasswordSaved":"Contrase\u00f1a guardada.","PasswordMatchError":"La Contrase\u00f1a y la confirmaci\u00f3n de la contrase\u00f1a deben coincidir.","OptionOff":"No","OptionOn":"Si","OptionRelease":"Versi\u00f3n Oficial","OptionBeta":"Beta","OptionDev":"Desarrollo (Inestable)","UninstallPluginHeader":"Desinstalar Complemento","UninstallPluginConfirmation":"\u00bfEst\u00e1 seguro que desea desinstalar {0}?","NoPluginConfigurationMessage":"El complemento no requiere configuraci\u00f3n","NoPluginsInstalledMessage":"No tiene complementos instalados.","BrowsePluginCatalogMessage":"Navege en el catalogo de complementos para ver los complementos disponibles."}
+{
+    "SettingsSaved": "Configuraci\u00f3n guardada.",
+    "AddUser": "Agregar usuario",
+    "Users": "Usuarios",
+    "Delete": "Eliminar",
+    "Administrator": "Administrador",
+    "Password": "Contrase\u00f1a",
+    "DeleteImage": "Eliminar imagen",
+    "DeleteImageConfirmation": "\u00bfEst\u00e1 seguro que desea eliminar esta imagen?",
+    "FileReadCancelled": "La lectura del archivo ha sido cancelada.",
+    "FileNotFound": "Archivo no encontrado.",
+    "FileReadError": "Ha ocurrido un error al leer el archivo.",
+    "DeleteUser": "Eliminar Usuario",
+    "DeleteUserConfirmation": "\u00bfEsta seguro que desea eliminar a {0}?",
+    "PasswordResetHeader": "Restablecer Contrase\u00f1a",
+    "PasswordResetComplete": "La contrase\u00f1a ha sido restablecida.",
+    "PasswordResetConfirmation": "\u00bfEst\u00e1 seguro que desea restablecer la contrase\u00f1a?",
+    "PasswordSaved": "Contrase\u00f1a guardada.",
+    "PasswordMatchError": "La Contrase\u00f1a y la confirmaci\u00f3n de la contrase\u00f1a deben coincidir.",
+    "OptionOff": "No",
+    "OptionOn": "Si",
+    "OptionRelease": "Versi\u00f3n Oficial",
+    "OptionBeta": "Beta",
+    "OptionDev": "Desarrollo (Inestable)",
+    "UninstallPluginHeader": "Desinstalar Complemento",
+    "UninstallPluginConfirmation": "\u00bfEst\u00e1 seguro que desea desinstalar {0}?",
+    "NoPluginConfigurationMessage": "El complemento no requiere configuraci\u00f3n",
+    "NoPluginsInstalledMessage": "No tiene complementos instalados.",
+    "BrowsePluginCatalogMessage": "Navege en el catalogo de complementos para ver los complementos disponibles."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/fr.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Param\u00e8tres sauvegard\u00e9s.","AddUser":"Ajouter utilisateur","Users":"Utilisateurs","Delete":"Supprimer","Administrator":"Administrateur","Password":"Mot de passe","DeleteImage":"Supprimer Image","DeleteImageConfirmation":"\u00cates-vous s\u00fbr de vouloir supprimer l'image?","FileReadCancelled":"La lecture du fichier a \u00e9t\u00e9 annul\u00e9e.","FileNotFound":"Fichier non trouv\u00e9","FileReadError":"Un erreur est survenue pendant la lecture du fichier.","DeleteUser":"Supprimer utilisateur","DeleteUserConfirmation":"\u00cates-vous s\u00fbr de vouloir supprimer {0}?","PasswordResetHeader":"R\u00e9initialisation du mot de passe","PasswordResetComplete":"Le mot de passe a \u00e9t\u00e9 r\u00e9initialis\u00e9.","PasswordResetConfirmation":"\u00cates-vous s\u00fbr de vouloir r\u00e9initialiser le mot de passe?","PasswordSaved":"Mot de passe sauvegard\u00e9.","PasswordMatchError":"Le mot de passe et sa confirmation doivent correspondre.","OptionOff":"Off","OptionOn":"On","OptionRelease":"Version officielle","OptionBeta":"Beta","OptionDev":"Dev (Instable)","UninstallPluginHeader":"D\u00e9sinstaller Plug-in","UninstallPluginConfirmation":"\u00cates-vous s\u00fbr de vouloir d\u00e9sinstaller {0}?","NoPluginConfigurationMessage":"Ce module d'extension n'a rien \u00e0 configurer.","NoPluginsInstalledMessage":"Vous n'avez aucun Plugin install\u00e9.","BrowsePluginCatalogMessage":"Explorer notre catalogue de Plugins disponibles."}
+{
+    "SettingsSaved": "Param\u00e8tres sauvegard\u00e9s.",
+    "AddUser": "Ajouter utilisateur",
+    "Users": "Utilisateurs",
+    "Delete": "Supprimer",
+    "Administrator": "Administrateur",
+    "Password": "Mot de passe",
+    "DeleteImage": "Supprimer Image",
+    "DeleteImageConfirmation": "\u00cates-vous s\u00fbr de vouloir supprimer l'image?",
+    "FileReadCancelled": "La lecture du fichier a \u00e9t\u00e9 annul\u00e9e.",
+    "FileNotFound": "Fichier non trouv\u00e9",
+    "FileReadError": "Un erreur est survenue pendant la lecture du fichier.",
+    "DeleteUser": "Supprimer utilisateur",
+    "DeleteUserConfirmation": "\u00cates-vous s\u00fbr de vouloir supprimer {0}?",
+    "PasswordResetHeader": "R\u00e9initialisation du mot de passe",
+    "PasswordResetComplete": "Le mot de passe a \u00e9t\u00e9 r\u00e9initialis\u00e9.",
+    "PasswordResetConfirmation": "\u00cates-vous s\u00fbr de vouloir r\u00e9initialiser le mot de passe?",
+    "PasswordSaved": "Mot de passe sauvegard\u00e9.",
+    "PasswordMatchError": "Le mot de passe et sa confirmation doivent correspondre.",
+    "OptionOff": "Off",
+    "OptionOn": "On",
+    "OptionRelease": "Version officielle",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev (Instable)",
+    "UninstallPluginHeader": "D\u00e9sinstaller Plug-in",
+    "UninstallPluginConfirmation": "\u00cates-vous s\u00fbr de vouloir d\u00e9sinstaller {0}?",
+    "NoPluginConfigurationMessage": "Ce module d'extension n'a rien \u00e0 configurer.",
+    "NoPluginsInstalledMessage": "Vous n'avez aucun Plugin install\u00e9.",
+    "BrowsePluginCatalogMessage": "Explorer notre catalogue de Plugins disponibles."
+}

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/JavaScript/he.json


+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/it.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Settaggi salvati.","AddUser":"Aggiungi utente","Users":"Utenti","Delete":"Elimina","Administrator":"Amministratore","Password":"Password","DeleteImage":"Elimina immagine","DeleteImageConfirmation":"Sei sicuro di voler eliminare questa immagine?","FileReadCancelled":"Il file letto \u00e8 stato cancellato.","FileNotFound":"File non trovato","FileReadError":"Errore durante la lettura del file.","DeleteUser":"Elimina utente","DeleteUserConfirmation":"Sei sicuro di voler eliminare {0}?","PasswordResetHeader":"Ripristina Password","PasswordResetComplete":"la password \u00e8 stata ripristinata.","PasswordResetConfirmation":"Sei sicuro di voler ripristinare la password?","PasswordSaved":"Password salvata.","PasswordMatchError":"Le password non coincidono.","OptionOff":"Off","OptionOn":"On","OptionRelease":"Versione Ufficiale","OptionBeta":"Beta","OptionDev":"Dev (instabile)","UninstallPluginHeader":"Disinstalla Plugin","UninstallPluginConfirmation":"Sei sicuro di voler Disinstallare {0}?","NoPluginConfigurationMessage":"Questo Plugin non \u00e8 stato configurato.","NoPluginsInstalledMessage":"non ci sono Plugins installati.","BrowsePluginCatalogMessage":"Sfoglia il catalogo dei Plugins."}
+{
+    "SettingsSaved": "Settaggi salvati.",
+    "AddUser": "Aggiungi utente",
+    "Users": "Utenti",
+    "Delete": "Elimina",
+    "Administrator": "Amministratore",
+    "Password": "Password",
+    "DeleteImage": "Elimina immagine",
+    "DeleteImageConfirmation": "Sei sicuro di voler eliminare questa immagine?",
+    "FileReadCancelled": "Il file letto \u00e8 stato cancellato.",
+    "FileNotFound": "File non trovato",
+    "FileReadError": "Errore durante la lettura del file.",
+    "DeleteUser": "Elimina utente",
+    "DeleteUserConfirmation": "Sei sicuro di voler eliminare {0}?",
+    "PasswordResetHeader": "Ripristina Password",
+    "PasswordResetComplete": "la password \u00e8 stata ripristinata.",
+    "PasswordResetConfirmation": "Sei sicuro di voler ripristinare la password?",
+    "PasswordSaved": "Password salvata.",
+    "PasswordMatchError": "Le password non coincidono.",
+    "OptionOff": "Off",
+    "OptionOn": "On",
+    "OptionRelease": "Versione Ufficiale",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev (instabile)",
+    "UninstallPluginHeader": "Disinstalla Plugin",
+    "UninstallPluginConfirmation": "Sei sicuro di voler Disinstallare {0}?",
+    "NoPluginConfigurationMessage": "Questo Plugin non \u00e8 stato configurato.",
+    "NoPluginsInstalledMessage": "non ci sono Plugins installati.",
+    "BrowsePluginCatalogMessage": "Sfoglia il catalogo dei Plugins."
+}

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/JavaScript/kk.json


+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/ms.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Seting Disimpan","AddUser":"Tambah Pengguna","Users":"Para Pengguna","Delete":"Padam","Administrator":"Administrator","Password":"Password","DeleteImage":"Delete Image","DeleteImageConfirmation":"Are you sure you wish to delete this image?","FileReadCancelled":"The file read has been canceled.","FileNotFound":"File not found.","FileReadError":"An error occurred while reading the file.","DeleteUser":"Delete User","DeleteUserConfirmation":"Are you sure you wish to delete {0}?","PasswordResetHeader":"Password Reset","PasswordResetComplete":"The password has been reset.","PasswordResetConfirmation":"Are you sure you wish to reset the password?","PasswordSaved":"Password saved.","PasswordMatchError":"Password and password confirmation must match.","OptionOff":"Off","OptionOn":"On","OptionRelease":"Official Release","OptionBeta":"Beta","OptionDev":"Dev (Unstable)","UninstallPluginHeader":"Uninstall Plugin","UninstallPluginConfirmation":"Are you sure you wish to uninstall {0}?","NoPluginConfigurationMessage":"This plugin has nothing to configure.","NoPluginsInstalledMessage":"You have no plugins installed.","BrowsePluginCatalogMessage":"Browse our plugin catalog to view available plugins."}
+{
+    "SettingsSaved": "Seting Disimpan",
+    "AddUser": "Tambah Pengguna",
+    "Users": "Para Pengguna",
+    "Delete": "Padam",
+    "Administrator": "Administrator",
+    "Password": "Password",
+    "DeleteImage": "Delete Image",
+    "DeleteImageConfirmation": "Are you sure you wish to delete this image?",
+    "FileReadCancelled": "The file read has been canceled.",
+    "FileNotFound": "File not found.",
+    "FileReadError": "An error occurred while reading the file.",
+    "DeleteUser": "Delete User",
+    "DeleteUserConfirmation": "Are you sure you wish to delete {0}?",
+    "PasswordResetHeader": "Password Reset",
+    "PasswordResetComplete": "The password has been reset.",
+    "PasswordResetConfirmation": "Are you sure you wish to reset the password?",
+    "PasswordSaved": "Password saved.",
+    "PasswordMatchError": "Password and password confirmation must match.",
+    "OptionOff": "Off",
+    "OptionOn": "On",
+    "OptionRelease": "Official Release",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev (Unstable)",
+    "UninstallPluginHeader": "Uninstall Plugin",
+    "UninstallPluginConfirmation": "Are you sure you wish to uninstall {0}?",
+    "NoPluginConfigurationMessage": "This plugin has nothing to configure.",
+    "NoPluginsInstalledMessage": "You have no plugins installed.",
+    "BrowsePluginCatalogMessage": "Browse our plugin catalog to view available plugins."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/nb.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Innstillinger lagret","AddUser":"Legg til bruker","Users":"Brukere","Delete":"Slett","Administrator":"Administrator","Password":"PAssord","DeleteImage":"Slett bilde","DeleteImageConfirmation":"Er du sikker p\u00e5 at du vil slette bildet?","FileReadCancelled":"Lesing av filen avbrutt","FileNotFound":"Fil ikke funnet","FileReadError":"Feil oppstod i det filen ble lest","DeleteUser":"Slett bruker","DeleteUserConfirmation":"Er du sikker p\u00e5 at du vil slette{0}?","PasswordResetHeader":"Resett passord","PasswordResetComplete":"Passordet har blitt resatt","PasswordResetConfirmation":"Er du sikker p\u00e5 at du vil resette passordet?","PasswordSaved":"Passord lagret","PasswordMatchError":"Passord og passord-verifiseringen m\u00e5 matche","OptionOff":"Av","OptionOn":"P\u00e5","OptionRelease":"Sluppet","OptionBeta":"Beta","OptionDev":"Dev","UninstallPluginHeader":"Avinstaller plugin","UninstallPluginConfirmation":"Are you sure you wish to uninstall {0}?","NoPluginConfigurationMessage":"Denne pluginn-en har intet \u00e5 konfigurere","NoPluginsInstalledMessage":"Du har ikke installert noen plugins enn\u00e5","BrowsePluginCatalogMessage":"Browse v\u00e5r plugin-katalog for \u00e5 se tilgjengelige plugins"}
+{
+    "SettingsSaved": "Innstillinger lagret",
+    "AddUser": "Legg til bruker",
+    "Users": "Brukere",
+    "Delete": "Slett",
+    "Administrator": "Administrator",
+    "Password": "PAssord",
+    "DeleteImage": "Slett bilde",
+    "DeleteImageConfirmation": "Er du sikker p\u00e5 at du vil slette bildet?",
+    "FileReadCancelled": "Lesing av filen avbrutt",
+    "FileNotFound": "Fil ikke funnet",
+    "FileReadError": "Feil oppstod i det filen ble lest",
+    "DeleteUser": "Slett bruker",
+    "DeleteUserConfirmation": "Er du sikker p\u00e5 at du vil slette{0}?",
+    "PasswordResetHeader": "Resett passord",
+    "PasswordResetComplete": "Passordet har blitt resatt",
+    "PasswordResetConfirmation": "Er du sikker p\u00e5 at du vil resette passordet?",
+    "PasswordSaved": "Passord lagret",
+    "PasswordMatchError": "Passord og passord-verifiseringen m\u00e5 matche",
+    "OptionOff": "Av",
+    "OptionOn": "P\u00e5",
+    "OptionRelease": "Sluppet",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev",
+    "UninstallPluginHeader": "Avinstaller plugin",
+    "UninstallPluginConfirmation": "Are you sure you wish to uninstall {0}?",
+    "NoPluginConfigurationMessage": "Denne pluginn-en har intet \u00e5 konfigurere",
+    "NoPluginsInstalledMessage": "Du har ikke installert noen plugins enn\u00e5",
+    "BrowsePluginCatalogMessage": "Browse v\u00e5r plugin-katalog for \u00e5 se tilgjengelige plugins"
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/nl.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Instellingen opgeslagen.","AddUser":"Gebruiker toevoegen","Users":"Gebruikers","Delete":"Verwijderen","Administrator":"Beheerder","Password":"Wachtwoord","DeleteImage":"Verwijder afbeelding","DeleteImageConfirmation":"Weet je zeker dat je deze afbeelding wilt verwijderen?","FileReadCancelled":"Bestand lezen is geannuleerd.","FileNotFound":"Bestand niet gevonden.","FileReadError":"Er is een fout opgetreden bij het lezen van het bestand.","DeleteUser":"Verwijder gebruiker","DeleteUserConfirmation":"Weet je zeker dat je {0} wilt verwijderen?","PasswordResetHeader":"Wachtwoord opnieuw instellen","PasswordResetComplete":"Het wachtwoord is opnieuw ingesteld.","PasswordResetConfirmation":"Weet je zeker dat je het wachtwoord opnieuw in wilt stellen?","PasswordSaved":"Wachtwoord opgeslagen.","PasswordMatchError":"Wachtwoord en wachtwoord bevestiging moeten hetzelfde zijn.","OptionOff":"Uit","OptionOn":"Aan","OptionRelease":"Offici\u00eble Release","OptionBeta":"Beta","OptionDev":"Dev (Instabiel)","UninstallPluginHeader":"Plug-in de\u00efnstalleren","UninstallPluginConfirmation":"Weet u zeker dat u {0} wilt de\u00efnstalleren?","NoPluginConfigurationMessage":"Deze plug-in heeft niets in te stellen","NoPluginsInstalledMessage":"U heeft geen plug-ins ge\u00efnstalleerd","BrowsePluginCatalogMessage":"Bekijk de Plug-in catalogus voor beschikbare plug-ins."}
+{
+    "SettingsSaved": "Instellingen opgeslagen.",
+    "AddUser": "Gebruiker toevoegen",
+    "Users": "Gebruikers",
+    "Delete": "Verwijderen",
+    "Administrator": "Beheerder",
+    "Password": "Wachtwoord",
+    "DeleteImage": "Verwijder afbeelding",
+    "DeleteImageConfirmation": "Weet je zeker dat je deze afbeelding wilt verwijderen?",
+    "FileReadCancelled": "Bestand lezen is geannuleerd.",
+    "FileNotFound": "Bestand niet gevonden.",
+    "FileReadError": "Er is een fout opgetreden bij het lezen van het bestand.",
+    "DeleteUser": "Verwijder gebruiker",
+    "DeleteUserConfirmation": "Weet je zeker dat je {0} wilt verwijderen?",
+    "PasswordResetHeader": "Wachtwoord opnieuw instellen",
+    "PasswordResetComplete": "Het wachtwoord is opnieuw ingesteld.",
+    "PasswordResetConfirmation": "Weet je zeker dat je het wachtwoord opnieuw in wilt stellen?",
+    "PasswordSaved": "Wachtwoord opgeslagen.",
+    "PasswordMatchError": "Wachtwoord en wachtwoord bevestiging moeten hetzelfde zijn.",
+    "OptionOff": "Uit",
+    "OptionOn": "Aan",
+    "OptionRelease": "Offici\u00eble Release",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev (Instabiel)",
+    "UninstallPluginHeader": "Plug-in de\u00efnstalleren",
+    "UninstallPluginConfirmation": "Weet u zeker dat u {0} wilt de\u00efnstalleren?",
+    "NoPluginConfigurationMessage": "Deze plug-in heeft niets in te stellen",
+    "NoPluginsInstalledMessage": "U heeft geen plug-ins ge\u00efnstalleerd",
+    "BrowsePluginCatalogMessage": "Bekijk de Plug-in catalogus voor beschikbare plug-ins."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/pt_BR.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Ajustes salvos.","AddUser":"Adicionar Usu\u00e1rio","Users":"Usu\u00e1rios","Delete":"Apagar","Administrator":"Administrador","Password":"Senha","DeleteImage":"Apagar Imagem","DeleteImageConfirmation":"Tem certeza que deseja apagar esta imagem?","FileReadCancelled":"A leitura do arquivo foi cancelada.","FileNotFound":"Arquivo n\u00e3o encontrado.","FileReadError":"Ocorreu um erro ao ler o arquivo.","DeleteUser":"Apagar Usu\u00e1rio","DeleteUserConfirmation":"Deseja realmente apagar {0}?","PasswordResetHeader":"Redefinir Senha","PasswordResetComplete":"A senha foi redefinida.","PasswordResetConfirmation":"Deseja realmente redefinir a senha?","PasswordSaved":"Senha salva.","PasswordMatchError":"A senha e a confirma\u00e7\u00e3o da senha devem ser iguais.","OptionOff":"Off","OptionOn":"On","OptionRelease":"Lan\u00e7amento Oficial","OptionBeta":"Beta","OptionDev":"Dev (Inst\u00e1vel)","UninstallPluginHeader":"Desinstalar Plugin","UninstallPluginConfirmation":"Deseja realmente desinstalar {0}?","NoPluginConfigurationMessage":"Este plugin n\u00e3o necessita configurar.","NoPluginsInstalledMessage":"N\u00e3o existem plugins instalados.","BrowsePluginCatalogMessage":"Navegue pelo cat\u00e1logo de plugins para ver os dispon\u00edveis."}
+{
+    "SettingsSaved": "Ajustes salvos.",
+    "AddUser": "Adicionar Usu\u00e1rio",
+    "Users": "Usu\u00e1rios",
+    "Delete": "Apagar",
+    "Administrator": "Administrador",
+    "Password": "Senha",
+    "DeleteImage": "Apagar Imagem",
+    "DeleteImageConfirmation": "Tem certeza que deseja apagar esta imagem?",
+    "FileReadCancelled": "A leitura do arquivo foi cancelada.",
+    "FileNotFound": "Arquivo n\u00e3o encontrado.",
+    "FileReadError": "Ocorreu um erro ao ler o arquivo.",
+    "DeleteUser": "Apagar Usu\u00e1rio",
+    "DeleteUserConfirmation": "Deseja realmente apagar {0}?",
+    "PasswordResetHeader": "Redefinir Senha",
+    "PasswordResetComplete": "A senha foi redefinida.",
+    "PasswordResetConfirmation": "Deseja realmente redefinir a senha?",
+    "PasswordSaved": "Senha salva.",
+    "PasswordMatchError": "A senha e a confirma\u00e7\u00e3o da senha devem ser iguais.",
+    "OptionOff": "Off",
+    "OptionOn": "On",
+    "OptionRelease": "Lan\u00e7amento Oficial",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev (Inst\u00e1vel)",
+    "UninstallPluginHeader": "Desinstalar Plugin",
+    "UninstallPluginConfirmation": "Deseja realmente desinstalar {0}?",
+    "NoPluginConfigurationMessage": "Este plugin n\u00e3o necessita configurar.",
+    "NoPluginsInstalledMessage": "N\u00e3o existem plugins instalados.",
+    "BrowsePluginCatalogMessage": "Navegue pelo cat\u00e1logo de plugins para ver os dispon\u00edveis."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/pt_PT.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Configura\u00e7\u00f5es guardadas.","AddUser":"Adicionar Utilizador","Users":"Utilizadores","Delete":"Apagar","Administrator":"Administrador","Password":"Senha","DeleteImage":"Apagar Imagem","DeleteImageConfirmation":"Tem a certeza que deseja apagar a imagem?","FileReadCancelled":"A leitura do ficheiro foi cancelada.","FileNotFound":"Ficheiro n\u00e3o encontrado.","FileReadError":"Ocorreu um erro ao ler o ficheiro.","DeleteUser":"Apagar Utilizador","DeleteUserConfirmation":"Tem a certeza que deseja apagar {0}?","PasswordResetHeader":"Redefinir Senha","PasswordResetComplete":"A senha foi redefinida.","PasswordResetConfirmation":"Tem a certeza que deseja redefinir a senha?","PasswordSaved":"Senha guardada.","PasswordMatchError":"A senha e a confirma\u00e7\u00e3o da senha devem coincidir.","OptionOff":"Desligado","OptionOn":"Ligado","OptionRelease":"Lan\u00e7amento Oficial","OptionBeta":"Beta","OptionDev":"Dev (Inst\u00e1vel)","UninstallPluginHeader":"Desinstalar extens\u00e3o","UninstallPluginConfirmation":"Tem a certeza que deseja desinstalar {0}?","NoPluginConfigurationMessage":"Esta extens\u00e3o n\u00e3o \u00e9 configur\u00e1vel.","NoPluginsInstalledMessage":"N\u00e3o tem extens\u00f5es instaladas.","BrowsePluginCatalogMessage":"Navegue o nosso cat\u00e1logo de extens\u00f5es, para ver as extens\u00f5es dispon\u00edveis."}
+{
+    "SettingsSaved": "Configura\u00e7\u00f5es guardadas.",
+    "AddUser": "Adicionar Utilizador",
+    "Users": "Utilizadores",
+    "Delete": "Apagar",
+    "Administrator": "Administrador",
+    "Password": "Senha",
+    "DeleteImage": "Apagar Imagem",
+    "DeleteImageConfirmation": "Tem a certeza que deseja apagar a imagem?",
+    "FileReadCancelled": "A leitura do ficheiro foi cancelada.",
+    "FileNotFound": "Ficheiro n\u00e3o encontrado.",
+    "FileReadError": "Ocorreu um erro ao ler o ficheiro.",
+    "DeleteUser": "Apagar Utilizador",
+    "DeleteUserConfirmation": "Tem a certeza que deseja apagar {0}?",
+    "PasswordResetHeader": "Redefinir Senha",
+    "PasswordResetComplete": "A senha foi redefinida.",
+    "PasswordResetConfirmation": "Tem a certeza que deseja redefinir a senha?",
+    "PasswordSaved": "Senha guardada.",
+    "PasswordMatchError": "A senha e a confirma\u00e7\u00e3o da senha devem coincidir.",
+    "OptionOff": "Desligado",
+    "OptionOn": "Ligado",
+    "OptionRelease": "Lan\u00e7amento Oficial",
+    "OptionBeta": "Beta",
+    "OptionDev": "Dev (Inst\u00e1vel)",
+    "UninstallPluginHeader": "Desinstalar extens\u00e3o",
+    "UninstallPluginConfirmation": "Tem a certeza que deseja desinstalar {0}?",
+    "NoPluginConfigurationMessage": "Esta extens\u00e3o n\u00e3o \u00e9 configur\u00e1vel.",
+    "NoPluginsInstalledMessage": "N\u00e3o tem extens\u00f5es instaladas.",
+    "BrowsePluginCatalogMessage": "Navegue o nosso cat\u00e1logo de extens\u00f5es, para ver as extens\u00f5es dispon\u00edveis."
+}

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/JavaScript/ru.json


+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/sv.json

@@ -1 +1,30 @@
-{"SettingsSaved":"Inst\u00e4llningarna sparade.","AddUser":"Skapa anv\u00e4ndare","Users":"Anv\u00e4ndare","Delete":"Ta bort","Administrator":"Administrat\u00f6r","Password":"L\u00f6senord","DeleteImage":"Ta bort bild","DeleteImageConfirmation":"\u00c4r du s\u00e4ker p\u00e5 att du vill ta bort den h\u00e4r bilden?","FileReadCancelled":"Inl\u00e4sningen av filen har avbrutits.","FileNotFound":"Kan inte hitta filen.","FileReadError":"Ett fel intr\u00e4ffade vid inl\u00e4sningen av filen.","DeleteUser":"Ta bort anv\u00e4ndare","DeleteUserConfirmation":"\u00c4r du s\u00e4ker p\u00e5 att du vill ta bort  {0}?","PasswordResetHeader":"\u00c5terst\u00e4ll l\u00f6senordet","PasswordResetComplete":"L\u00f6senordet har \u00e5terst\u00e4llts.","PasswordResetConfirmation":"\u00c4r du s\u00e4ker p\u00e5 att du vill \u00e5terst\u00e4lla l\u00f6senordet?","PasswordSaved":"L\u00f6senordet har sparats.","PasswordMatchError":"L\u00f6senordet och bekr\u00e4ftelsen m\u00e5ste \u00f6verensst\u00e4mma.","OptionOff":"Av","OptionOn":"P\u00e5","OptionRelease":"Officiell version","OptionBeta":"Betaversion","OptionDev":"Utvecklarversion (instabil)","UninstallPluginHeader":"Avinstallera till\u00e4gg","UninstallPluginConfirmation":"\u00c4r du s\u00e4ker p\u00e5 att du vill avinstallera {0}?","NoPluginConfigurationMessage":"Detta till\u00e4gg har inga inst\u00e4llningar.","NoPluginsInstalledMessage":"Du har inte installerat n\u00e5gra till\u00e4gg.","BrowsePluginCatalogMessage":"Bes\u00f6k katalogen f\u00f6r att se tillg\u00e4ngliga till\u00e4gg."}
+{
+    "SettingsSaved": "Inst\u00e4llningarna sparade.",
+    "AddUser": "Skapa anv\u00e4ndare",
+    "Users": "Anv\u00e4ndare",
+    "Delete": "Ta bort",
+    "Administrator": "Administrat\u00f6r",
+    "Password": "L\u00f6senord",
+    "DeleteImage": "Ta bort bild",
+    "DeleteImageConfirmation": "\u00c4r du s\u00e4ker p\u00e5 att du vill ta bort den h\u00e4r bilden?",
+    "FileReadCancelled": "Inl\u00e4sningen av filen har avbrutits.",
+    "FileNotFound": "Kan inte hitta filen.",
+    "FileReadError": "Ett fel intr\u00e4ffade vid inl\u00e4sningen av filen.",
+    "DeleteUser": "Ta bort anv\u00e4ndare",
+    "DeleteUserConfirmation": "\u00c4r du s\u00e4ker p\u00e5 att du vill ta bort  {0}?",
+    "PasswordResetHeader": "\u00c5terst\u00e4ll l\u00f6senordet",
+    "PasswordResetComplete": "L\u00f6senordet har \u00e5terst\u00e4llts.",
+    "PasswordResetConfirmation": "\u00c4r du s\u00e4ker p\u00e5 att du vill \u00e5terst\u00e4lla l\u00f6senordet?",
+    "PasswordSaved": "L\u00f6senordet har sparats.",
+    "PasswordMatchError": "L\u00f6senordet och bekr\u00e4ftelsen m\u00e5ste \u00f6verensst\u00e4mma.",
+    "OptionOff": "Av",
+    "OptionOn": "P\u00e5",
+    "OptionRelease": "Officiell version",
+    "OptionBeta": "Betaversion",
+    "OptionDev": "Utvecklarversion (instabil)",
+    "UninstallPluginHeader": "Avinstallera till\u00e4gg",
+    "UninstallPluginConfirmation": "\u00c4r du s\u00e4ker p\u00e5 att du vill avinstallera {0}?",
+    "NoPluginConfigurationMessage": "Detta till\u00e4gg har inga inst\u00e4llningar.",
+    "NoPluginsInstalledMessage": "Du har inte installerat n\u00e5gra till\u00e4gg.",
+    "BrowsePluginCatalogMessage": "Bes\u00f6k katalogen f\u00f6r att se tillg\u00e4ngliga till\u00e4gg."
+}

+ 30 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/zh_TW.json

@@ -1 +1,30 @@
-{"SettingsSaved":"\u8a2d\u7f6e\u5df2\u4fdd\u5b58\u3002","AddUser":"\u6dfb\u52a0\u7528\u6236","Users":"\u7528\u6236","Delete":"\u522a\u9664","Administrator":"\u7ba1\u7406\u54e1","Password":"\u5bc6\u78bc","DeleteImage":"\u522a\u9664\u5716\u50cf","DeleteImageConfirmation":"\u4f60\u78ba\u5b9a\u8981\u522a\u9664\u9019\u5f35\u5716\u50cf\uff1f","FileReadCancelled":"\u6a94\u6848\u8b80\u53d6\u5df2\u88ab\u53d6\u6d88\u3002","FileNotFound":"\u672a\u627e\u5230\u6a94\u6848\u3002","FileReadError":"\u5728\u8b80\u53d6\u6a94\u6848\u6642\u767c\u751f\u932f\u8aa4\u3002","DeleteUser":"\u522a\u9664\u7528\u6236","DeleteUserConfirmation":"\u4f60\u78ba\u5b9a\u8981\u522a\u9664{0}\uff1f","PasswordResetHeader":"\u91cd\u8a2d\u5bc6\u78bc","PasswordResetComplete":"\u5bc6\u78bc\u5df2\u91cd\u8a2d","PasswordResetConfirmation":"\u4f60\u78ba\u5b9a\u8981\u91cd\u8a2d\u5bc6\u78bc\uff1f","PasswordSaved":"\u5bc6\u78bc\u5df2\u4fdd\u5b58\u3002","PasswordMatchError":"\u5bc6\u78bc\u548c\u5bc6\u78bc\u78ba\u8a8d\u5fc5\u9808\u4e00\u81f4\u3002","OptionOff":"\u95dc\u9589","OptionOn":"\u958b\u555f","OptionRelease":"\u6b63\u5f0f\u7248\u672c","OptionBeta":"\u516c\u6e2c\u7248\u672c","OptionDev":"\u958b\u767c\u7248\u672c","UninstallPluginHeader":"\u5378\u8f09\u63d2\u4ef6","UninstallPluginConfirmation":"\u4f60\u78ba\u5b9a\u8981\u5378\u8f09{0}\uff1f","NoPluginConfigurationMessage":"\u9019\u500b\u63d2\u4ef6\u6c92\u6709\u8a2d\u5b9a\u9078\u9805\u3002","NoPluginsInstalledMessage":"\u4f60\u6c92\u6709\u5b89\u88dd\u63d2\u4ef6\u3002","BrowsePluginCatalogMessage":"\u700f\u89bd\u6211\u5011\u7684\u63d2\u4ef6\u76ee\u9304\u4f86\u67e5\u770b\u53ef\u7528\u7684\u63d2\u4ef6\u3002"}
+{
+    "SettingsSaved": "\u8a2d\u7f6e\u5df2\u4fdd\u5b58\u3002",
+    "AddUser": "\u6dfb\u52a0\u7528\u6236",
+    "Users": "\u7528\u6236",
+    "Delete": "\u522a\u9664",
+    "Administrator": "\u7ba1\u7406\u54e1",
+    "Password": "\u5bc6\u78bc",
+    "DeleteImage": "\u522a\u9664\u5716\u50cf",
+    "DeleteImageConfirmation": "\u4f60\u78ba\u5b9a\u8981\u522a\u9664\u9019\u5f35\u5716\u50cf\uff1f",
+    "FileReadCancelled": "\u6a94\u6848\u8b80\u53d6\u5df2\u88ab\u53d6\u6d88\u3002",
+    "FileNotFound": "\u672a\u627e\u5230\u6a94\u6848\u3002",
+    "FileReadError": "\u5728\u8b80\u53d6\u6a94\u6848\u6642\u767c\u751f\u932f\u8aa4\u3002",
+    "DeleteUser": "\u522a\u9664\u7528\u6236",
+    "DeleteUserConfirmation": "\u4f60\u78ba\u5b9a\u8981\u522a\u9664{0}\uff1f",
+    "PasswordResetHeader": "\u91cd\u8a2d\u5bc6\u78bc",
+    "PasswordResetComplete": "\u5bc6\u78bc\u5df2\u91cd\u8a2d",
+    "PasswordResetConfirmation": "\u4f60\u78ba\u5b9a\u8981\u91cd\u8a2d\u5bc6\u78bc\uff1f",
+    "PasswordSaved": "\u5bc6\u78bc\u5df2\u4fdd\u5b58\u3002",
+    "PasswordMatchError": "\u5bc6\u78bc\u548c\u5bc6\u78bc\u78ba\u8a8d\u5fc5\u9808\u4e00\u81f4\u3002",
+    "OptionOff": "\u95dc\u9589",
+    "OptionOn": "\u958b\u555f",
+    "OptionRelease": "\u6b63\u5f0f\u7248\u672c",
+    "OptionBeta": "\u516c\u6e2c\u7248\u672c",
+    "OptionDev": "\u958b\u767c\u7248\u672c",
+    "UninstallPluginHeader": "\u5378\u8f09\u63d2\u4ef6",
+    "UninstallPluginConfirmation": "\u4f60\u78ba\u5b9a\u8981\u5378\u8f09{0}\uff1f",
+    "NoPluginConfigurationMessage": "\u9019\u500b\u63d2\u4ef6\u6c92\u6709\u8a2d\u5b9a\u9078\u9805\u3002",
+    "NoPluginsInstalledMessage": "\u4f60\u6c92\u6709\u5b89\u88dd\u63d2\u4ef6\u3002",
+    "BrowsePluginCatalogMessage": "\u700f\u89bd\u6211\u5011\u7684\u63d2\u4ef6\u76ee\u9304\u4f86\u67e5\u770b\u53ef\u7528\u7684\u63d2\u4ef6\u3002"
+}

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/ar.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/ca.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/cs.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/de.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/el.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/en_GB.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/en_US.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/es.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/es_MX.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/fr.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/he.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/it.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/kk.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/ms.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/nb.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/nl.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/pt_BR.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/pt_PT.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/ru.json


+ 3 - 1
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -543,5 +543,7 @@
 	"LabelBlastMessageInterval": "Alive message interval (seconds)",
 	"LabelBlastMessageIntervalHelp": "Determines the duration in seconds between server alive messages.",
 	"LabelDefaultUser": "Default user:",
-	"LabelDefaultUserHelp": "Determines which user library should be displayed on connected devices. This can be overridden for each device using profiles."
+	"LabelDefaultUserHelp": "Determines which user library should be displayed on connected devices. This can be overridden for each device using profiles.",
+	"TitleDlna": "DLNA",
+	"HeaderServerSettings": "Server Settings"
 }

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/sv.json


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Server/zh_TW.json


+ 2 - 1
MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs

@@ -153,7 +153,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
                 {
                     if (extractImages)
                     {
-                        if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso)
+                        if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso ||
+                             video.VideoType == VideoType.BluRay)
                         {
                             continue;
                         }

+ 3 - 4
MediaBrowser.Server.Implementations/Session/SessionManager.cs

@@ -282,14 +282,13 @@ namespace MediaBrowser.Server.Implementations.Session
                 info.Item = GetItemInfo(libraryItem, runtimeTicks);
             }
 
-            session.PlayState.IsPaused = info.IsPaused;
-            session.PlayState.PositionTicks = info.PositionTicks;
             session.NowPlayingItem = info.Item;
             session.LastActivityDate = DateTime.UtcNow;
-            session.PlayState.MediaSourceId = info.MediaSourceId;
 
+            session.PlayState.IsPaused = info.IsPaused;
+            session.PlayState.PositionTicks = info.PositionTicks;
+            session.PlayState.MediaSourceId = info.MediaSourceId;
             session.PlayState.CanSeek = info.CanSeek;
-
             session.PlayState.IsMuted = info.IsMuted;
             session.PlayState.VolumeLevel = info.VolumeLevel;
             session.PlayState.AudioStreamIndex = info.AudioStreamIndex;

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