Browse Source

add playback of in-progress recordings

Luke Pulverenti 8 years ago
parent
commit
daaae69df5
49 changed files with 570 additions and 397 deletions
  1. 4 28
      MediaBrowser.Api/Library/LibraryService.cs
  2. 27 2
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  3. 0 10
      MediaBrowser.Controller/Channels/IChannelManager.cs
  4. 6 0
      MediaBrowser.Controller/Entities/AggregateFolder.cs
  5. 1 1
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  6. 20 8
      MediaBrowser.Controller/Entities/BaseItem.cs
  7. 19 22
      MediaBrowser.Controller/Entities/Folder.cs
  8. 7 4
      MediaBrowser.Controller/Entities/Game.cs
  9. 0 23
      MediaBrowser.Controller/Entities/IHasThemeMedia.cs
  10. 1 0
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  11. 1 6
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  12. 0 2
      MediaBrowser.Controller/Entities/TV/Season.cs
  13. 0 1
      MediaBrowser.Controller/Entities/UserView.cs
  14. 2 14
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  15. 7 1
      MediaBrowser.Controller/Entities/Video.cs
  16. 2 8
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  17. 1 0
      MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
  18. 2 1
      MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
  19. 2 1
      MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
  20. 0 1
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  21. 2 2
      MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
  22. 1 60
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  23. 1 1
      MediaBrowser.Dlna/Main/DlnaEntryPoint.cs
  24. 2 2
      MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs
  25. 0 1
      MediaBrowser.Model/Configuration/DlnaOptions.cs
  26. 0 1
      MediaBrowser.Model/Dto/BaseItemDto.cs
  27. 0 1
      MediaBrowser.Model/LiveTv/RecordingStatus.cs
  28. 24 0
      MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
  29. 0 2
      MediaBrowser.Model/Querying/ItemFields.cs
  30. 8 0
      MediaBrowser.Providers/Manager/ProviderUtils.cs
  31. 4 8
      MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
  32. 5 1
      MediaBrowser.Providers/People/MovieDbPersonProvider.cs
  33. 0 8
      MediaBrowser.Providers/People/PersonMetadataService.cs
  34. 0 92
      MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
  35. 2 12
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  36. 1 1
      MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs
  37. 2 11
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  38. 1 1
      MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
  39. 5 8
      MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
  40. 0 1
      MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
  41. 20 14
      MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
  42. 95 9
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  43. 29 0
      MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
  44. 28 15
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  45. 2 2
      MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
  46. 121 3
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  47. 106 0
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs
  48. 4 3
      MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
  49. 5 5
      MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

+ 4 - 28
MediaBrowser.Api/Library/LibraryService.cs

@@ -835,14 +835,14 @@ namespace MediaBrowser.Api.Library
                                   : (Folder)_libraryManager.RootFolder)
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);
                            : _libraryManager.GetItemById(request.Id);
 
 
-            while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
+            while (item.ThemeSongIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
             {
             {
                 item = item.GetParent();
                 item = item.GetParent();
             }
             }
 
 
             var dtoOptions = GetDtoOptions(request);
             var dtoOptions = GetDtoOptions(request);
 
 
-            var dtos = GetThemeSongIds(item).Select(_libraryManager.GetItemById)
+            var dtos = item.ThemeSongIds.Select(_libraryManager.GetItemById)
                             .Where(i => i != null)
                             .Where(i => i != null)
                             .OrderBy(i => i.SortName)
                             .OrderBy(i => i.SortName)
                             .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
                             .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@@ -879,14 +879,14 @@ namespace MediaBrowser.Api.Library
                                   : (Folder)_libraryManager.RootFolder)
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);
                            : _libraryManager.GetItemById(request.Id);
 
 
-            while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
+            while (item.ThemeVideoIds.Count == 0 && request.InheritFromParent && item.GetParent() != null)
             {
             {
                 item = item.GetParent();
                 item = item.GetParent();
             }
             }
 
 
             var dtoOptions = GetDtoOptions(request);
             var dtoOptions = GetDtoOptions(request);
 
 
-            var dtos = GetThemeVideoIds(item).Select(_libraryManager.GetItemById)
+            var dtos = item.ThemeVideoIds.Select(_libraryManager.GetItemById)
                             .Where(i => i != null)
                             .Where(i => i != null)
                             .OrderBy(i => i.SortName)
                             .OrderBy(i => i.SortName)
                             .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
                             .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
@@ -901,30 +901,6 @@ namespace MediaBrowser.Api.Library
             };
             };
         }
         }
 
 
-        private List<Guid> GetThemeVideoIds(BaseItem item)
-        {
-            var i = item as IHasThemeMedia;
-
-            if (i != null)
-            {
-                return i.ThemeVideoIds;
-            }
-
-            return new List<Guid>();
-        }
-
-        private List<Guid> GetThemeSongIds(BaseItem item)
-        {
-            var i = item as IHasThemeMedia;
-
-            if (i != null)
-            {
-                return i.ThemeSongIds;
-            }
-
-            return new List<Guid>();
-        }
-
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 
 
         public object Get(GetYearIndex request)
         public object Get(GetYearIndex request)

+ 27 - 2
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -677,6 +677,12 @@ namespace MediaBrowser.Api.LiveTv
         public string Container { get; set; }
         public string Container { get; set; }
     }
     }
 
 
+    [Route("/LiveTv/LiveRecordings/{Id}/stream", "GET", Summary = "Gets a live tv channel")]
+    public class GetLiveRecordingFile
+    {
+        public string Id { get; set; }
+    }
+
     public class LiveTvService : BaseApiService
     public class LiveTvService : BaseApiService
     {
     {
         private readonly ILiveTvManager _liveTvManager;
         private readonly ILiveTvManager _liveTvManager;
@@ -698,13 +704,32 @@ namespace MediaBrowser.Api.LiveTv
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
         }
         }
 
 
+        public async Task<object> Get(GetLiveRecordingFile request)
+        {
+            var path = EmbyTV.Current.GetActiveRecordingPath(request.Id);
+
+            if (path == null)
+            {
+                throw new FileNotFoundException();
+            }
+
+            var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+
+            outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType(path);
+
+            var streamSource = new ProgressiveFileCopier(_fileSystem, path, outputHeaders, null, Logger, CancellationToken.None)
+            {
+                AllowEndOfFile = false
+            };
+            return ResultFactory.GetAsyncStreamWriter(streamSource);
+        }
+
         public async Task<object> Get(GetLiveStreamFile request)
         public async Task<object> Get(GetLiveStreamFile request)
         {
         {
             var directStreamProvider = (await EmbyTV.Current.GetLiveStream(request.Id).ConfigureAwait(false)) as IDirectStreamProvider;
             var directStreamProvider = (await EmbyTV.Current.GetLiveStream(request.Id).ConfigureAwait(false)) as IDirectStreamProvider;
             var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             var outputHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 
 
-            // TODO: Don't hardcode this
-            outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file.ts");
+            outputHeaders["Content-Type"] = Model.Net.MimeTypes.GetMimeType("file." + request.Container);
 
 
             var streamSource = new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
             var streamSource = new ProgressiveFileCopier(directStreamProvider, outputHeaders, null, Logger, CancellationToken.None)
             {
             {

+ 0 - 10
MediaBrowser.Controller/Channels/IChannelManager.cs

@@ -134,15 +134,5 @@ namespace MediaBrowser.Controller.Channels
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>BaseItemDto.</returns>
         /// <returns>BaseItemDto.</returns>
         Task<BaseItemDto> GetChannelFolder(string userId, CancellationToken cancellationToken);
         Task<BaseItemDto> GetChannelFolder(string userId, CancellationToken cancellationToken);
-
-        /// <summary>
-        /// Downloads the channel item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="destinationPath">The destination path.</param>
-        /// <param name="progress">The progress.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        Task DownloadChannelItem(BaseItem item, string destinationPath, IProgress<double> progress, CancellationToken cancellationToken);
     }
     }
 }
 }

+ 6 - 0
MediaBrowser.Controller/Entities/AggregateFolder.cs

@@ -34,6 +34,12 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        [IgnoreDataMember]
+        public override bool IsPhysicalRoot
+        {
+            get { return true; }
+        }
+
         public override bool CanDelete()
         public override bool CanDelete()
         {
         {
             return false;
             return false;

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

@@ -62,7 +62,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         [IgnoreDataMember]
         [IgnoreDataMember]
         public override bool SupportsAddingToPlaylist
         public override bool SupportsAddingToPlaylist
         {
         {
-            get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
+            get { return true; }
         }
         }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]

+ 20 - 8
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -37,6 +37,8 @@ namespace MediaBrowser.Controller.Entities
     {
     {
         protected BaseItem()
         protected BaseItem()
         {
         {
+            ThemeSongIds = new List<Guid>();
+            ThemeVideoIds = new List<Guid>();
             Keywords = new List<string>();
             Keywords = new List<string>();
             Tags = new List<string>();
             Tags = new List<string>();
             Genres = new List<string>();
             Genres = new List<string>();
@@ -45,6 +47,7 @@ namespace MediaBrowser.Controller.Entities
             LockedFields = new List<MetadataFields>();
             LockedFields = new List<MetadataFields>();
             ImageInfos = new List<ItemImageInfo>();
             ImageInfos = new List<ItemImageInfo>();
             InheritedTags = new List<string>();
             InheritedTags = new List<string>();
+            ProductionLocations = new List<string>();
         }
         }
 
 
         public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
         public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
@@ -65,6 +68,9 @@ namespace MediaBrowser.Controller.Entities
         public static string ThemeSongFilename = "theme";
         public static string ThemeSongFilename = "theme";
         public static string ThemeVideosFolderName = "backdrops";
         public static string ThemeVideosFolderName = "backdrops";
 
 
+        public List<Guid> ThemeSongIds { get; set; }
+        public List<Guid> ThemeVideoIds { get; set; }
+
         [IgnoreDataMember]
         [IgnoreDataMember]
         public string PreferredMetadataCountryCode { get; set; }
         public string PreferredMetadataCountryCode { get; set; }
         [IgnoreDataMember]
         [IgnoreDataMember]
@@ -876,6 +882,7 @@ namespace MediaBrowser.Controller.Entities
         public List<string> Tags { get; set; }
         public List<string> Tags { get; set; }
 
 
         public List<string> Keywords { get; set; }
         public List<string> Keywords { get; set; }
+        public List<string> ProductionLocations { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the home page URL.
         /// Gets or sets the home page URL.
@@ -991,7 +998,7 @@ namespace MediaBrowser.Controller.Entities
         /// Loads the theme songs.
         /// Loads the theme songs.
         /// </summary>
         /// </summary>
         /// <returns>List{Audio.Audio}.</returns>
         /// <returns>List{Audio.Audio}.</returns>
-        private IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
+        private static IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
         {
         {
             var files = fileSystemChildren.Where(i => i.IsDirectory)
             var files = fileSystemChildren.Where(i => i.IsDirectory)
                 .Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
                 .Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
@@ -1027,7 +1034,7 @@ namespace MediaBrowser.Controller.Entities
         /// Loads the video backdrops.
         /// Loads the video backdrops.
         /// </summary>
         /// </summary>
         /// <returns>List{Video}.</returns>
         /// <returns>List{Video}.</returns>
-        private IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
+        private static IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
         {
         {
             var files = fileSystemChildren.Where(i => i.IsDirectory)
             var files = fileSystemChildren.Where(i => i.IsDirectory)
                 .Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
                 .Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
@@ -1113,6 +1120,12 @@ namespace MediaBrowser.Controller.Entities
             get { return true; }
             get { return true; }
         }
         }
 
 
+        [IgnoreDataMember]
+        public virtual bool SupportsThemeMedia
+        {
+            get { return false; }
+        }
+
         /// <summary>
         /// <summary>
         /// Refreshes owned items such as trailers, theme videos, special features, etc.
         /// Refreshes owned items such as trailers, theme videos, special features, etc.
         /// Returns true or false indicating if changes were found.
         /// Returns true or false indicating if changes were found.
@@ -1131,14 +1144,13 @@ namespace MediaBrowser.Controller.Entities
 
 
             if (LocationType == LocationType.FileSystem && GetParent() != null)
             if (LocationType == LocationType.FileSystem && GetParent() != null)
             {
             {
-                var hasThemeMedia = this as IHasThemeMedia;
-                if (hasThemeMedia != null)
+                if (SupportsThemeMedia)
                 {
                 {
                     if (!DetectIsInMixedFolder())
                     if (!DetectIsInMixedFolder())
                     {
                     {
-                        themeSongsChanged = await RefreshThemeSongs(hasThemeMedia, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+                        themeSongsChanged = await RefreshThemeSongs(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
 
 
-                        themeVideosChanged = await RefreshThemeVideos(hasThemeMedia, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+                        themeVideosChanged = await RefreshThemeVideos(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
                     }
                     }
                 }
                 }
 
 
@@ -1176,7 +1188,7 @@ namespace MediaBrowser.Controller.Entities
             return itemsChanged;
             return itemsChanged;
         }
         }
 
 
-        private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
+        private static async Task<bool> RefreshThemeVideos(BaseItem item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
         {
         {
             var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService).ToList();
             var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService).ToList();
 
 
@@ -1207,7 +1219,7 @@ namespace MediaBrowser.Controller.Entities
         /// <summary>
         /// <summary>
         /// Refreshes the theme songs.
         /// Refreshes the theme songs.
         /// </summary>
         /// </summary>
-        private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
+        private static async Task<bool> RefreshThemeSongs(BaseItem item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
         {
         {
             var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService).ToList();
             var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService).ToList();
             var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
             var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();

+ 19 - 22
MediaBrowser.Controller/Entities/Folder.cs

@@ -22,13 +22,18 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// <summary>
     /// Class Folder
     /// Class Folder
     /// </summary>
     /// </summary>
-    public class Folder : BaseItem, IHasThemeMedia
+    public class Folder : BaseItem
     {
     {
         public static IUserManager UserManager { get; set; }
         public static IUserManager UserManager { get; set; }
         public static IUserViewManager UserViewManager { get; set; }
         public static IUserViewManager UserViewManager { get; set; }
 
 
-        public List<Guid> ThemeSongIds { get; set; }
-        public List<Guid> ThemeVideoIds { get; set; }
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is root.
+        /// </summary>
+        /// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value>
+        public bool IsRoot { get; set; }
+
+        public virtual List<LinkedChild> LinkedChildren { get; set; }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]
         public DateTime? DateLastMediaAdded { get; set; }
         public DateTime? DateLastMediaAdded { get; set; }
@@ -36,9 +41,12 @@ namespace MediaBrowser.Controller.Entities
         public Folder()
         public Folder()
         {
         {
             LinkedChildren = new List<LinkedChild>();
             LinkedChildren = new List<LinkedChild>();
+        }
 
 
-            ThemeSongIds = new List<Guid>();
-            ThemeVideoIds = new List<Guid>();
+        [IgnoreDataMember]
+        public override bool SupportsThemeMedia
+        {
+            get { return true; }
         }
         }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]
@@ -47,6 +55,12 @@ namespace MediaBrowser.Controller.Entities
             get { return false; }
             get { return false; }
         }
         }
 
 
+        [IgnoreDataMember]
+        public virtual bool IsPhysicalRoot
+        {
+            get { return false; }
+        }
+
         /// <summary>
         /// <summary>
         /// Gets a value indicating whether this instance is folder.
         /// Gets a value indicating whether this instance is folder.
         /// </summary>
         /// </summary>
@@ -117,19 +131,6 @@ namespace MediaBrowser.Controller.Entities
             return true;
             return true;
         }
         }
 
 
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is physical root.
-        /// </summary>
-        /// <value><c>true</c> if this instance is physical root; otherwise, <c>false</c>.</value>
-        public bool IsPhysicalRoot { get; set; }
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance is root.
-        /// </summary>
-        /// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value>
-        public bool IsRoot { get; set; }
-
-        public virtual List<LinkedChild> LinkedChildren { get; set; }
-
         [IgnoreDataMember]
         [IgnoreDataMember]
         protected virtual bool SupportsShortcutChildren
         protected virtual bool SupportsShortcutChildren
         {
         {
@@ -178,8 +179,6 @@ namespace MediaBrowser.Controller.Entities
             item.SetParent(null);
             item.SetParent(null);
         }
         }
 
 
-        #region Indexing
-
         /// <summary>
         /// <summary>
         /// Returns the valid set of index by options for this folder type.
         /// Returns the valid set of index by options for this folder type.
         /// Override or extend to modify.
         /// Override or extend to modify.
@@ -207,8 +206,6 @@ namespace MediaBrowser.Controller.Entities
             get { return GetIndexByOptions(); }
             get { return GetIndexByOptions(); }
         }
         }
 
 
-        #endregion
-
         /// <summary>
         /// <summary>
         /// Gets the actual children.
         /// Gets the actual children.
         /// </summary>
         /// </summary>

+ 7 - 4
MediaBrowser.Controller/Entities/Game.cs

@@ -8,11 +8,8 @@ using System.Runtime.Serialization;
 
 
 namespace MediaBrowser.Controller.Entities
 namespace MediaBrowser.Controller.Entities
 {
 {
-    public class Game : BaseItem, IHasTrailers, IHasThemeMedia, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo>
+    public class Game : BaseItem, IHasTrailers, IHasScreenshots, ISupportsPlaceHolders, IHasLookupInfo<GameInfo>
     {
     {
-        public List<Guid> ThemeSongIds { get; set; }
-        public List<Guid> ThemeVideoIds { get; set; }
-
         public Game()
         public Game()
         {
         {
             MultiPartGameFiles = new List<string>();
             MultiPartGameFiles = new List<string>();
@@ -39,6 +36,12 @@ namespace MediaBrowser.Controller.Entities
             get { return true; }
             get { return true; }
         }
         }
 
 
+        [IgnoreDataMember]
+        public override bool SupportsThemeMedia
+        {
+            get { return true; }
+        }
+
         /// <summary>
         /// <summary>
         /// Gets or sets the remote trailers.
         /// Gets or sets the remote trailers.
         /// </summary>
         /// </summary>

+ 0 - 23
MediaBrowser.Controller/Entities/IHasThemeMedia.cs

@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Entities
-{
-    /// <summary>
-    /// Interface IHasThemeMedia
-    /// </summary>
-    public interface IHasThemeMedia
-    {
-        /// <summary>
-        /// Gets or sets the theme song ids.
-        /// </summary>
-        /// <value>The theme song ids.</value>
-        List<Guid> ThemeSongIds { get; set; }
-
-        /// <summary>
-        /// Gets or sets the theme video ids.
-        /// </summary>
-        /// <value>The theme video ids.</value>
-        List<Guid> ThemeVideoIds { get; set; }
-    }
-}

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

@@ -165,6 +165,7 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             switch (name)
             switch (name)
             {
             {
+                case ItemFields.ProductionLocations:
                 case ItemFields.Keywords:
                 case ItemFields.Keywords:
                 case ItemFields.Taglines:
                 case ItemFields.Taglines:
                 case ItemFields.ShortOverview:
                 case ItemFields.ShortOverview:

+ 1 - 6
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -15,21 +15,16 @@ namespace MediaBrowser.Controller.Entities.Movies
     /// <summary>
     /// <summary>
     /// Class Movie
     /// Class Movie
     /// </summary>
     /// </summary>
-    public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasBudget, IHasTrailers, IHasThemeMedia, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
+    public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasBudget, IHasTrailers, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
     {
     {
         public List<Guid> SpecialFeatureIds { get; set; }
         public List<Guid> SpecialFeatureIds { get; set; }
 
 
-        public List<Guid> ThemeSongIds { get; set; }
-        public List<Guid> ThemeVideoIds { get; set; }
-
         public Movie()
         public Movie()
         {
         {
             SpecialFeatureIds = new List<Guid>();
             SpecialFeatureIds = new List<Guid>();
             RemoteTrailers = new List<MediaUrl>();
             RemoteTrailers = new List<MediaUrl>();
             LocalTrailerIds = new List<Guid>();
             LocalTrailerIds = new List<Guid>();
             RemoteTrailerIds = new List<Guid>();
             RemoteTrailerIds = new List<Guid>();
-            ThemeSongIds = new List<Guid>();
-            ThemeVideoIds = new List<Guid>();
             Taglines = new List<string>();
             Taglines = new List<string>();
         }
         }
 
 

+ 0 - 2
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -154,8 +154,6 @@ namespace MediaBrowser.Controller.Entities.TV
 
 
             Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
             Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
 
 
-            var id = Guid.NewGuid().ToString("N");
-
             var items = GetEpisodes(user).Where(filter);
             var items = GetEpisodes(user).Where(filter);
 
 
             var result = PostFilterAndSort(items, query, false, false);
             var result = PostFilterAndSort(items, query, false, false);

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

@@ -13,7 +13,6 @@ namespace MediaBrowser.Controller.Entities
     public class UserView : Folder
     public class UserView : Folder
     {
     {
         public string ViewType { get; set; }
         public string ViewType { get; set; }
-        public Guid ParentId { get; set; }
         public Guid DisplayParentId { get; set; }
         public Guid DisplayParentId { get; set; }
 
 
         public Guid? UserId { get; set; }
         public Guid? UserId { get; set; }

+ 2 - 14
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -1497,13 +1497,7 @@ namespace MediaBrowser.Controller.Entities
             {
             {
                 var filterValue = query.HasThemeSong.Value;
                 var filterValue = query.HasThemeSong.Value;
 
 
-                var themeCount = 0;
-                var iHasThemeMedia = item as IHasThemeMedia;
-
-                if (iHasThemeMedia != null)
-                {
-                    themeCount = iHasThemeMedia.ThemeSongIds.Count;
-                }
+                var themeCount = item.ThemeSongIds.Count;
                 var ok = filterValue ? themeCount > 0 : themeCount == 0;
                 var ok = filterValue ? themeCount > 0 : themeCount == 0;
 
 
                 if (!ok)
                 if (!ok)
@@ -1516,13 +1510,7 @@ namespace MediaBrowser.Controller.Entities
             {
             {
                 var filterValue = query.HasThemeVideo.Value;
                 var filterValue = query.HasThemeVideo.Value;
 
 
-                var themeCount = 0;
-                var iHasThemeMedia = item as IHasThemeMedia;
-
-                if (iHasThemeMedia != null)
-                {
-                    themeCount = iHasThemeMedia.ThemeVideoIds.Count;
-                }
+                var themeCount = item.ThemeVideoIds.Count;
                 var ok = filterValue ? themeCount > 0 : themeCount == 0;
                 var ok = filterValue ? themeCount > 0 : themeCount == 0;
 
 
                 if (!ok)
                 if (!ok)

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

@@ -63,6 +63,12 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        [IgnoreDataMember]
+        public override bool SupportsThemeMedia
+        {
+            get { return true; }
+        }
+
         public int? TotalBitrate { get; set; }
         public int? TotalBitrate { get; set; }
         public ExtraType? ExtraType { get; set; }
         public ExtraType? ExtraType { get; set; }
 
 
@@ -164,7 +170,7 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         [IgnoreDataMember]
         public override bool SupportsAddingToPlaylist
         public override bool SupportsAddingToPlaylist
         {
         {
-            get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
+            get { return true; }
         }
         }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]

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

@@ -301,18 +301,12 @@ namespace MediaBrowser.Controller.LiveTv
         /// <summary>
         /// <summary>
         /// Gets the recording media sources.
         /// Gets the recording media sources.
         /// </summary>
         /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
-        Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(string id, CancellationToken cancellationToken);
+        Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Gets the channel media sources.
         /// Gets the channel media sources.
         /// </summary>
         /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task&lt;IEnumerable&lt;MediaSourceInfo&gt;&gt;.</returns>
-        Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(string id, CancellationToken cancellationToken);
+        Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(IHasMediaSources item, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Adds the information to recording dto.
         /// Adds the information to recording dto.

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

@@ -33,6 +33,7 @@ namespace MediaBrowser.Controller.LiveTv
         bool CanDelete(User user);
         bool CanDelete(User user);
 
 
         string SeriesTimerId { get; set; }
         string SeriesTimerId { get; set; }
+        string TimerId { get; set; }
         RecordingStatus Status { get; set; }
         RecordingStatus Status { get; set; }
         DateTime? EndDate { get; set; }
         DateTime? EndDate { get; set; }
         DateTime DateLastSaved { get; set; }
         DateTime DateLastSaved { get; set; }

+ 2 - 1
MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs

@@ -20,6 +20,7 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         [IgnoreDataMember]
         public bool IsSeries { get; set; }
         public bool IsSeries { get; set; }
         public string SeriesTimerId { get; set; }
         public string SeriesTimerId { get; set; }
+        public string TimerId { get; set; }
         [IgnoreDataMember]
         [IgnoreDataMember]
         public DateTime StartDate { get; set; }
         public DateTime StartDate { get; set; }
         public RecordingStatus Status { get; set; }
         public RecordingStatus Status { get; set; }
@@ -112,7 +113,7 @@ namespace MediaBrowser.Controller.LiveTv
 
 
         public override bool CanDelete()
         public override bool CanDelete()
         {
         {
-            return true;
+            return Status == RecordingStatus.Completed;
         }
         }
 
 
         public override bool IsAuthorizedToDelete(User user)
         public override bool IsAuthorizedToDelete(User user)

+ 2 - 1
MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs

@@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         [IgnoreDataMember]
         public bool IsSeries { get; set; }
         public bool IsSeries { get; set; }
         public string SeriesTimerId { get; set; }
         public string SeriesTimerId { get; set; }
+        public string TimerId { get; set; }
         [IgnoreDataMember]
         [IgnoreDataMember]
         public DateTime StartDate { get; set; }
         public DateTime StartDate { get; set; }
         public RecordingStatus Status { get; set; }
         public RecordingStatus Status { get; set; }
@@ -111,7 +112,7 @@ namespace MediaBrowser.Controller.LiveTv
 
 
         public override bool CanDelete()
         public override bool CanDelete()
         {
         {
-            return true;
+            return Status == RecordingStatus.Completed;
         }
         }
 
 
         public override bool IsAuthorizedToDelete(User user)
         public override bool IsAuthorizedToDelete(User user)

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

@@ -149,7 +149,6 @@
     <Compile Include="Entities\IHasShortOverview.cs" />
     <Compile Include="Entities\IHasShortOverview.cs" />
     <Compile Include="Entities\IHasSpecialFeatures.cs" />
     <Compile Include="Entities\IHasSpecialFeatures.cs" />
     <Compile Include="Entities\IHasStartDate.cs" />
     <Compile Include="Entities\IHasStartDate.cs" />
-    <Compile Include="Entities\IHasThemeMedia.cs" />
     <Compile Include="Entities\IHasTrailers.cs" />
     <Compile Include="Entities\IHasTrailers.cs" />
     <Compile Include="Entities\IHasUserData.cs" />
     <Compile Include="Entities\IHasUserData.cs" />
     <Compile Include="Entities\IHiddenFromDisplay.cs" />
     <Compile Include="Entities\IHiddenFromDisplay.cs" />

+ 2 - 2
MediaBrowser.Controller/Providers/BaseItemXmlParser.cs

@@ -347,7 +347,7 @@ namespace MediaBrowser.Controller.Providers
                             var person = item as Person;
                             var person = item as Person;
                             if (person != null)
                             if (person != null)
                             {
                             {
-                                person.PlaceOfBirth = val;
+                                person.ProductionLocations = new List<string> { val };
                             }
                             }
                         }
                         }
 
 
@@ -790,7 +790,7 @@ namespace MediaBrowser.Controller.Providers
                     }
                     }
 
 
                 default:
                 default:
-                {
+                    {
                         string readerName = reader.Name;
                         string readerName = reader.Name;
                         string providerIdValue;
                         string providerIdValue;
                         if (_validProviderIds.TryGetValue(readerName, out providerIdValue))
                         if (_validProviderIds.TryGetValue(readerName, out providerIdValue))

+ 1 - 60
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -428,14 +428,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
 
                     return ApplyPaging(result, startIndex, limit);
                     return ApplyPaging(result, startIndex, limit);
                 }
                 }
-                if (stubType.Value == StubType.Folder)
-                {
-                    var movie = item as Movie;
-                    if (movie != null)
-                    {
-                        return ApplyPaging(await GetMovieItems(movie).ConfigureAwait(false), startIndex, limit);
-                    }
-                }
 
 
                 var person = item as Person;
                 var person = item as Person;
                 if (person != null)
                 if (person != null)
@@ -468,14 +460,11 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
 
             }).ConfigureAwait(false);
             }).ConfigureAwait(false);
 
 
-            var options = _config.GetDlnaConfiguration();
-
             var serverItems = queryResult
             var serverItems = queryResult
                 .Items
                 .Items
                 .Select(i => new ServerItem
                 .Select(i => new ServerItem
                 {
                 {
-                    Item = i,
-                    StubType = GetDisplayStubType(i, item, options)
+                    Item = i
                 })
                 })
                 .ToArray();
                 .ToArray();
 
 
@@ -519,29 +508,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
             return result;
             return result;
         }
         }
 
 
-        private StubType? GetDisplayStubType(BaseItem item, BaseItem context, DlnaOptions options)
-        {
-            if (context == null || context.IsFolder)
-            {
-                var movie = item as Movie;
-                if (movie != null && options.EnableMovieFolders)
-                {
-                    if (movie.GetTrailerIds().Count > 0 ||
-                        movie.SpecialFeatureIds.Count > 0)
-                    {
-                        return StubType.Folder;
-                    }
-
-                    if (EnablePeopleDisplay(item))
-                    {
-                        return StubType.Folder;
-                    }
-                }
-            }
-
-            return null;
-        }
-
         private bool EnablePeopleDisplay(BaseItem item)
         private bool EnablePeopleDisplay(BaseItem item)
         {
         {
             if (_libraryManager.GetPeopleNames(new InternalPeopleQuery
             if (_libraryManager.GetPeopleNames(new InternalPeopleQuery
@@ -556,31 +522,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
             return false;
             return false;
         }
         }
 
 
-        private Task<QueryResult<ServerItem>> GetMovieItems(Movie item)
-        {
-            var list = new List<BaseItem>();
-
-            list.Add(item);
-
-            list.AddRange(item.GetTrailerIds().Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
-            list.AddRange(item.SpecialFeatureIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null));
-
-            var serverItems = list.Select(i => new ServerItem { Item = i, StubType = null })
-                .ToList();
-
-            serverItems.Add(new ServerItem
-            {
-                Item = item,
-                StubType = StubType.People
-            });
-
-            return Task.FromResult(new QueryResult<ServerItem>
-            {
-                Items = serverItems.ToArray(),
-                TotalRecordCount = serverItems.Count
-            });
-        }
-
         private ServerItem GetItemFromObjectId(string id, User user)
         private ServerItem GetItemFromObjectId(string id, User user)
         {
         {
             return DidlBuilder.IsIdRoot(id)
             return DidlBuilder.IsIdRoot(id)

+ 1 - 1
MediaBrowser.Dlna/Main/DlnaEntryPoint.cs

@@ -220,7 +220,7 @@ namespace MediaBrowser.Dlna.Main
                 return;
                 return;
             }
             }
 
 
-            var cacheLength = _config.GetDlnaConfiguration().BlastAliveMessageIntervalSeconds * 2;
+            var cacheLength = _config.GetDlnaConfiguration().BlastAliveMessageIntervalSeconds;
             _Publisher.SupportPnpRootDevice = false;
             _Publisher.SupportPnpRootDevice = false;
 
 
             var addresses = (await _appHost.GetLocalIpAddresses().ConfigureAwait(false)).ToList();
             var addresses = (await _appHost.GetLocalIpAddresses().ConfigureAwait(false)).ToList();

+ 2 - 2
MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs

@@ -66,9 +66,9 @@ namespace MediaBrowser.LocalMetadata.Savers
 
 
             XmlSaverHelpers.AddCommonNodes(person, _libraryManager, builder);
             XmlSaverHelpers.AddCommonNodes(person, _libraryManager, builder);
 
 
-            if (!string.IsNullOrEmpty(person.PlaceOfBirth))
+            if (person.ProductionLocations.Count > 0)
             {
             {
-                builder.Append("<PlaceOfBirth>" + SecurityElement.Escape(person.PlaceOfBirth) + "</PlaceOfBirth>");
+                builder.Append("<PlaceOfBirth>" + SecurityElement.Escape(person.ProductionLocations[0]) + "</PlaceOfBirth>");
             }
             }
 
 
             builder.Append("</Item>");
             builder.Append("</Item>");

+ 0 - 1
MediaBrowser.Model/Configuration/DlnaOptions.cs

@@ -10,7 +10,6 @@ namespace MediaBrowser.Model.Configuration
         public int ClientDiscoveryIntervalSeconds { get; set; }
         public int ClientDiscoveryIntervalSeconds { get; set; }
         public int BlastAliveMessageIntervalSeconds { get; set; }
         public int BlastAliveMessageIntervalSeconds { get; set; }
         public string DefaultUserId { get; set; }
         public string DefaultUserId { get; set; }
-        public bool EnableMovieFolders { get; set; }
 
 
         public DlnaOptions()
         public DlnaOptions()
         {
         {

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

@@ -1213,7 +1213,6 @@ namespace MediaBrowser.Model.Dto
         /// </summary>
         /// </summary>
         /// <value>The timer identifier.</value>
         /// <value>The timer identifier.</value>
         public string TimerId { get; set; }
         public string TimerId { get; set; }
-        public RecordingStatus TimerStatus { get; set; }
         /// <summary>
         /// <summary>
         /// Gets or sets the current program.
         /// Gets or sets the current program.
         /// </summary>
         /// </summary>

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

@@ -7,7 +7,6 @@ namespace MediaBrowser.Model.LiveTv
         Scheduled,
         Scheduled,
         InProgress,
         InProgress,
         Completed,
         Completed,
-        Aborted,
         Cancelled,
         Cancelled,
         ConflictedOk,
         ConflictedOk,
         ConflictedNotOk,
         ConflictedNotOk,

+ 24 - 0
MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs

@@ -68,6 +68,30 @@ namespace MediaBrowser.Model.LiveTv
         {
         {
             get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Primary); }
             get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Primary); }
         }
         }
+
+        /// <summary>
+        /// Gets or sets the parent thumb item id.
+        /// </summary>
+        /// <value>The parent thumb item id.</value>
+        public string ParentThumbItemId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the parent thumb image tag.
+        /// </summary>
+        /// <value>The parent thumb image tag.</value>
+        public string ParentThumbImageTag { get; set; }
+
+        /// <summary>
+        /// Gets or sets the parent primary image item identifier.
+        /// </summary>
+        /// <value>The parent primary image item identifier.</value>
+        public string ParentPrimaryImageItemId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the parent primary image tag.
+        /// </summary>
+        /// <value>The parent primary image tag.</value>
+        public string ParentPrimaryImageTag { get; set; }
     }
     }
 
 
     public enum KeepUntil
     public enum KeepUntil

+ 0 - 2
MediaBrowser.Model/Querying/ItemFields.cs

@@ -154,8 +154,6 @@
         /// </summary>
         /// </summary>
         People,
         People,
 
 
-        PlaceOfBirth,
-
         /// <summary>
         /// <summary>
         /// The production locations
         /// The production locations
         /// </summary>
         /// </summary>

+ 8 - 0
MediaBrowser.Providers/Manager/ProviderUtils.cs

@@ -170,6 +170,14 @@ namespace MediaBrowser.Providers.Manager
                 }
                 }
             }
             }
 
 
+            if (!lockedFields.Contains(MetadataFields.ProductionLocations))
+            {
+                if (replaceData || target.ProductionLocations.Count == 0)
+                {
+                    target.ProductionLocations = source.ProductionLocations;
+                }
+            }
+
             if (replaceData || !target.VoteCount.HasValue)
             if (replaceData || !target.VoteCount.HasValue)
             {
             {
                 target.VoteCount = source.VoteCount;
                 target.VoteCount = source.VoteCount;

+ 4 - 8
MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs

@@ -148,14 +148,10 @@ namespace MediaBrowser.Providers.Movies
 
 
             if (movieData.production_countries != null)
             if (movieData.production_countries != null)
             {
             {
-                //var hasProductionLocations = movie as IHasProductionLocations;
-                //if (hasProductionLocations != null)
-                //{
-                //    hasProductionLocations.ProductionLocations = movieData
-                //        .production_countries
-                //        .Select(i => i.name)
-                //        .ToList();
-                //}
+                movie.ProductionLocations = movieData
+                    .production_countries
+                    .Select(i => i.name)
+                    .ToList();
             }
             }
 
 
             movie.SetProviderId(MetadataProviders.Tmdb, movieData.id.ToString(_usCulture));
             movie.SetProviderId(MetadataProviders.Tmdb, movieData.id.ToString(_usCulture));

+ 5 - 1
MediaBrowser.Providers/People/MovieDbPersonProvider.cs

@@ -179,7 +179,11 @@ namespace MediaBrowser.Providers.People
 
 
                 item.Name = info.name;
                 item.Name = info.name;
                 item.HomePageUrl = info.homepage;
                 item.HomePageUrl = info.homepage;
-                item.PlaceOfBirth = info.place_of_birth;
+
+                if (!string.IsNullOrWhiteSpace(info.place_of_birth))
+                {
+                    item.ProductionLocations = new List<string> { info.place_of_birth };
+                }
                 item.Overview = info.biography;
                 item.Overview = info.biography;
 
 
                 DateTime date;
                 DateTime date;

+ 0 - 8
MediaBrowser.Providers/People/PersonMetadataService.cs

@@ -15,14 +15,6 @@ namespace MediaBrowser.Providers.People
         protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
         {
         {
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
             ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
-
-            var sourceItem = source.Item;
-            var targetItem = target.Item;
-
-            if (replaceData || string.IsNullOrEmpty(targetItem.PlaceOfBirth))
-            {
-                targetItem.PlaceOfBirth = sourceItem.PlaceOfBirth;
-            }
         }
         }
 
 
         public PersonMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
         public PersonMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)

+ 0 - 92
MediaBrowser.Server.Implementations/Channels/ChannelManager.cs

@@ -1577,97 +1577,5 @@ namespace MediaBrowser.Server.Implementations.Channels
 
 
             return await _libraryManager.GetNamedView(name, "channels", "zz_" + name, cancellationToken).ConfigureAwait(false);
             return await _libraryManager.GetNamedView(name, "channels", "zz_" + name, cancellationToken).ConfigureAwait(false);
         }
         }
-
-        public async Task DownloadChannelItem(BaseItem item, string destination,
-            IProgress<double> progress, CancellationToken cancellationToken)
-        {
-            var sources = await GetDynamicMediaSources(item, cancellationToken)
-                .ConfigureAwait(false);
-
-            var list = sources.Where(i => i.Protocol == MediaProtocol.Http).ToList();
-
-            foreach (var source in list)
-            {
-                await TryDownloadChannelItem(source, item, destination, progress, cancellationToken).ConfigureAwait(false);
-                return;
-            }
-        }
-
-        private async Task TryDownloadChannelItem(MediaSourceInfo source,
-            BaseItem item,
-            string destination,
-            IProgress<double> progress,
-            CancellationToken cancellationToken)
-        {
-            var options = new HttpRequestOptions
-            {
-                CancellationToken = cancellationToken,
-                Url = source.Path,
-                Progress = new Progress<double>()
-            };
-
-            var channel = GetChannel(item.ChannelId);
-            var channelProvider = GetChannelProvider(channel);
-            var features = channelProvider.GetChannelFeatures();
-
-            if (!features.SupportsContentDownloading)
-            {
-                throw new ArgumentException("The channel does not support downloading.");
-            }
-
-            var limit = features.DailyDownloadLimit;
-
-            foreach (var header in source.RequiredHttpHeaders)
-            {
-                options.RequestHeaders[header.Key] = header.Value;
-            }
-
-            _fileSystem.CreateDirectory(Path.GetDirectoryName(destination));
-
-            // Determine output extension
-            var response = await _httpClient.GetTempFileResponse(options).ConfigureAwait(false);
-
-            if (response.ContentType.StartsWith("text/html"))
-            {
-                throw new HttpException("File not found")
-                {
-                    StatusCode = HttpStatusCode.NotFound
-                };
-            }
-
-            if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase) && response.ContentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
-            {
-                var extension = response.ContentType.Split('/')
-                        .Last()
-                        .Replace("quicktime", "mov", StringComparison.OrdinalIgnoreCase);
-
-                destination += "." + extension;
-            }
-            else if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) && response.ContentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase))
-            {
-                var extension = response.ContentType.Replace("audio/mpeg", "audio/mp3", StringComparison.OrdinalIgnoreCase)
-                        .Split('/')
-                        .Last();
-
-                destination += "." + extension;
-            }
-            else
-            {
-                _fileSystem.DeleteFile(response.TempFilePath);
-
-                throw new ApplicationException("Unexpected response type encountered: " + response.ContentType);
-            }
-
-            _fileSystem.CopyFile(response.TempFilePath, destination, true);
-
-            try
-            {
-                _fileSystem.DeleteFile(response.TempFilePath);
-            }
-            catch
-            {
-
-            }
-        }
     }
     }
 }
 }

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

@@ -907,15 +907,6 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.Keywords = item.Keywords;
                 dto.Keywords = item.Keywords;
             }
             }
 
 
-            if (fields.Contains(ItemFields.PlaceOfBirth))
-            {
-                var person = item as Person;
-                if (person != null)
-                {
-                    dto.PlaceOfBirth = person.PlaceOfBirth;
-                }
-            }
-
             var hasAspectRatio = item as IHasAspectRatio;
             var hasAspectRatio = item as IHasAspectRatio;
             if (hasAspectRatio != null)
             if (hasAspectRatio != null)
             {
             {
@@ -1432,10 +1423,9 @@ namespace MediaBrowser.Server.Implementations.Dto
                 SetBookProperties(dto, book);
                 SetBookProperties(dto, book);
             }
             }
 
 
-            var movie = item as Movie;
-            if (movie != null)
+            if (item.ProductionLocations.Count > 0 || item is Movie)
             {
             {
-                dto.ProductionLocations = new string[] { };
+                dto.ProductionLocations = item.ProductionLocations.ToArray();
             }
             }
 
 
             var photo = item as Photo;
             var photo = item as Photo;

+ 1 - 1
MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp
         public Container Container { get; set; }
         public Container Container { get; set; }
         private readonly HttpListenerRequest request;
         private readonly HttpListenerRequest request;
         private readonly IHttpResponse response;
         private readonly IHttpResponse response;
-        private IMemoryStreamProvider _memoryStreamProvider;
+        private readonly IMemoryStreamProvider _memoryStreamProvider;
 
 
         public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, RequestAttributes requestAttributes, ILogger logger, IMemoryStreamProvider memoryStreamProvider)
         public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, RequestAttributes requestAttributes, ILogger logger, IMemoryStreamProvider memoryStreamProvider)
         {
         {

+ 2 - 11
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -335,15 +335,6 @@ namespace MediaBrowser.Server.Implementations.Library
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// Updates the item in library cache.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        private void UpdateItemInLibraryCache(BaseItem item)
-        {
-            RegisterItem(item);
-        }
-
         public void RegisterItem(BaseItem item)
         public void RegisterItem(BaseItem item)
         {
         {
             if (item == null)
             if (item == null)
@@ -1777,7 +1768,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
             foreach (var item in list)
             foreach (var item in list)
             {
             {
-                UpdateItemInLibraryCache(item);
+                RegisterItem(item);
             }
             }
 
 
             if (ItemAdded != null)
             if (ItemAdded != null)
@@ -1818,7 +1809,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
             await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false);
             await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false);
 
 
-            UpdateItemInLibraryCache(item);
+            RegisterItem(item);
 
 
             if (ItemUpdated != null)
             if (ItemUpdated != null)
             {
             {

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs

@@ -54,8 +54,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
             if (!args.IsDirectory) return null;
             if (!args.IsDirectory) return null;
 
 
             // Avoid mis-identifying top folders
             // Avoid mis-identifying top folders
-            if (args.Parent.IsRoot) return null;
             if (args.HasParent<MusicAlbum>()) return null;
             if (args.HasParent<MusicAlbum>()) return null;
+            if (args.Parent.IsRoot) return null;
 
 
             var collectionType = args.GetCollectionType();
             var collectionType = args.GetCollectionType();
 
 

+ 5 - 8
MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs

@@ -51,9 +51,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
         {
         {
             if (!args.IsDirectory) return null;
             if (!args.IsDirectory) return null;
 
 
-            // Avoid mis-identifying top folders
-            if (args.Parent.IsRoot) return null;
-
             // Don't allow nested artists
             // Don't allow nested artists
             if (args.HasParent<MusicArtist>() || args.HasParent<MusicAlbum>())
             if (args.HasParent<MusicArtist>() || args.HasParent<MusicAlbum>())
             {
             {
@@ -70,12 +67,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
                 return null;
                 return null;
             }
             }
 
 
-            if (args.IsDirectory)
+            if (args.ContainsFileSystemEntryByName("artist.nfo"))
             {
             {
-                if (args.ContainsFileSystemEntryByName("artist.nfo"))
-                {
-                    return new MusicArtist();
-                }
+                return new MusicArtist();
             }
             }
 
 
             if (_config.Configuration.EnableSimpleArtistDetection)
             if (_config.Configuration.EnableSimpleArtistDetection)
@@ -83,6 +77,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
                 return null;
                 return null;
             }
             }
 
 
+            // Avoid mis-identifying top folders
+            if (args.Parent.IsRoot) return null;
+
             var directoryService = args.DirectoryService;
             var directoryService = args.DirectoryService;
 
 
             var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
             var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);

+ 0 - 1
MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs

@@ -51,7 +51,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
             base.SetInitialItemValues(item, args);
             base.SetInitialItemValues(item, args);
 
 
             item.IsRoot = args.Parent == null;
             item.IsRoot = args.Parent == null;
-            item.IsPhysicalRoot = args.IsPhysicalRoot;
         }
         }
     }
     }
 }
 }

+ 20 - 14
MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -59,6 +59,15 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
                     return null;
                     return null;
                 }
                 }
 
 
+                if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
+                {
+                    return new Series
+                    {
+                        Path = args.Path,
+                        Name = Path.GetFileName(args.Path)
+                    };
+                }
+
                 var collectionType = args.GetCollectionType();
                 var collectionType = args.GetCollectionType();
                 if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                 {
                 {
@@ -72,23 +81,20 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
                         };
                         };
                     }
                     }
                 }
                 }
-                else
+                else if (string.IsNullOrWhiteSpace(collectionType))
                 {
                 {
-                    if (string.IsNullOrWhiteSpace(collectionType))
+                    if (args.Parent.IsRoot)
                     {
                     {
-                        if (args.Parent.IsRoot)
-                        {
-                            return null;
-                        }
-                        if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false) ||
-                            args.ContainsFileSystemEntryByName("tvshow.nfo"))
+                        return null;
+                    }
+
+                    if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false))
+                    {
+                        return new Series
                         {
                         {
-                            return new Series
-                            {
-                                Path = args.Path,
-                                Name = Path.GetFileName(args.Path)
-                            };
-                        }
+                            Path = args.Path,
+                            Name = Path.GetFileName(args.Path)
+                        };
                     }
                     }
                 }
                 }
             }
             }

+ 95 - 9
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -28,6 +28,7 @@ using System.Threading.Tasks;
 using System.Xml;
 using System.Xml;
 using CommonIO;
 using CommonIO;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
@@ -38,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 {
 {
     public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable
     public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable
     {
     {
-        private readonly IApplicationHost _appHpst;
+        private readonly IServerApplicationHost _appHost;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
@@ -64,11 +65,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
         private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
             new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
             new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
 
 
-        public EmbyTV(IApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder)
+        public EmbyTV(IServerApplicationHost appHost, ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IServerConfigurationManager config, ILiveTvManager liveTvManager, IFileSystem fileSystem, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager, IFileOrganizationService organizationService, IMediaEncoder mediaEncoder)
         {
         {
             Current = this;
             Current = this;
 
 
-            _appHpst = appHost;
+            _appHost = appHost;
             _logger = logger;
             _logger = logger;
             _httpClient = httpClient;
             _httpClient = httpClient;
             _config = config;
             _config = config;
@@ -293,7 +294,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
 
             status.Tuners = list;
             status.Tuners = list;
             status.Status = LiveTvServiceStatus.Ok;
             status.Status = LiveTvServiceStatus.Ok;
-            status.Version = _appHpst.ApplicationVersion.ToString();
+            status.Version = _appHost.ApplicationVersion.ToString();
             status.IsVisible = false;
             status.IsVisible = false;
             return status;
             return status;
         }
         }
@@ -659,7 +660,63 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
 
         public async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken)
         public async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken)
         {
         {
-            return new List<RecordingInfo>();
+            return _activeRecordings.Values.ToList().Select(GetRecordingInfo).ToList();
+        }
+
+        public string GetActiveRecordingPath(string id)
+        {
+            ActiveRecordingInfo info;
+
+            if (_activeRecordings.TryGetValue(id, out info))
+            {
+                return info.Path;
+            }
+            return null;
+        }
+
+        private RecordingInfo GetRecordingInfo(ActiveRecordingInfo info)
+        {
+            var timer = info.Timer;
+            var program = info.Program;
+
+            var result = new RecordingInfo
+            {
+                ChannelId = timer.ChannelId,
+                CommunityRating = timer.CommunityRating,
+                DateLastUpdated = DateTime.UtcNow,
+                EndDate = timer.EndDate,
+                EpisodeTitle = timer.EpisodeTitle,
+                Genres = timer.Genres,
+                Id = "recording" + timer.Id,
+                IsKids = timer.IsKids,
+                IsMovie = timer.IsMovie,
+                IsNews = timer.IsNews,
+                IsRepeat = timer.IsRepeat,
+                IsSeries = timer.IsProgramSeries,
+                IsSports = timer.IsSports,
+                Name = timer.Name,
+                OfficialRating = timer.OfficialRating,
+                OriginalAirDate = timer.OriginalAirDate,
+                Overview = timer.Overview,
+                ProgramId = timer.ProgramId,
+                SeriesTimerId = timer.SeriesTimerId,
+                StartDate = timer.StartDate,
+                Status = RecordingStatus.InProgress,
+                TimerId = timer.Id
+            };
+
+            if (program != null)
+            {
+                result.Audio = program.Audio;
+                result.ImagePath = program.ImagePath;
+                result.ImageUrl = program.ImageUrl;
+                result.IsHD = program.IsHD;
+                result.IsLive = program.IsLive;
+                result.IsPremiere = program.IsPremiere;
+                result.ShowId = program.ShowId;
+            }
+
+            return result;
         }
         }
 
 
         public Task<IEnumerable<TimerInfo>> GetTimersAsync(CancellationToken cancellationToken)
         public Task<IEnumerable<TimerInfo>> GetTimersAsync(CancellationToken cancellationToken)
@@ -954,7 +1011,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
 
         public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)
         public Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)
         {
         {
-            throw new NotImplementedException();
+            ActiveRecordingInfo info;
+
+            recordingId = recordingId.Replace("recording", string.Empty);
+
+            if (_activeRecordings.TryGetValue(recordingId, out info))
+            {
+                return Task.FromResult(new List<MediaSourceInfo>
+                {
+                    new MediaSourceInfo
+                    {
+                        Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveRecordings/" + recordingId + "/stream",
+                        Id = recordingId,
+                        SupportsDirectPlay = false,
+                        SupportsDirectStream = true,
+                        SupportsTranscoding = true,
+                        IsInfiniteStream = true,
+                        RequiresOpening = false,
+                        RequiresClosing = false,
+                        Protocol = Model.MediaInfo.MediaProtocol.Http,
+                        BufferMs = 0
+                    }
+                });
+            }
+
+            throw new FileNotFoundException();
         }
         }
 
 
         public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
         public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
@@ -1031,7 +1112,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 var activeRecordingInfo = new ActiveRecordingInfo
                 var activeRecordingInfo = new ActiveRecordingInfo
                 {
                 {
                     CancellationTokenSource = new CancellationTokenSource(),
                     CancellationTokenSource = new CancellationTokenSource(),
-                    TimerId = timer.Id
+                    Timer = timer
                 };
                 };
 
 
                 if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
                 if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
@@ -1168,6 +1249,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
             if (programInfo != null)
             if (programInfo != null)
             {
             {
                 RecordingHelper.CopyProgramInfoToTimerInfo(programInfo, timer);
                 RecordingHelper.CopyProgramInfoToTimerInfo(programInfo, timer);
+                activeRecordingInfo.Program = programInfo;
             }
             }
 
 
             string seriesPath = null;
             string seriesPath = null;
@@ -1394,7 +1476,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
                 return true;
                 return true;
             }
             }
 
 
-            var hasRecordingAtPath = _activeRecordings.Values.ToList().Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.TimerId, timerId, StringComparison.OrdinalIgnoreCase));
+            var hasRecordingAtPath = _activeRecordings
+                .Values
+                .ToList()
+                .Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
 
 
             if (hasRecordingAtPath)
             if (hasRecordingAtPath)
             {
             {
@@ -1878,7 +1963,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
         class ActiveRecordingInfo
         class ActiveRecordingInfo
         {
         {
             public string Path { get; set; }
             public string Path { get; set; }
-            public string TimerId { get; set; }
+            public TimerInfo Timer { get; set; }
+            public ProgramInfo Program { get; set; }
             public CancellationTokenSource CancellationTokenSource { get; set; }
             public CancellationTokenSource CancellationTokenSource { get; set; }
         }
         }
     }
     }

+ 29 - 0
MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -10,6 +10,7 @@ using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
@@ -125,6 +126,34 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
             dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days);
             dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days);
 
 
+            if (!string.IsNullOrWhiteSpace(info.SeriesId))
+            {
+                var program = _libraryManager.GetItemList(new InternalItemsQuery
+                {
+                    IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
+                    ExternalSeriesId = info.SeriesId,
+                    Limit = 1,
+                    ImageTypes = new ImageType[] { ImageType.Primary }
+
+                }).FirstOrDefault();
+
+                if (program != null)
+                {
+                    var image = program.GetImageInfo(ImageType.Primary, 0);
+                    if (image != null)
+                    {
+                        try
+                        {
+                            dto.ParentPrimaryImageTag = _imageProcessor.GetImageCacheTag(program, image);
+                            dto.ParentPrimaryImageItemId = program.Id.ToString("N");
+                        }
+                        catch (Exception ex)
+                        {
+                        }
+                    }
+                }
+            }
+
             return dto;
             return dto;
         }
         }
 
 

+ 28 - 15
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -235,20 +235,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             return await GetLiveStream(id, mediaSourceId, true, cancellationToken).ConfigureAwait(false);
             return await GetLiveStream(id, mediaSourceId, true, cancellationToken).ConfigureAwait(false);
         }
         }
 
 
-        public async Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(string id, CancellationToken cancellationToken)
+        public async Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
         {
         {
-            var item = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false);
-            var service = GetService(item);
+            var baseItem = (BaseItem)item;
+            var service = GetService(baseItem);
 
 
-            return await service.GetRecordingStreamMediaSources(id, cancellationToken).ConfigureAwait(false);
+            return await service.GetRecordingStreamMediaSources(baseItem.ExternalId, cancellationToken).ConfigureAwait(false);
         }
         }
 
 
-        public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(string id, CancellationToken cancellationToken)
+        public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
         {
         {
-            var item = GetInternalChannel(id);
-            var service = GetService(item);
+            var baseItem = (LiveTvChannel)item;
+            var service = GetService(baseItem);
 
 
-            var sources = await service.GetChannelStreamMediaSources(item.ExternalId, cancellationToken).ConfigureAwait(false);
+            var sources = await service.GetChannelStreamMediaSources(baseItem.ExternalId, cancellationToken).ConfigureAwait(false);
 
 
             if (sources.Count == 0)
             if (sources.Count == 0)
             {
             {
@@ -259,7 +259,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
             foreach (var source in list)
             foreach (var source in list)
             {
             {
-                Normalize(source, service, item.ChannelType == ChannelType.TV);
+                Normalize(source, service, baseItem.ChannelType == ChannelType.TV);
             }
             }
 
 
             return list;
             return list;
@@ -738,6 +738,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             recording.IsRepeat = info.IsRepeat;
             recording.IsRepeat = info.IsRepeat;
             recording.IsSports = info.IsSports;
             recording.IsSports = info.IsSports;
             recording.SeriesTimerId = info.SeriesTimerId;
             recording.SeriesTimerId = info.SeriesTimerId;
+            recording.TimerId = info.TimerId;
             recording.StartDate = info.StartDate;
             recording.StartDate = info.StartDate;
 
 
             if (!dataChanged)
             if (!dataChanged)
@@ -1083,10 +1084,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
                 if (timer != null)
                 if (timer != null)
                 {
                 {
-                    program.TimerId = _tvDtoService.GetInternalTimerId(serviceName, timer.Id)
-                        .ToString("N");
+                    if (timer.Status != RecordingStatus.Cancelled && timer.Status != RecordingStatus.Error)
+                    {
+                        program.TimerId = _tvDtoService.GetInternalTimerId(serviceName, timer.Id)
+                            .ToString("N");
 
 
-                    program.TimerStatus = timer.Status;
+                        program.Status = timer.Status.ToString();
+                    }
 
 
                     if (!string.IsNullOrEmpty(timer.SeriesTimerId))
                     if (!string.IsNullOrEmpty(timer.SeriesTimerId))
                     {
                     {
@@ -1432,7 +1436,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         private DateTime _lastRecordingRefreshTime;
         private DateTime _lastRecordingRefreshTime;
         private async Task RefreshRecordings(CancellationToken cancellationToken)
         private async Task RefreshRecordings(CancellationToken cancellationToken)
         {
         {
-            const int cacheMinutes = 5;
+            const int cacheMinutes = 3;
 
 
             if ((DateTime.UtcNow - _lastRecordingRefreshTime).TotalMinutes < cacheMinutes)
             if ((DateTime.UtcNow - _lastRecordingRefreshTime).TotalMinutes < cacheMinutes)
             {
             {
@@ -1482,7 +1486,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
         private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
         private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
         {
         {
-            if (user == null || (query.IsInProgress ?? false))
+            if (user == null)
+            {
+                return new QueryResult<BaseItem>();
+            }
+
+            if ((query.IsInProgress ?? false))
             {
             {
                 return new QueryResult<BaseItem>();
                 return new QueryResult<BaseItem>();
             }
             }
@@ -1628,7 +1637,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 return new QueryResult<BaseItem>();
                 return new QueryResult<BaseItem>();
             }
             }
 
 
-            if (_services.Count == 1)
+            if (_services.Count == 1 && !(query.IsInProgress ?? false))
             {
             {
                 return GetEmbyRecordings(query, new DtoOptions(), user);
                 return GetEmbyRecordings(query, new DtoOptions(), user);
             }
             }
@@ -1824,6 +1833,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 ? null
                 ? null
                 : _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
                 : _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
 
 
+            dto.TimerId = string.IsNullOrEmpty(info.TimerId)
+                ? null
+                : _tvDtoService.GetInternalTimerId(service.Name, info.TimerId).ToString("N");
+
             dto.StartDate = info.StartDate;
             dto.StartDate = info.StartDate;
             dto.RecordingStatus = info.Status;
             dto.RecordingStatus = info.Status;
             dto.IsRepeat = info.IsRepeat;
             dto.IsRepeat = info.IsRepeat;

+ 2 - 2
MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -65,12 +65,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             {
             {
                 if (item is ILiveTvRecording)
                 if (item is ILiveTvRecording)
                 {
                 {
-                    sources = await _liveTvManager.GetRecordingMediaSources(item.Id.ToString("N"), cancellationToken)
+                    sources = await _liveTvManager.GetRecordingMediaSources(item, cancellationToken)
                                 .ConfigureAwait(false);
                                 .ConfigureAwait(false);
                 }
                 }
                 else
                 else
                 {
                 {
-                    sources = await _liveTvManager.GetChannelMediaSources(item.Id.ToString("N"), cancellationToken)
+                    sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken)
                                 .ConfigureAwait(false);
                                 .ConfigureAwait(false);
                 }
                 }
             }
             }

+ 121 - 3
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -21,10 +21,13 @@ using System.Threading.Tasks;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Collections;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.LiveTv;
+using MediaBrowser.Server.Implementations.Devices;
+using MediaBrowser.Server.Implementations.Playlists;
 
 
 namespace MediaBrowser.Server.Implementations.Persistence
 namespace MediaBrowser.Server.Implementations.Persistence
 {
 {
@@ -279,6 +282,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _connection.AddColumn(Logger, "TypedBaseItems", "Keywords", "Text");
             _connection.AddColumn(Logger, "TypedBaseItems", "Keywords", "Text");
             _connection.AddColumn(Logger, "TypedBaseItems", "ProviderIds", "Text");
             _connection.AddColumn(Logger, "TypedBaseItems", "ProviderIds", "Text");
             _connection.AddColumn(Logger, "TypedBaseItems", "Images", "Text");
             _connection.AddColumn(Logger, "TypedBaseItems", "Images", "Text");
+            _connection.AddColumn(Logger, "TypedBaseItems", "ProductionLocations", "Text");
+            _connection.AddColumn(Logger, "TypedBaseItems", "ThemeSongIds", "Text");
+            _connection.AddColumn(Logger, "TypedBaseItems", "ThemeVideoIds", "Text");
 
 
             _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT");
             _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT");
             _connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text");
             _connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text");
@@ -428,7 +434,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
             "Tagline",
             "Tagline",
             "Keywords",
             "Keywords",
             "ProviderIds",
             "ProviderIds",
-            "Images"
+            "Images",
+            "ProductionLocations",
+            "ThemeSongIds",
+            "ThemeVideoIds"
         };
         };
 
 
         private readonly string[] _mediaStreamSaveColumns =
         private readonly string[] _mediaStreamSaveColumns =
@@ -556,7 +565,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 "Tagline",
                 "Tagline",
                 "Keywords",
                 "Keywords",
                 "ProviderIds",
                 "ProviderIds",
-                "Images"
+                "Images",
+                "ProductionLocations",
+                "ThemeSongIds",
+                "ThemeVideoIds"
             };
             };
             _saveItemCommand = _connection.CreateCommand();
             _saveItemCommand = _connection.CreateCommand();
             _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
             _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
@@ -1007,10 +1019,46 @@ namespace MediaBrowser.Server.Implementations.Persistence
                     _saveItemCommand.GetParameter(index++).Value = item.ExternalSeriesId;
                     _saveItemCommand.GetParameter(index++).Value = item.ExternalSeriesId;
                     _saveItemCommand.GetParameter(index++).Value = item.ShortOverview;
                     _saveItemCommand.GetParameter(index++).Value = item.ShortOverview;
                     _saveItemCommand.GetParameter(index++).Value = item.Tagline;
                     _saveItemCommand.GetParameter(index++).Value = item.Tagline;
-                    _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Keywords.ToArray());
+
+                    if (item.Keywords.Count > 0)
+                    {
+                        _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Keywords.ToArray());
+                    }
+                    else
+                    {
+                        _saveItemCommand.GetParameter(index++).Value = null;
+                    }
+
                     _saveItemCommand.GetParameter(index++).Value = SerializeProviderIds(item);
                     _saveItemCommand.GetParameter(index++).Value = SerializeProviderIds(item);
                     _saveItemCommand.GetParameter(index++).Value = SerializeImages(item);
                     _saveItemCommand.GetParameter(index++).Value = SerializeImages(item);
 
 
+                    if (item.ProductionLocations.Count > 0)
+                    {
+                        _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.ProductionLocations.ToArray());
+                    }
+                    else
+                    {
+                        _saveItemCommand.GetParameter(index++).Value = null;
+                    }
+
+                    if (item.ThemeSongIds.Count > 0)
+                    {
+                        _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.ThemeSongIds.Select(i => i.ToString("N")).ToArray());
+                    }
+                    else
+                    {
+                        _saveItemCommand.GetParameter(index++).Value = null;
+                    }
+
+                    if (item.ThemeVideoIds.Count > 0)
+                    {
+                        _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.ThemeVideoIds.Select(i => i.ToString("N")).ToArray());
+                    }
+                    else
+                    {
+                        _saveItemCommand.GetParameter(index++).Value = null;
+                    }
+
                     _saveItemCommand.Transaction = transaction;
                     _saveItemCommand.Transaction = transaction;
 
 
                     _saveItemCommand.ExecuteNonQuery();
                     _saveItemCommand.ExecuteNonQuery();
@@ -1217,6 +1265,46 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 {
                 {
                     return false;
                     return false;
                 }
                 }
+                if (type == typeof(Person))
+                {
+                    return false;
+                }
+                if (type == typeof(RecordingGroup))
+                {
+                    return false;
+                }
+                if (type == typeof(Channel))
+                {
+                    return false;
+                }
+                if (type == typeof(ManualCollectionsFolder))
+                {
+                    return false;
+                }
+                if (type == typeof(CameraUploadsFolder))
+                {
+                    return false;
+                }
+                if (type == typeof(PlaylistsFolder))
+                {
+                    return false;
+                }
+                if (type == typeof(UserRootFolder))
+                {
+                    return false;
+                }
+                if (type == typeof(PhotoAlbum))
+                {
+                    return false;
+                }
+                if (type == typeof(Season))
+                {
+                    return false;
+                }
+                if (type == typeof(MusicArtist))
+                {
+                    return false;
+                }
             }
             }
             if (_config.Configuration.SkipDeserializationForPrograms)
             if (_config.Configuration.SkipDeserializationForPrograms)
             {
             {
@@ -1775,6 +1863,27 @@ namespace MediaBrowser.Server.Implementations.Persistence
             }
             }
             index++;
             index++;
 
 
+            if (query.HasField(ItemFields.ProductionLocations))
+            {
+                if (!reader.IsDBNull(index))
+                {
+                    item.ProductionLocations = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+                }
+                index++;
+            }
+
+            if (!reader.IsDBNull(index))
+            {
+                item.ThemeSongIds = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList();
+            }
+            index++;
+
+            if (!reader.IsDBNull(index))
+            {
+                item.ThemeVideoIds = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList();
+            }
+            index++;
+
             if (string.IsNullOrWhiteSpace(item.Tagline))
             if (string.IsNullOrWhiteSpace(item.Tagline))
             {
             {
                 var movie = item as Movie;
                 var movie = item as Movie;
@@ -1784,6 +1893,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 }
                 }
             }
             }
 
 
+            if (type == typeof(Person) && item.ProductionLocations.Count == 0)
+            {
+                var person = (Person)item;
+                if (!string.IsNullOrWhiteSpace(person.PlaceOfBirth))
+                {
+                    item.ProductionLocations = new List<string> { person.PlaceOfBirth };
+                }
+            }
+
             return item;
             return item;
         }
         }
 
 

+ 106 - 0
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -377,6 +377,7 @@ namespace MediaBrowser.Server.Startup.Common
             ServiceStack.Text.JsConfig<Movie>.ExcludePropertyNames = new[] { "Keywords" };
             ServiceStack.Text.JsConfig<Movie>.ExcludePropertyNames = new[] { "Keywords" };
             ServiceStack.Text.JsConfig<Trailer>.ExcludePropertyNames = new[] { "ShortOverview" };
             ServiceStack.Text.JsConfig<Trailer>.ExcludePropertyNames = new[] { "ShortOverview" };
             ServiceStack.Text.JsConfig<Series>.ExcludePropertyNames = new[] { "ShortOverview" };
             ServiceStack.Text.JsConfig<Series>.ExcludePropertyNames = new[] { "ShortOverview" };
+            ServiceStack.Text.JsConfig<Person>.ExcludePropertyNames = new[] { "PlaceOfBirth" };
 
 
             ServiceStack.Text.JsConfig<LiveTvProgram>.ExcludePropertyNames = new[] { "ProviderIds" };
             ServiceStack.Text.JsConfig<LiveTvProgram>.ExcludePropertyNames = new[] { "ProviderIds" };
             ServiceStack.Text.JsConfig<LiveTvChannel>.ExcludePropertyNames = new[] { "ProviderIds" };
             ServiceStack.Text.JsConfig<LiveTvChannel>.ExcludePropertyNames = new[] { "ProviderIds" };
@@ -448,6 +449,111 @@ namespace MediaBrowser.Server.Startup.Common
             ServiceStack.Text.JsConfig<Channel>.ExcludePropertyNames = new[] { "ImageInfos" };
             ServiceStack.Text.JsConfig<Channel>.ExcludePropertyNames = new[] { "ImageInfos" };
             ServiceStack.Text.JsConfig<AggregateFolder>.ExcludePropertyNames = new[] { "ImageInfos" };
             ServiceStack.Text.JsConfig<AggregateFolder>.ExcludePropertyNames = new[] { "ImageInfos" };
 
 
+            ServiceStack.Text.JsConfig<LiveTvProgram>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<LiveTvChannel>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<LiveTvVideoRecording>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<LiveTvAudioRecording>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Series>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Audio>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<MusicAlbum>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<MusicArtist>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<MusicGenre>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<MusicVideo>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Movie>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Playlist>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<AudioPodcast>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Trailer>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<BoxSet>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Episode>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Season>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Book>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<CollectionFolder>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Folder>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Game>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<GameGenre>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<GameSystem>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Genre>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Person>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Photo>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<PhotoAlbum>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Studio>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<UserRootFolder>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<UserView>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Video>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Year>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<Channel>.ExcludePropertyNames = new[] { "ProductionLocations" };
+            ServiceStack.Text.JsConfig<AggregateFolder>.ExcludePropertyNames = new[] { "ProductionLocations" };
+
+            ServiceStack.Text.JsConfig<LiveTvProgram>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<LiveTvChannel>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<LiveTvVideoRecording>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<LiveTvAudioRecording>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Series>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Audio>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<MusicAlbum>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<MusicArtist>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<MusicGenre>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<MusicVideo>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Movie>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Playlist>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<AudioPodcast>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Trailer>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<BoxSet>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Episode>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Season>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Book>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<CollectionFolder>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Folder>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Game>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<GameGenre>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<GameSystem>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Genre>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Person>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Photo>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<PhotoAlbum>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Studio>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<UserRootFolder>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<UserView>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Video>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Year>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<Channel>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+            ServiceStack.Text.JsConfig<AggregateFolder>.ExcludePropertyNames = new[] { "ThemeSongIds" };
+
+            ServiceStack.Text.JsConfig<LiveTvProgram>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<LiveTvChannel>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<LiveTvVideoRecording>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<LiveTvAudioRecording>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Series>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Audio>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<MusicAlbum>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<MusicArtist>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<MusicGenre>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<MusicVideo>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Movie>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Playlist>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<AudioPodcast>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Trailer>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<BoxSet>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Episode>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Season>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Book>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<CollectionFolder>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Folder>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Game>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<GameGenre>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<GameSystem>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Genre>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Person>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Photo>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<PhotoAlbum>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Studio>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<UserRootFolder>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<UserView>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Video>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Year>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<Channel>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+            ServiceStack.Text.JsConfig<AggregateFolder>.ExcludePropertyNames = new[] { "ThemeVideoIds" };
+
             return result;
             return result;
         }
         }
 
 

+ 4 - 3
MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs

@@ -505,9 +505,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers
 
 
                         if (!string.IsNullOrWhiteSpace(val))
                         if (!string.IsNullOrWhiteSpace(val))
                         {
                         {
-                            //var countries = val.Split('/')
-                            //    .Select(i => i.Trim())
-                            //    .Where(i => !string.IsNullOrWhiteSpace(i));
+                            item.ProductionLocations = val.Split('/')
+                                .Select(i => i.Trim())
+                                .Where(i => !string.IsNullOrWhiteSpace(i))
+                                .ToList();
                         }
                         }
                         break;
                         break;
                     }
                     }

+ 5 - 5
MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

@@ -80,7 +80,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
                     "imdbid",
                     "imdbid",
                     "imdb_id",
                     "imdb_id",
                     "plotkeyword",
                     "plotkeyword",
-                    //"country",
+                    "country",
                     "audiodbalbumid",
                     "audiodbalbumid",
                     "audiodbartistid",
                     "audiodbartistid",
                     "awardsummary",
                     "awardsummary",
@@ -723,10 +723,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
                 writer.WriteElementString("tagline", item.Tagline);
                 writer.WriteElementString("tagline", item.Tagline);
             }
             }
 
 
-            //foreach (var country in hasProductionLocations.ProductionLocations)
-            //{
-            //    writer.WriteElementString("country", country);
-            //}
+            foreach (var country in item.ProductionLocations)
+            {
+                writer.WriteElementString("country", country);
+            }
 
 
             foreach (var genre in item.Genres)
             foreach (var genre in item.Genres)
             {
             {