ソースを参照

support sync for live tv recordings

Luke Pulverenti 10 年 前
コミット
933fca78e6

+ 10 - 3
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
@@ -460,6 +461,9 @@ namespace MediaBrowser.Api.LiveTv
 
 
         public async Task<object> Get(GetRecordings request)
         public async Task<object> Get(GetRecordings request)
         {
         {
+            var options = new DtoOptions();
+            options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
+
             var result = await _liveTvManager.GetRecordings(new RecordingQuery
             var result = await _liveTvManager.GetRecordings(new RecordingQuery
             {
             {
                 ChannelId = request.ChannelId,
                 ChannelId = request.ChannelId,
@@ -471,7 +475,7 @@ namespace MediaBrowser.Api.LiveTv
                 SeriesTimerId = request.SeriesTimerId,
                 SeriesTimerId = request.SeriesTimerId,
                 IsInProgress = request.IsInProgress
                 IsInProgress = request.IsInProgress
 
 
-            }, CancellationToken.None).ConfigureAwait(false);
+            }, options, CancellationToken.None).ConfigureAwait(false);
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }
@@ -480,7 +484,10 @@ namespace MediaBrowser.Api.LiveTv
         {
         {
             var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);
             var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);
 
 
-            var result = await _liveTvManager.GetRecording(request.Id, CancellationToken.None, user).ConfigureAwait(false);
+            var options = new DtoOptions();
+            options.DeviceId = AuthorizationContext.GetAuthorizationInfo(Request).DeviceId;
+
+            var result = await _liveTvManager.GetRecording(request.Id, options, CancellationToken.None, user).ConfigureAwait(false);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }

+ 8 - 0
MediaBrowser.Controller/Dto/IDtoService.cs

@@ -35,6 +35,14 @@ namespace MediaBrowser.Controller.Dto
         /// <returns>Task{BaseItemDto}.</returns>
         /// <returns>Task{BaseItemDto}.</returns>
         BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
         BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
 
 
+        /// <summary>
+        /// Fills the synchronize information.
+        /// </summary>
+        /// <param name="dtos">The dtos.</param>
+        /// <param name="options">The options.</param>
+        /// <param name="user">The user.</param>
+        void FillSyncInfo(IEnumerable<IHasSyncInfo> dtos, DtoOptions options, User user);
+
         /// <summary>
         /// <summary>
         /// Gets the base item dto.
         /// Gets the base item dto.
         /// </summary>
         /// </summary>

+ 20 - 0
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -50,6 +50,16 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             var user = query.User;
             var user = query.User;
 
 
+            if (query.IncludeItemTypes != null && 
+                query.IncludeItemTypes.Length == 1 && 
+                string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase))
+            {
+                if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
+                {
+                    return await FindPlaylists(queryParent, user, query).ConfigureAwait(false);
+                }
+            }
+
             switch (viewType)
             switch (viewType)
             {
             {
                 case CollectionType.Channels:
                 case CollectionType.Channels:
@@ -240,6 +250,16 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query)
+        {
+            var collectionFolders = user.RootFolder.GetChildren(user, true).Select(i => i.Id).ToList();
+
+            var list = _playlistManager.GetPlaylists(user.Id.ToString("N"))
+                .Where(i => i.GetChildren(user, true).Any(media => _libraryManager.GetCollectionFolders(media).Select(c => c.Id).Any(collectionFolders.Contains)));
+
+            return GetResult(list, parent, query);
+        }
+
         private int GetSpecialItemsLimit()
         private int GetSpecialItemsLimit()
         {
         {
             return 50;
             return 50;

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

@@ -1,4 +1,5 @@
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.LiveTv;
@@ -74,10 +75,11 @@ namespace MediaBrowser.Controller.LiveTv
         /// Gets the recording.
         /// Gets the recording.
         /// </summary>
         /// </summary>
         /// <param name="id">The identifier.</param>
         /// <param name="id">The identifier.</param>
-        /// <param name="user">The user.</param>
+        /// <param name="options">The options.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
+        /// <param name="user">The user.</param>
         /// <returns>Task{RecordingInfoDto}.</returns>
         /// <returns>Task{RecordingInfoDto}.</returns>
-        Task<RecordingInfoDto> GetRecording(string id, CancellationToken cancellationToken, User user = null);
+        Task<RecordingInfoDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null);
 
 
         /// <summary>
         /// <summary>
         /// Gets the channel.
         /// Gets the channel.
@@ -103,14 +105,15 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{TimerInfoDto}.</returns>
         /// <returns>Task{TimerInfoDto}.</returns>
         Task<SeriesTimerInfoDto> GetSeriesTimer(string id, CancellationToken cancellationToken);
         Task<SeriesTimerInfoDto> GetSeriesTimer(string id, CancellationToken cancellationToken);
-        
+
         /// <summary>
         /// <summary>
         /// Gets the recordings.
         /// Gets the recordings.
         /// </summary>
         /// </summary>
         /// <param name="query">The query.</param>
         /// <param name="query">The query.</param>
+        /// <param name="options">The options.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>QueryResult{RecordingInfoDto}.</returns>
         /// <returns>QueryResult{RecordingInfoDto}.</returns>
-        Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken);
+        Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Gets the timers.
         /// Gets the timers.

+ 3 - 0
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -464,6 +464,9 @@
     <Compile Include="..\MediaBrowser.Model\Dto\IHasServerId.cs">
     <Compile Include="..\MediaBrowser.Model\Dto\IHasServerId.cs">
       <Link>Dto\IHasServerId.cs</Link>
       <Link>Dto\IHasServerId.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dto\IHasSyncInfo.cs">
+      <Link>Dto\IHasSyncInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dto\IItemDto.cs">
     <Compile Include="..\MediaBrowser.Model\Dto\IItemDto.cs">
       <Link>Dto\IItemDto.cs</Link>
       <Link>Dto\IItemDto.cs</Link>
     </Compile>
     </Compile>

+ 3 - 0
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -429,6 +429,9 @@
     <Compile Include="..\MediaBrowser.Model\Dto\IHasServerId.cs">
     <Compile Include="..\MediaBrowser.Model\Dto\IHasServerId.cs">
       <Link>Dto\IHasServerId.cs</Link>
       <Link>Dto\IHasServerId.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dto\IHasSyncInfo.cs">
+      <Link>Dto\IHasSyncInfo.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dto\IItemDto.cs">
     <Compile Include="..\MediaBrowser.Model\Dto\IItemDto.cs">
       <Link>Dto\IItemDto.cs</Link>
       <Link>Dto\IItemDto.cs</Link>
     </Compile>
     </Compile>

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

@@ -3,6 +3,7 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Providers;
 using MediaBrowser.Model.Providers;
+using MediaBrowser.Model.Sync;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.ComponentModel;
@@ -16,7 +17,7 @@ namespace MediaBrowser.Model.Dto
     /// This holds information about a BaseItem in a format that is convenient for the client.
     /// This holds information about a BaseItem in a format that is convenient for the client.
     /// </summary>
     /// </summary>
     [DebuggerDisplay("Name = {Name}, ID = {Id}, Type = {Type}")]
     [DebuggerDisplay("Name = {Name}, ID = {Id}, Type = {Type}")]
-    public class BaseItemDto : IHasProviderIds, IHasPropertyChangedEvent, IItemDto, IHasServerId
+    public class BaseItemDto : IHasProviderIds, IHasPropertyChangedEvent, IItemDto, IHasServerId, IHasSyncInfo
     {
     {
         /// <summary>
         /// <summary>
         /// Gets or sets the name.
         /// Gets or sets the name.
@@ -87,6 +88,11 @@ namespace MediaBrowser.Model.Dto
         /// </summary>
         /// </summary>
         /// <value><c>null</c> if [is synced] contains no value, <c>true</c> if [is synced]; otherwise, <c>false</c>.</value>
         /// <value><c>null</c> if [is synced] contains no value, <c>true</c> if [is synced]; otherwise, <c>false</c>.</value>
         public bool? IsSynced { get; set; }
         public bool? IsSynced { get; set; }
+        /// <summary>
+        /// Gets or sets the synchronize status.
+        /// </summary>
+        /// <value>The synchronize status.</value>
+        public SyncJobItemStatus? SyncStatus { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Gets or sets the DVD season number.
         /// Gets or sets the DVD season number.

+ 13 - 0
MediaBrowser.Model/Dto/IHasSyncInfo.cs

@@ -0,0 +1,13 @@
+using MediaBrowser.Model.Sync;
+
+namespace MediaBrowser.Model.Dto
+{
+    public interface IHasSyncInfo
+    {
+        string Id { get; }
+        bool? SupportsSync { get; set; }
+        bool? HasSyncJob { get; set; }
+        bool? IsSynced { get; set; }
+        SyncJobItemStatus? SyncStatus { get; set; }
+    }
+}

+ 23 - 1
MediaBrowser.Model/LiveTv/RecordingInfoDto.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Library;
+using MediaBrowser.Model.Sync;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.ComponentModel;
@@ -11,7 +12,7 @@ using System.Runtime.Serialization;
 namespace MediaBrowser.Model.LiveTv
 namespace MediaBrowser.Model.LiveTv
 {
 {
     [DebuggerDisplay("Name = {Name}, ChannelName = {ChannelName}")]
     [DebuggerDisplay("Name = {Name}, ChannelName = {ChannelName}")]
-    public class RecordingInfoDto : IHasPropertyChangedEvent, IItemDto, IHasServerId
+    public class RecordingInfoDto : IHasPropertyChangedEvent, IItemDto, IHasServerId, IHasSyncInfo
     {
     {
         /// <summary>
         /// <summary>
         /// Id of the recording.
         /// Id of the recording.
@@ -35,6 +36,27 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         /// </summary>
         /// <value>The original primary image aspect ratio.</value>
         /// <value>The original primary image aspect ratio.</value>
         public double? OriginalPrimaryImageAspectRatio { get; set; }
         public double? OriginalPrimaryImageAspectRatio { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether [supports synchronize].
+        /// </summary>
+        /// <value><c>null</c> if [supports synchronize] contains no value, <c>true</c> if [supports synchronize]; otherwise, <c>false</c>.</value>
+        public bool? SupportsSync { get; set; }
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance has synchronize job.
+        /// </summary>
+        /// <value><c>null</c> if [has synchronize job] contains no value, <c>true</c> if [has synchronize job]; otherwise, <c>false</c>.</value>
+        public bool? HasSyncJob { get; set; }
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is synced.
+        /// </summary>
+        /// <value><c>null</c> if [is synced] contains no value, <c>true</c> if [is synced]; otherwise, <c>false</c>.</value>
+        public bool? IsSynced { get; set; }
+        /// <summary>
+        /// Gets or sets the synchronize status.
+        /// </summary>
+        /// <value>The synchronize status.</value>
+        public SyncJobItemStatus? SyncStatus { get; set; }
         
         
         /// <summary>
         /// <summary>
         /// Gets or sets the series timer identifier.
         /// Gets or sets the series timer identifier.

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

@@ -138,6 +138,7 @@
     <Compile Include="Dlna\SubtitleStreamInfo.cs" />
     <Compile Include="Dlna\SubtitleStreamInfo.cs" />
     <Compile Include="Drawing\ImageOrientation.cs" />
     <Compile Include="Drawing\ImageOrientation.cs" />
     <Compile Include="Dto\IHasServerId.cs" />
     <Compile Include="Dto\IHasServerId.cs" />
+    <Compile Include="Dto\IHasSyncInfo.cs" />
     <Compile Include="Dto\MetadataEditorInfo.cs" />
     <Compile Include="Dto\MetadataEditorInfo.cs" />
     <Compile Include="Dto\NameIdPair.cs" />
     <Compile Include="Dto\NameIdPair.cs" />
     <Compile Include="Dto\NameValuePair.cs" />
     <Compile Include="Dto\NameValuePair.cs" />

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

@@ -188,7 +188,22 @@ namespace MediaBrowser.Server.Implementations.Dto
             return new Tuple<IEnumerable<string>, IEnumerable<string>>(result1.Items, result2.Items);
             return new Tuple<IEnumerable<string>, IEnumerable<string>>(result1.Items, result2.Items);
         }
         }
 
 
-        private void FillSyncInfo(BaseItemDto dto, BaseItem item, DtoOptions options, User user)
+        public void FillSyncInfo(IEnumerable<IHasSyncInfo> dtos, DtoOptions options, User user)
+        {
+            if (options.Fields.Contains(ItemFields.SyncInfo))
+            {
+                var tuple = GetItemIdsWithSyncJobs(options);
+
+                foreach (var dto in dtos)
+                {
+                    var item = _libraryManager.GetItemById(dto.Id);
+
+                    FillSyncInfo(dto, item, tuple.Item1, tuple.Item2, options, user);
+                }
+            }
+        }
+
+        private void FillSyncInfo(IHasSyncInfo dto, BaseItem item, DtoOptions options, User user)
         {
         {
             if (options.Fields.Contains(ItemFields.SyncInfo))
             if (options.Fields.Contains(ItemFields.SyncInfo))
             {
             {
@@ -202,10 +217,20 @@ namespace MediaBrowser.Server.Implementations.Dto
 
 
                 dto.HasSyncJob = tuple.Item1.Contains(dto.Id, StringComparer.OrdinalIgnoreCase);
                 dto.HasSyncJob = tuple.Item1.Contains(dto.Id, StringComparer.OrdinalIgnoreCase);
                 dto.IsSynced = tuple.Item2.Contains(dto.Id, StringComparer.OrdinalIgnoreCase);
                 dto.IsSynced = tuple.Item2.Contains(dto.Id, StringComparer.OrdinalIgnoreCase);
+
+                if (dto.IsSynced.Value)
+                {
+                    dto.SyncStatus = SyncJobItemStatus.Synced;
+                }
+
+                else if (dto.HasSyncJob.Value)
+                {
+                    dto.SyncStatus = SyncJobItemStatus.Queued;
+                }
             }
             }
         }
         }
 
 
-        private void FillSyncInfo(BaseItemDto dto, BaseItem item, IEnumerable<string> itemIdsWithPendingSyncJobs, IEnumerable<string> syncedItemIds, DtoOptions options, User user)
+        private void FillSyncInfo(IHasSyncInfo dto, BaseItem item, IEnumerable<string> itemIdsWithPendingSyncJobs, IEnumerable<string> syncedItemIds, DtoOptions options, User user)
         {
         {
             if (options.Fields.Contains(ItemFields.SyncInfo))
             if (options.Fields.Contains(ItemFields.SyncInfo))
             {
             {
@@ -217,6 +242,16 @@ namespace MediaBrowser.Server.Implementations.Dto
             {
             {
                 dto.HasSyncJob = itemIdsWithPendingSyncJobs.Contains(dto.Id, StringComparer.OrdinalIgnoreCase);
                 dto.HasSyncJob = itemIdsWithPendingSyncJobs.Contains(dto.Id, StringComparer.OrdinalIgnoreCase);
                 dto.IsSynced = syncedItemIds.Contains(dto.Id, StringComparer.OrdinalIgnoreCase);
                 dto.IsSynced = syncedItemIds.Contains(dto.Id, StringComparer.OrdinalIgnoreCase);
+
+                if (dto.IsSynced.Value)
+                {
+                    dto.SyncStatus = SyncJobItemStatus.Synced;
+                }
+
+                else if (dto.HasSyncJob.Value)
+                {
+                    dto.SyncStatus = SyncJobItemStatus.Queued;
+                }
             }
             }
         }
         }
 
 

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

@@ -8,12 +8,12 @@ using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
-using MediaBrowser.Model.Querying;
 
 
 namespace MediaBrowser.Server.Implementations.LiveTv
 namespace MediaBrowser.Server.Implementations.LiveTv
 {
 {

+ 19 - 6
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -1148,7 +1148,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             RefreshIfNeeded(programs.Take(500));
             RefreshIfNeeded(programs.Take(500));
 
 
             // Load these now which will prefetch metadata
             // Load these now which will prefetch metadata
-            await GetRecordings(new RecordingQuery(), cancellationToken).ConfigureAwait(false);
+            var dtoOptions = new DtoOptions();
+            dtoOptions.Fields.Remove(ItemFields.SyncInfo);
+            await GetRecordings(new RecordingQuery(), dtoOptions, cancellationToken).ConfigureAwait(false);
             progress.Report(100);
             progress.Report(100);
         }
         }
 
 
@@ -1322,7 +1324,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             };
             };
         }
         }
 
 
-        public async Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken)
+        public async Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken)
         {
         {
             var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
             var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
 
 
@@ -1338,6 +1340,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 })
                 })
                 .ToArray();
                 .ToArray();
 
 
+            if (user != null)
+            {
+                _dtoService.FillSyncInfo(returnArray, new DtoOptions(), user);
+            }
+
             return new QueryResult<RecordingInfoDto>
             return new QueryResult<RecordingInfoDto>
             {
             {
                 Items = returnArray,
                 Items = returnArray,
@@ -1410,7 +1417,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
         public async Task DeleteRecording(string recordingId)
         public async Task DeleteRecording(string recordingId)
         {
         {
-            var recording = await GetRecording(recordingId, CancellationToken.None).ConfigureAwait(false);
+            var dtoOptions = new DtoOptions();
+            dtoOptions.Fields.Remove(ItemFields.SyncInfo);
+
+            var recording = await GetRecording(recordingId, dtoOptions, CancellationToken.None).ConfigureAwait(false);
 
 
             if (recording == null)
             if (recording == null)
             {
             {
@@ -1450,14 +1460,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             await service.CancelSeriesTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
             await service.CancelSeriesTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
         }
         }
 
 
-        public async Task<RecordingInfoDto> GetRecording(string id, CancellationToken cancellationToken, User user = null)
+        public async Task<RecordingInfoDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null)
         {
         {
             var results = await GetRecordings(new RecordingQuery
             var results = await GetRecordings(new RecordingQuery
             {
             {
                 UserId = user == null ? null : user.Id.ToString("N"),
                 UserId = user == null ? null : user.Id.ToString("N"),
                 Id = id
                 Id = id
 
 
-            }, cancellationToken).ConfigureAwait(false);
+            }, options, cancellationToken).ConfigureAwait(false);
 
 
             return results.Items.FirstOrDefault();
             return results.Items.FirstOrDefault();
         }
         }
@@ -1737,11 +1747,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
         public async Task<QueryResult<RecordingGroupDto>> GetRecordingGroups(RecordingGroupQuery query, CancellationToken cancellationToken)
         public async Task<QueryResult<RecordingGroupDto>> GetRecordingGroups(RecordingGroupQuery query, CancellationToken cancellationToken)
         {
         {
+            var dtoOptions = new DtoOptions();
+            dtoOptions.Fields.Remove(ItemFields.SyncInfo);
+            
             var recordingResult = await GetRecordings(new RecordingQuery
             var recordingResult = await GetRecordings(new RecordingQuery
             {
             {
                 UserId = query.UserId
                 UserId = query.UserId
 
 
-            }, cancellationToken).ConfigureAwait(false);
+            }, dtoOptions, cancellationToken).ConfigureAwait(false);
 
 
             var recordings = recordingResult.Items;
             var recordings = recordingResult.Items;
 
 

+ 1 - 1
MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs

@@ -94,7 +94,7 @@ namespace MediaBrowser.Server.Implementations.Photos
 
 
         protected abstract Task<List<BaseItem>> GetItemsWithImages(IHasImages item);
         protected abstract Task<List<BaseItem>> GetItemsWithImages(IHasImages item);
 
 
-        private const string Version = "27";
+        private const string Version = "29";
         protected string GetConfigurationCacheKey(List<BaseItem> items, string itemName)
         protected string GetConfigurationCacheKey(List<BaseItem> items, string itemName)
         {
         {
             var parts = Version + "_" + (itemName ?? string.Empty) + "_" +
             var parts = Version + "_" + (itemName ?? string.Empty) + "_" +