diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 93eac058d6..9efa609efe 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Providers; +using System; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Users; @@ -6,6 +7,7 @@ using MoreLinq; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using System.Threading.Tasks; using MediaBrowser.Model.Configuration; namespace MediaBrowser.Controller.Entities.TV @@ -127,6 +129,30 @@ namespace MediaBrowser.Controller.Entities.TV get { return (IndexNumber ?? -1) == 0; } } + public override Task> GetItems(InternalItemsQuery query) + { + var user = query.User; + + Func filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + + IEnumerable items; + + if (query.User == null) + { + items = query.Recursive + ? GetRecursiveChildren(filter) + : Children.Where(filter); + } + else + { + items = GetEpisodes(query.User).Where(filter); + } + + var result = PostFilterAndSort(items, query); + + return Task.FromResult(result); + } + /// /// Gets the episodes. /// diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 420b3c3135..aa07ab3781 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -157,6 +157,32 @@ namespace MediaBrowser.Controller.Entities.TV return GetSeasons(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes); } + public override Task> GetItems(InternalItemsQuery query) + { + var user = query.User; + + Func filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + + IEnumerable items; + + if (query.User == null) + { + items = query.Recursive + ? GetRecursiveChildren(filter) + : Children.Where(filter); + } + else + { + items = query.Recursive + ? GetRecursiveChildren(user, filter) + : GetSeasons(user).Where(filter); + } + + var result = PostFilterAndSort(items, query); + + return Task.FromResult(result); + } + public IEnumerable GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired) { var seasons = base.GetChildren(user, true) diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index bc102bd40a..5527c16463 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -204,6 +204,8 @@ namespace MediaBrowser.Model.Configuration public int MigrationVersion { get; set; } + public bool DownloadImagesInAdvance { get; set; } + /// /// Initializes a new instance of the class. /// diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 370187801e..73e09c1e6f 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -18,6 +18,7 @@ using System.Threading; using System.Threading.Tasks; using CommonIO; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Providers.Manager @@ -520,6 +521,16 @@ namespace MediaBrowser.Providers.Manager private bool EnableImageStub(IHasImages item, ImageType type) { + if (item is LiveTvProgram) + { + return true; + } + + if (_config.Configuration.DownloadImagesInAdvance) + { + return false; + } + if (item.LocationType == LocationType.Remote || item.LocationType == LocationType.Virtual) { return true; diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index e18da565df..416cc51bd2 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -283,12 +283,7 @@ namespace MediaBrowser.Providers.Manager if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary)) { - personEntity.SetImage(new ItemImageInfo - { - Path = person.ImageUrl, - Type = ImageType.Primary, - IsPlaceholder = true - }, 0); + await AddPersonImage(personEntity, person.ImageUrl, cancellationToken).ConfigureAwait(false); saveEntity = true; updateType = updateType | ItemUpdateType.ImageUpdate; @@ -302,6 +297,23 @@ namespace MediaBrowser.Providers.Manager } } + private async Task AddPersonImage(Person personEntity, string imageUrl, CancellationToken cancellationToken) + { + if (ServerConfigurationManager.Configuration.DownloadImagesInAdvance) + { + await ProviderManager.SaveImage(personEntity, imageUrl, null, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); + } + else + { + personEntity.SetImage(new ItemImageInfo + { + Path = imageUrl, + Type = ImageType.Primary, + IsPlaceholder = true + }, 0); + } + } + private readonly Task _cachedTask = Task.FromResult(true); protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index f0c7a34d9a..1fbd3cb3e5 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -300,17 +300,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - try - { - _fileSystem.DeleteFile(remove.Path); - } - catch (DirectoryNotFoundException) + if (!string.IsNullOrWhiteSpace(remove.Path)) { + try + { + _fileSystem.DeleteFile(remove.Path); + } + catch (DirectoryNotFoundException) + { - } - catch (FileNotFoundException) - { + } + catch (FileNotFoundException) + { + } } _recordingProvider.Delete(remove); } @@ -726,6 +729,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer, info)).Trim() + ".ts"; recordPath = Path.Combine(recordPath, recordingFileName); + recordPath = EnsureFileUnique(recordPath); _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath)); var recordingId = info.Id.GetMD5().ToString("N"); @@ -859,6 +863,24 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } + private string EnsureFileUnique(string path) + { + var originalPath = path; + var index = 1; + + while (_fileSystem.FileExists(path)) + { + var parent = Path.GetDirectoryName(originalPath); + var name = Path.GetFileNameWithoutExtension(originalPath); + name += "-" + index.ToString(CultureInfo.InvariantCulture); + + path = Path.ChangeExtension(Path.Combine(parent, name), Path.GetExtension(originalPath)); + index++; + } + + return path; + } + private async Task GetRecorder() { if (GetConfiguration().EnableRecordingEncoding) diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs index 92a33993a9..0a03e60fae 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs @@ -90,7 +90,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun await _liveTvManager.SaveTunerHost(new TunerHostInfo { Type = HdHomerunHost.DeviceType, - Url = url + Url = url, + DataVersion = 1 }).ConfigureAwait(false); } diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs index 39779ecf2a..e2e54e0569 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs @@ -338,6 +338,12 @@ namespace MediaBrowser.Server.Implementations.Sync return series.GetEpisodes(user, false, false); } + var season = item as Season; + if (season != null) + { + return season.GetEpisodes(user, false, false); + } + if (item.IsFolder) { var folder = (Folder)item; @@ -377,6 +383,9 @@ namespace MediaBrowser.Server.Implementations.Sync { await EnsureSyncJobItems(null, cancellationToken).ConfigureAwait(false); + // Look job items that are supposedly transfering, but need to be requeued because the synced files have been deleted somehow + await HandleDeletedSyncFiles(cancellationToken).ConfigureAwait(false); + // If it already has a converting status then is must have been aborted during conversion var result = _syncManager.GetJobItems(new SyncJobItemQuery { @@ -389,6 +398,28 @@ namespace MediaBrowser.Server.Implementations.Sync CleanDeadSyncFiles(); } + private async Task HandleDeletedSyncFiles(CancellationToken cancellationToken) + { + // Look job items that are supposedly transfering, but need to be requeued because the synced files have been deleted somehow + var result = _syncManager.GetJobItems(new SyncJobItemQuery + { + Statuses = new[] { SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Transferring }, + AddMetadata = false + }); + + foreach (var item in result.Items) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (string.IsNullOrWhiteSpace(item.OutputPath) || !_fileSystem.FileExists(item.OutputPath)) + { + item.Status = SyncJobItemStatus.Queued; + await _syncManager.UpdateSyncJobItemInternal(item).ConfigureAwait(false); + await UpdateJobStatus(item.JobId).ConfigureAwait(false); + } + } + } + private void CleanDeadSyncFiles() { // TODO diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index 50a960956f..8ebc8d91e1 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -559,6 +559,12 @@ namespace MediaBrowser.Server.Implementations.Sync jobItem.Status = SyncJobItemStatus.Synced; jobItem.Progress = 100; + await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); + + var processor = GetSyncJobProcessor(); + + await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(jobItem.TemporaryPath)) { try @@ -573,12 +579,6 @@ namespace MediaBrowser.Server.Implementations.Sync _logger.ErrorException("Error deleting temporary job file: {0}", ex, jobItem.OutputPath); } } - - await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false); - - var processor = GetSyncJobProcessor(); - - await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false); } private SyncJobProcessor GetSyncJobProcessor() @@ -1015,7 +1015,7 @@ namespace MediaBrowser.Server.Implementations.Sync { var jobItem = _repo.GetJobItem(id); - if (jobItem.Status != SyncJobItemStatus.Queued && jobItem.Status != SyncJobItemStatus.ReadyToTransfer && jobItem.Status != SyncJobItemStatus.Converting && jobItem.Status != SyncJobItemStatus.Failed && jobItem.Status != SyncJobItemStatus.Synced) + if (jobItem.Status != SyncJobItemStatus.Queued && jobItem.Status != SyncJobItemStatus.ReadyToTransfer && jobItem.Status != SyncJobItemStatus.Converting && jobItem.Status != SyncJobItemStatus.Failed && jobItem.Status != SyncJobItemStatus.Synced && jobItem.Status != SyncJobItemStatus.Transferring) { throw new ArgumentException("Operation is not valid for this job item"); }