diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs index cda7ce0c67..64b6b18d6f 100644 --- a/MediaBrowser.Api/ItemUpdateService.cs +++ b/MediaBrowser.Api/ItemUpdateService.cs @@ -243,11 +243,7 @@ namespace MediaBrowser.Api hasBudget.Revenue = request.Revenue; } - var hasOriginalTitle = item as IHasOriginalTitle; - if (hasOriginalTitle != null) - { - hasOriginalTitle.OriginalTitle = hasOriginalTitle.OriginalTitle; - } + item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle; var hasCriticRating = item as IHasCriticRating; if (hasCriticRating != null) diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index 5b821df0bd..ecc17374f8 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -386,6 +386,8 @@ namespace MediaBrowser.Api.LiveTv [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? EnableUserData { get; set; } + public string SeriesTimerId { get; set; } + /// /// Fields to return within the items, in addition to basic information /// @@ -985,6 +987,7 @@ namespace MediaBrowser.Api.LiveTv query.IsSeries = request.IsSeries; query.IsKids = request.IsKids; query.IsSports = request.IsSports; + query.SeriesTimerId = request.SeriesTimerId; query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 30e0f3ee72..492058f98a 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -256,7 +256,10 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] - public string ExternalSeriesId + public string ExternalSeriesId { get; set; } + + [IgnoreDataMember] + public string ExternalSeriesIdLegacy { get { return this.GetProviderId("ProviderExternalSeriesId"); } set diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 5e70cd5879..60af2c56af 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -139,6 +139,7 @@ namespace MediaBrowser.Controller.Entities public DayOfWeek[] AirDays { get; set; } public SeriesStatus[] SeriesStatuses { get; set; } public string AlbumArtistStartsWithOrGreater { get; set; } + public string ExternalSeriesId { get; set; } public string[] AlbumNames { get; set; } public string[] ArtistNames { get; set; } diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs index a828e22e30..5c73ed833f 100644 --- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs @@ -27,6 +27,8 @@ namespace MediaBrowser.Controller.LiveTv /// public string Name { get; set; } + public string ServiceName { get; set; } + /// /// Description of the recording. /// diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index 5d2231da88..51e115cc2a 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -339,12 +339,9 @@ namespace MediaBrowser.Dlna.Main if (_Publisher != null) { var devices = _Publisher.Devices.ToList(); + var tasks = devices.Select(i => _Publisher.RemoveDevice(i)).ToArray(); - foreach (var device in devices) - { - var task = _Publisher.RemoveDevice(device); - Task.WaitAll(task); - } + Task.WaitAll(tasks); //foreach (var device in devices) //{ // try diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs index 7886342e77..ad57d14732 100644 --- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -41,6 +41,7 @@ namespace MediaBrowser.Model.LiveTv /// /// The user identifier. public string UserId { get; set; } + public string SeriesTimerId { get; set; } /// /// The earliest date for which a program starts to return diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index f736307f05..025f5a1626 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -565,6 +565,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv }; } + var seriesId = info.SeriesId; + if (string.IsNullOrWhiteSpace(seriesId) && info.IsSeries) + { + seriesId = info.Name.GetMD5().ToString("N"); + } + if (!item.ParentId.Equals(channel.Id)) { forceUpdate = true; @@ -584,7 +590,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.EpisodeTitle = info.EpisodeTitle; item.ExternalId = info.Id; - item.ExternalSeriesId = info.SeriesId; + item.ExternalSeriesIdLegacy = seriesId; + + if (!string.IsNullOrWhiteSpace(seriesId) && !string.Equals(item.ExternalSeriesId, seriesId, StringComparison.Ordinal)) + { + forceUpdate = true; + } + item.ExternalSeriesId = seriesId; + item.Genres = info.Genres; item.IsHD = info.IsHD; item.IsKids = info.IsKids; @@ -825,7 +838,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user); var list = new List>(); - list.Add(new Tuple(dto, program.ServiceName, program.ExternalId, program.ExternalSeriesId)); + list.Add(new Tuple(dto, program.ServiceName, program.ExternalId, program.ExternalSeriesIdLegacy)); await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false); @@ -866,6 +879,27 @@ namespace MediaBrowser.Server.Implementations.LiveTv TopParentIds = new[] { topFolder.Id.ToString("N") } }; + if (!string.IsNullOrWhiteSpace(query.SeriesTimerId)) + { + var seriesTimers = await GetSeriesTimersInternal(new SeriesTimerQuery {}, cancellationToken).ConfigureAwait(false); + var seriesTimer = seriesTimers.Items.FirstOrDefault(i => string.Equals(_tvDtoService.GetInternalSeriesTimerId(i.ServiceName, i.Id).ToString("N"), query.SeriesTimerId, StringComparison.OrdinalIgnoreCase)); + if (seriesTimer != null) + { + internalQuery.ExternalSeriesId = seriesTimer.SeriesId; + + if (string.IsNullOrWhiteSpace(seriesTimer.SeriesId)) + { + // Better to return nothing than every program in the database + return new QueryResult(); + } + } + else + { + // Better to return nothing than every program in the database + return new QueryResult(); + } + } + if (query.HasAired.HasValue) { if (query.HasAired.Value) @@ -1730,7 +1764,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv dto.ServiceName = serviceName; } - recordingTuples.Add(new Tuple(dto, serviceName, program.ExternalId, program.ExternalSeriesId)); + recordingTuples.Add(new Tuple(dto, serviceName, program.ExternalId, program.ExternalSeriesIdLegacy)); } await AddRecordingInfo(recordingTuples, CancellationToken.None).ConfigureAwait(false); @@ -2005,6 +2039,56 @@ namespace MediaBrowser.Server.Implementations.LiveTv return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); } + private async Task> GetSeriesTimersInternal(SeriesTimerQuery query, CancellationToken cancellationToken) + { + var tasks = _services.Select(async i => + { + try + { + var recs = await i.GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false); + return recs.Select(r => + { + r.ServiceName = i.Name; + return new Tuple(r, i); + }); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting recordings", ex); + return new List>(); + } + }); + var results = await Task.WhenAll(tasks).ConfigureAwait(false); + var timers = results.SelectMany(i => i.ToList()); + + if (string.Equals(query.SortBy, "Priority", StringComparison.OrdinalIgnoreCase)) + { + timers = query.SortOrder == SortOrder.Descending ? + timers.OrderBy(i => i.Item1.Priority).ThenByStringDescending(i => i.Item1.Name) : + timers.OrderByDescending(i => i.Item1.Priority).ThenByString(i => i.Item1.Name); + } + else + { + timers = query.SortOrder == SortOrder.Descending ? + timers.OrderByStringDescending(i => i.Item1.Name) : + timers.OrderByString(i => i.Item1.Name); + } + + var returnArray = timers + .Select(i => + { + return i.Item1; + + }) + .ToArray(); + + return new QueryResult + { + Items = returnArray, + TotalRecordCount = returnArray.Length + }; + } + public async Task> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken) { var tasks = _services.Select(async i => diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 672cd1bc21..c843ab596d 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -270,6 +270,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.AddColumn(Logger, "TypedBaseItems", "SeasonId", "GUID"); _connection.AddColumn(Logger, "TypedBaseItems", "SeriesId", "GUID"); _connection.AddColumn(Logger, "TypedBaseItems", "SeriesSortName", "Text"); + _connection.AddColumn(Logger, "TypedBaseItems", "ExternalSeriesId", "Text"); _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT"); _connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text"); @@ -413,7 +414,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "SeriesSortName", "PresentationUniqueKey", "InheritedParentalRatingValue", - "InheritedTags" + "InheritedTags", + "ExternalSeriesId" }; private readonly string[] _mediaStreamSaveColumns = @@ -535,7 +537,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "SeasonName", "SeasonId", "SeriesId", - "SeriesSortName" + "SeriesSortName", + "ExternalSeriesId" }; _saveItemCommand = _connection.CreateCommand(); _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values ("; @@ -975,6 +978,8 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveItemCommand.GetParameter(index++).Value = null; } + _saveItemCommand.GetParameter(index++).Value = item.ExternalSeriesId; + _saveItemCommand.Transaction = transaction; _saveItemCommand.ExecuteNonQuery(); @@ -1466,6 +1471,12 @@ namespace MediaBrowser.Server.Implementations.Persistence } index++; + if (!reader.IsDBNull(index)) + { + item.ExternalSeriesId = reader.GetString(index); + } + index++; + return item; } @@ -2852,6 +2863,12 @@ namespace MediaBrowser.Server.Implementations.Persistence cmd.Parameters.Add(cmd, "@MinSortName", DbType.String).Value = query.MinSortName; } + if (!string.IsNullOrWhiteSpace(query.ExternalSeriesId)) + { + whereClauses.Add("ExternalSeriesId=@ExternalSeriesId"); + cmd.Parameters.Add(cmd, "@ExternalSeriesId", DbType.String).Value = query.ExternalSeriesId; + } + if (!string.IsNullOrWhiteSpace(query.Name)) { whereClauses.Add("CleanName=@Name");