Explorar el Código

Merge branch 'dev' of https://github.com/MediaBrowser/MediaBrowser into dev

Luke hace 9 años
padre
commit
6f343203ce
Se han modificado 36 ficheros con 401 adiciones y 235 borrados
  1. 20 1
      MediaBrowser.Api/Library/LibraryService.cs
  2. 16 0
      MediaBrowser.Api/Music/InstantMixService.cs
  3. 3 2
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  4. 4 0
      MediaBrowser.Controller/Entities/BaseItem.cs
  5. 2 1
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  6. 41 27
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  7. 3 24
      MediaBrowser.Model/ApiClient/IApiClient.cs
  8. 6 1
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  9. 151 0
      MediaBrowser.Providers/Folders/DefaultImageProvider.cs
  10. 1 1
      MediaBrowser.Providers/Manager/ProviderManager.cs
  11. 1 0
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  12. 1 1
      MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
  13. 1 1
      MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
  14. 18 0
      MediaBrowser.Server.Implementations/Library/MusicManager.cs
  15. 1 1
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  16. 2 31
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
  17. 67 86
      MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  18. 0 8
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  19. 45 1
      MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
  20. 8 0
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  21. 1 1
      MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
  22. 1 39
      MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
  23. BIN
      MediaBrowser.Server.Implementations/UserViews/livetv/1.jpg
  24. BIN
      MediaBrowser.Server.Implementations/UserViews/livetv/2.jpg
  25. BIN
      MediaBrowser.Server.Implementations/UserViews/livetv/3.jpg
  26. BIN
      MediaBrowser.Server.Implementations/UserViews/livetv/4.jpg
  27. BIN
      MediaBrowser.Server.Implementations/UserViews/livetv/5.jpg
  28. BIN
      MediaBrowser.Server.Implementations/UserViews/livetv/6.jpg
  29. BIN
      MediaBrowser.Server.Implementations/UserViews/livetv/7.jpg
  30. BIN
      MediaBrowser.Server.Implementations/UserViews/livetv/8.jpg
  31. 0 1
      MediaBrowser.WebDashboard/Api/PackageCreator.cs
  32. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  33. 1 1
      Nuget/MediaBrowser.Common.nuspec
  34. 1 1
      Nuget/MediaBrowser.Model.Signed.nuspec
  35. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec
  36. 2 2
      SharedVersion.cs

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

@@ -270,6 +270,7 @@ namespace MediaBrowser.Api.Library
         private readonly ILiveTvManager _liveTv;
         private readonly IChannelManager _channelManager;
         private readonly ITVSeriesManager _tvManager;
+        private readonly ILibraryMonitor _libraryMonitor;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="LibraryService" /> class.
@@ -422,7 +423,25 @@ namespace MediaBrowser.Api.Library
 
         public void Post(PostUpdatedSeries request)
         {
-            Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None));
+            var series = _libraryManager.GetItems(new InternalItemsQuery
+            {
+                IncludeItemTypes = new[] { typeof(Series).Name }
+
+            }).Items;
+
+            series = series.Where(i => string.Equals(request.TvdbId, i.GetProviderId(MetadataProviders.Tvdb), StringComparison.OrdinalIgnoreCase)).ToArray();
+
+            if (series.Length > 0)
+            {
+                foreach (var item in series)
+                {
+                    _libraryMonitor.ReportFileSystemChanged(item.Path);
+                }
+            }
+            else
+            {
+                Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None));
+            }
         }
 
         public object Get(GetDownload request)

+ 16 - 0
MediaBrowser.Api/Music/InstantMixService.cs

@@ -54,6 +54,11 @@ namespace MediaBrowser.Api.Music
         public string Id { get; set; }
     }
 
+    [Route("/Items/{Id}/InstantMix", "GET", Summary = "Creates an instant playlist based on a given item")]
+    public class GetInstantMixFromItem : BaseGetSimilarItemsFromItem
+    {
+    }
+
     [Authenticated]
     public class InstantMixService : BaseApiService
     {
@@ -71,6 +76,17 @@ namespace MediaBrowser.Api.Music
             _libraryManager = libraryManager;
         }
 
+        public object Get(GetInstantMixFromItem request)
+        {
+            var item = _libraryManager.GetItemById(request.Id);
+
+            var user = _userManager.GetUserById(request.UserId);
+
+            var items = _musicManager.GetInstantMixFromItem(item, user);
+
+            return GetResult(items, user, request);
+        }
+
         public object Get(GetInstantMixFromArtistId request)
         {
             var item = _libraryManager.GetItemById(request.Id);

+ 3 - 2
MediaBrowser.Api/Playback/Hls/BaseHlsService.cs

@@ -1,5 +1,4 @@
-using System.Linq;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Dlna;
@@ -317,6 +316,7 @@ namespace MediaBrowser.Api.Playback.Hls
         {
             if (videoStream.KeyFrames == null || videoStream.KeyFrames.Count == 0)
             {
+                Logger.Debug("Cannot stream copy video due to missing keyframe info");
                 return false;
             }
 
@@ -328,6 +328,7 @@ namespace MediaBrowser.Api.Playback.Hls
                 // Don't allow really long segments because this could result in long download times
                 if (length > 10000)
                 {
+                    Logger.Debug("Cannot stream copy video due to long segment length of {0}ms", length);
                     return false;
                 }
                 previousSegment = frame;

+ 4 - 0
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -419,6 +419,10 @@ namespace MediaBrowser.Controller.Entities
 
                 return _sortName ?? (_sortName = CreateSortName());
             }
+            set
+            {
+                _sortName = value;
+            }
         }
 
         public string GetInternalMetadataPath()

+ 2 - 1
MediaBrowser.Controller/Entities/InternalItemsQuery.cs

@@ -97,7 +97,8 @@ namespace MediaBrowser.Controller.Entities
         public int? MaxParentalRating { get; set; }
 
         public bool? IsCurrentSchema { get; set; }
-        
+        public bool? HasDeadParentId { get; set; }
+    
         public InternalItemsQuery()
         {
             Tags = new string[] { };

+ 41 - 27
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -8,6 +8,7 @@ using MediaBrowser.Controller.Session;
 using MediaBrowser.MediaEncoding.Probing;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.MediaInfo;
@@ -242,21 +243,27 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
                         if (extractKeyFrameInterval && mediaInfo.RunTimeTicks.HasValue)
                         {
-                            foreach (var stream in mediaInfo.MediaStreams)
+                            if (ConfigurationManager.Configuration.EnableVideoFrameAnalysis && mediaInfo.Size.HasValue && mediaInfo.Size.Value <= ConfigurationManager.Configuration.VideoFrameAnalysisLimitBytes)
                             {
-                                if (stream.Type == MediaStreamType.Video && string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase))
+                                foreach (var stream in mediaInfo.MediaStreams)
                                 {
-                                    try
+                                    if (stream.Type == MediaStreamType.Video &&
+                                        string.Equals(stream.Codec, "h264", StringComparison.OrdinalIgnoreCase) &&
+                                        !stream.IsInterlaced &&
+                                        !(stream.IsAnamorphic ?? false))
                                     {
-                                        //stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken).ConfigureAwait(false);
-                                    }
-                                    catch (OperationCanceledException)
-                                    {
-
-                                    }
-                                    catch (Exception ex)
-                                    {
-                                        _logger.ErrorException("Error getting key frame interval", ex);
+                                        try
+                                        {
+                                            stream.KeyFrames = await GetKeyFrames(inputPath, stream.Index, cancellationToken).ConfigureAwait(false);
+                                        }
+                                        catch (OperationCanceledException)
+                                        {
+
+                                        }
+                                        catch (Exception ex)
+                                        {
+                                            _logger.ErrorException("Error getting key frame interval", ex);
+                                        }
                                     }
                                 }
                             }
@@ -282,7 +289,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
         private async Task<List<int>> GetKeyFrames(string inputPath, int videoStreamIndex, CancellationToken cancellationToken)
         {
-            const string args = "-i {0} -select_streams v:{1} -show_packets -print_format compact -show_entries packet=flags -show_entries packet=pts_time";
+            inputPath = inputPath.Split(new[] { ':' }, 2).Last().Trim('"');
+
+            const string args = "-show_packets -print_format compact -select_streams v:{1} -show_entries packet=flags -show_entries packet=pts_time \"{0}\"";
 
             var process = new Process
             {
@@ -294,7 +303,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
                     // Must consume both or ffmpeg may hang due to deadlocks. See comments below.   
                     RedirectStandardOutput = true,
                     RedirectStandardError = true,
-                    RedirectStandardInput = true,
                     FileName = FFProbePath,
                     Arguments = string.Format(args, inputPath, videoStreamIndex.ToString(CultureInfo.InvariantCulture)).Trim(),
 
@@ -307,9 +315,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
 
             _logger.Debug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
 
-            using (var processWrapper = new ProcessWrapper(process, this, _logger))
+            using (process)
             {
-                StartProcess(processWrapper);
+                var start = DateTime.UtcNow;
+
+                process.Start();
 
                 var lines = new List<int>();
 
@@ -326,28 +336,32 @@ namespace MediaBrowser.MediaEncoding.Encoder
                         throw;
                     }
                 }
-                finally
-                {
-                    StopProcess(processWrapper, 100, true);
-                }
 
+                process.WaitForExit();
+
+                _logger.Debug("Keyframe extraction took {0} seconds", (DateTime.UtcNow - start).TotalSeconds);
+                //_logger.Debug("Found keyframes {0}", string.Join(",", lines.ToArray()));
                 return lines;
             }
         }
 
-        private async Task StartReadingOutput(Stream source, List<int> lines, CancellationToken cancellationToken)
+        private async Task StartReadingOutput(Stream source, List<int> keyframes, CancellationToken cancellationToken)
         {
             try
             {
                 using (var reader = new StreamReader(source))
                 {
-                    while (!reader.EndOfStream)
-                    {
-                        cancellationToken.ThrowIfCancellationRequested();
+                    var text = await reader.ReadToEndAsync().ConfigureAwait(false);
 
-                        var line = await reader.ReadLineAsync().ConfigureAwait(false);
+                    var lines = StringHelper.RegexSplit(text, "\r\n");
+                    foreach (var line in lines)
+                    {
+                        if (string.IsNullOrWhiteSpace(line))
+                        {
+                            continue;
+                        }
 
-                        var values = (line ?? string.Empty).Split('|')
+                        var values = line.Split('|')
                             .Where(i => !string.IsNullOrWhiteSpace(i))
                             .Select(i => i.Split('='))
                             .Where(i => i.Length == 2)
@@ -361,7 +375,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                             if (values.TryGetValue("pts_time", out pts_time) && double.TryParse(pts_time, NumberStyles.Any, CultureInfo.InvariantCulture, out frameSeconds))
                             {
                                 var ms = frameSeconds * 1000;
-                                lines.Add(Convert.ToInt32(ms));
+                                keyframes.Add(Convert.ToInt32(ms));
                             }
                         }
                     }

+ 3 - 24
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -320,32 +320,11 @@ namespace MediaBrowser.Model.ApiClient
         Task<ItemsResult> GetUserViews(string userId, CancellationToken cancellationToken = default(CancellationToken));
 
         /// <summary>
-        /// Gets the instant mix from song async.
+        /// Gets the instant mix from item asynchronous.
         /// </summary>
         /// <param name="query">The query.</param>
-        /// <returns>Task{ItemsResult}.</returns>
-        Task<ItemsResult> GetInstantMixFromSongAsync(SimilarItemsQuery query);
-
-        /// <summary>
-        /// Gets the instant mix from album async.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>Task{ItemsResult}.</returns>
-        Task<ItemsResult> GetInstantMixFromAlbumAsync(SimilarItemsQuery query);
-
-        /// <summary>
-        /// Gets the instant mix from artist async.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>Task{ItemsResult}.</returns>
-        Task<ItemsResult> GetInstantMixFromArtistAsync(SimilarItemsQuery query);
-
-        /// <summary>
-        /// Gets the instant mix from music genre async.
-        /// </summary>
-        /// <param name="query">The query.</param>
-        /// <returns>Task{ItemsResult}.</returns>
-        Task<ItemsResult> GetInstantMixFromMusicGenreAsync(SimilarItemsQuery query);
+        /// <returns>Task&lt;ItemsResult&gt;.</returns>
+        Task<ItemsResult> GetInstantMixFromItemAsync(SimilarItemsQuery query);
 
         /// <summary>
         /// Gets the similar movies async.

+ 6 - 1
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -222,11 +222,13 @@ namespace MediaBrowser.Model.Configuration
         public bool DisableXmlSavers { get; set; }
         public bool EnableWindowsShortcuts { get; set; }
 
+        public bool EnableVideoFrameAnalysis { get; set; }
+        public long VideoFrameAnalysisLimitBytes { get; set; }
+        
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// </summary>
         public ServerConfiguration()
-            : base()
         {
             ImageSavingConvention = ImageSavingConvention.Compatible;
             PublicPort = 8096;
@@ -271,6 +273,9 @@ namespace MediaBrowser.Model.Configuration
 
             PeopleMetadataOptions = new PeopleMetadataOptions();
 
+            EnableVideoFrameAnalysis = true;
+            VideoFrameAnalysisLimitBytes = 600000000;
+
             InsecureApps9 = new[]
             {
                 "Chromecast",

+ 151 - 0
MediaBrowser.Providers/Folders/DefaultImageProvider.cs

@@ -0,0 +1,151 @@
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+using MediaBrowser.Providers.Genres;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.Folders
+{
+    public class DefaultImageProvider : IRemoteImageProvider, IHasItemChangeMonitor
+    {
+        private readonly IHttpClient _httpClient;
+
+        public DefaultImageProvider(IHttpClient httpClient)
+        {
+            _httpClient = httpClient;
+        }
+
+        public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+        {
+            return new List<ImageType>
+            {
+                ImageType.Primary,
+                ImageType.Thumb
+            };
+        }
+
+        public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
+        {
+            var view = item as UserView;
+
+            if (view != null)
+            {
+                return GetImages(view.ViewType, cancellationToken);
+            }
+
+            var folder = (ICollectionFolder)item;
+            return GetImages(folder.CollectionType, cancellationToken);
+        }
+
+        private Task<IEnumerable<RemoteImageInfo>> GetImages(string viewType, CancellationToken cancellationToken)
+        {
+            var url = GetImageUrl(viewType);
+
+            var list = new List<RemoteImageInfo>();
+
+            if (!string.IsNullOrWhiteSpace(url))
+            {
+                    list.AddRange(new List<RemoteImageInfo>{
+                     new RemoteImageInfo
+                     {
+                          ProviderName = Name,
+                          Url = url,
+                          Type = ImageType.Primary
+                     },
+
+                     new RemoteImageInfo
+                     {
+                          ProviderName = Name,
+                          Url = url,
+                          Type = ImageType.Thumb
+                     }
+                });
+            }
+
+            return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
+        }
+
+        private string GetImageUrl(string viewType)
+        {
+            const string urlPrefix = "https://raw.githubusercontent.com/MediaBrowser/Emby.Resources/master/images/folders/";
+
+            if (string.Equals(viewType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
+            {
+                //return urlPrefix + "books.png";
+            }
+            if (string.Equals(viewType, CollectionType.Games, StringComparison.OrdinalIgnoreCase))
+            {
+                //return urlPrefix + "games.png";
+            }
+            if (string.Equals(viewType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
+            {
+                //return urlPrefix + "music.png";
+            }
+            if (string.Equals(viewType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
+            {
+                //return urlPrefix + "photos.png";
+            }
+            if (string.Equals(viewType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
+            {
+                //return urlPrefix + "tv.png";
+            }
+            if (string.Equals(viewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase))
+            {
+                //return urlPrefix + "channels.png";
+            }
+            if (string.Equals(viewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+            {
+                return urlPrefix + "livetv.png";
+            }
+            if (string.Equals(viewType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
+            {
+                //return urlPrefix + "movies.png";
+            }
+            if (string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
+            {
+                return urlPrefix + "playlists.png";
+            }
+
+            return null;
+            //return urlPrefix + "generic.png";
+        }
+
+        public string Name
+        {
+            get { return "Default Image Provider"; }
+        }
+
+        public bool Supports(IHasImages item)
+        {
+            var view = item as UserView;
+
+            if (view != null)
+            {
+                return true;
+            }
+
+            return item is ICollectionFolder;
+        }
+
+        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+        {
+            return _httpClient.GetResponse(new HttpRequestOptions
+            {
+                CancellationToken = cancellationToken,
+                Url = url,
+                ResourcePool = GenreImageProvider.ImageDownloadResourcePool
+            });
+        }
+
+        public bool HasChanged(IHasMetadata item, MetadataStatus status, IDirectoryService directoryService)
+        {
+            return GetSupportedImages(item).Any(i => !item.HasImage(i));
+        }
+    }
+}

+ 1 - 1
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -189,7 +189,7 @@ namespace MediaBrowser.Providers.Manager
 
             var results = await Task.WhenAll(tasks).ConfigureAwait(false);
 
-            var images = results.SelectMany(i => i);
+            var images = results.SelectMany(i => i.ToList());
 
             return images;
         }

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

@@ -83,6 +83,7 @@
     <Compile Include="BoxSets\MovieDbBoxSetProvider.cs" />
     <Compile Include="Channels\ChannelMetadataService.cs" />
     <Compile Include="Chapters\ChapterManager.cs" />
+    <Compile Include="Folders\DefaultImageProvider.cs" />
     <Compile Include="Folders\FolderMetadataService.cs" />
     <Compile Include="Channels\AudioChannelItemMetadataService.cs" />
     <Compile Include="Folders\UserViewMetadataService.cs" />

+ 1 - 1
MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs

@@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             _timer = new Timer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(3));
         }
 
-        private readonly string[] _ipLookups = { "http://bot.whatismyipaddress.com", "https://connect.mediabrowser.tv/service/ip" };
+        private readonly string[] _ipLookups = { "http://bot.whatismyipaddress.com", "https://connect.emby.media/service/ip" };
 
         private async void TimerCallback(object state)
         {

+ 1 - 1
MediaBrowser.Server.Implementations/Connect/ConnectManager.cs

@@ -371,7 +371,7 @@ namespace MediaBrowser.Server.Implementations.Connect
 
         private string GetConnectUrl(string handler)
         {
-            return "https://connect.mediabrowser.tv/service/" + handler;
+            return "https://connect.emby.media/service/" + handler;
         }
 
         public async Task<UserLinkResult> LinkUser(string userId, string connectUsername)

+ 18 - 0
MediaBrowser.Server.Implementations/Library/MusicManager.cs

@@ -52,6 +52,18 @@ namespace MediaBrowser.Server.Implementations.Library
             return GetInstantMixFromGenres(genres, user);
         }
 
+        public IEnumerable<Audio> GetInstantMixFromFolder(Folder item, User user)
+        {
+            var genres = item
+                .GetRecursiveChildren(user, i => i is Audio)
+               .Cast<Audio>()
+               .SelectMany(i => i.Genres)
+               .Concat(item.Genres)
+               .DistinctNames();
+
+            return GetInstantMixFromGenres(genres, user);
+        }
+
         public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user)
         {
             var genres = item
@@ -113,6 +125,12 @@ namespace MediaBrowser.Server.Implementations.Library
             {
                 return GetInstantMixFromSong(song, user);
             }
+
+            var folder = item as Folder;
+            if (folder != null)
+            {
+                return GetInstantMixFromFolder(folder, user);
+            }
             
             return new Audio[] { };
         }

+ 1 - 1
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -455,7 +455,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 }
             }
 
-            throw new ApplicationException("Tuner not found.");
+            throw new NotImplementedException();
         }
 
         public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)

+ 2 - 31
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs

@@ -1,41 +1,12 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Controller.Plugins;
 
 namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 {
     public class EntryPoint : IServerEntryPoint
     {
-        private readonly IConfigurationManager _config;
-        private readonly ISecurityManager _manager;
-
-        public EntryPoint(IConfigurationManager config, ISecurityManager manager)
-        {
-            _config = config;
-            _manager = manager;
-        }
-
-        public async void Run()
+        public void Run()
         {
             EmbyTV.Current.Start();
-
-            if (GetConfiguration().ListingProviders.Count > 0 || GetConfiguration().TunerHosts.Count > 0)
-            {
-                try
-                {
-                    await _manager.GetRegistrationStatus("livetvguide").ConfigureAwait(false);
-                }
-                catch
-                {
-                    
-                }
-            }
-        }
-
-        private LiveTvOptions GetConfiguration()
-        {
-            return _config.GetConfiguration<LiveTvOptions>("livetv");
         }
 
         public void Dispose()

+ 67 - 86
MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -39,68 +39,45 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
             var url = info.Url;
             var urlHash = url.GetMD5().ToString("N");
 
-            int position = 0;
             string line;
             // Read the file and display it line by line.
             var file = new StreamReader(url);
             var channels = new List<M3UChannel>();
+
+            string channnelName = null;
+            string channelNumber = null;
+
             while ((line = file.ReadLine()) != null)
             {
                 line = line.Trim();
-                if (!String.IsNullOrWhiteSpace(line))
+                if (string.IsNullOrWhiteSpace(line))
                 {
-                    if (position == 0 && !line.StartsWith("#EXTM3U"))
-                    {
-                        throw new ApplicationException("wrong file");
-                    }
-                    if (position % 2 == 0)
-                    {
-                        if (position != 0)
-                        {
-                            channels.Last().Path = line;
-                        }
-                        else
-                        {
-                            line = line.Replace("#EXTM3U", "");
-                            line = line.Trim();
-                            var vars = line.Split(' ').ToList();
-                            foreach (var variable in vars)
-                            {
-                                var list = variable.Replace('"', ' ').Split('=');
-                                switch (list[0])
-                                {
-                                    case ("id"):
-                                        //_id = list[1];
-                                        break;
-                                }
-                            }
-                        }
-                    }
-                    else
+                    continue;
+                }
+
+                if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
+                {
+                    continue;
+                }
+
+                if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase))
+                {
+                    var parts = line.Split(new[] { ':' }, 2).Last().Split(new[] { ',' }, 2);
+                    channelNumber = parts[0];
+                    channnelName = parts[1];
+                }
+                else if (!string.IsNullOrWhiteSpace(channelNumber))
+                {
+                    channels.Add(new M3UChannel
                     {
-                        if (!line.StartsWith("#EXTINF:")) { throw new ApplicationException("Bad file"); }
-                        line = line.Replace("#EXTINF:", "");
-                        var nameStart = line.LastIndexOf(',');
-                        line = line.Substring(0, nameStart);
-                        var vars = line.Split(' ').ToList();
-                        vars.RemoveAt(0);
-                        channels.Add(new M3UChannel());
-                        foreach (var variable in vars)
-                        {
-                            var list = variable.Replace('"', ' ').Split('=');
-                            switch (list[0])
-                            {
-                                case "tvg-id":
-                                    channels.Last().Id = ChannelIdPrefix + urlHash + list[1];
-                                    channels.Last().Number = list[1];
-                                    break;
-                                case "tvg-name":
-                                    channels.Last().Name = list[1];
-                                    break;
-                            }
-                        }
-                    }
-                    position++;
+                        Name = channnelName,
+                        Number = channelNumber,
+                        Id = ChannelIdPrefix + urlHash + channelNumber,
+                        Path = line
+                    });
+
+                    channelNumber = null;
+                    channnelName = null;
                 }
             }
             file.Close();
@@ -125,6 +102,35 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
         }
 
         protected override async Task<MediaSourceInfo> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+        {
+            var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
+
+            return sources.First();
+        }
+
+        class M3UChannel : ChannelInfo
+        {
+            public string Path { get; set; }
+
+            public M3UChannel()
+            {
+            }
+        }
+
+        public async Task Validate(TunerHostInfo info)
+        {
+            if (!File.Exists(info.Url))
+            {
+                throw new FileNotFoundException();
+            }
+        }
+
+        protected override bool IsValidChannelId(string channelId)
+        {
+            return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
+        }
+
+        protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
         {
             var urlHash = info.Url.GetMD5().ToString("N");
             var prefix = ChannelIdPrefix + urlHash;
@@ -133,29 +139,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
                 return null;
             }
 
-            channelId = channelId.Substring(prefix.Length);
+            //channelId = channelId.Substring(prefix.Length);
 
             var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
             var m3uchannels = channels.Cast<M3UChannel>();
-            var channel = m3uchannels.FirstOrDefault(c => c.Id == channelId);
+            var channel = m3uchannels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase));
             if (channel != null)
             {
                 var path = channel.Path;
                 MediaProtocol protocol = MediaProtocol.File;
-                if (path.StartsWith("http"))
+                if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
                 {
                     protocol = MediaProtocol.Http;
                 }
-                else if (path.StartsWith("rtmp"))
+                else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase))
                 {
                     protocol = MediaProtocol.Rtmp;
                 }
-                else if (path.StartsWith("rtsp"))
+                else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase))
                 {
                     protocol = MediaProtocol.Rtsp;
                 }
 
-                return new MediaSourceInfo
+                var mediaSource = new MediaSourceInfo
                 {
                     Path = channel.Path,
                     Protocol = protocol,
@@ -179,35 +185,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
                     RequiresOpening = false,
                     RequiresClosing = false
                 };
-            }
-            throw new ApplicationException("Host doesnt provide this channel");
-        }
-
-        class M3UChannel : ChannelInfo
-        {
-            public string Path { get; set; }
-
-            public M3UChannel()
-            {
-            }
-        }
 
-        public async Task Validate(TunerHostInfo info)
-        {
-            if (!File.Exists(info.Url))
-            {
-                throw new FileNotFoundException();
+                return new List<MediaSourceInfo> { mediaSource };
             }
-        }
-
-        protected override bool IsValidChannelId(string channelId)
-        {
-            return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
-        }
-
-        protected override Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
-        {
-            throw new NotImplementedException();
+            return new List<MediaSourceInfo> { };
         }
 
         protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)

+ 0 - 8
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -495,14 +495,6 @@
       <Link>swagger-ui\swagger-ui.min.js</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <EmbeddedResource Include="UserViews\livetv\1.jpg" />
-    <EmbeddedResource Include="UserViews\livetv\2.jpg" />
-    <EmbeddedResource Include="UserViews\livetv\3.jpg" />
-    <EmbeddedResource Include="UserViews\livetv\4.jpg" />
-    <EmbeddedResource Include="UserViews\livetv\5.jpg" />
-    <EmbeddedResource Include="UserViews\livetv\6.jpg" />
-    <EmbeddedResource Include="UserViews\livetv\7.jpg" />
-    <EmbeddedResource Include="UserViews\livetv\8.jpg" />
     <EmbeddedResource Include="Localization\iso6392.txt" />
     <EmbeddedResource Include="Localization\Ratings\be.txt" />
   </ItemGroup>

+ 45 - 1
MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs

@@ -46,9 +46,16 @@ namespace MediaBrowser.Server.Implementations.Persistence
         public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
         {
             var innerProgress = new ActionableProgress<double>();
-            innerProgress.RegisterAction(progress.Report);
+            innerProgress.RegisterAction(p => progress.Report(.95 * p));
 
             await UpdateToLatestSchema(cancellationToken, innerProgress).ConfigureAwait(false);
+
+            innerProgress = new ActionableProgress<double>();
+            innerProgress.RegisterAction(p => progress.Report(95 + (.05 * p)));
+
+            //await CleanDeadItems(cancellationToken, innerProgress).ConfigureAwait(false);
+
+            progress.Report(100);
         }
 
         private async Task UpdateToLatestSchema(CancellationToken cancellationToken, IProgress<double> progress)
@@ -92,6 +99,43 @@ namespace MediaBrowser.Server.Implementations.Persistence
             progress.Report(100);
         }
 
+        private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
+        {
+            var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
+            {
+                HasDeadParentId = true
+            });
+
+            var numComplete = 0;
+            var numItems = itemIds.Count;
+
+            _logger.Debug("Cleaning {0} items with dead parent links", numItems);
+
+            foreach (var itemId in itemIds)
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var item = _libraryManager.GetItemById(itemId);
+
+                if (item != null)
+                {
+                    _logger.Debug("Cleaning item {0} type: {1} path: {2}", item.Name, item.GetType().Name, item.Path ?? string.Empty);
+
+                    await _libraryManager.DeleteItem(item, new DeleteOptions
+                    {
+                        DeleteFileLocation = false
+                    });
+                }
+
+                numComplete++;
+                double percent = numComplete;
+                percent /= numItems;
+                progress.Report(percent * 100);
+            }
+
+            progress.Report(100);
+        }
+
         public IEnumerable<ITaskTrigger> GetDefaultTriggers()
         {
             return new ITaskTrigger[] 

+ 8 - 0
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -1096,6 +1096,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 }
             }
 
+            if (query.HasDeadParentId.HasValue)
+            {
+                if (query.HasDeadParentId.Value)
+                {
+                    whereClauses.Add("ParentId NOT NULL AND ParentId NOT IN (select guid from TypedBaseItems)");
+                }
+            }
+
             if (addPaging)
             {
                 if (query.StartIndex.HasValue && query.StartIndex.Value > 0)

+ 1 - 1
MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs

@@ -39,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
         {
             var list = new ITaskTrigger[] { 
 
-                new IntervalTrigger{ Interval = TimeSpan.FromHours(8)}
+                new IntervalTrigger{ Interval = TimeSpan.FromHours(12)}
 
             }.ToList();
 

+ 1 - 39
MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs

@@ -11,8 +11,6 @@ using MediaBrowser.Server.Implementations.Photos;
 using MoreLinq;
 using System;
 using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
 
@@ -152,9 +150,7 @@ namespace MediaBrowser.Server.Implementations.UserViews
                 CollectionType.Games,
                 CollectionType.Music,
                 CollectionType.BoxSets,
-                CollectionType.Playlists,
                 CollectionType.Channels,
-                CollectionType.LiveTv,
                 CollectionType.Books,
                 CollectionType.Photos,
                 CollectionType.HomeVideos,
@@ -170,7 +166,7 @@ namespace MediaBrowser.Server.Implementations.UserViews
             var view = (UserView)item;
             if (imageType == ImageType.Primary && IsUsingCollectionStrip(view))
             {
-                if (itemsWithImages.Count == 0 && !string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+                if (itemsWithImages.Count == 0)
                 {
                     return false;
                 }
@@ -180,39 +176,5 @@ namespace MediaBrowser.Server.Implementations.UserViews
 
             return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
         }
-
-        protected override IEnumerable<String> GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable<BaseItem> items)
-        {
-            var userView = primaryItem as UserView;
-
-            if (userView != null && string.Equals(userView.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
-            {
-                var list = new List<string>();
-                for (int i = 1; i <= 8; i++)
-                {
-                    list.Add(ExtractLiveTvResource(i.ToString(CultureInfo.InvariantCulture), ApplicationPaths));
-                }
-                return list;
-            }
-
-            return base.GetStripCollageImagePaths(primaryItem, items);
-        }
-
-        private string ExtractLiveTvResource(string name, IApplicationPaths paths)
-        {
-            var namespacePath = GetType().Namespace + ".livetv." + name + ".jpg";
-            var tempPath = Path.Combine(paths.TempDirectory, Guid.NewGuid().ToString("N") + ".jpg");
-            Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
-
-            using (var stream = GetType().Assembly.GetManifestResourceStream(namespacePath))
-            {
-                using (var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.Read))
-                {
-                    stream.CopyTo(fileStream);
-                }
-            }
-
-            return tempPath;
-        }
     }
 }

BIN
MediaBrowser.Server.Implementations/UserViews/livetv/1.jpg


BIN
MediaBrowser.Server.Implementations/UserViews/livetv/2.jpg


BIN
MediaBrowser.Server.Implementations/UserViews/livetv/3.jpg


BIN
MediaBrowser.Server.Implementations/UserViews/livetv/4.jpg


BIN
MediaBrowser.Server.Implementations/UserViews/livetv/5.jpg


BIN
MediaBrowser.Server.Implementations/UserViews/livetv/6.jpg


BIN
MediaBrowser.Server.Implementations/UserViews/livetv/7.jpg


BIN
MediaBrowser.Server.Implementations/UserViews/livetv/8.jpg


+ 0 - 1
MediaBrowser.WebDashboard/Api/PackageCreator.cs

@@ -423,7 +423,6 @@ namespace MediaBrowser.WebDashboard.Api
 
             var files = new[]
                             {
-                                "thirdparty/fontawesome/css/font-awesome.min.css" + versionString,
                                 "css/all.css" + versionString
                             };
 

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.632</version>
+        <version>3.0.633</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.632" />
+            <dependency id="MediaBrowser.Common" version="3.0.633" />
             <dependency id="NLog" version="3.2.1" />
             <dependency id="SimpleInjector" version="2.8.0" />
         </dependencies>

+ 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.632</version>
+        <version>3.0.633</version>
         <title>MediaBrowser.Common</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 1 - 1
Nuget/MediaBrowser.Model.Signed.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Model.Signed</id>
-        <version>3.0.632</version>
+        <version>3.0.633</version>
         <title>MediaBrowser.Model - Signed Edition</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.632</version>
+        <version>3.0.633</version>
         <title>Media Browser.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.632" />
+            <dependency id="MediaBrowser.Common" version="3.0.633" />
 			<dependency id="Interfaces.IO" version="1.0.0.5" />
         </dependencies>
     </metadata>

+ 2 - 2
SharedVersion.cs

@@ -1,4 +1,4 @@
 using System.Reflection;
 
-//[assembly: AssemblyVersion("3.0.*")]
-[assembly: AssemblyVersion("3.0.5713.4")]
+[assembly: AssemblyVersion("3.0.*")]
+//[assembly: AssemblyVersion("3.0.5713.5")]