瀏覽代碼

Merge pull request #2839 from MediaBrowser/dev

Dev
Luke 8 年之前
父節點
當前提交
2bd37fd2c2
共有 39 個文件被更改,包括 433 次插入469 次删除
  1. 0 18
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  2. 1 15
      Emby.Dlna/Didl/DidlBuilder.cs
  3. 61 20
      Emby.Server.Implementations/Dto/DtoService.cs
  4. 0 1
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  5. 2 0
      Emby.Server.Implementations/IO/LibraryMonitor.cs
  6. 13 4
      Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
  7. 96 12
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  8. 4 0
      Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
  9. 15 9
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  10. 84 45
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  11. 16 6
      Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
  12. 1 0
      Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  13. 0 179
      Emby.Server.Implementations/Localization/Core/core.json
  14. 0 14
      Emby.Server.Implementations/Localization/Core/en-US.json
  15. 1 1
      Emby.Server.Implementations/Localization/LocalizationManager.cs
  16. 1 9
      Emby.Server.Implementations/Updates/InstallationManager.cs
  17. 5 5
      MediaBrowser.Controller/Entities/Folder.cs
  18. 1 1
      MediaBrowser.Controller/Entities/IHasMediaSources.cs
  19. 38 1
      MediaBrowser.Controller/Entities/Video.cs
  20. 4 0
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  21. 9 0
      MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
  22. 3 0
      MediaBrowser.Controller/LiveTv/TimerInfo.cs
  23. 10 0
      MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
  24. 5 0
      MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
  25. 2 1
      MediaBrowser.Model/Configuration/EncodingOptions.cs
  26. 5 0
      MediaBrowser.Model/Dlna/ITranscoderSupport.cs
  27. 13 6
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  28. 14 14
      MediaBrowser.Model/Dlna/StreamInfo.cs
  29. 1 0
      MediaBrowser.Model/Dto/MediaSourceInfo.cs
  30. 1 0
      MediaBrowser.Model/LiveTv/LiveTvOptions.cs
  31. 9 4
      MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
  32. 0 68
      MediaBrowser.Server.Mac.sln
  33. 0 4
      MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
  34. 2 2
      MediaBrowser.Server.Mac/MacAppHost.cs
  35. 11 25
      MediaBrowser.Server.Mac/Main.cs
  36. 1 1
      MediaBrowser.Server.Mac/Native/MonoFileSystem.cs
  37. 1 1
      Nuget/MediaBrowser.Common.nuspec
  38. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec
  39. 1 1
      SharedVersion.cs

+ 0 - 18
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -508,23 +508,6 @@ namespace Emby.Dlna.ContentDirectory
 
             if (stubType.HasValue)
             {
-                if (stubType.Value == StubType.People)
-                {
-                    var items = _libraryManager.GetPeopleItems(new InternalPeopleQuery
-                    {
-                        ItemId = item.Id
-
-                    });
-
-                    var result = new QueryResult<ServerItem>
-                    {
-                        Items = items.Select(i => new ServerItem(i)).ToArray(items.Count),
-                        TotalRecordCount = items.Count
-                    };
-
-                    return ApplyPaging(result, startIndex, limit);
-                }
-
                 var person = item as Person;
                 if (person != null)
                 {
@@ -1356,7 +1339,6 @@ namespace Emby.Dlna.ContentDirectory
     public enum StubType
     {
         Folder = 0,
-        People = 1,
         Latest = 2,
         Playlists = 3,
         Albums = 4,

+ 1 - 15
Emby.Dlna/Didl/DidlBuilder.cs

@@ -236,7 +236,7 @@ namespace Emby.Dlna.Didl
                 AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
             }
 
-            var subtitleProfiles = streamInfo.GetSubtitleProfiles(false, _serverAddress, _accessToken)
+            var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken)
                 .Where(subtitle => subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
                 .ToList();
 
@@ -391,14 +391,6 @@ namespace Emby.Dlna.Didl
 
         private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
         {
-            if (itemStubType.HasValue && itemStubType.Value == StubType.People)
-            {
-                if (item is Video)
-                {
-                    return _localization.GetLocalizedString("HeaderCastCrew");
-                }
-                return _localization.GetLocalizedString("HeaderPeople");
-            }
             if (itemStubType.HasValue && itemStubType.Value == StubType.Latest)
             {
                 return _localization.GetLocalizedString("ViewTypeMusicLatest");
@@ -961,12 +953,6 @@ namespace Emby.Dlna.Didl
 
         private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
         {
-            if (stubType.HasValue && stubType.Value == StubType.People)
-            {
-                AddEmbeddedImageAsCover("people", writer);
-                return;
-            }
-
             ImageDownloadInfo imageInfo = null;
 
             if (context is UserView)

+ 61 - 20
Emby.Server.Implementations/Dto/DtoService.cs

@@ -115,14 +115,10 @@ namespace Emby.Server.Implementations.Dto
             var programTuples = new List<Tuple<BaseItem, BaseItemDto>>();
             var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>();
 
-            var refreshQueue = options.Fields.Contains(ItemFields.RefreshState)
-                ? _providerManager.GetRefreshQueue()
-                : null;
-
             var index = 0;
             foreach (var item in items)
             {
-                var dto = GetBaseItemDtoInternal(item, options, refreshQueue, user, owner);
+                var dto = GetBaseItemDtoInternal(item, options, user, owner);
 
                 var tvChannel = item as LiveTvChannel;
                 if (tvChannel != null)
@@ -176,11 +172,7 @@ namespace Emby.Server.Implementations.Dto
         {
             var syncDictionary = GetSyncedItemProgress(options);
 
-            var refreshQueue = options.Fields.Contains(ItemFields.RefreshState)
-                ? _providerManager.GetRefreshQueue()
-                : null;
-
-            var dto = GetBaseItemDtoInternal(item, options, refreshQueue, user, owner);
+            var dto = GetBaseItemDtoInternal(item, options, user, owner);
             var tvChannel = item as LiveTvChannel;
             if (tvChannel != null)
             {
@@ -312,7 +304,7 @@ namespace Emby.Server.Implementations.Dto
             }
         }
 
-        private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, Dictionary<Guid, Guid> currentRefreshQueue, User user = null, BaseItem owner = null)
+        private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
         {
             var fields = options.Fields;
 
@@ -377,6 +369,8 @@ namespace Emby.Server.Implementations.Dto
                     {
                         dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user);
                     }
+
+                    NormalizeMediaSourceContainers(dto);
                 }
             }
 
@@ -412,25 +406,72 @@ namespace Emby.Server.Implementations.Dto
                 dto.Etag = item.GetEtag(user);
             }
 
-            if (currentRefreshQueue != null)
+            var liveTvManager = _livetvManager();
+            if (item is ILiveTvRecording)
             {
-                //dto.RefreshState = item.GetRefreshState(currentRefreshQueue);
+                liveTvManager.AddInfoToRecordingDto(item, dto, user);
             }
-
-            if (item is ILiveTvRecording)
+            else
             {
-                _livetvManager().AddInfoToRecordingDto(item, dto, user);
+                var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
+                if (activeRecording != null)
+                {
+                    dto.Type = "Recording";
+                    dto.CanDownload = false;
+                    if (!string.IsNullOrWhiteSpace(dto.SeriesName))
+                    {
+                        dto.EpisodeTitle = dto.Name;
+                        dto.Name = dto.SeriesName;
+                    }
+                    liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user);
+                }
             }
 
             return dto;
         }
 
+        private void NormalizeMediaSourceContainers(BaseItemDto dto)
+        {
+            foreach (var mediaSource in dto.MediaSources)
+            {
+                var container = mediaSource.Container;
+                if (string.IsNullOrWhiteSpace(container))
+                {
+                    continue;
+                }
+                var containers = container.Split(new[] { ',' });
+                if (containers.Length < 2)
+                {
+                    continue;
+                }
+
+                var path = mediaSource.Path;
+                string fileExtensionContainer = null;
+
+                if (!string.IsNullOrWhiteSpace(path))
+                {
+                    path = Path.GetExtension(path);
+                    if (!string.IsNullOrWhiteSpace(path))
+                    {
+                        path = Path.GetExtension(path);
+                        if (!string.IsNullOrWhiteSpace(path))
+                        {
+                            path = path.TrimStart('.');
+                        }
+                        if (!string.IsNullOrWhiteSpace(path) && containers.Contains(path, StringComparer.OrdinalIgnoreCase))
+                        {
+                            fileExtensionContainer = path;
+                        }
+                    }
+                }
+
+                mediaSource.Container = fileExtensionContainer ?? containers[0];
+            }
+        }
+
         public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Dictionary<string, SyncedItemProgress> syncProgress, User user = null)
         {
-            var refreshQueue = options.Fields.Contains(ItemFields.RefreshState)
-                ? _providerManager.GetRefreshQueue()
-                : null;
-            var dto = GetBaseItemDtoInternal(item, options, refreshQueue, user);
+            var dto = GetBaseItemDtoInternal(item, options, user);
 
             if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts))
             {

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

@@ -698,7 +698,6 @@
     <EmbeddedResource Include="Localization\Core\ar.json" />
     <EmbeddedResource Include="Localization\Core\bg-BG.json" />
     <EmbeddedResource Include="Localization\Core\ca.json" />
-    <EmbeddedResource Include="Localization\Core\core.json" />
     <EmbeddedResource Include="Localization\Core\cs.json" />
     <EmbeddedResource Include="Localization\Core\da.json" />
     <EmbeddedResource Include="Localization\Core\de.json" />

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

@@ -85,6 +85,8 @@ namespace Emby.Server.Implementations.IO
 
         public bool IsPathLocked(string path)
         {
+            // This method is not used by the core but it used by auto-organize
+
             var lockedPaths = _tempIgnoredPaths.Keys.ToList();
             return lockedPaths.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path));
         }

+ 13 - 4
Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs

@@ -1,6 +1,8 @@
-using MediaBrowser.Controller.Configuration;
+using System.Globalization;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Globalization;
 using MediaBrowser.Naming.Common;
 using MediaBrowser.Naming.TV;
 
@@ -17,15 +19,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
         private readonly IServerConfigurationManager _config;
 
         private readonly ILibraryManager _libraryManager;
+        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+        private readonly ILocalizationManager _localization;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="SeasonResolver"/> class.
         /// </summary>
         /// <param name="config">The config.</param>
-        public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager)
+        public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization)
         {
             _config = config;
             _libraryManager = libraryManager;
+            _localization = localization;
         }
 
         /// <summary>
@@ -47,9 +52,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                     SeriesName = series.Name
                 };
 
-                if (season.IndexNumber.HasValue && season.IndexNumber.Value == 0)
+                if (season.IndexNumber.HasValue)
                 {
-                    season.Name = _config.Configuration.SeasonZeroDisplayName;
+                    var seasonNumber = season.IndexNumber.Value;
+
+                    season.Name = seasonNumber == 0 ?
+                        _config.Configuration.SeasonZeroDisplayName :
+                        string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.ToString(UsCulture));
                 }
 
                 return season;

+ 96 - 12
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -830,6 +830,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             existingTimer.IsKids = updatedTimer.IsKids;
             existingTimer.IsNews = updatedTimer.IsNews;
             existingTimer.IsMovie = updatedTimer.IsMovie;
+            existingTimer.IsSeries = updatedTimer.IsSeries;
+            existingTimer.IsLive = updatedTimer.IsLive;
+            existingTimer.IsPremiere = updatedTimer.IsPremiere;
             existingTimer.IsProgramSeries = updatedTimer.IsProgramSeries;
             existingTimer.IsRepeat = updatedTimer.IsRepeat;
             existingTimer.IsSports = updatedTimer.IsSports;
@@ -861,7 +864,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
         public async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken)
         {
-            return _activeRecordings.Values.ToList().Select(GetRecordingInfo).ToList();
+            return new List<RecordingInfo>();
+            //return _activeRecordings.Values.ToList().Select(GetRecordingInfo).ToList();
         }
 
         public string GetActiveRecordingPath(string id)
@@ -875,6 +879,28 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             return null;
         }
 
+        public IEnumerable<ActiveRecordingInfo> GetAllActiveRecordings()
+        {
+            return _activeRecordings.Values;
+        }
+
+        public ActiveRecordingInfo GetActiveRecordingInfo(string path)
+        {
+            if (string.IsNullOrWhiteSpace(path))
+            {
+                return null;
+            }
+
+            foreach (var recording in _activeRecordings.Values)
+            {
+                if (string.Equals(recording.Path, path, StringComparison.Ordinal))
+                {
+                    return recording;
+                }
+            }
+            return null;
+        }
+
         private RecordingInfo GetRecordingInfo(ActiveRecordingInfo info)
         {
             var timer = info.Timer;
@@ -1245,6 +1271,33 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             throw new FileNotFoundException();
         }
 
+        public async Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken)
+        {
+            var stream = new MediaSourceInfo
+            {
+                Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
+                Id = info.Id,
+                SupportsDirectPlay = false,
+                SupportsDirectStream = true,
+                SupportsTranscoding = true,
+                IsInfiniteStream = true,
+                RequiresOpening = false,
+                RequiresClosing = false,
+                Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
+                BufferMs = 0,
+                IgnoreDts = true,
+                IgnoreIndex = true
+            };
+
+            var isAudio = false;
+            await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
+
+            return new List<MediaSourceInfo>
+            {
+                stream
+            };
+        }
+
         public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
         {
             // Ignore the consumer id
@@ -1327,7 +1380,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 var activeRecordingInfo = new ActiveRecordingInfo
                 {
                     CancellationTokenSource = new CancellationTokenSource(),
-                    Timer = timer
+                    Timer = timer,
+                    Id = timer.Id
                 };
 
                 if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
@@ -1493,7 +1547,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
                 recordPath = EnsureFileUnique(recordPath, timer.Id);
 
-                _libraryManager.RegisterIgnoredPath(recordPath);
                 _libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
                 _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(recordPath));
                 activeRecordingInfo.Path = recordPath;
@@ -1512,6 +1565,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     _timerProvider.AddOrUpdate(timer, false);
 
                     SaveRecordingMetadata(timer, recordPath, seriesPath);
+                    TriggerRefresh(recordPath);
                     EnforceKeepUpTo(timer, seriesPath);
                 };
 
@@ -1543,7 +1597,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 }
             }
 
-            _libraryManager.UnRegisterIgnoredPath(recordPath);
+            TriggerRefresh(recordPath);
             _libraryMonitor.ReportFileSystemChangeComplete(recordPath, true);
 
             ActiveRecordingInfo removed;
@@ -1574,6 +1628,44 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             OnRecordingStatusChanged();
         }
 
+        private void TriggerRefresh(string path)
+        {
+            var item = GetAffectedBaseItem(_fileSystem.GetDirectoryName(path));
+
+            if (item != null)
+            {
+                item.ChangedExternally();
+            }
+        }
+
+        private BaseItem GetAffectedBaseItem(string path)
+        {
+            BaseItem item = null;
+
+            while (item == null && !string.IsNullOrEmpty(path))
+            {
+                item = _libraryManager.FindByPath(path, null);
+
+                path = _fileSystem.GetDirectoryName(path);
+            }
+
+            if (item != null)
+            {
+                // If the item has been deleted find the first valid parent that still exists
+                while (!_fileSystem.DirectoryExists(item.Path) && !_fileSystem.FileExists(item.Path))
+                {
+                    item = item.GetParent();
+
+                    if (item == null)
+                    {
+                        break;
+                    }
+                }
+            }
+
+            return item;
+        }
+
         private void OnRecordingStatusChanged()
         {
             EventHelper.FireEventIfNotNull(RecordingStatusChanged, this, new RecordingStatusChangedEventArgs
@@ -2621,14 +2713,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             return list;
         }
 
-        class ActiveRecordingInfo
-        {
-            public string Path { get; set; }
-            public TimerInfo Timer { get; set; }
-            public ProgramInfo Program { get; set; }
-            public CancellationTokenSource CancellationTokenSource { get; set; }
-        }
-
         private const int TunerDiscoveryDurationMs = 3000;
 
         public async Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)

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

@@ -58,6 +58,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             timerInfo.OriginalAirDate = programInfo.OriginalAirDate;
             timerInfo.IsProgramSeries = programInfo.IsSeries;
 
+            timerInfo.IsSeries = programInfo.IsSeries;
+            timerInfo.IsLive = programInfo.IsLive;
+            timerInfo.IsPremiere = programInfo.IsPremiere;
+
             timerInfo.HomePageUrl = programInfo.HomePageUrl;
             timerInfo.CommunityRating = programInfo.CommunityRating;
             timerInfo.Overview = programInfo.Overview;

+ 15 - 9
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -247,7 +247,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             ProgramAudio audioType = ProgramAudio.Stereo;
 
             bool repeat = programInfo.@new == null;
-            string newID = programInfo.programID + "T" + startAt.Ticks + "C" + channelId;
+
+            var programId = programInfo.programID ?? string.Empty;
+
+            string newID = programId + "T" + startAt.Ticks + "C" + channelId;
 
             if (programInfo.audioProperties != null)
             {
@@ -300,7 +303,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 Etag = programInfo.md5
             };
 
-            var showId = programInfo.programID ?? string.Empty;
+            var showId = programId;
 
             if (!info.IsSeries)
             {
@@ -339,11 +342,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
             if (details.descriptions != null)
             {
-                if (details.descriptions.description1000 != null)
+                if (details.descriptions.description1000 != null && details.descriptions.description1000.Count > 0)
                 {
                     info.Overview = details.descriptions.description1000[0].description;
                 }
-                else if (details.descriptions.description100 != null)
+                else if (details.descriptions.description100 != null && details.descriptions.description100.Count > 0)
                 {
                     info.Overview = details.descriptions.description100[0].description;
                 }
@@ -351,16 +354,19 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
             if (info.IsSeries)
             {
-                info.SeriesId = programInfo.programID.Substring(0, 10);
+                info.SeriesId = programId.Substring(0, 10);
 
                 if (details.metadata != null)
                 {
                     var gracenote = details.metadata.Find(x => x.Gracenote != null).Gracenote;
-                    info.SeasonNumber = gracenote.season;
-
-                    if (gracenote.episode > 0)
+                    if (gracenote != null)
                     {
-                        info.EpisodeNumber = gracenote.episode;
+                        info.SeasonNumber = gracenote.season;
+
+                        if (gracenote.episode > 0)
+                        {
+                            info.EpisodeNumber = gracenote.episode;
+                        }
                     }
                 }
             }

+ 84 - 45
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.LiveTv
 
         private readonly LiveTvDtoService _tvDtoService;
 
-        private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
+        private ILiveTvService[] _services = new ILiveTvService[] { };
 
         private readonly SemaphoreSlim _refreshRecordingsLock = new SemaphoreSlim(1, 1);
 
@@ -124,7 +124,7 @@ namespace Emby.Server.Implementations.LiveTv
         /// <param name="listingProviders">The listing providers.</param>
         public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
         {
-            _services.AddRange(services);
+            _services = services.ToArray();
             _tunerHosts.AddRange(tunerHosts);
             _listingProviders.AddRange(listingProviders);
 
@@ -1221,9 +1221,9 @@ namespace Emby.Server.Implementations.LiveTv
             await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
 
             var numComplete = 0;
-            double progressPerService = _services.Count == 0
+            double progressPerService = _services.Length == 0
                 ? 0
-                : 1 / _services.Count;
+                : 1 / _services.Length;
 
             var newChannelIdList = new List<Guid>();
             var newProgramIdList = new List<Guid>();
@@ -1255,7 +1255,7 @@ namespace Emby.Server.Implementations.LiveTv
 
                 numComplete++;
                 double percent = numComplete;
-                percent /= _services.Count;
+                percent /= _services.Length;
 
                 progress.Report(100 * percent);
             }
@@ -1561,11 +1561,6 @@ namespace Emby.Server.Implementations.LiveTv
                 return new QueryResult<BaseItem>();
             }
 
-            if ((query.IsInProgress ?? false))
-            {
-                return new QueryResult<BaseItem>();
-            }
-
             var folderIds = EmbyTV.EmbyTV.Current.GetRecordingFolders()
                 .SelectMany(i => i.Locations)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
@@ -1577,13 +1572,10 @@ namespace Emby.Server.Implementations.LiveTv
 
             var excludeItemTypes = new List<string>();
 
-            if (!query.IsInProgress.HasValue)
-            {
-                folderIds.Add(internalLiveTvFolderId);
+            folderIds.Add(internalLiveTvFolderId);
 
-                excludeItemTypes.Add(typeof(LiveTvChannel).Name);
-                excludeItemTypes.Add(typeof(LiveTvProgram).Name);
-            }
+            excludeItemTypes.Add(typeof(LiveTvChannel).Name);
+            excludeItemTypes.Add(typeof(LiveTvProgram).Name);
 
             if (folderIds.Count == 0)
             {
@@ -1632,6 +1624,19 @@ namespace Emby.Server.Implementations.LiveTv
                 }
             }
 
+            if ((query.IsInProgress ?? false))
+            {
+                // TODO: filter
+                var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
+                var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
+
+                return new QueryResult<BaseItem>
+                {
+                    Items = items,
+                    TotalRecordCount = items.Length
+                };
+            }
+
             return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             {
                 MediaTypes = new[] { MediaType.Video },
@@ -1659,11 +1664,6 @@ namespace Emby.Server.Implementations.LiveTv
                 return new QueryResult<BaseItemDto>();
             }
 
-            if (_services.Count > 1)
-            {
-                return new QueryResult<BaseItemDto>();
-            }
-
             if (user == null || (query.IsInProgress ?? false))
             {
                 return new QueryResult<BaseItemDto>();
@@ -1722,13 +1722,9 @@ namespace Emby.Server.Implementations.LiveTv
 
             var folder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
 
-            if (_services.Count == 1 && (!query.IsInProgress.HasValue || !query.IsInProgress.Value) && (!query.IsLibraryItem.HasValue || query.IsLibraryItem.Value))
+            // TODO: Figure out how to merge emby recordings + service recordings
+            if (_services.Length == 1)
             {
-                if (!query.IsInProgress.HasValue)
-                {
-                    await RefreshRecordings(folder.Id, cancellationToken).ConfigureAwait(false);
-                }
-
                 return GetEmbyRecordings(query, options, folder.Id, user);
             }
 
@@ -1920,6 +1916,11 @@ namespace Emby.Server.Implementations.LiveTv
             await AddRecordingInfo(programTuples, CancellationToken.None).ConfigureAwait(false);
         }
 
+        public ActiveRecordingInfo GetActiveRecordingInfo(string path)
+        {
+            return EmbyTV.EmbyTV.Current.GetActiveRecordingInfo(path);
+        }
+
         public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, User user = null)
         {
             var recording = (ILiveTvRecording)item;
@@ -1949,27 +1950,72 @@ namespace Emby.Server.Implementations.LiveTv
             dto.IsKids = info.IsKids;
             dto.IsPremiere = info.IsPremiere;
 
-            dto.CanDelete = user == null
-                ? recording.CanDelete()
-                : recording.CanDelete(user);
-
-            if (dto.MediaSources == null)
+            if (info.Status == RecordingStatus.InProgress && info.EndDate.HasValue)
             {
-                dto.MediaSources = recording.GetMediaSources(true);
+                var now = DateTime.UtcNow.Ticks;
+                var start = info.StartDate.Ticks;
+                var end = info.EndDate.Value.Ticks;
+
+                var pct = now - start;
+                pct /= end;
+                pct *= 100;
+                dto.CompletionPercentage = pct;
             }
 
-            if (dto.MediaStreams == null)
+            if (channel != null)
             {
-                dto.MediaStreams = dto.MediaSources.SelectMany(i => i.MediaStreams).ToArray();
+                dto.ChannelName = channel.Name;
+
+                if (channel.HasImage(ImageType.Primary))
+                {
+                    dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
+                }
             }
+        }
 
-            if (info.Status == RecordingStatus.InProgress && info.EndDate.HasValue)
+        public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null)
+        {
+            var service = EmbyTV.EmbyTV.Current;
+
+            var info = activeRecordingInfo.Timer;
+
+            var channel = string.IsNullOrWhiteSpace(info.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(service.Name, info.ChannelId));
+
+            dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId)
+                ? null
+                : _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
+
+            dto.TimerId = string.IsNullOrEmpty(info.Id)
+                ? null
+                : _tvDtoService.GetInternalTimerId(service.Name, info.Id).ToString("N");
+
+            var startDate = info.StartDate;
+            var endDate = info.EndDate;
+
+            dto.StartDate = startDate;
+            dto.EndDate = endDate;
+            dto.Status = info.Status.ToString();
+            dto.IsRepeat = info.IsRepeat;
+            dto.EpisodeTitle = info.EpisodeTitle;
+            dto.IsMovie = info.IsMovie;
+            dto.IsSeries = info.IsSeries;
+            dto.IsSports = info.IsSports;
+            dto.IsLive = info.IsLive;
+            dto.IsNews = info.IsNews;
+            dto.IsKids = info.IsKids;
+            dto.IsPremiere = info.IsPremiere;
+
+            if (info.Status == RecordingStatus.InProgress)
             {
+                startDate = info.StartDate.AddSeconds(0 - info.PrePaddingSeconds);
+                endDate = info.EndDate.AddSeconds(info.PostPaddingSeconds);
+
                 var now = DateTime.UtcNow.Ticks;
-                var start = info.StartDate.Ticks;
-                var end = info.EndDate.Value.Ticks;
+                var start = startDate.Ticks;
+                var end = endDate.Ticks;
 
                 var pct = now - start;
+
                 pct /= end;
                 pct *= 100;
                 dto.CompletionPercentage = pct;
@@ -2098,7 +2144,6 @@ namespace Emby.Server.Implementations.LiveTv
 
             if (service is EmbyTV.EmbyTV)
             {
-                // We can't trust that we'll be able to direct stream it through emby server,  no matter what the provider says
                 return service.DeleteRecordingAsync(GetItemExternalId(recording), CancellationToken.None);
             }
 
@@ -2348,7 +2393,6 @@ namespace Emby.Server.Implementations.LiveTv
             var currentChannelsDict = new Dictionary<string, BaseItemDto>();
 
             var addCurrentProgram = options.AddCurrentProgram;
-            var addMediaSources = options.Fields.Contains(ItemFields.MediaSources);
             var addServiceName = options.Fields.Contains(ItemFields.ServiceName);
 
             foreach (var tuple in tuples)
@@ -2367,11 +2411,6 @@ namespace Emby.Server.Implementations.LiveTv
 
                 currentChannelsDict[dto.Id] = dto;
 
-                if (addMediaSources)
-                {
-                    dto.MediaSources = channel.GetMediaSources(true);
-                }
-
                 if (addCurrentProgram)
                 {
                     var channelIdString = channel.Id.ToString("N");

+ 16 - 6
Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -43,9 +43,11 @@ namespace Emby.Server.Implementations.LiveTv
 
             if (baseItem.SourceType == SourceType.LiveTV)
             {
-                if (string.IsNullOrWhiteSpace(baseItem.Path))
+                var activeRecordingInfo = _liveTvManager.GetActiveRecordingInfo(item.Path);
+
+                if (string.IsNullOrWhiteSpace(baseItem.Path) || activeRecordingInfo != null)
                 {
-                    return GetMediaSourcesInternal(item, cancellationToken);
+                    return GetMediaSourcesInternal(item, activeRecordingInfo, cancellationToken);
                 }
             }
 
@@ -56,7 +58,7 @@ namespace Emby.Server.Implementations.LiveTv
         private const char StreamIdDelimeter = '_';
         private const string StreamIdDelimeterString = "_";
 
-        private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(IHasMediaSources item, CancellationToken cancellationToken)
+        private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(IHasMediaSources item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
         {
             IEnumerable<MediaSourceInfo> sources;
 
@@ -67,12 +69,20 @@ namespace Emby.Server.Implementations.LiveTv
                 if (item is ILiveTvRecording)
                 {
                     sources = await _liveTvManager.GetRecordingMediaSources(item, cancellationToken)
-                                .ConfigureAwait(false);
+                        .ConfigureAwait(false);
                 }
                 else
                 {
-                    sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken)
-                                .ConfigureAwait(false);
+                    if (activeRecordingInfo != null)
+                    {
+                        sources = await EmbyTV.EmbyTV.Current.GetRecordingStreamMediaSources(activeRecordingInfo, cancellationToken)
+                            .ConfigureAwait(false);
+                    }
+                    else
+                    {
+                        sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken)
+                            .ConfigureAwait(false);
+                    }
                 }
             }
             catch (NotImplementedException)

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

@@ -165,6 +165,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                 RequiresOpening = true,
                 RequiresClosing = true,
                 RequiresLooping = info.EnableStreamLooping,
+                EnableMpDecimate = info.EnableMpDecimate,
 
                 ReadAtNativeFramerate = false,
 

+ 0 - 179
Emby.Server.Implementations/Localization/Core/core.json

@@ -1,179 +0,0 @@
-{
-    "AppDeviceValues": "App: {0}, Device: {1}",
-    "UserDownloadingItemWithValues": "{0} is downloading {1}",
-    "FolderTypeMixed": "Mixed content",
-    "FolderTypeMovies": "Movies",
-    "FolderTypeMusic": "Music",
-    "FolderTypeAdultVideos": "Adult videos",
-    "FolderTypePhotos": "Photos",
-    "FolderTypeMusicVideos": "Music videos",
-    "FolderTypeHomeVideos": "Home videos",
-    "FolderTypeGames": "Games",
-    "FolderTypeBooks": "Books",
-    "FolderTypeTvShows": "TV",
-    "FolderTypeInherit": "Inherit",
-    "HeaderCastCrew": "Cast & Crew",
-    "HeaderPeople": "People",
-    "ValueSpecialEpisodeName": "Special - {0}",
-    "LabelChapterName": "Chapter {0}",
-    "NameSeasonNumber": "Season {0}",
-    "LabelExit": "Exit",
-    "LabelVisitCommunity": "Visit Community",
-    "LabelGithub": "Github",
-    "LabelApiDocumentation": "Api Documentation",
-    "LabelDeveloperResources": "Developer Resources",
-    "LabelBrowseLibrary": "Browse Library",
-    "LabelConfigureServer": "Configure Emby",
-    "LabelRestartServer": "Restart Server",
-    "CategorySync": "Sync",
-    "CategoryUser": "User",
-    "CategorySystem": "System",
-    "CategoryApplication": "Application",
-    "CategoryPlugin": "Plugin",
-    "NotificationOptionPluginError": "Plugin failure",
-    "NotificationOptionApplicationUpdateAvailable": "Application update available",
-    "NotificationOptionApplicationUpdateInstalled": "Application update installed",
-    "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
-    "NotificationOptionPluginInstalled": "Plugin installed",
-    "NotificationOptionPluginUninstalled": "Plugin uninstalled",
-    "NotificationOptionVideoPlayback": "Video playback started",
-    "NotificationOptionAudioPlayback": "Audio playback started",
-    "NotificationOptionGamePlayback": "Game playback started",
-    "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
-    "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
-    "NotificationOptionGamePlaybackStopped": "Game playback stopped",
-    "NotificationOptionTaskFailed": "Scheduled task failure",
-    "NotificationOptionInstallationFailed": "Installation failure",
-    "NotificationOptionNewLibraryContent": "New content added",
-    "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)",
-    "NotificationOptionCameraImageUploaded": "Camera image uploaded",
-    "NotificationOptionUserLockedOut": "User locked out",
-    "NotificationOptionServerRestartRequired": "Server restart required",
-    "ViewTypePlaylists": "Playlists",
-    "ViewTypeMovies": "Movies",
-    "ViewTypeTvShows": "TV",
-    "ViewTypeGames": "Games",
-    "ViewTypeMusic": "Music",
-    "ViewTypeMusicGenres": "Genres",
-    "ViewTypeMusicArtists": "Artists",
-    "ViewTypeBoxSets": "Collections",
-    "ViewTypeChannels": "Channels",
-    "ViewTypeLiveTV": "Live TV",
-    "ViewTypeLiveTvNowPlaying": "Now Airing",
-    "ViewTypeLatestGames": "Latest Games",
-    "ViewTypeRecentlyPlayedGames": "Recently Played",
-    "ViewTypeGameFavorites": "Favorites",
-    "ViewTypeGameSystems": "Game Systems",
-    "ViewTypeGameGenres": "Genres",
-    "ViewTypeTvResume": "Resume",
-    "ViewTypeTvNextUp": "Next Up",
-    "ViewTypeTvLatest": "Latest",
-    "ViewTypeTvShowSeries": "Series",
-    "ViewTypeTvGenres": "Genres",
-    "ViewTypeTvFavoriteSeries": "Favorite Series",
-    "ViewTypeTvFavoriteEpisodes": "Favorite Episodes",
-    "ViewTypeMovieResume": "Resume",
-    "ViewTypeMovieLatest": "Latest",
-    "ViewTypeMovieMovies": "Movies",
-    "ViewTypeMovieCollections": "Collections",
-    "ViewTypeMovieFavorites": "Favorites",
-    "ViewTypeMovieGenres": "Genres",
-    "ViewTypeMusicLatest": "Latest",
-    "ViewTypeMusicPlaylists": "Playlists",
-    "ViewTypeMusicAlbums": "Albums",
-    "ViewTypeMusicAlbumArtists": "Album Artists",
-    "HeaderOtherDisplaySettings": "Display Settings",
-    "ViewTypeMusicSongs": "Songs",
-    "ViewTypeMusicFavorites": "Favorites",
-    "ViewTypeMusicFavoriteAlbums": "Favorite Albums",
-    "ViewTypeMusicFavoriteArtists": "Favorite Artists",
-    "ViewTypeMusicFavoriteSongs": "Favorite Songs",
-    "ViewTypeFolders": "Folders",
-    "ViewTypeLiveTvRecordingGroups": "Recordings",
-    "ViewTypeLiveTvChannels": "Channels",
-    "ScheduledTaskFailedWithName": "{0} failed",
-    "LabelRunningTimeValue": "Running time: {0}",
-    "ScheduledTaskStartedWithName": "{0} started",
-    "VersionNumber": "Version {0}",
-    "PluginInstalledWithName": "{0} was installed",
-    "PluginUpdatedWithName": "{0} was updated",
-    "PluginUninstalledWithName": "{0} was uninstalled",
-    "ItemAddedWithName": "{0} was added to the library",
-    "ItemRemovedWithName": "{0} was removed from the library",
-    "LabelIpAddressValue": "Ip address: {0}",
-    "DeviceOnlineWithName": "{0} is connected",
-    "UserOnlineFromDevice": "{0} is online from {1}",
-    "ProviderValue": "Provider: {0}",
-    "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
-    "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
-    "UserCreatedWithName": "User {0} has been created",
-    "UserPasswordChangedWithName": "Password has been changed for user {0}",
-    "UserDeletedWithName": "User {0} has been deleted",
-    "MessageServerConfigurationUpdated": "Server configuration has been updated",
-    "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
-    "MessageApplicationUpdated": "Emby Server has been updated",
-    "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
-    "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
-    "DeviceOfflineWithName": "{0} has disconnected",
-    "UserLockedOutWithName": "User {0} has been locked out",
-    "UserOfflineFromDevice": "{0} has disconnected from {1}",
-    "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
-    "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
-    "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
-    "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
-    "HeaderUnidentified": "Unidentified",
-    "HeaderImagePrimary": "Primary",
-    "HeaderImageBackdrop": "Backdrop",
-    "HeaderImageLogo": "Logo",
-    "HeaderUserPrimaryImage": "User Image",
-    "HeaderOverview": "Overview",
-    "HeaderShortOverview": "Short Overview",
-    "HeaderType": "Type",
-    "HeaderSeverity": "Severity",
-    "HeaderUser": "User",
-    "HeaderName": "Name",
-    "HeaderDate": "Date",
-    "HeaderPremiereDate": "Premiere Date",
-    "HeaderDateAdded": "Date Added",
-    "HeaderReleaseDate": "Release date",
-    "HeaderRuntime": "Runtime",
-    "HeaderPlayCount": "Play Count",
-    "HeaderSeason": "Season",
-    "HeaderSeasonNumber": "Season number",
-    "HeaderSeries": "Series:",
-    "HeaderNetwork": "Network",
-    "HeaderYear": "Year:",
-    "HeaderYears": "Years:",
-    "HeaderParentalRating": "Parental Rating",
-    "HeaderCommunityRating": "Community rating",
-    "HeaderTrailers": "Trailers",
-    "HeaderSpecials": "Specials",
-    "HeaderGameSystems": "Game Systems",
-    "HeaderPlayers": "Players:",
-    "HeaderAlbumArtists": "Album Artists",
-    "HeaderAlbums": "Albums",
-    "HeaderDisc": "Disc",
-    "HeaderTrack": "Track",
-    "HeaderAudio": "Audio",
-    "HeaderVideo": "Video",
-    "HeaderEmbeddedImage": "Embedded image",
-    "HeaderResolution": "Resolution",
-    "HeaderSubtitles": "Subtitles",
-    "HeaderGenres": "Genres",
-    "HeaderCountries": "Countries",
-    "HeaderStatus": "Status",
-    "HeaderTracks": "Tracks",
-    "HeaderMusicArtist": "Music artist",
-    "HeaderLocked": "Locked",
-    "HeaderStudios": "Studios",
-    "HeaderActor": "Actors",
-    "HeaderComposer": "Composers",
-    "HeaderDirector": "Directors",
-    "HeaderGuestStar": "Guest star",
-    "HeaderProducer": "Producers",
-    "HeaderWriter": "Writers",
-    "HeaderParentalRatings": "Parental Ratings",
-    "HeaderCommunityRatings": "Community ratings",
-    "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly.",
-    "DbUpgradeMessage":  "Please wait while your Emby Server database is upgraded. {0}% complete."
-}

+ 0 - 14
Emby.Server.Implementations/Localization/Core/en-US.json

@@ -1,5 +1,4 @@
 {
-  "DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.",
   "AppDeviceValues": "App: {0}, Device: {1}",
   "UserDownloadingItemWithValues": "{0} is downloading {1}",
   "FolderTypeMixed": "Mixed content",
@@ -13,15 +12,12 @@
   "FolderTypeBooks": "Books",
   "FolderTypeTvShows": "TV",
   "FolderTypeInherit": "Inherit",
-  "HeaderCastCrew": "Cast & Crew",
-  "HeaderPeople": "People",
   "ValueSpecialEpisodeName": "Special - {0}",
   "LabelChapterName": "Chapter {0}",
   "NameSeasonUnknown": "Season Unknown",
   "NameSeasonNumber": "Season {0}",
   "LabelExit": "Exit",
   "LabelVisitCommunity": "Visit Community",
-  "LabelGithub": "Github",
   "LabelApiDocumentation": "Api Documentation",
   "LabelDeveloperResources": "Developer Resources",
   "LabelBrowseLibrary": "Browse Library",
@@ -164,15 +160,5 @@
   "HeaderStatus": "Status",
   "HeaderTracks": "Tracks",
   "HeaderMusicArtist": "Music artist",
-  "HeaderLocked": "Locked",
-  "HeaderStudios": "Studios",
-  "HeaderActor": "Actors",
-  "HeaderComposer": "Composers",
-  "HeaderDirector": "Directors",
-  "HeaderGuestStar": "Guest star",
-  "HeaderProducer": "Producers",
-  "HeaderWriter": "Writers",
-  "HeaderParentalRatings": "Parental Ratings",
-  "HeaderCommunityRatings": "Community ratings",
   "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly."
 }

+ 1 - 1
Emby.Server.Implementations/Localization/LocalizationManager.cs

@@ -335,7 +335,7 @@ namespace Emby.Server.Implementations.Localization
             const string prefix = "Core";
             var key = prefix + culture;
 
-            return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "core.json"));
+            return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "en-US.json"));
         }
 
         private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename)

+ 1 - 9
Emby.Server.Implementations/Updates/InstallationManager.cs

@@ -277,15 +277,7 @@ namespace Emby.Server.Implementations.Updates
 
         private TimeSpan GetCacheLength()
         {
-            switch (GetSystemUpdateLevel())
-            {
-                case PackageVersionClass.Beta:
-                    return TimeSpan.FromMinutes(30);
-                case PackageVersionClass.Dev:
-                    return TimeSpan.FromMinutes(3);
-                default:
-                    return TimeSpan.FromHours(24);
-            }
+            return TimeSpan.FromMinutes(3);
         }
 
         protected PackageInfo[] FilterPackages(List<PackageInfo> packages)

+ 5 - 5
MediaBrowser.Controller/Entities/Folder.cs

@@ -648,15 +648,15 @@ namespace MediaBrowser.Controller.Entities
 
         public static bool IsPathOffline(string path, List<string> allLibraryPaths)
         {
-            if (FileSystem.FileExists(path))
-            {
-                return false;
-            }
+            //if (FileSystem.FileExists(path))
+            //{
+            //    return false;
+            //}
 
             var originalPath = path;
 
             // Depending on whether the path is local or unc, it may return either null or '\' at the top
-            while (!string.IsNullOrEmpty(path) && path.Length > 1)
+            while (!string.IsNullOrWhiteSpace(path) && path.Length > 1)
             {
                 if (FileSystem.DirectoryExists(path))
                 {

+ 1 - 1
MediaBrowser.Controller/Entities/IHasMediaSources.cs

@@ -4,7 +4,7 @@ using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public interface IHasMediaSources : IHasUserData
+    public interface IHasMediaSources : IHasMetadata
     {
         /// <summary>
         /// Gets the media sources.

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

@@ -160,7 +160,7 @@ namespace MediaBrowser.Controller.Entities
 
         public string[] GetPlayableStreamFileNames()
         {
-            return GetPlayableStreamFiles().Select(System.IO.Path.GetFileName).ToArray(); 
+            return GetPlayableStreamFiles().Select(System.IO.Path.GetFileName).ToArray();
         }
 
         /// <summary>
@@ -234,6 +234,35 @@ namespace MediaBrowser.Controller.Entities
             return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
         }
 
+        [IgnoreDataMember]
+        public override SourceType SourceType
+        {
+            get
+            {
+                if (IsActiveRecording())
+                {
+                    return SourceType.LiveTV;
+                }
+
+                return base.SourceType;
+            }
+        }
+
+        protected bool IsActiveRecording()
+        {
+            return LiveTvManager.GetActiveRecordingInfo(Path) != null;
+        }
+
+        public override bool CanDelete()
+        {
+            if (IsActiveRecording())
+            {
+                return false;
+            }
+
+            return base.CanDelete();
+        }
+
         [IgnoreDataMember]
         protected virtual bool EnableDefaultVideoUserDataKeys
         {
@@ -616,6 +645,14 @@ namespace MediaBrowser.Controller.Entities
             var list = GetAllVideosForMediaSources();
             var result = list.Select(i => GetVersionInfo(enablePathSubstitution, i.Item1, i.Item2)).ToList();
 
+            if (IsActiveRecording())
+            {
+                foreach (var mediaSource in result)
+                {
+                    mediaSource.Type = MediaSourceType.Placeholder;
+                }
+            }
+
             return result.OrderBy(i =>
             {
                 if (i.VideoType == VideoType.VideoFile)

+ 4 - 0
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -384,5 +384,9 @@ namespace MediaBrowser.Controller.LiveTv
 
         string GetEmbyTvActiveRecordingPath(string id);
         Task<LiveStream> GetEmbyTvLiveStream(string id);
+
+        ActiveRecordingInfo GetActiveRecordingInfo(string path);
+
+        void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null);
     }
 }

+ 9 - 0
MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs

@@ -36,4 +36,13 @@ namespace MediaBrowser.Controller.LiveTv
         DateTime? EndDate { get; set; }
         DateTime DateCreated { get; set; }
     }
+
+    public class ActiveRecordingInfo
+    {
+        public string Id { get; set; }
+        public string Path { get; set; }
+        public TimerInfo Timer { get; set; }
+        public ProgramInfo Program { get; set; }
+        public CancellationTokenSource CancellationTokenSource { get; set; }
+    }
 }

+ 3 - 0
MediaBrowser.Controller/LiveTv/TimerInfo.cs

@@ -109,6 +109,9 @@ namespace MediaBrowser.Controller.LiveTv
         public bool IsKids { get; set; }
         public bool IsSports { get; set; }
         public bool IsNews { get; set; }
+        public bool IsSeries { get; set; }
+        public bool IsLive { get; set; }
+        public bool IsPremiere { get; set; }
         public int? ProductionYear { get; set; }
         public string EpisodeTitle { get; set; }
         public DateTime? OriginalAirDate { get; set; }

+ 10 - 0
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -778,6 +778,11 @@ namespace MediaBrowser.Controller.MediaEncoding
                 return false;
             }
 
+            if (state.EnableMpDecimate)
+            {
+                return false;
+            }
+
             if (videoStream.IsInterlaced)
             {
                 if (request.DeInterlace)
@@ -1449,6 +1454,11 @@ namespace MediaBrowser.Controller.MediaEncoding
                 }
             }
 
+            if (state.EnableMpDecimate)
+            {
+                filters.Add("mpdecimate,setpts=N/FRAME_RATE/TB");
+            }
+
             if (filters.Count > 0)
             {
                 output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray()));

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

@@ -127,6 +127,11 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
         }
 
+        public bool EnableMpDecimate
+        {
+            get { return MediaSource.EnableMpDecimate; }
+        }
+
         public string AlbumCoverPath { get; set; }
 
         public string InputAudioSync { get; set; }

+ 2 - 1
MediaBrowser.Model/Configuration/EncodingOptions.cs

@@ -14,6 +14,7 @@ namespace MediaBrowser.Model.Configuration
         public int H264Crf { get; set; }
         public string H264Preset { get; set; }
         public bool EnableHardwareEncoding { get; set; }
+        public bool EnableSubtitleExtraction { get; set; }
 
         public string[] HardwareDecodingCodecs { get; set; }
 
@@ -26,7 +27,7 @@ namespace MediaBrowser.Model.Configuration
             VaapiDevice = "/dev/dri/card0";
             H264Crf = 23;
             EnableHardwareEncoding = true;
-
+            EnableSubtitleExtraction = true;
             HardwareDecodingCodecs = new string[] { "h264", "vc1" };
         }
     }

+ 5 - 0
MediaBrowser.Model/Dlna/ITranscoderSupport.cs

@@ -4,6 +4,7 @@
     {
         bool CanEncodeToAudioCodec(string codec);
         bool CanEncodeToSubtitleCodec(string codec);
+        bool CanExtractSubtitles(string codec);
     }
 
     public class FullTranscoderSupport : ITranscoderSupport
@@ -16,5 +17,9 @@
         {
             return true;
         }
+        public bool CanExtractSubtitles(string codec)
+        {
+            return true;
+        }
     }
 }

+ 13 - 6
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -686,7 +686,7 @@ namespace MediaBrowser.Model.Dlna
 
                     if (subtitleStream != null)
                     {
-                        SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, null, null);
+                        SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, _transcoderSupport, null, null);
 
                         playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
                         playlistItem.SubtitleFormat = subtitleProfile.Format;
@@ -728,7 +728,7 @@ namespace MediaBrowser.Model.Dlna
 
                 if (subtitleStream != null)
                 {
-                    SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, transcodingProfile.Protocol, transcodingProfile.Container);
+                    SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Protocol, transcodingProfile.Container);
 
                     playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
                     playlistItem.SubtitleFormat = subtitleProfile.Format;
@@ -1183,7 +1183,7 @@ namespace MediaBrowser.Model.Dlna
         {
             if (subtitleStream != null)
             {
-                SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, playMethod, null, null);
+                SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile.SubtitleProfiles, playMethod, _transcoderSupport, null, null);
 
                 if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed)
                 {
@@ -1202,7 +1202,7 @@ namespace MediaBrowser.Model.Dlna
             return new Tuple<bool, TranscodeReason?>(result, TranscodeReason.ContainerBitrateExceedsLimit);
         }
 
-        public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, string transcodingSubProtocol, string transcodingContainer)
+        public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, string transcodingSubProtocol, string transcodingContainer)
         {
             if (!subtitleStream.IsExternal && (playMethod != PlayMethod.Transcode || !string.Equals(transcodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase)))
             {
@@ -1256,7 +1256,9 @@ namespace MediaBrowser.Model.Dlna
             }
 
             // Look for an external or hls profile that matches the stream type (text/graphical) and doesn't require conversion
-            return GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, false) ?? GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, true) ?? new SubtitleProfile
+            return GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ?? 
+                GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ?? 
+                new SubtitleProfile
             {
                 Method = SubtitleDeliveryMethod.Encode,
                 Format = subtitleStream.Codec
@@ -1291,7 +1293,7 @@ namespace MediaBrowser.Model.Dlna
             return false;
         }
 
-        private static SubtitleProfile GetExternalSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, bool allowConversion)
+        private static SubtitleProfile GetExternalSubtitleProfile(MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, bool allowConversion)
         {
             foreach (SubtitleProfile profile in subtitleProfiles)
             {
@@ -1310,6 +1312,11 @@ namespace MediaBrowser.Model.Dlna
                     continue;
                 }
 
+                if (!subtitleStream.IsExternal && !transcoderSupport.CanExtractSubtitles(subtitleStream.Codec))
+                {
+                    continue;
+                }
+
                 if ((profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) ||
                     (profile.Method == SubtitleDeliveryMethod.Hls && subtitleStream.IsTextSubtitleStream))
                 {

+ 14 - 14
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -309,14 +309,14 @@ namespace MediaBrowser.Model.Dlna
             return list;
         }
 
-        public List<SubtitleStreamInfo> GetExternalSubtitles(bool includeSelectedTrackOnly, string baseUrl, string accessToken)
+        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
         {
-            return GetExternalSubtitles(includeSelectedTrackOnly, false, baseUrl, accessToken);
+            return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
         }
 
-        public List<SubtitleStreamInfo> GetExternalSubtitles(bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
+        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
         {
-            List<SubtitleStreamInfo> list = GetSubtitleProfiles(includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken);
+            List<SubtitleStreamInfo> list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken);
             List<SubtitleStreamInfo> newList = new List<SubtitleStreamInfo>();
 
             // First add the selected track
@@ -331,12 +331,12 @@ namespace MediaBrowser.Model.Dlna
             return newList;
         }
 
-        public List<SubtitleStreamInfo> GetSubtitleProfiles(bool includeSelectedTrackOnly, string baseUrl, string accessToken)
+        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
         {
-            return GetSubtitleProfiles(includeSelectedTrackOnly, false, baseUrl, accessToken);
+            return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
         }
 
-        public List<SubtitleStreamInfo> GetSubtitleProfiles(bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
+        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
         {
             List<SubtitleStreamInfo> list = new List<SubtitleStreamInfo>();
 
@@ -352,7 +352,7 @@ namespace MediaBrowser.Model.Dlna
                 {
                     if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
                     {
-                        AddSubtitleProfiles(list, stream, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
+                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
                     }
                 }
             }
@@ -363,7 +363,7 @@ namespace MediaBrowser.Model.Dlna
                 {
                     if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
                     {
-                        AddSubtitleProfiles(list, stream, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
+                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
                     }
                 }
             }
@@ -371,28 +371,28 @@ namespace MediaBrowser.Model.Dlna
             return list;
         }
 
-        private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks)
+        private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks)
         {
             if (enableAllProfiles)
             {
                 foreach (SubtitleProfile profile in DeviceProfile.SubtitleProfiles)
                 {
-                    SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile });
+                    SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport);
 
                     list.Add(info);
                 }
             }
             else
             {
-                SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles);
+                SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport);
 
                 list.Add(info);
             }
         }
 
-        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles)
+        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
         {
-            SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, PlayMethod, SubProtocol, Container);
+            SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, subtitleProfiles, PlayMethod, transcoderSupport, SubProtocol, Container);
             SubtitleStreamInfo info = new SubtitleStreamInfo
             {
                 IsForced = stream.IsForced,

+ 1 - 0
MediaBrowser.Model/Dto/MediaSourceInfo.cs

@@ -41,6 +41,7 @@ namespace MediaBrowser.Model.Dto
         public string OpenToken { get; set; }
         public bool RequiresClosing { get; set; }
         public bool SupportsProbing { get; set; }
+        public bool EnableMpDecimate { get; set; }
         public string LiveStreamId { get; set; }
         public int? BufferMs { get; set; }
 

+ 1 - 0
MediaBrowser.Model/LiveTv/LiveTvOptions.cs

@@ -47,6 +47,7 @@ namespace MediaBrowser.Model.LiveTv
         public bool ImportFavoritesOnly { get; set; }
         public bool AllowHWTranscoding { get; set; }
         public bool EnableStreamLooping { get; set; }
+        public bool EnableMpDecimate { get; set; }
         public bool EnableNewHdhrChannelIds { get; set; }
         public string Source { get; set; }
 

+ 9 - 4
MediaBrowser.Providers/TV/MissingEpisodeProvider.cs

@@ -191,6 +191,8 @@ namespace MediaBrowser.Providers.TV
                    });
         }
 
+        private const double UnairedEpisodeThresholdDays = 2;
+
         /// <summary>
         /// Adds the missing episodes.
         /// </summary>
@@ -248,8 +250,7 @@ namespace MediaBrowser.Providers.TV
 
                 var targetSeries = DetermineAppropriateSeries(series, tuple.Item1);
 
-                var unairedThresholdDays = 2;
-                now = now.AddDays(0 - unairedThresholdDays);
+                now = now.AddDays(0 - UnairedEpisodeThresholdDays);
 
                 if (airDate.Value < now)
                 {
@@ -329,9 +330,13 @@ namespace MediaBrowser.Providers.TV
                             return true;
                         }
 
-                        if (!allowMissingEpisodes && i.Episode.IsMissingEpisode && !i.Episode.IsUnaired)
+                        if (!allowMissingEpisodes && i.Episode.IsMissingEpisode)
                         {
-                            return true;
+                            // If it's missing, but not unaired, remove it
+                            if (!i.Episode.PremiereDate.HasValue || i.Episode.PremiereDate.Value.ToLocalTime().Date.AddDays(UnairedEpisodeThresholdDays) < DateTime.Now.Date)
+                            {
+                                return true;
+                            }
                         }
 
                         return false;

+ 0 - 68
MediaBrowser.Server.Mac.sln

@@ -17,8 +17,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "Media
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Providers", "MediaBrowser.Providers\MediaBrowser.Providers.csproj", "{442B5058-DCAF-4263-BB6A-F21E31120A1B}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Implementations", "MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj", "{2E781478-814D-4A48-9D80-BFF206441A65}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.WebDashboard", "MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj", "{5624B7B5-B5A7-41D8-9F10-CC5611109619}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.XbmcMetadata", "MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj", "{23499896-B135-4527-8574-C26E926EA99E}"
@@ -41,8 +39,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Skia", "Emby.Drawing.Skia\Emby.Drawing.Skia.csproj", "{2312DA6D-FF86-4597-9777-BCEEC32D96DD}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Common.Implementations", "Emby.Common.Implementations\Emby.Common.Implementations.csproj", "{1E37A338-9F57-4B70-BD6D-BB9C591E319B}"
-EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener", "SocketHttpListener\SocketHttpListener.csproj", "{1D74413B-E7CF-455B-B021-F52BDF881542}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Nat", "Mono.Nat\Mono.Nat.csproj", "{CB7F2326-6497-4A3D-BA03-48513B17A7BE}"
@@ -210,26 +206,6 @@ Global
 		{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
 		{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x86.ActiveCfg = Release Mono|Any CPU
 		{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Signed|x86.Build.0 = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|Any CPU.Build.0 = Release|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.AppStore|x86.Build.0 = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.Build.0 = Debug|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|Any CPU.ActiveCfg = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|Any CPU.Build.0 = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|x86.ActiveCfg = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Release Mono|x86.Build.0 = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.Build.0 = Release|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.ActiveCfg = Release|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.Build.0 = Release|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.ActiveCfg = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Signed|Any CPU.Build.0 = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Signed|x86.ActiveCfg = Release Mono|Any CPU
-		{2E781478-814D-4A48-9D80-BFF206441A65}.Signed|x86.Build.0 = Release Mono|Any CPU
 		{5624B7B5-B5A7-41D8-9F10-CC5611109619}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
 		{5624B7B5-B5A7-41D8-9F10-CC5611109619}.AppStore|Any CPU.Build.0 = Release|Any CPU
 		{5624B7B5-B5A7-41D8-9F10-CC5611109619}.AppStore|x86.ActiveCfg = Release Mono|Any CPU
@@ -470,50 +446,6 @@ Global
 		{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x64.Build.0 = Release|Any CPU
 		{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x86.ActiveCfg = Release|Any CPU
 		{2312DA6D-FF86-4597-9777-BCEEC32D96DD}.Signed|x86.Build.0 = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.AppStore|Any CPU.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.AppStore|x86.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.AppStore|x86.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|x86.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|x86.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|Any CPU.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|Any CPU.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|x86.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|x86.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|Any CPU.Build.0 = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|x86.ActiveCfg = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|x86.Build.0 = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|Any CPU.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|Any CPU.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|x86.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|x86.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|Win32.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|Win32.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|x64.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Debug|x64.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|Mixed Platforms.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|Mixed Platforms.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|Win32.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|Win32.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|x64.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release Mono|x64.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|Mixed Platforms.Build.0 = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|Win32.ActiveCfg = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|Win32.Build.0 = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|x64.ActiveCfg = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Release|x64.Build.0 = Release|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|Mixed Platforms.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|Mixed Platforms.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|Win32.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|Win32.Build.0 = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|x64.ActiveCfg = Debug|Any CPU
-		{1E37A338-9F57-4B70-BD6D-BB9C591E319B}.Signed|x64.Build.0 = Debug|Any CPU
 		{1D74413B-E7CF-455B-B021-F52BDF881542}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
 		{1D74413B-E7CF-455B-B021-F52BDF881542}.AppStore|Any CPU.Build.0 = Debug|Any CPU
 		{1D74413B-E7CF-455B-B021-F52BDF881542}.AppStore|x86.ActiveCfg = Debug|Any CPU

+ 0 - 4
MediaBrowser.Server.Mac/Emby.Server.Mac.csproj

@@ -92,10 +92,6 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll</HintPath>
     </Reference>
-    <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
-      <HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
-      <Private>True</Private>
-    </Reference>
     <Reference Include="ServiceStack.Text, Version=4.5.8.0, Culture=neutral, processorArchitecture=MSIL">
       <HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
       <Private>True</Private>

+ 2 - 2
MediaBrowser.Server.Mac/MacAppHost.cs

@@ -19,11 +19,11 @@ namespace MediaBrowser.Server.Mac
 {
 	public class MacAppHost : ApplicationHost
 	{
-        public MacAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, IMemoryStreamFactory memoryStreamFactory, MediaBrowser.Common.Net.INetworkManager networkManager, Action<string, string, string> certificateGenerator, Func<string> defaultUsernameFactory) : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, memoryStreamFactory, networkManager, certificateGenerator, defaultUsernameFactory)
+        public MacAppHost(ServerApplicationPaths applicationPaths, ILogManager logManager, StartupOptions options, IFileSystem fileSystem, IPowerManagement powerManagement, string releaseAssetFilename, IEnvironmentInfo environmentInfo, MediaBrowser.Controller.Drawing.IImageEncoder imageEncoder, ISystemEvents systemEvents, MediaBrowser.Common.Net.INetworkManager networkManager) : base(applicationPaths, logManager, options, fileSystem, powerManagement, releaseAssetFilename, environmentInfo, imageEncoder, systemEvents, networkManager)
         {
         }
 
-        public override bool CanSelfRestart
+		public override bool CanSelfRestart
         {
             get
             {

+ 11 - 25
MediaBrowser.Server.Mac/Main.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations;
 using System;
 using System.Diagnostics;
 using System.Globalization;
@@ -20,21 +19,18 @@ using MonoMac.ObjCRuntime;
 using Emby.Server.Core;
 using Emby.Server.Core.Cryptography;
 using Emby.Server.Implementations;
-using Emby.Common.Implementations.Logging;
 using Emby.Server.Implementations.Logging;
-using Emby.Common.Implementations.EnvironmentInfo;
 using Emby.Server.Mac.Native;
 using Emby.Server.Implementations.IO;
-using Emby.Common.Implementations.Networking;
-using Emby.Common.Implementations.Cryptography;
 using Mono.Unix.Native;
 using MediaBrowser.Model.System;
 using MediaBrowser.Model.IO;
-using Emby.Server.Implementations.Logging;
 using Emby.Drawing;
 using Emby.Drawing.Skia;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Model.Drawing;
+using Emby.Server.Implementations.EnvironmentInfo;
+using Emby.Server.Implementations.Networking;
 
 namespace MediaBrowser.Server.Mac
 {
@@ -60,7 +56,7 @@ namespace MediaBrowser.Server.Mac
 
 			var appPaths = CreateApplicationPaths(appFolderPath, customProgramDataPath);
 
-			var logManager = new NlogManager(appPaths.LogDirectoryPath, "server");
+			var logManager = new SimpleLogManager(appPaths.LogDirectoryPath, "server");
 			logManager.ReloadLogger(LogSeverity.Info);
 			logManager.AddConsoleOutput();
 
@@ -90,9 +86,7 @@ namespace MediaBrowser.Server.Mac
 			// Within the mac bundle, go uo two levels then down into Resources folder
 			var resourcesPath = Path.Combine(Path.GetDirectoryName(appFolderPath), "Resources");
 
-			Action<string> createDirectoryFn = (string obj) => Directory.CreateDirectory(obj);
-
-			return new ServerApplicationPaths(programDataPath, appFolderPath, resourcesPath, createDirectoryFn);
+			return new ServerApplicationPaths(programDataPath, appFolderPath, resourcesPath);
 		}
 
 		/// <summary>
@@ -125,10 +119,7 @@ namespace MediaBrowser.Server.Mac
 									 environmentInfo,
 									 imageEncoder,
 									 new SystemEvents(logManager.GetLogger("SystemEvents")),
-									 new MemoryStreamProvider(),
-			                         new NetworkManager(logManager.GetLogger("NetworkManager")),
-									 GenerateCertificate,
-									 () => Environment.UserName);
+			                         new NetworkManager(logManager.GetLogger("NetworkManager")));
 
 			if (options.ContainsOption("-v")) {
 				Console.WriteLine (AppHost.ApplicationVersion.ToString());
@@ -152,16 +143,11 @@ namespace MediaBrowser.Server.Mac
 	        }
 	    }
 
-        private static void GenerateCertificate(string certPath, string certHost, string certPassword)
-        {
-			CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, certPassword, _logger);
-        }
-
         private static EnvironmentInfo GetEnvironmentInfo()
         {
             var info = new EnvironmentInfo()
             {
-                CustomOperatingSystem = MediaBrowser.Model.System.OperatingSystem.OSX
+                OperatingSystem = MediaBrowser.Model.System.OperatingSystem.OSX
             };
 
             var uname = GetUnixName();
@@ -186,23 +172,23 @@ namespace MediaBrowser.Server.Mac
 
             if (archX86.IsMatch(uname.machine))
             {
-                info.CustomArchitecture = Architecture.X86;
+                info.SystemArchitecture = Architecture.X86;
             }
             else if (string.Equals(uname.machine, "x86_64", StringComparison.OrdinalIgnoreCase))
             {
-                info.CustomArchitecture = Architecture.X64;
+                info.SystemArchitecture = Architecture.X64;
             }
             else if (uname.machine.StartsWith("arm", StringComparison.OrdinalIgnoreCase))
             {
-                info.CustomArchitecture = Architecture.Arm;
+                info.SystemArchitecture = Architecture.Arm;
             }
             else if (System.Environment.Is64BitOperatingSystem)
             {
-                info.CustomArchitecture = Architecture.X64;
+                info.SystemArchitecture = Architecture.X64;
             }
             else
             {
-                info.CustomArchitecture = Architecture.X86;
+                info.SystemArchitecture = Architecture.X86;
             }
 
             return info;

+ 1 - 1
MediaBrowser.Server.Mac/Native/MonoFileSystem.cs

@@ -1,4 +1,4 @@
-using Emby.Common.Implementations.IO;
+using Emby.Server.Implementations.IO;
 using MediaBrowser.Model.Logging;
 using Mono.Unix.Native;
 using MediaBrowser.Model.System;

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.736</version>
+        <version>3.0.740</version>
         <title>Emby.Common</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.736</version>
+        <version>3.0.740</version>
         <title>Emby.Server.Core</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Emby Server.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.736" />
+            <dependency id="MediaBrowser.Common" version="3.0.740" />
         </dependencies>
     </metadata>
     <files>

+ 1 - 1
SharedVersion.cs

@@ -1,3 +1,3 @@
 using System.Reflection;
 
-[assembly: AssemblyVersion("3.2.28.3")]
+[assembly: AssemblyVersion("3.2.28.4")]