瀏覽代碼

added music and game genre image downloading

Luke Pulverenti 11 年之前
父節點
當前提交
a5be2523c5

+ 0 - 28
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -194,20 +194,6 @@ namespace MediaBrowser.Api.LiveTv
     {
     }
 
-    [Route("/LiveTv/Recordings/{Id}/Stream", "GET")]
-    public class GetInternalRecordingStream
-    {
-        [ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public string Id { get; set; }
-    }
-
-    [Route("/LiveTv/Channels/{Id}/Stream", "GET")]
-    public class GetInternalChannelStream
-    {
-        [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public string Id { get; set; }
-    }
-
     [Route("/LiveTv/Recordings/Groups/{Id}", "GET")]
     [Api(Description = "Gets a recording group")]
     public class GetRecordingGroup : IReturn<RecordingGroupDto>
@@ -411,20 +397,6 @@ namespace MediaBrowser.Api.LiveTv
             Task.WaitAll(task);
         }
 
-        public object Get(GetInternalRecordingStream request)
-        {
-            var stream = _liveTvManager.GetRecordingStream(request.Id, CancellationToken.None).Result;
-
-            return ToStreamResult(stream.Stream, stream.MimeType);
-        }
-
-        public object Get(GetInternalChannelStream request)
-        {
-            var stream = _liveTvManager.GetChannelStream(request.Id, CancellationToken.None).Result;
-
-            return ToStreamResult(stream.Stream, stream.MimeType);
-        }
-
         public object Get(GetRecordingGroups request)
         {
             var result = _liveTvManager.GetRecordingGroups(new RecordingGroupQuery

+ 22 - 8
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -888,11 +888,18 @@ namespace MediaBrowser.Api.Playback
                 }
                 else
                 {
-                    state.MediaPath = string.Format("http://localhost:{0}/mediabrowser/LiveTv/Recordings/{1}/Stream",
-                        ServerConfigurationManager.Configuration.HttpServerPortNumber,
-                        request.Id);
+                    var streamInfo = await LiveTvManager.GetRecordingStream(request.Id, cancellationToken).ConfigureAwait(false);
 
-                    state.IsRemote = true;
+                    if (!string.IsNullOrEmpty(streamInfo.Path) && File.Exists(streamInfo.Path))
+                    {
+                        state.MediaPath = streamInfo.Path;
+                        state.IsRemote = false;
+                    }
+                    else if (!string.IsNullOrEmpty(streamInfo.Url))
+                    {
+                        state.MediaPath = streamInfo.Url;
+                        state.IsRemote = true;
+                    }
                 }
 
                 itemId = recording.Id;
@@ -905,11 +912,18 @@ namespace MediaBrowser.Api.Playback
                 state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
                 state.PlayableStreamFileNames = new List<string>();
 
-                state.MediaPath = string.Format("http://localhost:{0}/mediabrowser/LiveTv/Channels/{1}/Stream",
-                    ServerConfigurationManager.Configuration.HttpServerPortNumber,
-                    request.Id);
+                var streamInfo = await LiveTvManager.GetChannelStream(request.Id, cancellationToken).ConfigureAwait(false);
 
-                state.IsRemote = true;
+                if (!string.IsNullOrEmpty(streamInfo.Path) && File.Exists(streamInfo.Path))
+                {
+                    state.MediaPath = streamInfo.Path;
+                    state.IsRemote = false;
+                }
+                else if (!string.IsNullOrEmpty(streamInfo.Url))
+                {
+                    state.MediaPath = streamInfo.Url;
+                    state.IsRemote = true;
+                }
 
                 itemId = channel.Id;
             }

+ 2 - 2
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -161,7 +161,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="id">The identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Stream}.</returns>
-        Task<StreamResponseInfo> GetRecordingStream(string id, CancellationToken cancellationToken);
+        Task<LiveStreamInfo> GetRecordingStream(string id, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the channel stream.
@@ -169,7 +169,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="id">The identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{StreamResponseInfo}.</returns>
-        Task<StreamResponseInfo> GetChannelStream(string id, CancellationToken cancellationToken);
+        Task<LiveStreamInfo> GetChannelStream(string id, CancellationToken cancellationToken);
         
         /// <summary>
         /// Gets the program.

+ 2 - 2
MediaBrowser.Controller/LiveTv/ILiveTvService.cs

@@ -145,7 +145,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="recordingId">The recording identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Stream}.</returns>
-        Task<StreamResponseInfo> GetRecordingStream(string recordingId, CancellationToken cancellationToken);
+        Task<LiveStreamInfo> GetRecordingStream(string recordingId, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the channel stream.
@@ -153,6 +153,6 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="channelId">The channel identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Stream}.</returns>
-        Task<StreamResponseInfo> GetChannelStream(string channelId, CancellationToken cancellationToken);
+        Task<LiveStreamInfo> GetChannelStream(string channelId, CancellationToken cancellationToken);
     }
 }

+ 18 - 0
MediaBrowser.Controller/LiveTv/LiveStreamInfo.cs

@@ -0,0 +1,18 @@
+
+namespace MediaBrowser.Controller.LiveTv
+{
+    public class LiveStreamInfo
+    {
+        /// <summary>
+        /// Gets or sets the path.
+        /// </summary>
+        /// <value>The path.</value>
+        public string Path { get; set; }
+
+        /// <summary>
+        /// Gets or sets the URL.
+        /// </summary>
+        /// <value>The URL.</value>
+        public string Url { get; set; }
+    }
+}

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

@@ -109,6 +109,7 @@
     <Compile Include="Library\IUserDataManager.cs" />
     <Compile Include="Library\UserDataSaveEventArgs.cs" />
     <Compile Include="LiveTv\ILiveTvRecording.cs" />
+    <Compile Include="LiveTv\LiveStreamInfo.cs" />
     <Compile Include="LiveTv\LiveTvAudioRecording.cs" />
     <Compile Include="LiveTv\LiveTvChannel.cs" />
     <Compile Include="LiveTv\ChannelInfo.cs" />

+ 1 - 1
MediaBrowser.Mono.userprefs

@@ -3,7 +3,7 @@
   <MonoDevelop.Ide.Workbench ActiveDocument="MediaBrowser.Server.Mono\Networking\NetworkManager.cs">
     <Files>
       <File FileName="MediaBrowser.Server.Mono\app.config" Line="1" Column="1" />
-      <File FileName="MediaBrowser.Server.Mono\Networking\NetworkManager.cs" Line="6" Column="34" />
+      <File FileName="MediaBrowser.Server.Mono\Networking\NetworkManager.cs" Line="7" Column="26" />
     </Files>
   </MonoDevelop.Ide.Workbench>
   <MonoDevelop.Ide.DebuggingService.Breakpoints>

+ 160 - 0
MediaBrowser.Providers/ImagesByName/GameGenreImageProvider.cs

@@ -0,0 +1,160 @@
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Providers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.ImagesByName
+{
+    public class GameGenreImageProvider : BaseMetadataProvider
+    {
+        private readonly IProviderManager _providerManager;
+        private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(5, 5);
+
+        public GameGenreImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
+            : base(logManager, configurationManager)
+        {
+            _providerManager = providerManager;
+        }
+
+        public override bool Supports(BaseItem item)
+        {
+            return item is GameGenre;
+        }
+
+        public override bool RequiresInternet
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        public override ItemUpdateType ItemUpdateType
+        {
+            get
+            {
+                return ItemUpdateType.ImageUpdate;
+            }
+        }
+
+        protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
+        {
+            if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
+            {
+                return false;
+            }
+
+            // Try again periodically in case new images were added
+            if ((DateTime.UtcNow - providerInfo.LastRefreshed).TotalDays > 7)
+            {
+                return true;
+            }
+
+            return base.NeedsRefreshInternal(item, providerInfo);
+        }
+
+        protected override bool RefreshOnVersionChange
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        protected override string ProviderVersion
+        {
+            get
+            {
+                return "8";
+            }
+        }
+
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
+        {
+            if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
+            {
+                SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
+                return true;
+            }
+
+            var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, GameGenresManualImageProvider.ProviderName).ConfigureAwait(false);
+
+            await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
+
+            SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
+            return true;
+        }
+
+        private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
+        {
+            if (!item.LockedFields.Contains(MetadataFields.Images))
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                if (!item.HasImage(ImageType.Primary))
+                {
+                    await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false);
+                }
+                cancellationToken.ThrowIfCancellationRequested();
+
+                if (!item.HasImage(ImageType.Thumb))
+                {
+                    await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false);
+                }
+            }
+
+            if (!item.LockedFields.Contains(MetadataFields.Backdrops))
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                if (item.BackdropImagePaths.Count == 0)
+                {
+                    foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
+                    {
+                        await _providerManager.SaveImage(item, image.Url, _resourcePool, ImageType.Backdrop, null, cancellationToken)
+                            .ConfigureAwait(false);
+
+                        break;
+                    }
+                }
+            }
+        }
+
+
+        private async Task SaveImage(BaseItem item, IEnumerable<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken)
+        {
+            foreach (var image in images.Where(i => i.Type == type))
+            {
+                try
+                {
+                    await _providerManager.SaveImage(item, image.Url, _resourcePool, type, null, cancellationToken).ConfigureAwait(false);
+                    break;
+                }
+                catch (HttpException ex)
+                {
+                    // Sometimes fanart has bad url's in their xml
+                    if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
+                    {
+                        continue;
+                    }
+                    break;
+                }
+            }
+        }
+
+        public override MetadataProviderPriority Priority
+        {
+            get { return MetadataProviderPriority.Third; }
+        }
+    }
+}

+ 128 - 0
MediaBrowser.Providers/ImagesByName/GameGenresManualImageProvider.cs

@@ -0,0 +1,128 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.ImagesByName
+{
+    public class GameGenresManualImageProvider : IImageProvider
+    {
+        private readonly IServerConfigurationManager _config;
+        private readonly IHttpClient _httpClient;
+        private readonly IFileSystem _fileSystem;
+
+        private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1);
+
+        public GameGenresManualImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
+        {
+            _config = config;
+            _httpClient = httpClient;
+            _fileSystem = fileSystem;
+        }
+
+        public string Name
+        {
+            get { return ProviderName; }
+        }
+
+        public static string ProviderName
+        {
+            get { return "Media Browser"; }
+        }
+
+        public bool Supports(IHasImages item)
+        {
+            return item is GameGenre;
+        }
+
+        public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
+        {
+            return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
+        }
+
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+        {
+            return GetImages(item, true, true, cancellationToken);
+        }
+
+        private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, bool posters, bool thumbs, CancellationToken cancellationToken)
+        {
+            var list = new List<RemoteImageInfo>();
+
+            if (posters)
+            {
+                var posterPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegamegenreposters.txt");
+
+                await EnsurePosterList(posterPath, cancellationToken).ConfigureAwait(false);
+
+                list.Add(GetImage(item, posterPath, ImageType.Primary, "folder"));
+            }
+
+            cancellationToken.ThrowIfCancellationRequested();
+
+            if (thumbs)
+            {
+                var thumbsPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegamegenrethumbs.txt");
+
+                await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false);
+
+                list.Add(GetImage(item, thumbsPath, ImageType.Thumb, "thumb"));
+            }
+
+            return list.Where(i => i != null);
+        }
+
+        private RemoteImageInfo GetImage(IHasImages item, string filename, ImageType type, string remoteFilename)
+        {
+            var list = ImageUtils.GetAvailableImages(filename);
+
+            var match = ImageUtils.FindMatch(item, list);
+
+            if (!string.IsNullOrEmpty(match))
+            {
+                var url = GetUrl(match, remoteFilename);
+
+                return new RemoteImageInfo
+                {
+                    ProviderName = Name,
+                    Type = type,
+                    Url = url
+                };
+            }
+
+            return null;
+        }
+
+        private string GetUrl(string image, string filename)
+        {
+            return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/gamegenres/{0}/{1}.jpg", image, filename);
+        }
+
+        private Task EnsureThumbsList(string file, CancellationToken cancellationToken)
+        {
+            const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/gamegenrethumbs.txt";
+
+            return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
+        }
+
+        private Task EnsurePosterList(string file, CancellationToken cancellationToken)
+        {
+            const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/gamegenreposters.txt";
+
+            return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
+        }
+
+        public int Priority
+        {
+            get { return 0; }
+        }
+    }
+}

+ 161 - 0
MediaBrowser.Providers/ImagesByName/MusicGenreImageProvider.cs

@@ -0,0 +1,161 @@
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Net;
+using MediaBrowser.Model.Providers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.ImagesByName
+{
+    public class MusicGenreImageProvider : BaseMetadataProvider
+    {
+        private readonly IProviderManager _providerManager;
+        private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(5, 5);
+
+        public MusicGenreImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
+            : base(logManager, configurationManager)
+        {
+            _providerManager = providerManager;
+        }
+
+        public override bool Supports(BaseItem item)
+        {
+            return item is MusicGenre;
+        }
+
+        public override bool RequiresInternet
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        public override ItemUpdateType ItemUpdateType
+        {
+            get
+            {
+                return ItemUpdateType.ImageUpdate;
+            }
+        }
+
+        protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
+        {
+            if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
+            {
+                return false;
+            }
+
+            // Try again periodically in case new images were added
+            if ((DateTime.UtcNow - providerInfo.LastRefreshed).TotalDays > 7)
+            {
+                return true;
+            }
+
+            return base.NeedsRefreshInternal(item, providerInfo);
+        }
+
+        protected override bool RefreshOnVersionChange
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        protected override string ProviderVersion
+        {
+            get
+            {
+                return "8";
+            }
+        }
+
+        public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
+        {
+            if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
+            {
+                SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
+                return true;
+            }
+
+            var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, MusicGenresManualImageProvider.ProviderName).ConfigureAwait(false);
+
+            await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
+
+            SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
+            return true;
+        }
+
+        private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
+        {
+            if (!item.LockedFields.Contains(MetadataFields.Images))
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                if (!item.HasImage(ImageType.Primary))
+                {
+                    await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false);
+                }
+                cancellationToken.ThrowIfCancellationRequested();
+
+                if (!item.HasImage(ImageType.Thumb))
+                {
+                    await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false);
+                }
+            }
+
+            if (!item.LockedFields.Contains(MetadataFields.Backdrops))
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                if (item.BackdropImagePaths.Count == 0)
+                {
+                    foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
+                    {
+                        await _providerManager.SaveImage(item, image.Url, _resourcePool, ImageType.Backdrop, null, cancellationToken)
+                            .ConfigureAwait(false);
+
+                        break;
+                    }
+                }
+            }
+        }
+
+
+        private async Task SaveImage(BaseItem item, IEnumerable<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken)
+        {
+            foreach (var image in images.Where(i => i.Type == type))
+            {
+                try
+                {
+                    await _providerManager.SaveImage(item, image.Url, _resourcePool, type, null, cancellationToken).ConfigureAwait(false);
+                    break;
+                }
+                catch (HttpException ex)
+                {
+                    // Sometimes fanart has bad url's in their xml
+                    if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
+                    {
+                        continue;
+                    }
+                    break;
+                }
+            }
+        }
+
+        public override MetadataProviderPriority Priority
+        {
+            get { return MetadataProviderPriority.Third; }
+        }
+    }
+}

+ 129 - 0
MediaBrowser.Providers/ImagesByName/MusicGenresManualImageProvider.cs

@@ -0,0 +1,129 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.ImagesByName
+{
+    public class MusicGenresManualImageProvider : IImageProvider
+    {
+        private readonly IServerConfigurationManager _config;
+        private readonly IHttpClient _httpClient;
+        private readonly IFileSystem _fileSystem;
+
+        private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1);
+
+        public MusicGenresManualImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
+        {
+            _config = config;
+            _httpClient = httpClient;
+            _fileSystem = fileSystem;
+        }
+
+        public string Name
+        {
+            get { return ProviderName; }
+        }
+
+        public static string ProviderName
+        {
+            get { return "Media Browser"; }
+        }
+
+        public bool Supports(IHasImages item)
+        {
+            return item is MusicGenre;
+        }
+
+        public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
+        {
+            return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
+        }
+
+        public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+        {
+            return GetImages(item, true, true, cancellationToken);
+        }
+
+        private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, bool posters, bool thumbs, CancellationToken cancellationToken)
+        {
+            var list = new List<RemoteImageInfo>();
+
+            if (posters)
+            {
+                var posterPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotemusicgenreposters.txt");
+
+                await EnsurePosterList(posterPath, cancellationToken).ConfigureAwait(false);
+
+                list.Add(GetImage(item, posterPath, ImageType.Primary, "folder"));
+            }
+
+            cancellationToken.ThrowIfCancellationRequested();
+
+            if (thumbs)
+            {
+                var thumbsPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotemusicgenrethumbs.txt");
+
+                await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false);
+
+                list.Add(GetImage(item, thumbsPath, ImageType.Thumb, "thumb"));
+            }
+
+            return list.Where(i => i != null);
+        }
+
+        private RemoteImageInfo GetImage(IHasImages item, string filename, ImageType type, string remoteFilename)
+        {
+            var list = ImageUtils.GetAvailableImages(filename);
+
+            var match = ImageUtils.FindMatch(item, list);
+
+            if (!string.IsNullOrEmpty(match))
+            {
+                var url = GetUrl(match, remoteFilename);
+
+                return new RemoteImageInfo
+                {
+                    ProviderName = Name,
+                    Type = type,
+                    Url = url
+                };
+            }
+
+            return null;
+        }
+
+        private string GetUrl(string image, string filename)
+        {
+            return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/musicgenres/{0}/{1}.jpg", image, filename);
+        }
+
+        private Task EnsureThumbsList(string file, CancellationToken cancellationToken)
+        {
+            const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/musicgenrethumbs.txt";
+
+            return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
+        }
+
+        private Task EnsurePosterList(string file, CancellationToken cancellationToken)
+        {
+            const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/musicgenreposters.txt";
+
+            return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
+        }
+
+        public int Priority
+        {
+            get { return 0; }
+        }
+    }
+}

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

@@ -66,6 +66,10 @@
     <Compile Include="Games\GameSystemProviderFromXml.cs" />
     <Compile Include="ImageFromMediaLocationProvider.cs" />
     <Compile Include="ImagesByNameProvider.cs" />
+    <Compile Include="ImagesByName\MusicGenreImageProvider.cs" />
+    <Compile Include="ImagesByName\MusicGenresManualImageProvider.cs" />
+    <Compile Include="ImagesByName\GameGenreImageProvider.cs" />
+    <Compile Include="ImagesByName\GameGenresManualImageProvider.cs" />
     <Compile Include="ImagesByName\GenreImageProvider.cs" />
     <Compile Include="ImagesByName\GenresManualImageProvider.cs" />
     <Compile Include="ImagesByName\ImageUtils.cs" />

+ 0 - 3
MediaBrowser.Server.Implementations/Library/LuceneSearchEngine.cs

@@ -40,9 +40,6 @@ namespace MediaBrowser.Server.Implementations.Library
 
         public void Dispose()
         {
-            //BaseItem.LibraryManager.LibraryChanged -= LibraryChanged;
-
-            //LuceneSearch.CloseAll();
         }
 
         /// <summary>

+ 3 - 3
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -153,18 +153,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             return await GetRecording(recording, service.Name, cancellationToken).ConfigureAwait(false);
         }
 
-        public async Task<StreamResponseInfo> GetRecordingStream(string id, CancellationToken cancellationToken)
+        public async Task<LiveStreamInfo> GetRecordingStream(string id, CancellationToken cancellationToken)
         {
             var service = ActiveService;
 
             var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
 
-            var recording = recordings.FirstOrDefault(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
+            var recording = recordings.First(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
 
             return await service.GetRecordingStream(recording.Id, cancellationToken).ConfigureAwait(false);
         }
 
-        public async Task<StreamResponseInfo> GetChannelStream(string id, CancellationToken cancellationToken)
+        public async Task<LiveStreamInfo> GetChannelStream(string id, CancellationToken cancellationToken)
         {
             var service = ActiveService;
 

+ 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.294</version>
+        <version>3.0.295</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.294" />
+            <dependency id="MediaBrowser.Common" version="3.0.295" />
             <dependency id="NLog" version="2.1.0" />
             <dependency id="SimpleInjector" version="2.4.0" />
             <dependency id="sharpcompress" version="0.10.2" />

+ 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.294</version>
+        <version>3.0.295</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser 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.294</version>
+        <version>3.0.295</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.294" />
+            <dependency id="MediaBrowser.Common" version="3.0.295" />
         </dependencies>
     </metadata>
     <files>