aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2016-11-03 19:59:50 -0400
committerGitHub <noreply@github.com>2016-11-03 19:59:50 -0400
commitc53745548ac2130f4cfbbe0d7a2804c36c8ae4eb (patch)
tree6ee298ebb5470c4f3bcbef8d814a0354901469c4 /MediaBrowser.Server.Implementations
parent338b04a0c58729ec70aed89924ea6bd12422872b (diff)
parent405a5f69c5967b4d919b5fe91396f12cb83e8aa8 (diff)
Merge pull request #2267 from MediaBrowser/dev
Dev
Diffstat (limited to 'MediaBrowser.Server.Implementations')
-rw-r--r--MediaBrowser.Server.Implementations/Activity/ActivityManager.cs56
-rw-r--r--MediaBrowser.Server.Implementations/Branding/BrandingConfigurationFactory.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs29
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs43
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs55
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelManager.cs1620
-rw-r--r--MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs257
-rw-r--r--MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs78
-rw-r--r--MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs86
-rw-r--r--MediaBrowser.Server.Implementations/Collections/CollectionManager.cs298
-rw-r--r--MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs10
-rw-r--r--MediaBrowser.Server.Implementations/Devices/DeviceManager.cs306
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs1609
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs561
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs121
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs10
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs340
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs71
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs544
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs54
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs77
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs41
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs203
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs133
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs138
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs162
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs831
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs33
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs80
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs283
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs96
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs101
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs210
-rw-r--r--MediaBrowser.Server.Implementations/IO/FileRefresher.cs22
-rw-r--r--MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs11
-rw-r--r--MediaBrowser.Server.Implementations/IO/ThrottledStream.cs393
-rw-r--r--MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs386
-rw-r--r--MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs148
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs3067
-rw-r--r--MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs102
-rw-r--r--MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs648
-rw-r--r--MediaBrowser.Server.Implementations/Library/MusicManager.cs157
-rw-r--r--MediaBrowser.Server.Implementations/Library/PathExtensions.cs45
-rw-r--r--MediaBrowser.Server.Implementations/Library/ResolverHelper.cs183
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs68
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs173
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs94
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs297
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs56
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs62
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs77
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs541
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs56
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs103
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs42
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs85
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs75
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs62
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs251
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs45
-rw-r--r--MediaBrowser.Server.Implementations/Library/SearchEngine.cs280
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserDataManager.cs287
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserManager.cs1021
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserViewManager.cs292
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs44
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs84
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs45
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs74
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs42
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs75
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs45
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs75
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs172
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs45
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs74
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs55
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs85
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs107
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs1976
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs36
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs326
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs16
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs23
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs149
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs105
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs28
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs167
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs1310
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs233
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs110
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs21
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs390
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs3022
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs219
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs88
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs82
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs83
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs249
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs159
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs570
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs147
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs167
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs169
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs96
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs93
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs2
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj595
-rw-r--r--MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs236
-rw-r--r--MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs170
-rw-r--r--MediaBrowser.Server.Implementations/News/NewsService.cs77
-rw-r--r--MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs198
-rw-r--r--MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs8
-rw-r--r--MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs61
-rw-r--r--MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs296
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs360
-rw-r--r--MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs361
-rw-r--r--MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs35
-rw-r--r--MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs104
-rw-r--r--MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs275
-rw-r--r--MediaBrowser.Server.Implementations/Reflection/AssemblyInfo.cs14
-rw-r--r--MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs201
-rw-r--r--MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs94
-rw-r--r--MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs140
-rw-r--r--MediaBrowser.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs105
-rw-r--r--MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs96
-rw-r--r--MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs148
-rw-r--r--MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs353
-rw-r--r--MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs292
-rw-r--r--MediaBrowser.Server.Implementations/Session/HttpSessionController.cs186
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs1930
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs485
-rw-r--r--MediaBrowser.Server.Implementations/Session/WebSocketController.cs288
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs71
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs160
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs47
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs46
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs99
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs51
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs39
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs29
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs37
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs33
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs69
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs69
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs54
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs58
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs39
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs58
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs58
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs41
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/NameComparer.cs33
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs40
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs63
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs46
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs59
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs52
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs33
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs39
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs32
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs37
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs33
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs47
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs30
-rw-r--r--MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs118
-rw-r--r--MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs302
-rw-r--r--MediaBrowser.Server.Implementations/Sync/IHasSyncQuality.cs31
-rw-r--r--MediaBrowser.Server.Implementations/Sync/MediaSync.cs502
-rw-r--r--MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs76
-rw-r--r--MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs92
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncConfig.cs29
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs89
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncHelper.cs24
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncJobOptions.cs18
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs988
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncManager.cs1361
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncNotificationEntryPoint.cs48
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncRegistrationInfo.cs31
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs158
-rw-r--r--MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs188
-rw-r--r--MediaBrowser.Server.Implementations/TV/SeriesPostScanTask.cs238
-rw-r--r--MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs226
-rw-r--r--MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs49
-rw-r--r--MediaBrowser.Server.Implementations/Threading/PeriodicTimer.cs72
-rw-r--r--MediaBrowser.Server.Implementations/Updates/InstallationManager.cs685
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs176
-rw-r--r--MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs190
-rw-r--r--MediaBrowser.Server.Implementations/Xml/XmlReaderSettingsFactory.cs20
-rw-r--r--MediaBrowser.Server.Implementations/packages.config5
189 files changed, 237 insertions, 42954 deletions
diff --git a/MediaBrowser.Server.Implementations/Activity/ActivityManager.cs b/MediaBrowser.Server.Implementations/Activity/ActivityManager.cs
deleted file mode 100644
index 2af9395c3..000000000
--- a/MediaBrowser.Server.Implementations/Activity/ActivityManager.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using MediaBrowser.Common.Events;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Activity
-{
- public class ActivityManager : IActivityManager
- {
- public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
-
- private readonly IActivityRepository _repo;
- private readonly ILogger _logger;
- private readonly IUserManager _userManager;
-
- public ActivityManager(ILogger logger, IActivityRepository repo, IUserManager userManager)
- {
- _logger = logger;
- _repo = repo;
- _userManager = userManager;
- }
-
- public async Task Create(ActivityLogEntry entry)
- {
- entry.Id = Guid.NewGuid().ToString("N");
- entry.Date = DateTime.UtcNow;
-
- await _repo.Create(entry).ConfigureAwait(false);
-
- EventHelper.FireEventIfNotNull(EntryCreated, this, new GenericEventArgs<ActivityLogEntry>(entry), _logger);
- }
-
- public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
- {
- var result = _repo.GetActivityLogEntries(minDate, startIndex, limit);
-
- foreach (var item in result.Items.Where(i => !string.IsNullOrWhiteSpace(i.UserId)))
- {
- var user = _userManager.GetUserById(item.UserId);
-
- if (user != null)
- {
- var dto = _userManager.GetUserDto(user);
- item.UserPrimaryImageTag = dto.PrimaryImageTag;
- }
- }
-
- return result;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Branding/BrandingConfigurationFactory.cs b/MediaBrowser.Server.Implementations/Branding/BrandingConfigurationFactory.cs
deleted file mode 100644
index d6cd3424b..000000000
--- a/MediaBrowser.Server.Implementations/Branding/BrandingConfigurationFactory.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Branding;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Server.Implementations.Branding
-{
- public class BrandingConfigurationFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new[]
- {
- new ConfigurationStore
- {
- ConfigurationType = typeof(BrandingOptions),
- Key = "branding"
- }
- };
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs b/MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs
deleted file mode 100644
index 9dfb0404e..000000000
--- a/MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Configuration;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Server.Implementations.Channels
-{
- public static class ChannelConfigurationExtension
- {
- public static ChannelOptions GetChannelsConfiguration(this IConfigurationManager manager)
- {
- return manager.GetConfiguration<ChannelOptions>("channels");
- }
- }
-
- public class ChannelConfigurationFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- Key = "channels",
- ConfigurationType = typeof (ChannelOptions)
- }
- };
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
deleted file mode 100644
index fae78b9bc..000000000
--- a/MediaBrowser.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Dto;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Channels
-{
- public class ChannelDynamicMediaSourceProvider : IMediaSourceProvider
- {
- private readonly ChannelManager _channelManager;
-
- public ChannelDynamicMediaSourceProvider(IChannelManager channelManager)
- {
- _channelManager = (ChannelManager)channelManager;
- }
-
- public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
- {
- var baseItem = (BaseItem) item;
-
- if (baseItem.SourceType == SourceType.Channel)
- {
- return _channelManager.GetDynamicMediaSources(baseItem, cancellationToken);
- }
-
- return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
- }
-
- public Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task CloseMediaSource(string liveStreamId)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs
deleted file mode 100644
index c98f71ce2..000000000
--- a/MediaBrowser.Server.Implementations/Channels/ChannelImageProvider.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Channels
-{
- public class ChannelImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
- {
- private readonly IChannelManager _channelManager;
-
- public ChannelImageProvider(IChannelManager channelManager)
- {
- _channelManager = channelManager;
- }
-
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- return GetChannel(item).GetSupportedChannelImages();
- }
-
- public Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
- {
- var channel = GetChannel(item);
-
- return channel.GetChannelImage(type, cancellationToken);
- }
-
- public string Name
- {
- get { return "Channel Image Provider"; }
- }
-
- public bool Supports(IHasImages item)
- {
- return item is Channel;
- }
-
- private IChannel GetChannel(IHasImages item)
- {
- var channel = (Channel)item;
-
- return ((ChannelManager)_channelManager).GetChannelProvider(channel);
- }
-
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
- {
- return GetSupportedImages(item).Any(i => !item.HasImage(i));
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
deleted file mode 100644
index 300973ce1..000000000
--- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
+++ /dev/null
@@ -1,1620 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Globalization;
-
-namespace MediaBrowser.Server.Implementations.Channels
-{
- public class ChannelManager : IChannelManager
- {
- private IChannel[] _channels;
-
- private readonly IUserManager _userManager;
- private readonly IUserDataManager _userDataManager;
- private readonly IDtoService _dtoService;
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly IHttpClient _httpClient;
- private readonly IProviderManager _providerManager;
-
- private readonly ILocalizationManager _localization;
- private readonly ConcurrentDictionary<Guid, bool> _refreshedItems = new ConcurrentDictionary<Guid, bool>();
-
- public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, IJsonSerializer jsonSerializer, ILocalizationManager localization, IHttpClient httpClient, IProviderManager providerManager)
- {
- _userManager = userManager;
- _dtoService = dtoService;
- _libraryManager = libraryManager;
- _logger = logger;
- _config = config;
- _fileSystem = fileSystem;
- _userDataManager = userDataManager;
- _jsonSerializer = jsonSerializer;
- _localization = localization;
- _httpClient = httpClient;
- _providerManager = providerManager;
- }
-
- private TimeSpan CacheLength
- {
- get
- {
- return TimeSpan.FromHours(6);
- }
- }
-
- public void AddParts(IEnumerable<IChannel> channels)
- {
- _channels = channels.ToArray();
- }
-
- public string ChannelDownloadPath
- {
- get
- {
- var options = _config.GetChannelsConfiguration();
-
- if (!string.IsNullOrWhiteSpace(options.DownloadPath))
- {
- return options.DownloadPath;
- }
-
- return Path.Combine(_config.ApplicationPaths.ProgramDataPath, "channels");
- }
- }
-
- private IEnumerable<IChannel> GetAllChannels()
- {
- return _channels
- .OrderBy(i => i.Name);
- }
-
- public IEnumerable<Guid> GetInstalledChannelIds()
- {
- return GetAllChannels().Select(i => GetInternalChannelId(i.Name));
- }
-
- public Task<QueryResult<Channel>> GetChannelsInternal(ChannelQuery query, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- var channels = GetAllChannels()
- .Select(GetChannelEntity)
- .OrderBy(i => i.SortName)
- .ToList();
-
- if (query.SupportsLatestItems.HasValue)
- {
- var val = query.SupportsLatestItems.Value;
- channels = channels.Where(i =>
- {
- try
- {
- return GetChannelProvider(i) is ISupportsLatestMedia == val;
- }
- catch
- {
- return false;
- }
-
- }).ToList();
- }
- if (query.IsFavorite.HasValue)
- {
- var val = query.IsFavorite.Value;
- channels = channels.Where(i => _userDataManager.GetUserData(user, i).IsFavorite == val)
- .ToList();
- }
-
- if (user != null)
- {
- channels = channels.Where(i =>
- {
- if (!i.IsVisible(user))
- {
- return false;
- }
-
- try
- {
- return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N"));
- }
- catch
- {
- return false;
- }
-
- }).ToList();
- }
-
- var all = channels;
- var totalCount = all.Count;
-
- if (query.StartIndex.HasValue)
- {
- all = all.Skip(query.StartIndex.Value).ToList();
- }
- if (query.Limit.HasValue)
- {
- all = all.Take(query.Limit.Value).ToList();
- }
-
- var returnItems = all.ToArray();
-
- var result = new QueryResult<Channel>
- {
- Items = returnItems,
- TotalRecordCount = totalCount
- };
-
- return Task.FromResult(result);
- }
-
- public async Task<QueryResult<BaseItemDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- var internalResult = await GetChannelsInternal(query, cancellationToken).ConfigureAwait(false);
-
- var dtoOptions = new DtoOptions();
-
- var returnItems = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
-
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnItems,
- TotalRecordCount = internalResult.TotalRecordCount
- };
-
- return result;
- }
-
- public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
- {
- _refreshedItems.Clear();
-
- var allChannelsList = GetAllChannels().ToList();
-
- var numComplete = 0;
-
- foreach (var channelInfo in allChannelsList)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- try
- {
- await GetChannel(channelInfo, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting channel information for {0}", ex, channelInfo.Name);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= allChannelsList.Count;
-
- progress.Report(100 * percent);
- }
-
- progress.Report(100);
- }
-
- private Channel GetChannelEntity(IChannel channel)
- {
- var item = GetChannel(GetInternalChannelId(channel.Name).ToString("N"));
-
- if (item == null)
- {
- item = GetChannel(channel, CancellationToken.None).Result;
- }
-
- return item;
- }
-
- private List<ChannelMediaInfo> GetSavedMediaSources(BaseItem item)
- {
- var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasources.json");
-
- try
- {
- return _jsonSerializer.DeserializeFromFile<List<ChannelMediaInfo>>(path) ?? new List<ChannelMediaInfo>();
- }
- catch
- {
- return new List<ChannelMediaInfo>();
- }
- }
-
- private void SaveMediaSources(BaseItem item, List<ChannelMediaInfo> mediaSources)
- {
- var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasources.json");
-
- if (mediaSources == null || mediaSources.Count == 0)
- {
- try
- {
- _fileSystem.DeleteFile(path);
- }
- catch
- {
-
- }
- return;
- }
-
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- _jsonSerializer.SerializeToFile(mediaSources, path);
- }
-
- public async Task<IEnumerable<MediaSourceInfo>> GetStaticMediaSources(BaseItem item, bool includeCachedVersions, CancellationToken cancellationToken)
- {
- IEnumerable<ChannelMediaInfo> results = new List<ChannelMediaInfo>();
- var video = item as Video;
- if (video != null)
- {
- results = video.ChannelMediaSources;
- }
- var audio = item as Audio;
- if (audio != null)
- {
- results = audio.ChannelMediaSources ?? GetSavedMediaSources(audio);
- }
-
- var sources = SortMediaInfoResults(results)
- .Select(i => GetMediaSource(item, i))
- .ToList();
-
- if (includeCachedVersions)
- {
- var cachedVersions = GetCachedChannelItemMediaSources(item);
- sources.InsertRange(0, cachedVersions);
- }
-
- return sources;
- }
-
- public async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(BaseItem item, CancellationToken cancellationToken)
- {
- var channel = GetChannel(item.ChannelId);
- var channelPlugin = GetChannelProvider(channel);
-
- var requiresCallback = channelPlugin as IRequiresMediaInfoCallback;
-
- IEnumerable<ChannelMediaInfo> results;
-
- if (requiresCallback != null)
- {
- results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken)
- .ConfigureAwait(false);
- }
- else
- {
- results = new List<ChannelMediaInfo>();
- }
-
- var list = SortMediaInfoResults(results)
- .Select(i => GetMediaSource(item, i))
- .ToList();
-
- var cachedVersions = GetCachedChannelItemMediaSources(item);
- list.InsertRange(0, cachedVersions);
-
- return list;
- }
-
- private readonly ConcurrentDictionary<string, Tuple<DateTime, List<ChannelMediaInfo>>> _channelItemMediaInfo =
- new ConcurrentDictionary<string, Tuple<DateTime, List<ChannelMediaInfo>>>();
-
- private async Task<IEnumerable<ChannelMediaInfo>> GetChannelItemMediaSourcesInternal(IRequiresMediaInfoCallback channel, string id, CancellationToken cancellationToken)
- {
- Tuple<DateTime, List<ChannelMediaInfo>> cachedInfo;
-
- if (_channelItemMediaInfo.TryGetValue(id, out cachedInfo))
- {
- if ((DateTime.UtcNow - cachedInfo.Item1).TotalMinutes < 5)
- {
- return cachedInfo.Item2;
- }
- }
-
- var mediaInfo = await channel.GetChannelItemMediaInfo(id, cancellationToken)
- .ConfigureAwait(false);
- var list = mediaInfo.ToList();
-
- var item2 = new Tuple<DateTime, List<ChannelMediaInfo>>(DateTime.UtcNow, list);
- _channelItemMediaInfo.AddOrUpdate(id, item2, (key, oldValue) => item2);
-
- return list;
- }
-
- private IEnumerable<MediaSourceInfo> GetCachedChannelItemMediaSources(BaseItem item)
- {
- var filenamePrefix = item.Id.ToString("N");
- var parentPath = Path.Combine(ChannelDownloadPath, item.ChannelId);
-
- try
- {
- var files = _fileSystem.GetFiles(parentPath);
-
- if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- files = files.Where(i => _libraryManager.IsVideoFile(i.FullName));
- }
- else
- {
- files = files.Where(i => _libraryManager.IsAudioFile(i.FullName));
- }
-
- var file = files
- .FirstOrDefault(i => i.Name.StartsWith(filenamePrefix, StringComparison.OrdinalIgnoreCase));
-
- if (file != null)
- {
- var cachedItem = _libraryManager.ResolvePath(file);
-
- if (cachedItem != null)
- {
- var hasMediaSources = _libraryManager.GetItemById(cachedItem.Id) as IHasMediaSources;
-
- if (hasMediaSources != null)
- {
- var source = hasMediaSources.GetMediaSources(true).FirstOrDefault();
-
- if (source != null)
- {
- return new[] { source };
- }
- }
- }
- }
- }
- catch (DirectoryNotFoundException)
- {
-
- }
-
- return new List<MediaSourceInfo>();
- }
-
- private MediaSourceInfo GetMediaSource(BaseItem item, ChannelMediaInfo info)
- {
- var source = info.ToMediaSource();
-
- source.RunTimeTicks = source.RunTimeTicks ?? item.RunTimeTicks;
-
- return source;
- }
-
- private IEnumerable<ChannelMediaInfo> SortMediaInfoResults(IEnumerable<ChannelMediaInfo> channelMediaSources)
- {
- var list = channelMediaSources.ToList();
-
- var options = _config.GetChannelsConfiguration();
-
- var width = options.PreferredStreamingWidth;
-
- if (width.HasValue)
- {
- var val = width.Value;
-
- var res = list
- .OrderBy(i => i.Width.HasValue && i.Width.Value <= val ? 0 : 1)
- .ThenBy(i => Math.Abs((i.Width ?? 0) - val))
- .ThenByDescending(i => i.Width ?? 0)
- .ThenBy(list.IndexOf)
- .ToList();
-
-
- return res;
- }
-
- return list
- .OrderByDescending(i => i.Width ?? 0)
- .ThenBy(list.IndexOf);
- }
-
- private async Task<Channel> GetChannel(IChannel channelInfo, CancellationToken cancellationToken)
- {
- var parentFolder = await GetInternalChannelFolder(cancellationToken).ConfigureAwait(false);
- var parentFolderId = parentFolder.Id;
-
- var id = GetInternalChannelId(channelInfo.Name);
- var idString = id.ToString("N");
-
- var path = Channel.GetInternalMetadataPath(_config.ApplicationPaths.InternalMetadataPath, id);
-
- var isNew = false;
- var forceUpdate = false;
-
- var item = _libraryManager.GetItemById(id) as Channel;
-
- if (item == null)
- {
- item = new Channel
- {
- Name = channelInfo.Name,
- Id = id,
- DateCreated = _fileSystem.GetCreationTimeUtc(path),
- DateModified = _fileSystem.GetLastWriteTimeUtc(path)
- };
-
- isNew = true;
- }
-
- if (!string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
- {
- isNew = true;
- }
- item.Path = path;
-
- if (!string.Equals(item.ChannelId, idString, StringComparison.OrdinalIgnoreCase))
- {
- forceUpdate = true;
- }
- item.ChannelId = idString;
-
- if (item.ParentId != parentFolderId)
- {
- forceUpdate = true;
- }
- item.ParentId = parentFolderId;
-
- item.OfficialRating = GetOfficialRating(channelInfo.ParentalRating);
- item.Overview = channelInfo.Description;
- item.HomePageUrl = channelInfo.HomePageUrl;
-
- if (string.IsNullOrWhiteSpace(item.Name))
- {
- item.Name = channelInfo.Name;
- }
-
- if (isNew)
- {
- await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
- }
- else if (forceUpdate)
- {
- await item.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
- }
-
- await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), cancellationToken);
- return item;
- }
-
- private string GetOfficialRating(ChannelParentalRating rating)
- {
- switch (rating)
- {
- case ChannelParentalRating.Adult:
- return "XXX";
- case ChannelParentalRating.UsR:
- return "R";
- case ChannelParentalRating.UsPG13:
- return "PG-13";
- case ChannelParentalRating.UsPG:
- return "PG";
- default:
- return null;
- }
- }
-
- public Channel GetChannel(string id)
- {
- return _libraryManager.GetItemById(id) as Channel;
- }
-
- public IEnumerable<ChannelFeatures> GetAllChannelFeatures()
- {
- return _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Channel).Name },
- SortBy = new[] { ItemSortBy.SortName }
-
- }).Select(i => GetChannelFeatures(i.Id.ToString("N")));
- }
-
- public ChannelFeatures GetChannelFeatures(string id)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- throw new ArgumentNullException("id");
- }
-
- var channel = GetChannel(id);
- var channelProvider = GetChannelProvider(channel);
-
- return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures());
- }
-
- public bool SupportsSync(string channelId)
- {
- if (string.IsNullOrWhiteSpace(channelId))
- {
- throw new ArgumentNullException("channelId");
- }
-
- //var channel = GetChannel(channelId);
- var channelProvider = GetChannelProvider(channelId);
-
- return channelProvider.GetChannelFeatures().SupportsContentDownloading;
- }
-
- public ChannelFeatures GetChannelFeaturesDto(Channel channel,
- IChannel provider,
- InternalChannelFeatures features)
- {
- var isIndexable = provider is IIndexableChannel;
- var supportsLatest = provider is ISupportsLatestMedia;
-
- return new ChannelFeatures
- {
- CanFilter = !features.MaxPageSize.HasValue,
- CanSearch = provider is ISearchableChannel,
- ContentTypes = features.ContentTypes,
- DefaultSortFields = features.DefaultSortFields,
- MaxPageSize = features.MaxPageSize,
- MediaTypes = features.MediaTypes,
- SupportsSortOrderToggle = features.SupportsSortOrderToggle,
- SupportsLatestMedia = supportsLatest,
- Name = channel.Name,
- Id = channel.Id.ToString("N"),
- SupportsContentDownloading = features.SupportsContentDownloading && (isIndexable || supportsLatest),
- AutoRefreshLevels = features.AutoRefreshLevels
- };
- }
-
- private Guid GetInternalChannelId(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
- return _libraryManager.GetNewItemId("Channel " + name, typeof(Channel));
- }
-
- public async Task<QueryResult<BaseItemDto>> GetLatestChannelItems(AllChannelMediaQuery query, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- var limit = query.Limit;
-
- // See below about parental control
- if (user != null)
- {
- query.StartIndex = null;
- query.Limit = null;
- }
-
- var internalResult = await GetLatestChannelItemsInternal(query, cancellationToken).ConfigureAwait(false);
-
- var items = internalResult.Items;
- var totalRecordCount = internalResult.TotalRecordCount;
-
- // Supporting parental control is a hack because it has to be done after querying the remote data source
- // This will get screwy if apps try to page, so limit to 10 results in an attempt to always keep them on the first page
- if (user != null)
- {
- items = items.Where(i => i.IsVisible(user))
- .Take(limit ?? 10)
- .ToArray();
-
- totalRecordCount = items.Length;
- }
-
- var dtoOptions = new DtoOptions();
-
- var returnItems = (await _dtoService.GetBaseItemDtos(items, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
-
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnItems,
- TotalRecordCount = totalRecordCount
- };
-
- return result;
- }
-
- public async Task<QueryResult<BaseItem>> GetLatestChannelItemsInternal(AllChannelMediaQuery query, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- if (!string.IsNullOrWhiteSpace(query.UserId) && user == null)
- {
- throw new ArgumentException("User not found.");
- }
-
- var channels = GetAllChannels();
-
- if (query.ChannelIds.Length > 0)
- {
- // Avoid implicitly captured closure
- var ids = query.ChannelIds;
- channels = channels
- .Where(i => ids.Contains(GetInternalChannelId(i.Name).ToString("N")))
- .ToArray();
- }
-
- // Avoid implicitly captured closure
- var userId = query.UserId;
-
- var tasks = channels
- .Select(async i =>
- {
- var indexable = i as ISupportsLatestMedia;
-
- if (indexable != null)
- {
- try
- {
- var result = await GetLatestItems(indexable, i, userId, cancellationToken).ConfigureAwait(false);
-
- var resultItems = result.ToList();
-
- return new Tuple<IChannel, ChannelItemResult>(i, new ChannelItemResult
- {
- Items = resultItems,
- TotalRecordCount = resultItems.Count
- });
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting all media from {0}", ex, i.Name);
- }
- }
- return new Tuple<IChannel, ChannelItemResult>(i, new ChannelItemResult());
- });
-
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
-
- var totalCount = results.Length;
-
- IEnumerable<Tuple<IChannel, ChannelItemInfo>> items = results
- .SelectMany(i => i.Item2.Items.Select(m => new Tuple<IChannel, ChannelItemInfo>(i.Item1, m)));
-
- if (query.ContentTypes.Length > 0)
- {
- // Avoid implicitly captured closure
- var contentTypes = query.ContentTypes;
-
- items = items.Where(i => contentTypes.Contains(i.Item2.ContentType));
- }
- if (query.ExtraTypes.Length > 0)
- {
- // Avoid implicitly captured closure
- var contentTypes = query.ExtraTypes;
-
- items = items.Where(i => contentTypes.Contains(i.Item2.ExtraType));
- }
-
- // Avoid implicitly captured closure
- var token = cancellationToken;
- var itemTasks = items.Select(i =>
- {
- var channelProvider = i.Item1;
- var internalChannelId = GetInternalChannelId(channelProvider.Name);
- return GetChannelItemEntity(i.Item2, channelProvider, internalChannelId, token);
- });
-
- var internalItems = await Task.WhenAll(itemTasks).ConfigureAwait(false);
-
- internalItems = ApplyFilters(internalItems, query.Filters, user).ToArray();
- RefreshIfNeeded(internalItems);
-
- if (query.StartIndex.HasValue)
- {
- internalItems = internalItems.Skip(query.StartIndex.Value).ToArray();
- }
- if (query.Limit.HasValue)
- {
- internalItems = internalItems.Take(query.Limit.Value).ToArray();
- }
-
- var returnItemArray = internalItems.ToArray();
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = totalCount,
- Items = returnItemArray
- };
- }
-
- private async Task<IEnumerable<ChannelItemInfo>> GetLatestItems(ISupportsLatestMedia indexable, IChannel channel, string userId, CancellationToken cancellationToken)
- {
- var cacheLength = CacheLength;
- var cachePath = GetChannelDataCachePath(channel, userId, "channelmanager-latest", null, false);
-
- try
- {
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
- {
- return _jsonSerializer.DeserializeFromFile<List<ChannelItemInfo>>(cachePath);
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (DirectoryNotFoundException)
- {
-
- }
-
- await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- try
- {
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
- {
- return _jsonSerializer.DeserializeFromFile<List<ChannelItemInfo>>(cachePath);
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (DirectoryNotFoundException)
- {
-
- }
-
- var result = await indexable.GetLatestMedia(new ChannelLatestMediaSearch
- {
- UserId = userId
-
- }, cancellationToken).ConfigureAwait(false);
-
- var resultItems = result.ToList();
-
- CacheResponse(resultItems, cachePath);
-
- return resultItems;
- }
- finally
- {
- _resourcePool.Release();
- }
- }
-
- public async Task<QueryResult<BaseItem>> GetAllMediaInternal(AllChannelMediaQuery query, CancellationToken cancellationToken)
- {
- var channels = GetAllChannels();
-
- if (query.ChannelIds.Length > 0)
- {
- // Avoid implicitly captured closure
- var ids = query.ChannelIds;
- channels = channels
- .Where(i => ids.Contains(GetInternalChannelId(i.Name).ToString("N")))
- .ToArray();
- }
-
- var tasks = channels
- .Select(async i =>
- {
- var indexable = i as IIndexableChannel;
-
- if (indexable != null)
- {
- try
- {
- var result = await GetAllItems(indexable, i, new InternalAllChannelMediaQuery
- {
- UserId = query.UserId,
- ContentTypes = query.ContentTypes,
- ExtraTypes = query.ExtraTypes,
- TrailerTypes = query.TrailerTypes
-
- }, cancellationToken).ConfigureAwait(false);
-
- return new Tuple<IChannel, ChannelItemResult>(i, result);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting all media from {0}", ex, i.Name);
- }
- }
- return new Tuple<IChannel, ChannelItemResult>(i, new ChannelItemResult());
- });
-
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
-
- var totalCount = results.Length;
-
- IEnumerable<Tuple<IChannel, ChannelItemInfo>> items = results
- .SelectMany(i => i.Item2.Items.Select(m => new Tuple<IChannel, ChannelItemInfo>(i.Item1, m)))
- .OrderBy(i => i.Item2.Name);
-
- if (query.StartIndex.HasValue)
- {
- items = items.Skip(query.StartIndex.Value);
- }
- if (query.Limit.HasValue)
- {
- items = items.Take(query.Limit.Value);
- }
-
- // Avoid implicitly captured closure
- var token = cancellationToken;
- var itemTasks = items.Select(i =>
- {
- var channelProvider = i.Item1;
- var internalChannelId = GetInternalChannelId(channelProvider.Name);
- return GetChannelItemEntity(i.Item2, channelProvider, internalChannelId, token);
- });
-
- var internalItems = await Task.WhenAll(itemTasks).ConfigureAwait(false);
-
- var returnItemArray = internalItems.ToArray();
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = totalCount,
- Items = returnItemArray
- };
- }
-
- public async Task<QueryResult<BaseItemDto>> GetAllMedia(AllChannelMediaQuery query, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- var internalResult = await GetAllMediaInternal(query, cancellationToken).ConfigureAwait(false);
-
- RefreshIfNeeded(internalResult.Items);
-
- var dtoOptions = new DtoOptions();
-
- var returnItems = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
-
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnItems,
- TotalRecordCount = internalResult.TotalRecordCount
- };
-
- return result;
- }
-
- private async Task<ChannelItemResult> GetAllItems(IIndexableChannel indexable, IChannel channel, InternalAllChannelMediaQuery query, CancellationToken cancellationToken)
- {
- var cacheLength = CacheLength;
- var folderId = _jsonSerializer.SerializeToString(query).GetMD5().ToString("N");
- var cachePath = GetChannelDataCachePath(channel, query.UserId, folderId, null, false);
-
- try
- {
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
- {
- return _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (DirectoryNotFoundException)
- {
-
- }
-
- await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- try
- {
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
- {
- return _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (DirectoryNotFoundException)
- {
-
- }
-
- var result = await indexable.GetAllMedia(query, cancellationToken).ConfigureAwait(false);
-
- CacheResponse(result, cachePath);
-
- return result;
- }
- finally
- {
- _resourcePool.Release();
- }
- }
-
- public async Task<QueryResult<BaseItem>> GetChannelItemsInternal(ChannelItemQuery query, IProgress<double> progress, CancellationToken cancellationToken)
- {
- // Get the internal channel entity
- var channel = GetChannel(query.ChannelId);
-
- // Find the corresponding channel provider plugin
- var channelProvider = GetChannelProvider(channel);
-
- var channelInfo = channelProvider.GetChannelFeatures();
-
- int? providerStartIndex = null;
- int? providerLimit = null;
-
- if (channelInfo.MaxPageSize.HasValue)
- {
- providerStartIndex = query.StartIndex;
-
- if (query.Limit.HasValue && query.Limit.Value > channelInfo.MaxPageSize.Value)
- {
- query.Limit = Math.Min(query.Limit.Value, channelInfo.MaxPageSize.Value);
- }
- providerLimit = query.Limit;
-
- // This will cause some providers to fail
- if (providerLimit == 0)
- {
- providerLimit = 1;
- }
- }
-
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- ChannelItemSortField? sortField = null;
- ChannelItemSortField parsedField;
- if (query.SortBy.Length == 1 &&
- Enum.TryParse(query.SortBy[0], true, out parsedField))
- {
- sortField = parsedField;
- }
-
- var sortDescending = query.SortOrder.HasValue && query.SortOrder.Value == SortOrder.Descending;
-
- var itemsResult = await GetChannelItems(channelProvider,
- user,
- query.FolderId,
- providerStartIndex,
- providerLimit,
- sortField,
- sortDescending,
- cancellationToken)
- .ConfigureAwait(false);
-
- var providerTotalRecordCount = providerLimit.HasValue ? itemsResult.TotalRecordCount : null;
-
- var tasks = itemsResult.Items.Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, cancellationToken));
-
- var internalItems = await Task.WhenAll(tasks).ConfigureAwait(false);
-
- if (user != null)
- {
- internalItems = internalItems.Where(i => i.IsVisible(user)).ToArray();
-
- if (providerTotalRecordCount.HasValue)
- {
- providerTotalRecordCount = providerTotalRecordCount.Value;
- }
- }
-
- return await GetReturnItems(internalItems, providerTotalRecordCount, user, query).ConfigureAwait(false);
- }
-
- public async Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrWhiteSpace(query.UserId)
- ? null
- : _userManager.GetUserById(query.UserId);
-
- var internalResult = await GetChannelItemsInternal(query, new Progress<double>(), cancellationToken).ConfigureAwait(false);
-
- var dtoOptions = new DtoOptions();
-
- var returnItems = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user).ConfigureAwait(false))
- .ToArray();
-
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnItems,
- TotalRecordCount = internalResult.TotalRecordCount
- };
-
- return result;
- }
-
- private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
- private async Task<ChannelItemResult> GetChannelItems(IChannel channel,
- User user,
- string folderId,
- int? startIndex,
- int? limit,
- ChannelItemSortField? sortField,
- bool sortDescending,
- CancellationToken cancellationToken)
- {
- var userId = user.Id.ToString("N");
-
- var cacheLength = CacheLength;
- var cachePath = GetChannelDataCachePath(channel, userId, folderId, sortField, sortDescending);
-
- try
- {
- if (!startIndex.HasValue && !limit.HasValue)
- {
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
- {
- return _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
- }
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (DirectoryNotFoundException)
- {
-
- }
-
- await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- try
- {
- if (!startIndex.HasValue && !limit.HasValue)
- {
- if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow)
- {
- return _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
- }
- }
- }
- catch (FileNotFoundException)
- {
-
- }
- catch (DirectoryNotFoundException)
- {
-
- }
-
- var query = new InternalChannelItemQuery
- {
- UserId = userId,
- StartIndex = startIndex,
- Limit = limit,
- SortBy = sortField,
- SortDescending = sortDescending
- };
-
- if (!string.IsNullOrWhiteSpace(folderId))
- {
- var categoryItem = _libraryManager.GetItemById(new Guid(folderId));
-
- query.FolderId = categoryItem.ExternalId;
- }
-
- var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
-
- if (!startIndex.HasValue && !limit.HasValue)
- {
- CacheResponse(result, cachePath);
- }
-
- return result;
- }
- finally
- {
- _resourcePool.Release();
- }
- }
-
- private void CacheResponse(object result, string path)
- {
- try
- {
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- _jsonSerializer.SerializeToFile(result, path);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error writing to channel cache file: {0}", ex, path);
- }
- }
-
- private string GetChannelDataCachePath(IChannel channel,
- string userId,
- string folderId,
- ChannelItemSortField? sortField,
- bool sortDescending)
- {
- var channelId = GetInternalChannelId(channel.Name).ToString("N");
-
- var userCacheKey = string.Empty;
-
- var hasCacheKey = channel as IHasCacheKey;
- if (hasCacheKey != null)
- {
- userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty;
- }
-
- var filename = string.IsNullOrWhiteSpace(folderId) ? "root" : folderId;
- filename += userCacheKey;
-
- var version = (channel.DataVersion ?? string.Empty).GetMD5().ToString("N");
-
- if (sortField.HasValue)
- {
- filename += "-sortField-" + sortField.Value;
- }
- if (sortDescending)
- {
- filename += "-sortDescending";
- }
-
- filename = filename.GetMD5().ToString("N");
-
- return Path.Combine(_config.ApplicationPaths.CachePath,
- "channels",
- channelId,
- version,
- filename + ".json");
- }
-
- private async Task<QueryResult<BaseItem>> GetReturnItems(IEnumerable<BaseItem> items,
- int? totalCountFromProvider,
- User user,
- ChannelItemQuery query)
- {
- items = ApplyFilters(items, query.Filters, user);
-
- items = _libraryManager.Sort(items, user, query.SortBy, query.SortOrder ?? SortOrder.Ascending);
-
- var all = items.ToList();
- var totalCount = totalCountFromProvider ?? all.Count;
-
- if (!totalCountFromProvider.HasValue)
- {
- if (query.StartIndex.HasValue)
- {
- all = all.Skip(query.StartIndex.Value).ToList();
- }
- if (query.Limit.HasValue)
- {
- all = all.Take(query.Limit.Value).ToList();
- }
- }
-
- var returnItemArray = all.ToArray();
- RefreshIfNeeded(returnItemArray);
-
- return new QueryResult<BaseItem>
- {
- Items = returnItemArray,
- TotalRecordCount = totalCount
- };
- }
-
- private string GetIdToHash(string externalId, string channelName)
- {
- // Increment this as needed to force new downloads
- // Incorporate Name because it's being used to convert channel entity to provider
- return externalId + (channelName ?? string.Empty) + "16";
- }
-
- private T GetItemById<T>(string idString, string channelName, string channnelDataVersion, out bool isNew)
- where T : BaseItem, new()
- {
- var id = GetIdToHash(idString, channelName).GetMBId(typeof(T));
-
- T item = null;
-
- try
- {
- item = _libraryManager.GetItemById(id) as T;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error retrieving channel item from database", ex);
- }
-
- if (item == null || !string.Equals(item.ExternalEtag, channnelDataVersion, StringComparison.Ordinal))
- {
- item = new T();
- isNew = true;
- }
- else
- {
- isNew = false;
- }
-
- item.ExternalEtag = channnelDataVersion;
- item.Id = id;
- return item;
- }
-
- private async Task<BaseItem> GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, CancellationToken cancellationToken)
- {
- BaseItem item;
- bool isNew;
- bool forceUpdate = false;
-
- if (info.Type == ChannelItemType.Folder)
- {
- if (info.FolderType == ChannelFolderType.MusicAlbum)
- {
- item = GetItemById<MusicAlbum>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- else if (info.FolderType == ChannelFolderType.MusicArtist)
- {
- item = GetItemById<MusicArtist>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- else if (info.FolderType == ChannelFolderType.PhotoAlbum)
- {
- item = GetItemById<PhotoAlbum>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- else if (info.FolderType == ChannelFolderType.Series)
- {
- item = GetItemById<Series>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- else if (info.FolderType == ChannelFolderType.Season)
- {
- item = GetItemById<Season>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- else
- {
- item = GetItemById<Folder>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- }
- else if (info.MediaType == ChannelMediaType.Audio)
- {
- if (info.ContentType == ChannelMediaContentType.Podcast)
- {
- item = GetItemById<AudioPodcast>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- else
- {
- item = GetItemById<Audio>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- }
- else
- {
- if (info.ContentType == ChannelMediaContentType.Episode)
- {
- item = GetItemById<Episode>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- else if (info.ContentType == ChannelMediaContentType.Movie)
- {
- item = GetItemById<Movie>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- else if (info.ContentType == ChannelMediaContentType.Trailer || info.ExtraType == ExtraType.Trailer)
- {
- item = GetItemById<Trailer>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- else
- {
- item = GetItemById<Video>(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew);
- }
- }
-
- item.RunTimeTicks = info.RunTimeTicks;
-
- if (isNew)
- {
- item.Name = info.Name;
- item.Genres = info.Genres;
- item.Studios = info.Studios;
- item.CommunityRating = info.CommunityRating;
- item.Overview = info.Overview;
- item.IndexNumber = info.IndexNumber;
- item.ParentIndexNumber = info.ParentIndexNumber;
- item.PremiereDate = info.PremiereDate;
- item.ProductionYear = info.ProductionYear;
- item.ProviderIds = info.ProviderIds;
- item.OfficialRating = info.OfficialRating;
- item.DateCreated = info.DateCreated ?? DateTime.UtcNow;
- item.Tags = info.Tags;
- item.HomePageUrl = info.HomePageUrl;
- }
- else if (info.Type == ChannelItemType.Folder && info.FolderType == ChannelFolderType.Container)
- {
- // At least update names of container folders
- if (item.Name != info.Name)
- {
- item.Name = info.Name;
- forceUpdate = true;
- }
- }
-
- var hasArtists = item as IHasArtist;
- if (hasArtists != null)
- {
- hasArtists.Artists = info.Artists;
- }
-
- var hasAlbumArtists = item as IHasAlbumArtist;
- if (hasAlbumArtists != null)
- {
- hasAlbumArtists.AlbumArtists = info.AlbumArtists;
- }
-
- var trailer = item as Trailer;
- if (trailer != null)
- {
- if (!info.TrailerTypes.SequenceEqual(trailer.TrailerTypes))
- {
- forceUpdate = true;
- }
- trailer.TrailerTypes = info.TrailerTypes;
- }
-
- item.ChannelId = internalChannelId.ToString("N");
-
- if (item.ParentId != internalChannelId)
- {
- forceUpdate = true;
- }
- item.ParentId = internalChannelId;
-
- if (!string.Equals(item.ExternalId, info.Id, StringComparison.OrdinalIgnoreCase))
- {
- forceUpdate = true;
- }
- item.ExternalId = info.Id;
-
- var channelAudioItem = item as Audio;
- if (channelAudioItem != null)
- {
- channelAudioItem.ExtraType = info.ExtraType;
-
- var mediaSource = info.MediaSources.FirstOrDefault();
- item.Path = mediaSource == null ? null : mediaSource.Path;
- }
-
- var channelVideoItem = item as Video;
- if (channelVideoItem != null)
- {
- channelVideoItem.ExtraType = info.ExtraType;
- channelVideoItem.ChannelMediaSources = info.MediaSources;
-
- var mediaSource = info.MediaSources.FirstOrDefault();
- item.Path = mediaSource == null ? null : mediaSource.Path;
- }
-
- if (!string.IsNullOrWhiteSpace(info.ImageUrl) && !item.HasImage(ImageType.Primary))
- {
- item.SetImagePath(ImageType.Primary, info.ImageUrl);
- }
-
- if (item.SourceType != SourceType.Channel)
- {
- item.SourceType = SourceType.Channel;
- forceUpdate = true;
- }
-
- if (isNew)
- {
- await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
-
- if (info.People != null && info.People.Count > 0)
- {
- await _libraryManager.UpdatePeople(item, info.People ?? new List<PersonInfo>()).ConfigureAwait(false);
- }
- }
- else if (forceUpdate)
- {
- await item.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
- }
-
- SaveMediaSources(item, info.MediaSources);
-
- return item;
- }
-
- private void RefreshIfNeeded(BaseItem[] programs)
- {
- foreach (var program in programs)
- {
- RefreshIfNeeded(program);
- }
- }
-
- private void RefreshIfNeeded(BaseItem program)
- {
- if (!_refreshedItems.ContainsKey(program.Id))
- {
- _refreshedItems.TryAdd(program.Id, true);
- _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem));
- }
-
- }
-
- internal IChannel GetChannelProvider(Channel channel)
- {
- if (channel == null)
- {
- throw new ArgumentNullException("channel");
- }
-
- var result = GetAllChannels()
- .FirstOrDefault(i => string.Equals(GetInternalChannelId(i.Name).ToString("N"), channel.ChannelId, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, channel.Name, StringComparison.OrdinalIgnoreCase));
-
- if (result == null)
- {
- throw new ResourceNotFoundException("No channel provider found for channel " + channel.Name);
- }
-
- return result;
- }
-
- internal IChannel GetChannelProvider(string internalChannelId)
- {
- if (internalChannelId == null)
- {
- throw new ArgumentNullException("internalChannelId");
- }
-
- var result = GetAllChannels()
- .FirstOrDefault(i => string.Equals(GetInternalChannelId(i.Name).ToString("N"), internalChannelId, StringComparison.OrdinalIgnoreCase));
-
- if (result == null)
- {
- throw new ResourceNotFoundException("No channel provider found for channel id " + internalChannelId);
- }
-
- return result;
- }
-
- private IEnumerable<BaseItem> ApplyFilters(IEnumerable<BaseItem> items, IEnumerable<ItemFilter> filters, User user)
- {
- foreach (var filter in filters.OrderByDescending(f => (int)f))
- {
- items = ApplyFilter(items, filter, user);
- }
-
- return items;
- }
-
- private IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> items, ItemFilter filter, User user)
- {
- // Avoid implicitly captured closure
- var currentUser = user;
-
- switch (filter)
- {
- case ItemFilter.IsFavoriteOrLikes:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- if (userdata == null)
- {
- return false;
- }
-
- var likes = userdata.Likes ?? false;
- var favorite = userdata.IsFavorite;
-
- return likes || favorite;
- });
-
- case ItemFilter.Likes:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
- });
-
- case ItemFilter.Dislikes:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
- });
-
- case ItemFilter.IsFavorite:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- return userdata != null && userdata.IsFavorite;
- });
-
- case ItemFilter.IsResumable:
- return items.Where(item =>
- {
- var userdata = _userDataManager.GetUserData(user, item);
-
- return userdata != null && userdata.PlaybackPositionTicks > 0;
- });
-
- case ItemFilter.IsPlayed:
- return items.Where(item => item.IsPlayed(currentUser));
-
- case ItemFilter.IsUnplayed:
- return items.Where(item => item.IsUnplayed(currentUser));
-
- case ItemFilter.IsFolder:
- return items.Where(item => item.IsFolder);
-
- case ItemFilter.IsNotFolder:
- return items.Where(item => !item.IsFolder);
- }
-
- return items;
- }
-
- public async Task<BaseItemDto> GetChannelFolder(string userId, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(userId) ? null : _userManager.GetUserById(userId);
-
- var folder = await GetInternalChannelFolder(cancellationToken).ConfigureAwait(false);
-
- return _dtoService.GetBaseItemDto(folder, new DtoOptions(), user);
- }
-
- public async Task<Folder> GetInternalChannelFolder(CancellationToken cancellationToken)
- {
- var name = _localization.GetLocalizedString("ViewTypeChannels");
-
- return await _libraryManager.GetNamedView(name, "channels", "zz_" + name, cancellationToken).ConfigureAwait(false);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
deleted file mode 100644
index afb842c1c..000000000
--- a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
+++ /dev/null
@@ -1,257 +0,0 @@
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Server.Implementations.Channels
-{
- public class ChannelPostScanTask
- {
- private readonly IChannelManager _channelManager;
- private readonly IUserManager _userManager;
- private readonly ILogger _logger;
- private readonly ILibraryManager _libraryManager;
-
- public ChannelPostScanTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager)
- {
- _channelManager = channelManager;
- _userManager = userManager;
- _logger = logger;
- _libraryManager = libraryManager;
- }
-
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var users = _userManager.Users
- .DistinctBy(GetUserDistinctValue)
- .Select(i => i.Id.ToString("N"))
- .ToList();
-
- var numComplete = 0;
-
- foreach (var user in users)
- {
- double percentPerUser = 1;
- percentPerUser /= users.Count;
- var startingPercent = numComplete * percentPerUser * 100;
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(startingPercent + percentPerUser * p));
-
- await DownloadContent(user, cancellationToken, innerProgress).ConfigureAwait(false);
-
- numComplete++;
- double percent = numComplete;
- percent /= users.Count;
- progress.Report(percent * 100);
- }
-
- await CleanDatabase(cancellationToken).ConfigureAwait(false);
-
- progress.Report(100);
- }
-
- public static string GetUserDistinctValue(User user)
- {
- var channels = user.Policy.EnabledChannels
- .OrderBy(i => i)
- .ToList();
-
- return string.Join("|", channels.ToArray());
- }
-
- private async Task DownloadContent(string user, CancellationToken cancellationToken, IProgress<double> progress)
- {
- var channels = await _channelManager.GetChannelsInternal(new ChannelQuery
- {
- UserId = user
-
- }, cancellationToken);
-
- var numComplete = 0;
- var numItems = channels.Items.Length;
-
- foreach (var channel in channels.Items)
- {
- var channelId = channel.Id.ToString("N");
-
- var features = _channelManager.GetChannelFeatures(channelId);
-
- const int currentRefreshLevel = 1;
- var maxRefreshLevel = features.AutoRefreshLevels ?? 0;
- maxRefreshLevel = Math.Max(maxRefreshLevel, 2);
-
- if (maxRefreshLevel > 0)
- {
- var innerProgress = new ActionableProgress<double>();
-
- var startingNumberComplete = numComplete;
- innerProgress.RegisterAction(p =>
- {
- double innerPercent = startingNumberComplete;
- innerPercent += p / 100;
- innerPercent /= numItems;
- progress.Report(innerPercent * 100);
- });
-
- try
- {
- await GetAllItems(user, channelId, null, currentRefreshLevel, maxRefreshLevel, innerProgress, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting channel content", ex);
- }
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= numItems;
- progress.Report(percent * 100);
- }
-
- progress.Report(100);
- }
-
- private async Task CleanDatabase(CancellationToken cancellationToken)
- {
- var installedChannelIds = ((ChannelManager)_channelManager).GetInstalledChannelIds();
-
- var databaseIds = _libraryManager.GetItemIds(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Channel).Name }
- });
-
- var invalidIds = databaseIds
- .Except(installedChannelIds)
- .ToList();
-
- foreach (var id in invalidIds)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- await CleanChannel(id, cancellationToken).ConfigureAwait(false);
- }
- }
-
- private async Task CleanChannel(Guid id, CancellationToken cancellationToken)
- {
- _logger.Info("Cleaning channel {0} from database", id);
-
- // Delete all channel items
- var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
- {
- ChannelIds = new[] { id.ToString("N") }
- });
-
- foreach (var deleteId in allIds)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- await DeleteItem(deleteId).ConfigureAwait(false);
- }
-
- // Finally, delete the channel itself
- await DeleteItem(id).ConfigureAwait(false);
- }
-
- private Task DeleteItem(Guid id)
- {
- var item = _libraryManager.GetItemById(id);
-
- if (item == null)
- {
- return Task.FromResult(true);
- }
-
- return _libraryManager.DeleteItem(item, new DeleteOptions
- {
- DeleteFileLocation = false
- });
- }
-
- private async Task GetAllItems(string user, string channelId, string folderId, int currentRefreshLevel, int maxRefreshLevel, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var folderItems = new List<string>();
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(p / 2));
-
- var result = await _channelManager.GetChannelItemsInternal(new ChannelItemQuery
- {
- ChannelId = channelId,
- UserId = user,
- FolderId = folderId
-
- }, innerProgress, cancellationToken);
-
- folderItems.AddRange(result.Items.Where(i => i.IsFolder).Select(i => i.Id.ToString("N")));
-
- var totalRetrieved = result.Items.Length;
- var totalCount = result.TotalRecordCount;
-
- while (totalRetrieved < totalCount)
- {
- result = await _channelManager.GetChannelItemsInternal(new ChannelItemQuery
- {
- ChannelId = channelId,
- UserId = user,
- StartIndex = totalRetrieved,
- FolderId = folderId
-
- }, new Progress<double>(), cancellationToken);
-
- folderItems.AddRange(result.Items.Where(i => i.IsFolder).Select(i => i.Id.ToString("N")));
-
- totalRetrieved += result.Items.Length;
- totalCount = result.TotalRecordCount;
- }
-
- progress.Report(50);
-
- if (currentRefreshLevel < maxRefreshLevel)
- {
- var numComplete = 0;
- var numItems = folderItems.Count;
-
- foreach (var folder in folderItems)
- {
- try
- {
- innerProgress = new ActionableProgress<double>();
-
- var startingNumberComplete = numComplete;
- innerProgress.RegisterAction(p =>
- {
- double innerPercent = startingNumberComplete;
- innerPercent += p / 100;
- innerPercent /= numItems;
- progress.Report(innerPercent * 50 + 50);
- });
-
- await GetAllItems(user, channelId, folder, currentRefreshLevel + 1, maxRefreshLevel, innerProgress, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting channel content", ex);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= numItems;
- progress.Report(percent * 50 + 50);
- }
- }
-
- progress.Report(100);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
deleted file mode 100644
index 8bcb3cda9..000000000
--- a/MediaBrowser.Server.Implementations/Channels/RefreshChannelsScheduledTask.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Channels
-{
- class RefreshChannelsScheduledTask : IScheduledTask
- {
- private readonly IChannelManager _channelManager;
- private readonly IUserManager _userManager;
- private readonly ILogger _logger;
- private readonly ILibraryManager _libraryManager;
-
- public RefreshChannelsScheduledTask(IChannelManager channelManager, IUserManager userManager, ILogger logger, ILibraryManager libraryManager)
- {
- _channelManager = channelManager;
- _userManager = userManager;
- _logger = logger;
- _libraryManager = libraryManager;
- }
-
- public string Name
- {
- get { return "Refresh Channels"; }
- }
-
- public string Description
- {
- get { return "Refreshes internet channel information."; }
- }
-
- public string Category
- {
- get { return "Internet Channels"; }
- }
-
- public async Task Execute(System.Threading.CancellationToken cancellationToken, IProgress<double> progress)
- {
- var manager = (ChannelManager)_channelManager;
-
- await manager.RefreshChannels(new Progress<double>(), cancellationToken).ConfigureAwait(false);
-
- await new ChannelPostScanTask(_channelManager, _userManager, _logger, _libraryManager).Run(progress, cancellationToken)
- .ConfigureAwait(false);
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
- };
- }
-
- public string Key
- {
- get { return "RefreshInternetChannels"; }
- }
-
- public bool IsHidden
- {
- get { return false; }
- }
-
- public bool IsEnabled
- {
- get { return true; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs b/MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs
deleted file mode 100644
index d70d4b9e6..000000000
--- a/MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Server.Implementations.Photos;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Server.Implementations.Collections
-{
- public class CollectionImageProvider : BaseDynamicImageProvider<BoxSet>
- {
- public CollectionImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
- {
- }
-
- protected override bool Supports(IHasImages item)
- {
- // Right now this is the only way to prevent this image from getting created ahead of internet image providers
- if (!item.IsLocked)
- {
- return false;
- }
-
- return base.Supports(item);
- }
-
- protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
- {
- var playlist = (BoxSet)item;
-
- var items = playlist.Children.Concat(playlist.GetLinkedChildren())
- .Select(i =>
- {
- var subItem = i;
-
- var episode = subItem as Episode;
-
- if (episode != null)
- {
- var series = episode.Series;
- if (series != null && series.HasImage(ImageType.Primary))
- {
- return series;
- }
- }
-
- if (subItem.HasImage(ImageType.Primary))
- {
- return subItem;
- }
-
- var parent = subItem.GetParent();
-
- if (parent != null && parent.HasImage(ImageType.Primary))
- {
- if (parent is MusicAlbum)
- {
- return parent;
- }
- }
-
- return null;
- })
- .Where(i => i != null)
- .DistinctBy(i => i.Id)
- .ToList();
-
- return Task.FromResult(GetFinalItems(items, 2));
- }
-
- protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
- {
- return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
deleted file mode 100644
index f074fd812..000000000
--- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
+++ /dev/null
@@ -1,298 +0,0 @@
-using MediaBrowser.Common.Events;
-using MediaBrowser.Controller.Collections;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Collections
-{
- public class CollectionManager : ICollectionManager
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
- private readonly ILibraryMonitor _iLibraryMonitor;
- private readonly ILogger _logger;
- private readonly IProviderManager _providerManager;
-
- public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
- public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
- public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
-
- public CollectionManager(ILibraryManager libraryManager, IFileSystem fileSystem, ILibraryMonitor iLibraryMonitor, ILogger logger, IProviderManager providerManager)
- {
- _libraryManager = libraryManager;
- _fileSystem = fileSystem;
- _iLibraryMonitor = iLibraryMonitor;
- _logger = logger;
- _providerManager = providerManager;
- }
-
- public Folder GetCollectionsFolder(string userId)
- {
- return _libraryManager.RootFolder.Children.OfType<ManualCollectionsFolder>()
- .FirstOrDefault() ?? _libraryManager.GetUserRootFolder().Children.OfType<ManualCollectionsFolder>()
- .FirstOrDefault();
- }
-
- public IEnumerable<BoxSet> GetCollections(User user)
- {
- var folder = GetCollectionsFolder(user.Id.ToString("N"));
- return folder == null ?
- new List<BoxSet>() :
- folder.GetChildren(user, true).OfType<BoxSet>();
- }
-
- public async Task<BoxSet> CreateCollection(CollectionCreationOptions options)
- {
- var name = options.Name;
-
- // Need to use the [boxset] suffix
- // If internet metadata is not found, or if xml saving is off there will be no collection.xml
- // This could cause it to get re-resolved as a plain folder
- var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
-
- var parentFolder = GetParentFolder(options.ParentId);
-
- if (parentFolder == null)
- {
- throw new ArgumentException();
- }
-
- var path = Path.Combine(parentFolder.Path, folderName);
-
- _iLibraryMonitor.ReportFileSystemChangeBeginning(path);
-
- try
- {
- _fileSystem.CreateDirectory(path);
-
- var collection = new BoxSet
- {
- Name = name,
- Path = path,
- IsLocked = options.IsLocked,
- ProviderIds = options.ProviderIds,
- Shares = options.UserIds.Select(i => new Share
- {
- UserId = i.ToString("N"),
- CanEdit = true
-
- }).ToList()
- };
-
- await parentFolder.AddChild(collection, CancellationToken.None).ConfigureAwait(false);
-
- if (options.ItemIdList.Count > 0)
- {
- await AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(_fileSystem)
- {
- // The initial adding of items is going to create a local metadata file
- // This will cause internet metadata to be skipped as a result
- MetadataRefreshMode = MetadataRefreshMode.FullRefresh
- });
- }
- else
- {
- _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem));
- }
-
- EventHelper.FireEventIfNotNull(CollectionCreated, this, new CollectionCreatedEventArgs
- {
- Collection = collection,
- Options = options
-
- }, _logger);
-
- return collection;
- }
- finally
- {
- // Refresh handled internally
- _iLibraryMonitor.ReportFileSystemChangeComplete(path, false);
- }
- }
-
- private Folder GetParentFolder(Guid? parentId)
- {
- if (parentId.HasValue)
- {
- if (parentId.Value == Guid.Empty)
- {
- throw new ArgumentNullException("parentId");
- }
-
- var folder = _libraryManager.GetItemById(parentId.Value) as Folder;
-
- // Find an actual physical folder
- if (folder is CollectionFolder)
- {
- var child = _libraryManager.RootFolder.Children.OfType<Folder>()
- .FirstOrDefault(i => folder.PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase));
-
- if (child != null)
- {
- return child;
- }
- }
- }
-
- return GetCollectionsFolder(string.Empty);
- }
-
- public Task AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
- {
- return AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(_fileSystem));
- }
-
- private async Task AddToCollection(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
- {
- var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
-
- if (collection == null)
- {
- throw new ArgumentException("No collection exists with the supplied Id");
- }
-
- var list = new List<LinkedChild>();
- var itemList = new List<BaseItem>();
- var currentLinkedChildren = collection.GetLinkedChildren().ToList();
-
- foreach (var itemId in ids)
- {
- var item = _libraryManager.GetItemById(itemId);
-
- if (item == null)
- {
- throw new ArgumentException("No item exists with the supplied Id");
- }
-
- itemList.Add(item);
-
- if (currentLinkedChildren.All(i => i.Id != itemId))
- {
- list.Add(LinkedChild.Create(item));
- }
- }
-
- if (list.Count > 0)
- {
- collection.LinkedChildren.AddRange(list);
-
- collection.UpdateRatingToContent();
-
- await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
-
- _providerManager.QueueRefresh(collection.Id, refreshOptions);
-
- if (fireEvent)
- {
- EventHelper.FireEventIfNotNull(ItemsAddedToCollection, this, new CollectionModifiedEventArgs
- {
- Collection = collection,
- ItemsChanged = itemList
-
- }, _logger);
- }
- }
- }
-
- public async Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
- {
- var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
-
- if (collection == null)
- {
- throw new ArgumentException("No collection exists with the supplied Id");
- }
-
- var list = new List<LinkedChild>();
- var itemList = new List<BaseItem>();
-
- foreach (var itemId in itemIds)
- {
- var childItem = _libraryManager.GetItemById(itemId);
-
- var child = collection.LinkedChildren.FirstOrDefault(i => (i.ItemId.HasValue && i.ItemId.Value == itemId) || (childItem != null && string.Equals(childItem.Path, i.Path, StringComparison.OrdinalIgnoreCase)));
-
- if (child == null)
- {
- throw new ArgumentException("No collection title exists with the supplied Id");
- }
-
- list.Add(child);
-
- if (childItem != null)
- {
- itemList.Add(childItem);
- }
- }
-
- foreach (var child in list)
- {
- collection.LinkedChildren.Remove(child);
- }
-
- collection.UpdateRatingToContent();
-
- await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
- _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(_fileSystem));
-
- EventHelper.FireEventIfNotNull(ItemsRemovedFromCollection, this, new CollectionModifiedEventArgs
- {
- Collection = collection,
- ItemsChanged = itemList
-
- }, _logger);
- }
-
- public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user)
- {
- var results = new Dictionary<Guid, BaseItem>();
-
- var allBoxsets = GetCollections(user).ToList();
-
- foreach (var item in items)
- {
- var grouping = item as ISupportsBoxSetGrouping;
-
- if (grouping == null)
- {
- results[item.Id] = item;
- }
- else
- {
- var itemId = item.Id;
-
- var currentBoxSets = allBoxsets
- .Where(i => i.GetLinkedChildren().Any(j => j.Id == itemId))
- .ToList();
-
- if (currentBoxSets.Count > 0)
- {
- foreach (var boxset in currentBoxSets)
- {
- results[boxset.Id] = boxset;
- }
- }
- else
- {
- results[item.Id] = item;
- }
- }
- }
-
- return results.Values;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
index 769b7821a..565eeb259 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
+++ b/MediaBrowser.Server.Implementations/Connect/ConnectEntryPoint.cs
@@ -14,13 +14,13 @@ using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
-using MediaBrowser.Server.Implementations.Threading;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Server.Implementations.Connect
{
public class ConnectEntryPoint : IServerEntryPoint
{
- private PeriodicTimer _timer;
+ private ITimer _timer;
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
@@ -29,8 +29,9 @@ namespace MediaBrowser.Server.Implementations.Connect
private readonly INetworkManager _networkManager;
private readonly IApplicationHost _appHost;
private readonly IFileSystem _fileSystem;
+ private readonly ITimerFactory _timerFactory;
- public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem)
+ public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory)
{
_httpClient = httpClient;
_appPaths = appPaths;
@@ -39,13 +40,14 @@ namespace MediaBrowser.Server.Implementations.Connect
_connectManager = connectManager;
_appHost = appHost;
_fileSystem = fileSystem;
+ _timerFactory = timerFactory;
}
public void Run()
{
LoadCachedAddress();
- _timer = new PeriodicTimer(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(1));
+ _timer = _timerFactory.Create(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(1));
((ConnectManager)_connectManager).Start();
}
diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs
deleted file mode 100644
index e2e59df08..000000000
--- a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs
+++ /dev/null
@@ -1,306 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
-
-namespace MediaBrowser.Server.Implementations.Devices
-{
- public class DeviceManager : IDeviceManager
- {
- private readonly IDeviceRepository _repo;
- private readonly IUserManager _userManager;
- private readonly IFileSystem _fileSystem;
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly INetworkManager _network;
-
- public event EventHandler<GenericEventArgs<CameraImageUploadInfo>> CameraImageUploaded;
-
- /// <summary>
- /// Occurs when [device options updated].
- /// </summary>
- public event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
-
- public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IServerConfigurationManager config, ILogger logger, INetworkManager network)
- {
- _repo = repo;
- _userManager = userManager;
- _fileSystem = fileSystem;
- _libraryMonitor = libraryMonitor;
- _config = config;
- _logger = logger;
- _network = network;
- }
-
- public async Task<DeviceInfo> RegisterDevice(string reportedId, string name, string appName, string appVersion, string usedByUserId)
- {
- if (string.IsNullOrWhiteSpace(reportedId))
- {
- throw new ArgumentNullException("reportedId");
- }
-
- var device = GetDevice(reportedId) ?? new DeviceInfo
- {
- Id = reportedId
- };
-
- device.ReportedName = name;
- device.AppName = appName;
- device.AppVersion = appVersion;
-
- if (!string.IsNullOrWhiteSpace(usedByUserId))
- {
- var user = _userManager.GetUserById(usedByUserId);
-
- device.LastUserId = user.Id.ToString("N");
- device.LastUserName = user.Name;
- }
-
- device.DateLastModified = DateTime.UtcNow;
-
- await _repo.SaveDevice(device).ConfigureAwait(false);
-
- return device;
- }
-
- public Task SaveCapabilities(string reportedId, ClientCapabilities capabilities)
- {
- return _repo.SaveCapabilities(reportedId, capabilities);
- }
-
- public ClientCapabilities GetCapabilities(string reportedId)
- {
- return _repo.GetCapabilities(reportedId);
- }
-
- public DeviceInfo GetDevice(string id)
- {
- return _repo.GetDevice(id);
- }
-
- public QueryResult<DeviceInfo> GetDevices(DeviceQuery query)
- {
- IEnumerable<DeviceInfo> devices = _repo.GetDevices().OrderByDescending(i => i.DateLastModified);
-
- if (query.SupportsContentUploading.HasValue)
- {
- var val = query.SupportsContentUploading.Value;
-
- devices = devices.Where(i => GetCapabilities(i.Id).SupportsContentUploading == val);
- }
-
- if (query.SupportsSync.HasValue)
- {
- var val = query.SupportsSync.Value;
-
- devices = devices.Where(i => GetCapabilities(i.Id).SupportsSync == val);
- }
-
- if (query.SupportsPersistentIdentifier.HasValue)
- {
- var val = query.SupportsPersistentIdentifier.Value;
-
- devices = devices.Where(i =>
- {
- var caps = GetCapabilities(i.Id);
- var deviceVal = caps.SupportsPersistentIdentifier;
- return deviceVal == val;
- });
- }
-
- if (!string.IsNullOrWhiteSpace(query.UserId))
- {
- devices = devices.Where(i => CanAccessDevice(query.UserId, i.Id));
- }
-
- var array = devices.ToArray();
- return new QueryResult<DeviceInfo>
- {
- Items = array,
- TotalRecordCount = array.Length
- };
- }
-
- public Task DeleteDevice(string id)
- {
- return _repo.DeleteDevice(id);
- }
-
- public ContentUploadHistory GetCameraUploadHistory(string deviceId)
- {
- return _repo.GetCameraUploadHistory(deviceId);
- }
-
- public async Task AcceptCameraUpload(string deviceId, Stream stream, LocalFileInfo file)
- {
- var device = GetDevice(deviceId);
- var path = GetUploadPath(device);
-
- if (!string.IsNullOrWhiteSpace(file.Album))
- {
- path = Path.Combine(path, _fileSystem.GetValidFilename(file.Album));
- }
-
- path = Path.Combine(path, file.Name);
- path = Path.ChangeExtension(path, MimeTypes.ToExtension(file.MimeType) ?? "jpg");
-
- _libraryMonitor.ReportFileSystemChangeBeginning(path);
-
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- try
- {
- using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
- {
- await stream.CopyToAsync(fs).ConfigureAwait(false);
- }
-
- _repo.AddCameraUpload(deviceId, file);
- }
- finally
- {
- _libraryMonitor.ReportFileSystemChangeComplete(path, true);
- }
-
- if (CameraImageUploaded != null)
- {
- EventHelper.FireEventIfNotNull(CameraImageUploaded, this, new GenericEventArgs<CameraImageUploadInfo>
- {
- Argument = new CameraImageUploadInfo
- {
- Device = device,
- FileInfo = file
- }
- }, _logger);
- }
- }
-
- private string GetUploadPath(DeviceInfo device)
- {
- if (!string.IsNullOrWhiteSpace(device.CameraUploadPath))
- {
- return device.CameraUploadPath;
- }
-
- var config = _config.GetUploadOptions();
- if (!string.IsNullOrWhiteSpace(config.CameraUploadPath))
- {
- return config.CameraUploadPath;
- }
-
- var path = DefaultCameraUploadsPath;
-
- if (config.EnableCameraUploadSubfolders)
- {
- path = Path.Combine(path, _fileSystem.GetValidFilename(device.Name));
- }
-
- return path;
- }
-
- private string DefaultCameraUploadsPath
- {
- get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); }
- }
-
- public async Task UpdateDeviceInfo(string id, DeviceOptions options)
- {
- var device = GetDevice(id);
-
- device.CustomName = options.CustomName;
- device.CameraUploadPath = options.CameraUploadPath;
-
- await _repo.SaveDevice(device).ConfigureAwait(false);
-
- EventHelper.FireEventIfNotNull(DeviceOptionsUpdated, this, new GenericEventArgs<DeviceInfo>(device), _logger);
- }
-
- public bool CanAccessDevice(string userId, string deviceId)
- {
- if (string.IsNullOrWhiteSpace(userId))
- {
- throw new ArgumentNullException("userId");
- }
- if (string.IsNullOrWhiteSpace(deviceId))
- {
- throw new ArgumentNullException("deviceId");
- }
-
- var user = _userManager.GetUserById(userId);
-
- if (user == null)
- {
- throw new ArgumentException("user not found");
- }
-
- if (!CanAccessDevice(user.Policy, deviceId))
- {
- var capabilities = GetCapabilities(deviceId);
-
- if (capabilities != null && capabilities.SupportsPersistentIdentifier)
- {
- return false;
- }
- }
-
- return true;
- }
-
- private bool CanAccessDevice(UserPolicy policy, string id)
- {
- if (policy.EnableAllDevices)
- {
- return true;
- }
-
- if (policy.IsAdministrator)
- {
- return true;
- }
-
- return ListHelper.ContainsIgnoreCase(policy.EnabledDevices, id);
- }
- }
-
- public class DevicesConfigStore : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- Key = "devices",
- ConfigurationType = typeof(DevicesOptions)
- }
- };
- }
- }
-
- public static class UploadConfigExtension
- {
- public static DevicesOptions GetUploadOptions(this IConfigurationManager config)
- {
- return config.GetConfiguration<DevicesOptions>("devices");
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
deleted file mode 100644
index a06656b21..000000000
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ /dev/null
@@ -1,1609 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Server.Implementations.Dto
-{
- public class DtoService : IDtoService
- {
- private readonly ILogger _logger;
- private readonly ILibraryManager _libraryManager;
- private readonly IUserDataManager _userDataRepository;
- private readonly IItemRepository _itemRepo;
-
- private readonly IImageProcessor _imageProcessor;
- private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
- private readonly IProviderManager _providerManager;
-
- private readonly Func<IChannelManager> _channelManagerFactory;
- private readonly ISyncManager _syncManager;
- private readonly IApplicationHost _appHost;
- private readonly Func<IDeviceManager> _deviceManager;
- private readonly Func<IMediaSourceManager> _mediaSourceManager;
- private readonly Func<ILiveTvManager> _livetvManager;
-
- public DtoService(ILogger logger, ILibraryManager libraryManager, IUserDataManager userDataRepository, IItemRepository itemRepo, IImageProcessor imageProcessor, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager, Func<IChannelManager> channelManagerFactory, ISyncManager syncManager, IApplicationHost appHost, Func<IDeviceManager> deviceManager, Func<IMediaSourceManager> mediaSourceManager, Func<ILiveTvManager> livetvManager)
- {
- _logger = logger;
- _libraryManager = libraryManager;
- _userDataRepository = userDataRepository;
- _itemRepo = itemRepo;
- _imageProcessor = imageProcessor;
- _config = config;
- _fileSystem = fileSystem;
- _providerManager = providerManager;
- _channelManagerFactory = channelManagerFactory;
- _syncManager = syncManager;
- _appHost = appHost;
- _deviceManager = deviceManager;
- _mediaSourceManager = mediaSourceManager;
- _livetvManager = livetvManager;
- }
-
- /// <summary>
- /// Converts a BaseItem to a DTOBaseItem
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="fields">The fields.</param>
- /// <param name="user">The user.</param>
- /// <param name="owner">The owner.</param>
- /// <returns>Task{DtoBaseItem}.</returns>
- /// <exception cref="System.ArgumentNullException">item</exception>
- public BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null)
- {
- var options = new DtoOptions
- {
- Fields = fields
- };
-
- return GetBaseItemDto(item, options, user, owner);
- }
-
- public async Task<List<BaseItemDto>> GetBaseItemDtos(IEnumerable<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
- {
- if (items == null)
- {
- throw new ArgumentNullException("items");
- }
-
- if (options == null)
- {
- throw new ArgumentNullException("options");
- }
-
- var syncDictionary = GetSyncedItemProgress(options);
-
- var list = new List<BaseItemDto>();
- var programTuples = new List<Tuple<BaseItem, BaseItemDto>>();
- var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>();
-
- foreach (var item in items)
- {
- var dto = await GetBaseItemDtoInternal(item, options, user, owner).ConfigureAwait(false);
-
- var tvChannel = item as LiveTvChannel;
- if (tvChannel != null)
- {
- channelTuples.Add(new Tuple<BaseItemDto, LiveTvChannel>(dto, tvChannel));
- }
- else if (item is LiveTvProgram)
- {
- programTuples.Add(new Tuple<BaseItem, BaseItemDto>(item, dto));
- }
-
- var byName = item as IItemByName;
-
- if (byName != null)
- {
- if (options.Fields.Contains(ItemFields.ItemCounts))
- {
- var libraryItems = byName.GetTaggedItems(new InternalItemsQuery(user)
- {
- Recursive = true
- });
-
- SetItemByNameInfo(item, dto, libraryItems.ToList(), user);
- }
- }
-
- FillSyncInfo(dto, item, options, user, syncDictionary);
-
- list.Add(dto);
- }
-
- if (programTuples.Count > 0)
- {
- await _livetvManager().AddInfoToProgramDto(programTuples, options.Fields, user).ConfigureAwait(false);
- }
-
- if (channelTuples.Count > 0)
- {
- _livetvManager().AddChannelInfo(channelTuples, options, user);
- }
-
- return list;
- }
-
- public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
- {
- var syncDictionary = GetSyncedItemProgress(options);
-
- var dto = GetBaseItemDtoInternal(item, options, user, owner).Result;
- var tvChannel = item as LiveTvChannel;
- if (tvChannel != null)
- {
- var list = new List<Tuple<BaseItemDto, LiveTvChannel>> { new Tuple<BaseItemDto, LiveTvChannel>(dto, tvChannel) };
- _livetvManager().AddChannelInfo(list, options, user);
- }
- else if (item is LiveTvProgram)
- {
- var list = new List<Tuple<BaseItem, BaseItemDto>> { new Tuple<BaseItem, BaseItemDto>(item, dto) };
- var task = _livetvManager().AddInfoToProgramDto(list, options.Fields, user);
- Task.WaitAll(task);
- }
-
- var byName = item as IItemByName;
-
- if (byName != null)
- {
- if (options.Fields.Contains(ItemFields.ItemCounts))
- {
- SetItemByNameInfo(item, dto, GetTaggedItems(byName, user), user);
- }
-
- FillSyncInfo(dto, item, options, user, syncDictionary);
- return dto;
- }
-
- FillSyncInfo(dto, item, options, user, syncDictionary);
-
- return dto;
- }
-
- private List<BaseItem> GetTaggedItems(IItemByName byName, User user)
- {
- var items = byName.GetTaggedItems(new InternalItemsQuery(user)
- {
- Recursive = true
-
- }).ToList();
-
- return items;
- }
-
- public Dictionary<string, SyncedItemProgress> GetSyncedItemProgress(DtoOptions options)
- {
- if (!options.Fields.Contains(ItemFields.BasicSyncInfo) &&
- !options.Fields.Contains(ItemFields.SyncInfo))
- {
- return new Dictionary<string, SyncedItemProgress>();
- }
-
- var deviceId = options.DeviceId;
- if (string.IsNullOrWhiteSpace(deviceId))
- {
- return new Dictionary<string, SyncedItemProgress>();
- }
-
- var caps = _deviceManager().GetCapabilities(deviceId);
- if (caps == null || !caps.SupportsSync)
- {
- return new Dictionary<string, SyncedItemProgress>();
- }
-
- return _syncManager.GetSyncedItemProgresses(new SyncJobItemQuery
- {
- TargetId = deviceId,
- Statuses = new[]
- {
- SyncJobItemStatus.Converting,
- SyncJobItemStatus.Queued,
- SyncJobItemStatus.Transferring,
- SyncJobItemStatus.ReadyToTransfer,
- SyncJobItemStatus.Synced
- }
- });
- }
-
- public void FillSyncInfo(IEnumerable<Tuple<BaseItem, BaseItemDto>> tuples, DtoOptions options, User user)
- {
- if (options.Fields.Contains(ItemFields.BasicSyncInfo) ||
- options.Fields.Contains(ItemFields.SyncInfo))
- {
- var syncProgress = GetSyncedItemProgress(options);
-
- foreach (var tuple in tuples)
- {
- var item = tuple.Item1;
-
- FillSyncInfo(tuple.Item2, item, options, user, syncProgress);
- }
- }
- }
-
- private void FillSyncInfo(IHasSyncInfo dto, BaseItem item, DtoOptions options, User user, Dictionary<string, SyncedItemProgress> syncProgress)
- {
- var hasFullSyncInfo = options.Fields.Contains(ItemFields.SyncInfo);
-
- if (!options.Fields.Contains(ItemFields.BasicSyncInfo) &&
- !hasFullSyncInfo)
- {
- return;
- }
-
- if (dto.SupportsSync ?? false)
- {
- SyncedItemProgress syncStatus;
- if (syncProgress.TryGetValue(dto.Id, out syncStatus))
- {
- if (syncStatus.Status == SyncJobItemStatus.Synced)
- {
- dto.SyncPercent = 100;
- }
- else
- {
- dto.SyncPercent = syncStatus.Progress;
- }
-
- if (hasFullSyncInfo)
- {
- dto.HasSyncJob = true;
- dto.SyncStatus = syncStatus.Status;
- }
- }
- }
- }
-
- private async Task<BaseItemDto> GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
- {
- var fields = options.Fields;
-
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- if (fields == null)
- {
- throw new ArgumentNullException("fields");
- }
-
- var dto = new BaseItemDto
- {
- ServerId = _appHost.SystemId
- };
-
- if (item.SourceType == SourceType.Channel)
- {
- dto.SourceType = item.SourceType.ToString();
- }
-
- if (fields.Contains(ItemFields.People))
- {
- AttachPeople(dto, item);
- }
-
- if (fields.Contains(ItemFields.PrimaryImageAspectRatio))
- {
- try
- {
- AttachPrimaryImageAspectRatio(dto, item);
- }
- catch (Exception ex)
- {
- // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions
- _logger.ErrorException("Error generating PrimaryImageAspectRatio for {0}", ex, item.Name);
- }
- }
-
- if (fields.Contains(ItemFields.DisplayPreferencesId))
- {
- dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N");
- }
-
- if (user != null)
- {
- await AttachUserSpecificInfo(dto, item, user, options).ConfigureAwait(false);
- }
-
- var hasMediaSources = item as IHasMediaSources;
- if (hasMediaSources != null)
- {
- if (fields.Contains(ItemFields.MediaSources))
- {
- if (user == null)
- {
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true).ToList();
- }
- else
- {
- dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user).ToList();
- }
- }
- }
-
- if (fields.Contains(ItemFields.Studios))
- {
- AttachStudios(dto, item);
- }
-
- AttachBasicFields(dto, item, owner, options);
-
- var collectionFolder = item as ICollectionFolder;
- if (collectionFolder != null)
- {
- dto.OriginalCollectionType = collectionFolder.CollectionType;
-
- dto.CollectionType = user == null ?
- collectionFolder.CollectionType :
- collectionFolder.GetViewType(user);
- }
-
- if (fields.Contains(ItemFields.CanDelete))
- {
- dto.CanDelete = user == null
- ? item.CanDelete()
- : item.CanDelete(user);
- }
-
- if (fields.Contains(ItemFields.CanDownload))
- {
- dto.CanDownload = user == null
- ? item.CanDownload()
- : item.CanDownload(user);
- }
-
- if (fields.Contains(ItemFields.Etag))
- {
- dto.Etag = item.GetEtag(user);
- }
-
- if (item is ILiveTvRecording)
- {
- _livetvManager().AddInfoToRecordingDto(item, dto, user);
- }
-
- return dto;
- }
-
- public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Dictionary<string, SyncedItemProgress> syncProgress, User user = null)
- {
- var dto = GetBaseItemDtoInternal(item, options, user).Result;
-
- if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts))
- {
- SetItemByNameInfo(item, dto, taggedItems, user);
- }
-
- FillSyncInfo(dto, item, options, user, syncProgress);
-
- return dto;
- }
-
- private void SetItemByNameInfo(BaseItem item, BaseItemDto dto, List<BaseItem> taggedItems, User user = null)
- {
- if (item is MusicArtist)
- {
- dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
- dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
- dto.SongCount = taggedItems.Count(i => i is Audio);
- }
- else if (item is MusicGenre)
- {
- dto.ArtistCount = taggedItems.Count(i => i is MusicArtist);
- dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
- dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
- dto.SongCount = taggedItems.Count(i => i is Audio);
- }
- else if (item is GameGenre)
- {
- dto.GameCount = taggedItems.Count(i => i is Game);
- }
- else
- {
- // This populates them all and covers Genre, Person, Studio, Year
-
- dto.ArtistCount = taggedItems.Count(i => i is MusicArtist);
- dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
- dto.EpisodeCount = taggedItems.Count(i => i is Episode);
- dto.GameCount = taggedItems.Count(i => i is Game);
- dto.MovieCount = taggedItems.Count(i => i is Movie);
- dto.TrailerCount = taggedItems.Count(i => i is Trailer);
- dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
- dto.SeriesCount = taggedItems.Count(i => i is Series);
- dto.ProgramCount = taggedItems.Count(i => i is LiveTvProgram);
- dto.SongCount = taggedItems.Count(i => i is Audio);
- }
-
- dto.ChildCount = taggedItems.Count;
- }
-
- /// <summary>
- /// Attaches the user specific info.
- /// </summary>
- private async Task AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions dtoOptions)
- {
- var fields = dtoOptions.Fields;
-
- if (item.IsFolder)
- {
- var folder = (Folder)item;
-
- if (dtoOptions.EnableUserData)
- {
- dto.UserData = await _userDataRepository.GetUserDataDto(item, dto, user).ConfigureAwait(false);
- }
-
- if (!dto.ChildCount.HasValue && item.SourceType == SourceType.Library)
- {
- dto.ChildCount = GetChildCount(folder, user);
- }
-
- if (fields.Contains(ItemFields.CumulativeRunTimeTicks))
- {
- dto.CumulativeRunTimeTicks = item.RunTimeTicks;
- }
-
- if (fields.Contains(ItemFields.DateLastMediaAdded))
- {
- dto.DateLastMediaAdded = folder.DateLastMediaAdded;
- }
- }
-
- else
- {
- if (dtoOptions.EnableUserData)
- {
- dto.UserData = _userDataRepository.GetUserDataDto(item, user).Result;
- }
- }
-
- dto.PlayAccess = item.GetPlayAccess(user);
-
- if (fields.Contains(ItemFields.BasicSyncInfo) || fields.Contains(ItemFields.SyncInfo))
- {
- var userCanSync = user != null && user.Policy.EnableSync;
- if (userCanSync && _syncManager.SupportsSync(item))
- {
- dto.SupportsSync = true;
- }
- }
-
- if (fields.Contains(ItemFields.SeasonUserData))
- {
- var episode = item as Episode;
-
- if (episode != null)
- {
- var season = episode.Season;
-
- if (season != null)
- {
- dto.SeasonUserData = await _userDataRepository.GetUserDataDto(season, user).ConfigureAwait(false);
- }
- }
- }
-
- var userView = item as UserView;
- if (userView != null)
- {
- dto.HasDynamicCategories = userView.ContainsDynamicCategories(user);
- }
-
- var collectionFolder = item as ICollectionFolder;
- if (collectionFolder != null)
- {
- dto.HasDynamicCategories = false;
- }
- }
-
- private int GetChildCount(Folder folder, User user)
- {
- // Right now this is too slow to calculate for top level folders on a per-user basis
- // Just return something so that apps that are expecting a value won't think the folders are empty
- if (folder is ICollectionFolder || folder is UserView)
- {
- return new Random().Next(1, 10);
- }
-
- return folder.GetChildCount(user);
- }
-
- /// <summary>
- /// Gets client-side Id of a server-side BaseItem
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="System.ArgumentNullException">item</exception>
- public string GetDtoId(BaseItem item)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- return item.Id.ToString("N");
- }
-
- /// <summary>
- /// Converts a UserItemData to a DTOUserItemData
- /// </summary>
- /// <param name="data">The data.</param>
- /// <returns>DtoUserItemData.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public UserItemDataDto GetUserItemDataDto(UserItemData data)
- {
- if (data == null)
- {
- throw new ArgumentNullException("data");
- }
-
- return new UserItemDataDto
- {
- IsFavorite = data.IsFavorite,
- Likes = data.Likes,
- PlaybackPositionTicks = data.PlaybackPositionTicks,
- PlayCount = data.PlayCount,
- Rating = data.Rating,
- Played = data.Played,
- LastPlayedDate = data.LastPlayedDate,
- Key = data.Key
- };
- }
- private void SetBookProperties(BaseItemDto dto, Book item)
- {
- dto.SeriesName = item.SeriesName;
- }
- private void SetPhotoProperties(BaseItemDto dto, Photo item)
- {
- dto.Width = item.Width;
- dto.Height = item.Height;
- dto.CameraMake = item.CameraMake;
- dto.CameraModel = item.CameraModel;
- dto.Software = item.Software;
- dto.ExposureTime = item.ExposureTime;
- dto.FocalLength = item.FocalLength;
- dto.ImageOrientation = item.Orientation;
- dto.Aperture = item.Aperture;
- dto.ShutterSpeed = item.ShutterSpeed;
-
- dto.Latitude = item.Latitude;
- dto.Longitude = item.Longitude;
- dto.Altitude = item.Altitude;
- dto.IsoSpeedRating = item.IsoSpeedRating;
-
- var album = item.AlbumEntity;
-
- if (album != null)
- {
- dto.Album = album.Name;
- dto.AlbumId = album.Id.ToString("N");
- }
- }
-
- private void SetMusicVideoProperties(BaseItemDto dto, MusicVideo item)
- {
- if (!string.IsNullOrEmpty(item.Album))
- {
- var parentAlbum = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
- Name = item.Album
-
- }).FirstOrDefault();
-
- if (parentAlbum != null)
- {
- dto.AlbumId = GetDtoId(parentAlbum);
- }
- }
-
- dto.Album = item.Album;
- }
-
- private void SetGameProperties(BaseItemDto dto, Game item)
- {
- dto.Players = item.PlayersSupported;
- dto.GameSystem = item.GameSystem;
- dto.MultiPartGameFiles = item.MultiPartGameFiles;
- }
-
- private void SetGameSystemProperties(BaseItemDto dto, GameSystem item)
- {
- dto.GameSystem = item.GameSystemName;
- }
-
- private List<string> GetImageTags(BaseItem item, List<ItemImageInfo> images)
- {
- return images
- .Select(p => GetImageCacheTag(item, p))
- .Where(i => i != null)
- .ToList();
- }
-
- private string GetImageCacheTag(BaseItem item, ImageType type)
- {
- try
- {
- return _imageProcessor.GetImageCacheTag(item, type);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0} image info", ex, type);
- return null;
- }
- }
-
- private string GetImageCacheTag(BaseItem item, ItemImageInfo image)
- {
- try
- {
- return _imageProcessor.GetImageCacheTag(item, image);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0} image info for {1}", ex, image.Type, image.Path);
- return null;
- }
- }
-
- /// <summary>
- /// Attaches People DTO's to a DTOBaseItem
- /// </summary>
- /// <param name="dto">The dto.</param>
- /// <param name="item">The item.</param>
- /// <returns>Task.</returns>
- private void AttachPeople(BaseItemDto dto, BaseItem item)
- {
- // Ordering by person type to ensure actors and artists are at the front.
- // This is taking advantage of the fact that they both begin with A
- // This should be improved in the future
- var people = _libraryManager.GetPeople(item).OrderBy(i => i.SortOrder ?? int.MaxValue)
- .ThenBy(i =>
- {
- if (i.IsType(PersonType.Actor))
- {
- return 0;
- }
- if (i.IsType(PersonType.GuestStar))
- {
- return 1;
- }
- if (i.IsType(PersonType.Director))
- {
- return 2;
- }
- if (i.IsType(PersonType.Writer))
- {
- return 3;
- }
- if (i.IsType(PersonType.Producer))
- {
- return 4;
- }
- if (i.IsType(PersonType.Composer))
- {
- return 4;
- }
-
- return 10;
- })
- .ToList();
-
- var list = new List<BaseItemPerson>();
-
- var dictionary = people.Select(p => p.Name)
- .Distinct(StringComparer.OrdinalIgnoreCase).Select(c =>
- {
- try
- {
- return _libraryManager.GetPerson(c);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting person {0}", ex, c);
- return null;
- }
-
- }).Where(i => i != null)
- .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
- .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
-
- for (var i = 0; i < people.Count; i++)
- {
- var person = people[i];
-
- var baseItemPerson = new BaseItemPerson
- {
- Name = person.Name,
- Role = person.Role,
- Type = person.Type
- };
-
- Person entity;
-
- if (dictionary.TryGetValue(person.Name, out entity))
- {
- baseItemPerson.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary);
- baseItemPerson.Id = entity.Id.ToString("N");
- list.Add(baseItemPerson);
- }
- }
-
- dto.People = list.ToArray();
- }
-
- /// <summary>
- /// Attaches the studios.
- /// </summary>
- /// <param name="dto">The dto.</param>
- /// <param name="item">The item.</param>
- /// <returns>Task.</returns>
- private void AttachStudios(BaseItemDto dto, BaseItem item)
- {
- var studios = item.Studios.ToList();
-
- dto.Studios = new StudioDto[studios.Count];
-
- var dictionary = studios.Distinct(StringComparer.OrdinalIgnoreCase).Select(name =>
- {
- try
- {
- return _libraryManager.GetStudio(name);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error getting studio {0}", ex, name);
- return null;
- }
- })
- .Where(i => i != null)
- .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
- .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
-
- for (var i = 0; i < studios.Count; i++)
- {
- var studio = studios[i];
-
- var studioDto = new StudioDto
- {
- Name = studio
- };
-
- Studio entity;
-
- if (dictionary.TryGetValue(studio, out entity))
- {
- studioDto.Id = entity.Id.ToString("N");
- studioDto.PrimaryImageTag = GetImageCacheTag(entity, ImageType.Primary);
- }
-
- dto.Studios[i] = studioDto;
- }
- }
-
- /// <summary>
- /// Gets the chapter info dto.
- /// </summary>
- /// <param name="chapterInfo">The chapter info.</param>
- /// <param name="item">The item.</param>
- /// <returns>ChapterInfoDto.</returns>
- private ChapterInfoDto GetChapterInfoDto(ChapterInfo chapterInfo, BaseItem item)
- {
- var dto = new ChapterInfoDto
- {
- Name = chapterInfo.Name,
- StartPositionTicks = chapterInfo.StartPositionTicks
- };
-
- if (!string.IsNullOrEmpty(chapterInfo.ImagePath))
- {
- dto.ImageTag = GetImageCacheTag(item, new ItemImageInfo
- {
- Path = chapterInfo.ImagePath,
- Type = ImageType.Chapter,
- DateModified = chapterInfo.ImageDateModified
- });
- }
-
- return dto;
- }
-
- public List<ChapterInfoDto> GetChapterInfoDtos(BaseItem item)
- {
- return _itemRepo.GetChapters(item.Id)
- .Select(c => GetChapterInfoDto(c, item))
- .ToList();
- }
-
- /// <summary>
- /// Sets simple property values on a DTOBaseItem
- /// </summary>
- /// <param name="dto">The dto.</param>
- /// <param name="item">The item.</param>
- /// <param name="owner">The owner.</param>
- /// <param name="options">The options.</param>
- private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem owner, DtoOptions options)
- {
- var fields = options.Fields;
-
- if (fields.Contains(ItemFields.DateCreated))
- {
- dto.DateCreated = item.DateCreated;
- }
-
- if (fields.Contains(ItemFields.DisplayMediaType))
- {
- dto.DisplayMediaType = item.DisplayMediaType;
- }
-
- if (fields.Contains(ItemFields.Settings))
- {
- dto.LockedFields = item.LockedFields;
- dto.LockData = item.IsLocked;
- dto.ForcedSortName = item.ForcedSortName;
- }
- dto.Container = item.Container;
-
- var hasBudget = item as IHasBudget;
- if (hasBudget != null)
- {
- if (fields.Contains(ItemFields.Budget))
- {
- dto.Budget = hasBudget.Budget;
- }
-
- if (fields.Contains(ItemFields.Revenue))
- {
- dto.Revenue = hasBudget.Revenue;
- }
- }
-
- dto.EndDate = item.EndDate;
-
- if (fields.Contains(ItemFields.HomePageUrl))
- {
- dto.HomePageUrl = item.HomePageUrl;
- }
-
- if (fields.Contains(ItemFields.ExternalUrls))
- {
- dto.ExternalUrls = _providerManager.GetExternalUrls(item).ToArray();
- }
-
- if (fields.Contains(ItemFields.Tags))
- {
- dto.Tags = item.Tags;
- }
-
- if (fields.Contains(ItemFields.Keywords))
- {
- dto.Keywords = item.Keywords;
- }
-
- var hasAspectRatio = item as IHasAspectRatio;
- if (hasAspectRatio != null)
- {
- dto.AspectRatio = hasAspectRatio.AspectRatio;
- }
-
- if (fields.Contains(ItemFields.Metascore))
- {
- var hasMetascore = item as IHasMetascore;
- if (hasMetascore != null)
- {
- dto.Metascore = hasMetascore.Metascore;
- }
- }
-
- if (fields.Contains(ItemFields.AwardSummary))
- {
- var hasAwards = item as IHasAwards;
- if (hasAwards != null)
- {
- dto.AwardSummary = hasAwards.AwardSummary;
- }
- }
-
- var backdropLimit = options.GetImageLimit(ImageType.Backdrop);
- if (backdropLimit > 0)
- {
- dto.BackdropImageTags = GetImageTags(item, item.GetImages(ImageType.Backdrop).Take(backdropLimit).ToList());
- }
-
- if (fields.Contains(ItemFields.ScreenshotImageTags))
- {
- var screenshotLimit = options.GetImageLimit(ImageType.Screenshot);
- if (screenshotLimit > 0)
- {
- dto.ScreenshotImageTags = GetImageTags(item, item.GetImages(ImageType.Screenshot).Take(screenshotLimit).ToList());
- }
- }
-
- if (fields.Contains(ItemFields.Genres))
- {
- dto.Genres = item.Genres;
- }
-
- if (options.EnableImages)
- {
- dto.ImageTags = new Dictionary<ImageType, string>();
-
- // Prevent implicitly captured closure
- var currentItem = item;
- foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type))
- .ToList())
- {
- if (options.GetImageLimit(image.Type) > 0)
- {
- var tag = GetImageCacheTag(item, image);
-
- if (tag != null)
- {
- dto.ImageTags[image.Type] = tag;
- }
- }
- }
- }
-
- dto.Id = GetDtoId(item);
- dto.IndexNumber = item.IndexNumber;
- dto.ParentIndexNumber = item.ParentIndexNumber;
-
- if (item.IsFolder)
- {
- dto.IsFolder = true;
- }
- else if (item is IHasMediaSources)
- {
- dto.IsFolder = false;
- }
-
- dto.MediaType = item.MediaType;
- dto.LocationType = item.LocationType;
- if (item.IsHD.HasValue && item.IsHD.Value)
- {
- dto.IsHD = item.IsHD;
- }
- dto.Audio = item.Audio;
-
- if (fields.Contains(ItemFields.Settings))
- {
- dto.PreferredMetadataCountryCode = item.PreferredMetadataCountryCode;
- dto.PreferredMetadataLanguage = item.PreferredMetadataLanguage;
- }
-
- dto.CriticRating = item.CriticRating;
-
- if (fields.Contains(ItemFields.CriticRatingSummary))
- {
- dto.CriticRatingSummary = item.CriticRatingSummary;
- }
-
- var hasTrailers = item as IHasTrailers;
- if (hasTrailers != null)
- {
- dto.LocalTrailerCount = hasTrailers.GetTrailerIds().Count;
- }
-
- var hasDisplayOrder = item as IHasDisplayOrder;
- if (hasDisplayOrder != null)
- {
- dto.DisplayOrder = hasDisplayOrder.DisplayOrder;
- }
-
- var userView = item as UserView;
- if (userView != null)
- {
- dto.CollectionType = userView.ViewType;
- }
-
- if (fields.Contains(ItemFields.RemoteTrailers))
- {
- dto.RemoteTrailers = hasTrailers != null ?
- hasTrailers.RemoteTrailers :
- new List<MediaUrl>();
- }
-
- dto.Name = item.Name;
- dto.OfficialRating = item.OfficialRating;
-
- if (fields.Contains(ItemFields.Overview))
- {
- dto.Overview = item.Overview;
- }
-
- if (fields.Contains(ItemFields.OriginalTitle))
- {
- dto.OriginalTitle = item.OriginalTitle;
- }
-
- if (fields.Contains(ItemFields.ShortOverview))
- {
- dto.ShortOverview = item.ShortOverview;
- }
-
- if (fields.Contains(ItemFields.ParentId))
- {
- var displayParentId = item.DisplayParentId;
- if (displayParentId.HasValue)
- {
- dto.ParentId = displayParentId.Value.ToString("N");
- }
- }
-
- AddInheritedImages(dto, item, options, owner);
-
- if (fields.Contains(ItemFields.Path))
- {
- dto.Path = GetMappedPath(item);
- }
-
- dto.PremiereDate = item.PremiereDate;
- dto.ProductionYear = item.ProductionYear;
-
- if (fields.Contains(ItemFields.ProviderIds))
- {
- dto.ProviderIds = item.ProviderIds;
- }
-
- dto.RunTimeTicks = item.RunTimeTicks;
-
- if (fields.Contains(ItemFields.SortName))
- {
- dto.SortName = item.SortName;
- }
-
- if (fields.Contains(ItemFields.CustomRating))
- {
- dto.CustomRating = item.CustomRating;
- }
-
- if (fields.Contains(ItemFields.Taglines))
- {
- if (!string.IsNullOrWhiteSpace(item.Tagline))
- {
- dto.Taglines = new List<string> { item.Tagline };
- }
-
- if (dto.Taglines == null)
- {
- dto.Taglines = new List<string>();
- }
- }
-
- dto.Type = item.GetClientTypeName();
- dto.CommunityRating = item.CommunityRating;
-
- if (fields.Contains(ItemFields.VoteCount))
- {
- dto.VoteCount = item.VoteCount;
- }
-
- //if (item.IsFolder)
- //{
- // var folder = (Folder)item;
-
- // if (fields.Contains(ItemFields.IndexOptions))
- // {
- // dto.IndexOptions = folder.IndexByOptionStrings.ToArray();
- // }
- //}
-
- var supportsPlaceHolders = item as ISupportsPlaceHolders;
- if (supportsPlaceHolders != null)
- {
- dto.IsPlaceHolder = supportsPlaceHolders.IsPlaceHolder;
- }
-
- // Add audio info
- var audio = item as Audio;
- if (audio != null)
- {
- dto.Album = audio.Album;
- dto.ExtraType = audio.ExtraType;
-
- var albumParent = audio.AlbumEntity;
-
- if (albumParent != null)
- {
- dto.AlbumId = GetDtoId(albumParent);
-
- dto.AlbumPrimaryImageTag = GetImageCacheTag(albumParent, ImageType.Primary);
- }
-
- //if (fields.Contains(ItemFields.MediaSourceCount))
- //{
- // Songs always have one
- //}
- }
-
- var hasArtist = item as IHasArtist;
- if (hasArtist != null)
- {
- dto.Artists = hasArtist.Artists;
-
- var artistItems = _libraryManager.GetArtists(new InternalItemsQuery
- {
- EnableTotalRecordCount = false,
- ItemIds = new[] { item.Id.ToString("N") }
- });
-
- dto.ArtistItems = artistItems.Items
- .Select(i =>
- {
- var artist = i.Item1;
- return new NameIdPair
- {
- Name = artist.Name,
- Id = artist.Id.ToString("N")
- };
- })
- .ToList();
-
- // Include artists that are not in the database yet, e.g., just added via metadata editor
- var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
- dto.ArtistItems.AddRange(hasArtist.Artists
- .Except(foundArtists, new DistinctNameComparer())
- .Select(i =>
- {
- // This should not be necessary but we're seeing some cases of it
- if (string.IsNullOrWhiteSpace(i))
- {
- return null;
- }
-
- var artist = _libraryManager.GetArtist(i);
- if (artist != null)
- {
- return new NameIdPair
- {
- Name = artist.Name,
- Id = artist.Id.ToString("N")
- };
- }
-
- return null;
-
- }).Where(i => i != null));
- }
-
- var hasAlbumArtist = item as IHasAlbumArtist;
- if (hasAlbumArtist != null)
- {
- dto.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
-
- var artistItems = _libraryManager.GetAlbumArtists(new InternalItemsQuery
- {
- EnableTotalRecordCount = false,
- ItemIds = new[] { item.Id.ToString("N") }
- });
-
- dto.AlbumArtists = artistItems.Items
- .Select(i =>
- {
- var artist = i.Item1;
- return new NameIdPair
- {
- Name = artist.Name,
- Id = artist.Id.ToString("N")
- };
- })
- .ToList();
- }
-
- // Add video info
- var video = item as Video;
- if (video != null)
- {
- dto.VideoType = video.VideoType;
- dto.Video3DFormat = video.Video3DFormat;
- dto.IsoType = video.IsoType;
-
- if (video.HasSubtitles)
- {
- dto.HasSubtitles = video.HasSubtitles;
- }
-
- if (video.AdditionalParts.Count != 0)
- {
- dto.PartCount = video.AdditionalParts.Count + 1;
- }
-
- if (fields.Contains(ItemFields.MediaSourceCount))
- {
- var mediaSourceCount = video.MediaSourceCount;
- if (mediaSourceCount != 1)
- {
- dto.MediaSourceCount = mediaSourceCount;
- }
- }
-
- if (fields.Contains(ItemFields.Chapters))
- {
- dto.Chapters = GetChapterInfoDtos(item);
- }
-
- dto.ExtraType = video.ExtraType;
- }
-
- if (fields.Contains(ItemFields.MediaStreams))
- {
- // Add VideoInfo
- var iHasMediaSources = item as IHasMediaSources;
-
- if (iHasMediaSources != null)
- {
- List<MediaStream> mediaStreams;
-
- if (dto.MediaSources != null && dto.MediaSources.Count > 0)
- {
- mediaStreams = dto.MediaSources.Where(i => new Guid(i.Id) == item.Id)
- .SelectMany(i => i.MediaStreams)
- .ToList();
- }
- else
- {
- mediaStreams = _mediaSourceManager().GetStaticMediaSources(iHasMediaSources, true).First().MediaStreams;
- }
-
- dto.MediaStreams = mediaStreams;
- }
- }
-
- var hasSpecialFeatures = item as IHasSpecialFeatures;
- if (hasSpecialFeatures != null)
- {
- var specialFeatureCount = hasSpecialFeatures.SpecialFeatureIds.Count;
-
- if (specialFeatureCount > 0)
- {
- dto.SpecialFeatureCount = specialFeatureCount;
- }
- }
-
- // Add EpisodeInfo
- var episode = item as Episode;
- if (episode != null)
- {
- dto.IndexNumberEnd = episode.IndexNumberEnd;
- dto.SeriesName = episode.SeriesName;
-
- if (fields.Contains(ItemFields.AlternateEpisodeNumbers))
- {
- dto.DvdSeasonNumber = episode.DvdSeasonNumber;
- dto.DvdEpisodeNumber = episode.DvdEpisodeNumber;
- dto.AbsoluteEpisodeNumber = episode.AbsoluteEpisodeNumber;
- }
-
- if (fields.Contains(ItemFields.SpecialEpisodeNumbers))
- {
- dto.AirsAfterSeasonNumber = episode.AirsAfterSeasonNumber;
- dto.AirsBeforeEpisodeNumber = episode.AirsBeforeEpisodeNumber;
- dto.AirsBeforeSeasonNumber = episode.AirsBeforeSeasonNumber;
- }
-
- var seasonId = episode.SeasonId;
- if (seasonId.HasValue)
- {
- dto.SeasonId = seasonId.Value.ToString("N");
- }
-
- dto.SeasonName = episode.SeasonName;
-
- var seriesId = episode.SeriesId;
- if (seriesId.HasValue)
- {
- dto.SeriesId = seriesId.Value.ToString("N");
- }
-
- Series episodeSeries = null;
-
- if (fields.Contains(ItemFields.SeriesGenres))
- {
- episodeSeries = episodeSeries ?? episode.Series;
- if (episodeSeries != null)
- {
- dto.SeriesGenres = episodeSeries.Genres.ToList();
- }
- }
-
- //if (fields.Contains(ItemFields.SeriesPrimaryImage))
- {
- episodeSeries = episodeSeries ?? episode.Series;
- if (episodeSeries != null)
- {
- dto.SeriesPrimaryImageTag = GetImageCacheTag(episodeSeries, ImageType.Primary);
- }
- }
-
- if (fields.Contains(ItemFields.SeriesStudio))
- {
- episodeSeries = episodeSeries ?? episode.Series;
- if (episodeSeries != null)
- {
- dto.SeriesStudio = episodeSeries.Studios.FirstOrDefault();
- }
- }
- }
-
- // Add SeriesInfo
- var series = item as Series;
- if (series != null)
- {
- dto.AirDays = series.AirDays;
- dto.AirTime = series.AirTime;
- dto.SeriesStatus = series.Status;
-
- dto.AnimeSeriesIndex = series.AnimeSeriesIndex;
- }
-
- // Add SeasonInfo
- var season = item as Season;
- if (season != null)
- {
- dto.SeriesName = season.SeriesName;
-
- var seriesId = season.SeriesId;
- if (seriesId.HasValue)
- {
- dto.SeriesId = seriesId.Value.ToString("N");
- }
-
- series = null;
-
- if (fields.Contains(ItemFields.SeriesStudio))
- {
- series = series ?? season.Series;
- if (series != null)
- {
- dto.SeriesStudio = series.Studios.FirstOrDefault();
- }
- }
-
- if (fields.Contains(ItemFields.SeriesPrimaryImage))
- {
- series = series ?? season.Series;
- if (series != null)
- {
- dto.SeriesPrimaryImageTag = GetImageCacheTag(series, ImageType.Primary);
- }
- }
- }
-
- var game = item as Game;
-
- if (game != null)
- {
- SetGameProperties(dto, game);
- }
-
- var gameSystem = item as GameSystem;
-
- if (gameSystem != null)
- {
- SetGameSystemProperties(dto, gameSystem);
- }
-
- var musicVideo = item as MusicVideo;
- if (musicVideo != null)
- {
- SetMusicVideoProperties(dto, musicVideo);
- }
-
- var book = item as Book;
- if (book != null)
- {
- SetBookProperties(dto, book);
- }
-
- if (item.ProductionLocations.Count > 0 || item is Movie)
- {
- dto.ProductionLocations = item.ProductionLocations.ToArray();
- }
-
- var photo = item as Photo;
- if (photo != null)
- {
- SetPhotoProperties(dto, photo);
- }
-
- dto.ChannelId = item.ChannelId;
-
- if (item.SourceType == SourceType.Channel && !string.IsNullOrWhiteSpace(item.ChannelId))
- {
- var channel = _libraryManager.GetItemById(item.ChannelId);
- if (channel != null)
- {
- dto.ChannelName = channel.Name;
- }
- }
- }
-
- private void AddInheritedImages(BaseItemDto dto, BaseItem item, DtoOptions options, BaseItem owner)
- {
- var logoLimit = options.GetImageLimit(ImageType.Logo);
- var artLimit = options.GetImageLimit(ImageType.Art);
- var thumbLimit = options.GetImageLimit(ImageType.Thumb);
- var backdropLimit = options.GetImageLimit(ImageType.Backdrop);
-
- if (logoLimit == 0 && artLimit == 0 && thumbLimit == 0 && backdropLimit == 0)
- {
- return;
- }
-
- BaseItem parent = null;
- var isFirst = true;
-
- while (((!dto.HasLogo && logoLimit > 0) || (!dto.HasArtImage && artLimit > 0) || (!dto.HasThumb && thumbLimit > 0) || parent is Series) &&
- (parent = parent ?? (isFirst ? item.GetParent() ?? owner : parent)) != null)
- {
- if (parent == null)
- {
- break;
- }
-
- var allImages = parent.ImageInfos;
-
- if (logoLimit > 0 && !dto.HasLogo && dto.ParentLogoItemId == null)
- {
- var image = allImages.FirstOrDefault(i => i.Type == ImageType.Logo);
-
- if (image != null)
- {
- dto.ParentLogoItemId = GetDtoId(parent);
- dto.ParentLogoImageTag = GetImageCacheTag(parent, image);
- }
- }
- if (artLimit > 0 && !dto.HasArtImage && dto.ParentArtItemId == null)
- {
- var image = allImages.FirstOrDefault(i => i.Type == ImageType.Art);
-
- if (image != null)
- {
- dto.ParentArtItemId = GetDtoId(parent);
- dto.ParentArtImageTag = GetImageCacheTag(parent, image);
- }
- }
- if (thumbLimit > 0 && !dto.HasThumb && (dto.ParentThumbItemId == null || parent is Series))
- {
- var image = allImages.FirstOrDefault(i => i.Type == ImageType.Thumb);
-
- if (image != null)
- {
- dto.ParentThumbItemId = GetDtoId(parent);
- dto.ParentThumbImageTag = GetImageCacheTag(parent, image);
- }
- }
- if (backdropLimit > 0 && !dto.HasBackdrop)
- {
- var images = allImages.Where(i => i.Type == ImageType.Backdrop).Take(backdropLimit).ToList();
-
- if (images.Count > 0)
- {
- dto.ParentBackdropItemId = GetDtoId(parent);
- dto.ParentBackdropImageTags = GetImageTags(parent, images);
- }
- }
-
- isFirst = false;
- parent = parent.GetParent();
- }
- }
-
- private string GetMappedPath(BaseItem item)
- {
- var path = item.Path;
-
- var locationType = item.LocationType;
-
- if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
- {
- path = _libraryManager.GetPathAfterNetworkSubstitution(path, item);
- }
-
- return path;
- }
-
- /// <summary>
- /// Attaches the primary image aspect ratio.
- /// </summary>
- /// <param name="dto">The dto.</param>
- /// <param name="item">The item.</param>
- /// <returns>Task.</returns>
- public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item)
- {
- dto.PrimaryImageAspectRatio = GetPrimaryImageAspectRatio(item);
- }
-
- public double? GetPrimaryImageAspectRatio(IHasImages item)
- {
- var imageInfo = item.GetImageInfo(ImageType.Primary, 0);
-
- if (imageInfo == null || !imageInfo.IsLocalFile)
- {
- return null;
- }
-
- ImageSize size;
-
- try
- {
- size = _imageProcessor.GetImageSize(imageInfo);
- }
- catch
- {
- //_logger.ErrorException("Failed to determine primary image aspect ratio for {0}", ex, path);
- return null;
- }
-
- var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList();
-
- foreach (var enhancer in supportedEnhancers)
- {
- try
- {
- size = enhancer.GetEnhancedImageSize(item, ImageType.Primary, 0, size);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in image enhancer: {0}", ex, enhancer.GetType().Name);
- }
- }
-
- var width = size.Width;
- var height = size.Height;
-
- if (width == 0 || height == 0)
- {
- return null;
- }
-
- var photo = item as Photo;
- if (photo != null && photo.Orientation.HasValue)
- {
- switch (photo.Orientation.Value)
- {
- case ImageOrientation.LeftBottom:
- case ImageOrientation.LeftTop:
- case ImageOrientation.RightBottom:
- case ImageOrientation.RightTop:
- var temp = height;
- height = width;
- width = temp;
- break;
- }
- }
-
- return width / height;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs
deleted file mode 100644
index 51f5f57b3..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/ActivityLogEntryPoint.cs
+++ /dev/null
@@ -1,561 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Controller.Subtitles;
-using MediaBrowser.Model.Activity;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Updates;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using MediaBrowser.Model.Globalization;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- public class ActivityLogEntryPoint : IServerEntryPoint
- {
- private readonly IInstallationManager _installationManager;
-
- //private readonly ILogManager _logManager;
- //private readonly ILogger _logger;
- private readonly ISessionManager _sessionManager;
- private readonly ITaskManager _taskManager;
- private readonly IActivityManager _activityManager;
- private readonly ILocalizationManager _localization;
-
- private readonly ILibraryManager _libraryManager;
- private readonly ISubtitleManager _subManager;
- private readonly IUserManager _userManager;
- private readonly IServerConfigurationManager _config;
- private readonly IServerApplicationHost _appHost;
-
- public ActivityLogEntryPoint(ISessionManager sessionManager, ITaskManager taskManager, IActivityManager activityManager, ILocalizationManager localization, IInstallationManager installationManager, ILibraryManager libraryManager, ISubtitleManager subManager, IUserManager userManager, IServerConfigurationManager config, IServerApplicationHost appHost)
- {
- //_logger = _logManager.GetLogger("ActivityLogEntryPoint");
- _sessionManager = sessionManager;
- _taskManager = taskManager;
- _activityManager = activityManager;
- _localization = localization;
- _installationManager = installationManager;
- _libraryManager = libraryManager;
- _subManager = subManager;
- _userManager = userManager;
- _config = config;
- //_logManager = logManager;
- _appHost = appHost;
- }
-
- public void Run()
- {
- //_taskManager.TaskExecuting += _taskManager_TaskExecuting;
- //_taskManager.TaskCompleted += _taskManager_TaskCompleted;
-
- //_installationManager.PluginInstalled += _installationManager_PluginInstalled;
- //_installationManager.PluginUninstalled += _installationManager_PluginUninstalled;
- //_installationManager.PluginUpdated += _installationManager_PluginUpdated;
-
- //_libraryManager.ItemAdded += _libraryManager_ItemAdded;
- //_libraryManager.ItemRemoved += _libraryManager_ItemRemoved;
-
- _sessionManager.SessionStarted += _sessionManager_SessionStarted;
- _sessionManager.AuthenticationFailed += _sessionManager_AuthenticationFailed;
- _sessionManager.AuthenticationSucceeded += _sessionManager_AuthenticationSucceeded;
- _sessionManager.SessionEnded += _sessionManager_SessionEnded;
-
- _sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
- _sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped;
-
- //_subManager.SubtitlesDownloaded += _subManager_SubtitlesDownloaded;
- _subManager.SubtitleDownloadFailure += _subManager_SubtitleDownloadFailure;
-
- _userManager.UserCreated += _userManager_UserCreated;
- _userManager.UserPasswordChanged += _userManager_UserPasswordChanged;
- _userManager.UserDeleted += _userManager_UserDeleted;
- _userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated;
- _userManager.UserLockedOut += _userManager_UserLockedOut;
-
- //_config.ConfigurationUpdated += _config_ConfigurationUpdated;
- //_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
-
- //_logManager.LoggerLoaded += _logManager_LoggerLoaded;
-
- _appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
- }
-
- void _userManager_UserLockedOut(object sender, GenericEventArgs<User> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name),
- Type = "UserLockedOut",
- UserId = e.Argument.Id.ToString("N")
- });
- }
-
- void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureForItem"), Notifications.Notifications.GetItemName(e.Item)),
- Type = "SubtitleDownloadFailure",
- ItemId = e.Item.Id.ToString("N"),
- ShortOverview = string.Format(_localization.GetLocalizedString("ProviderValue"), e.Provider),
- Overview = LogHelper.GetLogMessage(e.Exception).ToString()
- });
- }
-
- void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- //_logger.Warn("PlaybackStopped reported with null media info.");
- return;
- }
-
- if (item.IsThemeMedia)
- {
- // Don't report theme song or local trailer playback
- return;
- }
-
- if (e.Users.Count == 0)
- {
- return;
- }
-
- var user = e.Users.First();
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, item.Name),
- Type = "PlaybackStopped",
- ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName),
- UserId = user.Id.ToString("N")
- });
- }
-
- void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- //_logger.Warn("PlaybackStart reported with null media info.");
- return;
- }
-
- if (item.IsThemeMedia)
- {
- // Don't report theme song or local trailer playback
- return;
- }
-
- if (e.Users.Count == 0)
- {
- return;
- }
-
- var user = e.Users.First();
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, item.Name),
- Type = "PlaybackStart",
- ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName),
- UserId = user.Id.ToString("N")
- });
- }
-
- void _sessionManager_SessionEnded(object sender, SessionEventArgs e)
- {
- string name;
- var session = e.SessionInfo;
-
- if (string.IsNullOrWhiteSpace(session.UserName))
- {
- name = string.Format(_localization.GetLocalizedString("DeviceOfflineWithName"), session.DeviceName);
-
- // Causing too much spam for now
- return;
- }
- else
- {
- name = string.Format(_localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, session.DeviceName);
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = name,
- Type = "SessionEnded",
- ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint),
- UserId = session.UserId.HasValue ? session.UserId.Value.ToString("N") : null
- });
- }
-
- void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationRequest> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), e.Argument.Username),
- Type = "AuthenticationSucceeded",
- ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.RemoteEndPoint)
- });
- }
-
- void _sessionManager_AuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username),
- Type = "AuthenticationFailed",
- ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.RemoteEndPoint),
- Severity = LogSeverity.Error
- });
- }
-
- void _appHost_ApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = _localization.GetLocalizedString("MessageApplicationUpdated"),
- Type = "ApplicationUpdated",
- ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.versionStr),
- Overview = e.Argument.description
- });
- }
-
- void _logManager_LoggerLoaded(object sender, EventArgs e)
- {
- }
-
- void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("MessageNamedServerConfigurationUpdatedWithValue"), e.Key),
- Type = "NamedConfigurationUpdated"
- });
- }
-
- void _config_ConfigurationUpdated(object sender, EventArgs e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = _localization.GetLocalizedString("MessageServerConfigurationUpdated"),
- Type = "ServerConfigurationUpdated"
- });
- }
-
- void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("UserConfigurationUpdatedWithName"), e.Argument.Name),
- Type = "UserConfigurationUpdated",
- UserId = e.Argument.Id.ToString("N")
- });
- }
-
- void _userManager_UserDeleted(object sender, GenericEventArgs<User> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name),
- Type = "UserDeleted"
- });
- }
-
- void _userManager_UserPasswordChanged(object sender, GenericEventArgs<User> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name),
- Type = "UserPasswordChanged",
- UserId = e.Argument.Id.ToString("N")
- });
- }
-
- void _userManager_UserCreated(object sender, GenericEventArgs<User> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name),
- Type = "UserCreated",
- UserId = e.Argument.Id.ToString("N")
- });
- }
-
- void _subManager_SubtitlesDownloaded(object sender, SubtitleDownloadEventArgs e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("SubtitlesDownloadedForItem"), Notifications.Notifications.GetItemName(e.Item)),
- Type = "SubtitlesDownloaded",
- ItemId = e.Item.Id.ToString("N"),
- ShortOverview = string.Format(_localization.GetLocalizedString("ProviderValue"), e.Provider)
- });
- }
-
- void _sessionManager_SessionStarted(object sender, SessionEventArgs e)
- {
- string name;
- var session = e.SessionInfo;
-
- if (string.IsNullOrWhiteSpace(session.UserName))
- {
- name = string.Format(_localization.GetLocalizedString("DeviceOnlineWithName"), session.DeviceName);
-
- // Causing too much spam for now
- return;
- }
- else
- {
- name = string.Format(_localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, session.DeviceName);
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = name,
- Type = "SessionStarted",
- ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint),
- UserId = session.UserId.HasValue ? session.UserId.Value.ToString("N") : null
- });
- }
-
- void _libraryManager_ItemRemoved(object sender, ItemChangeEventArgs e)
- {
- if (e.Item.SourceType != SourceType.Library)
- {
- return;
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("ItemRemovedWithName"), Notifications.Notifications.GetItemName(e.Item)),
- Type = "ItemRemoved"
- });
- }
-
- void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
- {
- if (e.Item.SourceType != SourceType.Library)
- {
- return;
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("ItemAddedWithName"), Notifications.Notifications.GetItemName(e.Item)),
- Type = "ItemAdded",
- ItemId = e.Item.Id.ToString("N")
- });
- }
-
- void _installationManager_PluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name),
- Type = "PluginUpdated",
- ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.Item2.versionStr),
- Overview = e.Argument.Item2.description
- });
- }
-
- void _installationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name),
- Type = "PluginUninstalled"
- });
- }
-
- void _installationManager_PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
- {
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name),
- Type = "PluginInstalled",
- ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.versionStr)
- });
- }
-
- void _taskManager_TaskExecuting(object sender, GenericEventArgs<IScheduledTaskWorker> e)
- {
- var task = e.Argument;
-
- var activityTask = task.ScheduledTask as IConfigurableScheduledTask;
- if (activityTask != null && !activityTask.IsLogged)
- {
- return;
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("ScheduledTaskStartedWithName"), task.Name),
- Type = "ScheduledTaskStarted"
- });
- }
-
- void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
- {
- var result = e.Result;
- var task = e.Task;
-
- var activityTask = task.ScheduledTask as IConfigurableScheduledTask;
- if (activityTask != null && !activityTask.IsLogged)
- {
- return;
- }
-
- var time = result.EndTimeUtc - result.StartTimeUtc;
- var runningTime = string.Format(_localization.GetLocalizedString("LabelRunningTimeValue"), ToUserFriendlyString(time));
-
- if (result.Status == TaskCompletionStatus.Failed)
- {
- var vals = new List<string>();
-
- if (!string.IsNullOrWhiteSpace(e.Result.ErrorMessage))
- {
- vals.Add(e.Result.ErrorMessage);
- }
- if (!string.IsNullOrWhiteSpace(e.Result.LongErrorMessage))
- {
- vals.Add(e.Result.LongErrorMessage);
- }
-
- CreateLogEntry(new ActivityLogEntry
- {
- Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
- Type = "ScheduledTaskFailed",
- Overview = string.Join(Environment.NewLine, vals.ToArray()),
- ShortOverview = runningTime,
- Severity = LogSeverity.Error
- });
- }
- }
-
- private async void CreateLogEntry(ActivityLogEntry entry)
- {
- try
- {
- await _activityManager.Create(entry).ConfigureAwait(false);
- }
- catch
- {
- // Logged at lower levels
- }
- }
-
- public void Dispose()
- {
- _taskManager.TaskExecuting -= _taskManager_TaskExecuting;
- _taskManager.TaskCompleted -= _taskManager_TaskCompleted;
-
- _installationManager.PluginInstalled -= _installationManager_PluginInstalled;
- _installationManager.PluginUninstalled -= _installationManager_PluginUninstalled;
- _installationManager.PluginUpdated -= _installationManager_PluginUpdated;
-
- _libraryManager.ItemAdded -= _libraryManager_ItemAdded;
- _libraryManager.ItemRemoved -= _libraryManager_ItemRemoved;
-
- _sessionManager.SessionStarted -= _sessionManager_SessionStarted;
- _sessionManager.AuthenticationFailed -= _sessionManager_AuthenticationFailed;
- _sessionManager.AuthenticationSucceeded -= _sessionManager_AuthenticationSucceeded;
- _sessionManager.SessionEnded -= _sessionManager_SessionEnded;
-
- _sessionManager.PlaybackStart -= _sessionManager_PlaybackStart;
- _sessionManager.PlaybackStopped -= _sessionManager_PlaybackStopped;
-
- _subManager.SubtitlesDownloaded -= _subManager_SubtitlesDownloaded;
- _subManager.SubtitleDownloadFailure -= _subManager_SubtitleDownloadFailure;
-
- _userManager.UserCreated -= _userManager_UserCreated;
- _userManager.UserPasswordChanged -= _userManager_UserPasswordChanged;
- _userManager.UserDeleted -= _userManager_UserDeleted;
- _userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated;
- _userManager.UserLockedOut -= _userManager_UserLockedOut;
-
- _config.ConfigurationUpdated -= _config_ConfigurationUpdated;
- _config.NamedConfigurationUpdated -= _config_NamedConfigurationUpdated;
-
- //_logManager.LoggerLoaded -= _logManager_LoggerLoaded;
-
- _appHost.ApplicationUpdated -= _appHost_ApplicationUpdated;
- }
-
- /// <summary>
- /// Constructs a user-friendly string for this TimeSpan instance.
- /// </summary>
- public static string ToUserFriendlyString(TimeSpan span)
- {
- const int DaysInYear = 365;
- const int DaysInMonth = 30;
-
- // Get each non-zero value from TimeSpan component
- List<string> values = new List<string>();
-
- // Number of years
- int days = span.Days;
- if (days >= DaysInYear)
- {
- int years = days / DaysInYear;
- values.Add(CreateValueString(years, "year"));
- days = days % DaysInYear;
- }
- // Number of months
- if (days >= DaysInMonth)
- {
- int months = days / DaysInMonth;
- values.Add(CreateValueString(months, "month"));
- days = days % DaysInMonth;
- }
- // Number of days
- if (days >= 1)
- values.Add(CreateValueString(days, "day"));
- // Number of hours
- if (span.Hours >= 1)
- values.Add(CreateValueString(span.Hours, "hour"));
- // Number of minutes
- if (span.Minutes >= 1)
- values.Add(CreateValueString(span.Minutes, "minute"));
- // Number of seconds (include when 0 if no other components included)
- if (span.Seconds >= 1 || values.Count == 0)
- values.Add(CreateValueString(span.Seconds, "second"));
-
- // Combine values into string
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < values.Count; i++)
- {
- if (builder.Length > 0)
- builder.Append(i == values.Count - 1 ? " and " : ", ");
- builder.Append(values[i]);
- }
- // Return result
- return builder.ToString();
- }
-
- /// <summary>
- /// Constructs a string description of a time-span value.
- /// </summary>
- /// <param name="value">The value of this item</param>
- /// <param name="description">The name of this item (singular form)</param>
- private static string CreateValueString(int value, string description)
- {
- return String.Format("{0:#,##0} {1}",
- value, value == 1 ? description : String.Format("{0}s", description));
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
deleted file mode 100644
index 1067e052d..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/AutomaticRestartEntryPoint.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Tasks;
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- public class AutomaticRestartEntryPoint : IServerEntryPoint
- {
- private readonly IServerApplicationHost _appHost;
- private readonly ILogger _logger;
- private readonly ITaskManager _iTaskManager;
- private readonly ISessionManager _sessionManager;
- private readonly IServerConfigurationManager _config;
- private readonly ILiveTvManager _liveTvManager;
-
- private Timer _timer;
-
- public AutomaticRestartEntryPoint(IServerApplicationHost appHost, ILogger logger, ITaskManager iTaskManager, ISessionManager sessionManager, IServerConfigurationManager config, ILiveTvManager liveTvManager)
- {
- _appHost = appHost;
- _logger = logger;
- _iTaskManager = iTaskManager;
- _sessionManager = sessionManager;
- _config = config;
- _liveTvManager = liveTvManager;
- }
-
- public void Run()
- {
- if (_appHost.CanSelfRestart)
- {
- _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
- }
- }
-
- void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
- {
- DisposeTimer();
-
- if (_appHost.HasPendingRestart)
- {
- _timer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10));
- }
- }
-
- private async void TimerCallback(object state)
- {
- if (_config.Configuration.EnableAutomaticRestart)
- {
- var isIdle = await IsIdle().ConfigureAwait(false);
-
- if (isIdle)
- {
- DisposeTimer();
-
- try
- {
- _appHost.Restart();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error restarting server", ex);
- }
- }
- }
- }
-
- private async Task<bool> IsIdle()
- {
- if (_iTaskManager.ScheduledTasks.Any(i => i.State != TaskState.Idle))
- {
- return false;
- }
-
- if (_liveTvManager.Services.Count == 1)
- {
- try
- {
- var timers = await _liveTvManager.GetTimers(new TimerQuery(), CancellationToken.None).ConfigureAwait(false);
- if (timers.Items.Any(i => i.Status == RecordingStatus.InProgress))
- {
- return false;
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting timers", ex);
- }
- }
-
- var now = DateTime.UtcNow;
-
- return !_sessionManager.Sessions.Any(i => (now - i.LastActivityDate).TotalMinutes < 30);
- }
-
- public void Dispose()
- {
- _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
-
- DisposeTimer();
- }
-
- private void DisposeTimer()
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
index d86990a40..dcfa27cc0 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
@@ -11,7 +11,7 @@ using System.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
-using MediaBrowser.Server.Implementations.Threading;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Server.Implementations.EntryPoints
{
@@ -23,16 +23,18 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
private readonly IServerConfigurationManager _config;
private readonly IDeviceDiscovery _deviceDiscovery;
- private PeriodicTimer _timer;
+ private ITimer _timer;
private bool _isStarted;
+ private readonly ITimerFactory _timerFactory;
- public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient)
+ public ExternalPortForwarding(ILogManager logmanager, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient, ITimerFactory timerFactory)
{
_logger = logmanager.GetLogger("PortMapper");
_appHost = appHost;
_config = config;
_deviceDiscovery = deviceDiscovery;
_httpClient = httpClient;
+ _timerFactory = timerFactory;
}
private string _lastConfigIdentifier;
@@ -94,7 +96,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
NatUtility.StartDiscovery();
- _timer = new PeriodicTimer(ClearCreatedRules, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
+ _timer = _timerFactory.Create(ClearCreatedRules, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
_deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
deleted file mode 100644
index 9f06a9860..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ /dev/null
@@ -1,340 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- public class LibraryChangedNotifier : IServerEntryPoint
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
-
- private readonly ISessionManager _sessionManager;
- private readonly IUserManager _userManager;
- private readonly ILogger _logger;
-
- /// <summary>
- /// The _library changed sync lock
- /// </summary>
- private readonly object _libraryChangedSyncLock = new object();
-
- private readonly List<Folder> _foldersAddedTo = new List<Folder>();
- private readonly List<Folder> _foldersRemovedFrom = new List<Folder>();
-
- private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
- private readonly List<BaseItem> _itemsRemoved = new List<BaseItem>();
- private readonly List<BaseItem> _itemsUpdated = new List<BaseItem>();
-
- /// <summary>
- /// Gets or sets the library update timer.
- /// </summary>
- /// <value>The library update timer.</value>
- private Timer LibraryUpdateTimer { get; set; }
-
- /// <summary>
- /// The library update duration
- /// </summary>
- private const int LibraryUpdateDuration = 5000;
-
- public LibraryChangedNotifier(ILibraryManager libraryManager, ISessionManager sessionManager, IUserManager userManager, ILogger logger)
- {
- _libraryManager = libraryManager;
- _sessionManager = sessionManager;
- _userManager = userManager;
- _logger = logger;
- }
-
- public void Run()
- {
- _libraryManager.ItemAdded += libraryManager_ItemAdded;
- _libraryManager.ItemUpdated += libraryManager_ItemUpdated;
- _libraryManager.ItemRemoved += libraryManager_ItemRemoved;
-
- }
-
- /// <summary>
- /// Handles the ItemAdded event of the libraryManager control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
- void libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
- {
- if (!FilterItem(e.Item))
- {
- return;
- }
-
- lock (_libraryChangedSyncLock)
- {
- if (LibraryUpdateTimer == null)
- {
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
- Timeout.Infinite);
- }
- else
- {
- LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
- }
-
- if (e.Item.Parent != null)
- {
- _foldersAddedTo.Add(e.Item.Parent);
- }
-
- _itemsAdded.Add(e.Item);
- }
- }
-
- /// <summary>
- /// Handles the ItemUpdated event of the libraryManager control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
- void libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e)
- {
- if (!FilterItem(e.Item))
- {
- return;
- }
-
- lock (_libraryChangedSyncLock)
- {
- if (LibraryUpdateTimer == null)
- {
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
- Timeout.Infinite);
- }
- else
- {
- LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
- }
-
- _itemsUpdated.Add(e.Item);
- }
- }
-
- /// <summary>
- /// Handles the ItemRemoved event of the libraryManager control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
- void libraryManager_ItemRemoved(object sender, ItemChangeEventArgs e)
- {
- if (!FilterItem(e.Item))
- {
- return;
- }
-
- lock (_libraryChangedSyncLock)
- {
- if (LibraryUpdateTimer == null)
- {
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration,
- Timeout.Infinite);
- }
- else
- {
- LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
- }
-
- if (e.Item.Parent != null)
- {
- _foldersRemovedFrom.Add(e.Item.Parent);
- }
-
- _itemsRemoved.Add(e.Item);
- }
- }
-
- /// <summary>
- /// Libraries the update timer callback.
- /// </summary>
- /// <param name="state">The state.</param>
- private void LibraryUpdateTimerCallback(object state)
- {
- lock (_libraryChangedSyncLock)
- {
- // Remove dupes in case some were saved multiple times
- var foldersAddedTo = _foldersAddedTo.DistinctBy(i => i.Id).ToList();
-
- var foldersRemovedFrom = _foldersRemovedFrom.DistinctBy(i => i.Id).ToList();
-
- var itemsUpdated = _itemsUpdated
- .Where(i => !_itemsAdded.Contains(i))
- .DistinctBy(i => i.Id)
- .ToList();
-
- SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None);
-
- if (LibraryUpdateTimer != null)
- {
- LibraryUpdateTimer.Dispose();
- LibraryUpdateTimer = null;
- }
-
- _itemsAdded.Clear();
- _itemsRemoved.Clear();
- _itemsUpdated.Clear();
- _foldersAddedTo.Clear();
- _foldersRemovedFrom.Clear();
- }
- }
-
- /// <summary>
- /// Sends the change notifications.
- /// </summary>
- /// <param name="itemsAdded">The items added.</param>
- /// <param name="itemsUpdated">The items updated.</param>
- /// <param name="itemsRemoved">The items removed.</param>
- /// <param name="foldersAddedTo">The folders added to.</param>
- /// <param name="foldersRemovedFrom">The folders removed from.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- private async void SendChangeNotifications(List<BaseItem> itemsAdded, List<BaseItem> itemsUpdated, List<BaseItem> itemsRemoved, List<Folder> foldersAddedTo, List<Folder> foldersRemovedFrom, CancellationToken cancellationToken)
- {
- foreach (var user in _userManager.Users.ToList())
- {
- var id = user.Id;
- var userSessions = _sessionManager.Sessions
- .Where(u => u.UserId.HasValue && u.UserId.Value == id && u.SessionController != null && u.IsActive)
- .ToList();
-
- if (userSessions.Count > 0)
- {
- LibraryUpdateInfo info;
-
- try
- {
- info = GetLibraryUpdateInfo(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo,
- foldersRemovedFrom, id);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in GetLibraryUpdateInfo", ex);
- return;
- }
-
- foreach (var userSession in userSessions)
- {
- try
- {
- await userSession.SessionController.SendLibraryUpdateInfo(info, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending LibraryChanged message", ex);
- }
- }
- }
-
- }
- }
-
- /// <summary>
- /// Gets the library update info.
- /// </summary>
- /// <param name="itemsAdded">The items added.</param>
- /// <param name="itemsUpdated">The items updated.</param>
- /// <param name="itemsRemoved">The items removed.</param>
- /// <param name="foldersAddedTo">The folders added to.</param>
- /// <param name="foldersRemovedFrom">The folders removed from.</param>
- /// <param name="userId">The user id.</param>
- /// <returns>LibraryUpdateInfo.</returns>
- private LibraryUpdateInfo GetLibraryUpdateInfo(IEnumerable<BaseItem> itemsAdded, IEnumerable<BaseItem> itemsUpdated, IEnumerable<BaseItem> itemsRemoved, IEnumerable<Folder> foldersAddedTo, IEnumerable<Folder> foldersRemovedFrom, Guid userId)
- {
- var user = _userManager.GetUserById(userId);
-
- return new LibraryUpdateInfo
- {
- ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
-
- ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
-
- ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
-
- FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
-
- FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList()
- };
- }
-
- private bool FilterItem(BaseItem item)
- {
- if (!item.IsFolder && item.LocationType == LocationType.Virtual)
- {
- return false;
- }
-
- if (item is IItemByName && !(item is MusicArtist))
- {
- return false;
- }
-
- return item.SourceType == SourceType.Library;
- }
-
- /// <summary>
- /// Translates the physical item to user library.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="item">The item.</param>
- /// <param name="user">The user.</param>
- /// <param name="includeIfNotFound">if set to <c>true</c> [include if not found].</param>
- /// <returns>IEnumerable{``0}.</returns>
- private IEnumerable<T> TranslatePhysicalItemToUserLibrary<T>(T item, User user, bool includeIfNotFound = false)
- where T : BaseItem
- {
- // If the physical root changed, return the user root
- if (item is AggregateFolder)
- {
- return new[] { user.RootFolder as T };
- }
-
- // Return it only if it's in the user's library
- if (includeIfNotFound || item.IsVisibleStandalone(user))
- {
- return new[] { item };
- }
-
- return new T[] { };
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (dispose)
- {
- if (LibraryUpdateTimer != null)
- {
- LibraryUpdateTimer.Dispose();
- LibraryUpdateTimer = null;
- }
-
- _libraryManager.ItemAdded -= libraryManager_ItemAdded;
- _libraryManager.ItemUpdated -= libraryManager_ItemUpdated;
- _libraryManager.ItemRemoved -= libraryManager_ItemRemoved;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs b/MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs
deleted file mode 100644
index 47f22cae6..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/LoadRegistrations.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using MediaBrowser.Common.Security;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading.Tasks;
-using MediaBrowser.Server.Implementations.Threading;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- /// <summary>
- /// Class LoadRegistrations
- /// </summary>
- public class LoadRegistrations : IServerEntryPoint
- {
- /// <summary>
- /// The _security manager
- /// </summary>
- private readonly ISecurityManager _securityManager;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- private PeriodicTimer _timer;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LoadRegistrations" /> class.
- /// </summary>
- /// <param name="securityManager">The security manager.</param>
- /// <param name="logManager">The log manager.</param>
- public LoadRegistrations(ISecurityManager securityManager, ILogManager logManager)
- {
- _securityManager = securityManager;
-
- _logger = logManager.GetLogger("Registration Loader");
- }
-
- /// <summary>
- /// Runs this instance.
- /// </summary>
- public void Run()
- {
- _timer = new PeriodicTimer(s => LoadAllRegistrations(), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromHours(12));
- }
-
- private async Task LoadAllRegistrations()
- {
- try
- {
- await _securityManager.LoadAllRegistrationInfo().ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error loading registration info", ex);
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs
deleted file mode 100644
index f3d1dc8f9..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs
+++ /dev/null
@@ -1,544 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Notifications;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Updates;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities.TV;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
-{
- /// <summary>
- /// Creates notifications for various system events
- /// </summary>
- public class Notifications : IServerEntryPoint
- {
- private readonly IInstallationManager _installationManager;
- private readonly IUserManager _userManager;
- private readonly ILogger _logger;
-
- private readonly ITaskManager _taskManager;
- private readonly INotificationManager _notificationManager;
-
- private readonly ILibraryManager _libraryManager;
- private readonly ISessionManager _sessionManager;
- private readonly IServerApplicationHost _appHost;
-
- private Timer LibraryUpdateTimer { get; set; }
- private readonly object _libraryChangedSyncLock = new object();
-
- private readonly IConfigurationManager _config;
- private readonly IDeviceManager _deviceManager;
-
- public Notifications(IInstallationManager installationManager, IUserManager userManager, ILogger logger, ITaskManager taskManager, INotificationManager notificationManager, ILibraryManager libraryManager, ISessionManager sessionManager, IServerApplicationHost appHost, IConfigurationManager config, IDeviceManager deviceManager)
- {
- _installationManager = installationManager;
- _userManager = userManager;
- _logger = logger;
- _taskManager = taskManager;
- _notificationManager = notificationManager;
- _libraryManager = libraryManager;
- _sessionManager = sessionManager;
- _appHost = appHost;
- _config = config;
- _deviceManager = deviceManager;
- }
-
- public void Run()
- {
- _installationManager.PluginInstalled += _installationManager_PluginInstalled;
- _installationManager.PluginUpdated += _installationManager_PluginUpdated;
- _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
- _installationManager.PluginUninstalled += _installationManager_PluginUninstalled;
-
- _taskManager.TaskCompleted += _taskManager_TaskCompleted;
-
- _userManager.UserCreated += _userManager_UserCreated;
- _libraryManager.ItemAdded += _libraryManager_ItemAdded;
- _sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
- _sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped;
- _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
- _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
- _appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
- _deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded;
-
- _userManager.UserLockedOut += _userManager_UserLockedOut;
- }
-
- async void _userManager_UserLockedOut(object sender, GenericEventArgs<User> e)
- {
- var type = NotificationType.UserLockedOut.ToString();
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- notification.Variables["UserName"] = e.Argument.Name;
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- async void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
- {
- var type = NotificationType.CameraImageUploaded.ToString();
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- notification.Variables["DeviceName"] = e.Argument.Device.Name;
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- async void _appHost_ApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e)
- {
- var type = NotificationType.ApplicationUpdateInstalled.ToString();
-
- var notification = new NotificationRequest
- {
- NotificationType = type,
- Url = e.Argument.infoUrl
- };
-
- notification.Variables["Version"] = e.Argument.versionStr;
- notification.Variables["ReleaseNotes"] = e.Argument.description;
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- async void _installationManager_PluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e)
- {
- var type = NotificationType.PluginUpdateInstalled.ToString();
-
- var installationInfo = e.Argument.Item1;
-
- var notification = new NotificationRequest
- {
- Description = e.Argument.Item2.description,
- NotificationType = type
- };
-
- notification.Variables["Name"] = installationInfo.Name;
- notification.Variables["Version"] = installationInfo.Version.ToString();
- notification.Variables["ReleaseNotes"] = e.Argument.Item2.description;
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- async void _installationManager_PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
- {
- var type = NotificationType.PluginInstalled.ToString();
-
- var installationInfo = e.Argument;
-
- var notification = new NotificationRequest
- {
- Description = installationInfo.description,
- NotificationType = type
- };
-
- notification.Variables["Name"] = installationInfo.name;
- notification.Variables["Version"] = installationInfo.versionStr;
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e)
- {
- // This notification is for users who can't auto-update (aka running as service)
- if (!_appHost.HasUpdateAvailable || _appHost.CanSelfUpdate)
- {
- return;
- }
-
- var type = NotificationType.ApplicationUpdateAvailable.ToString();
-
- var notification = new NotificationRequest
- {
- Description = "Please see emby.media for details.",
- NotificationType = type
- };
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
- {
- if (!_appHost.HasPendingRestart)
- {
- return;
- }
-
- var type = NotificationType.ServerRestartRequired.ToString();
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- private NotificationOptions GetOptions()
- {
- return _config.GetConfiguration<NotificationOptions>("notifications");
- }
-
- void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- _logger.Warn("PlaybackStart reported with null media info.");
- return;
- }
-
- var video = e.Item as Video;
- if (video != null && video.IsThemeMedia)
- {
- return;
- }
-
- var type = GetPlaybackNotificationType(item.MediaType);
-
- SendPlaybackNotification(type, e);
- }
-
- void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
- {
- var item = e.MediaInfo;
-
- if (item == null)
- {
- _logger.Warn("PlaybackStopped reported with null media info.");
- return;
- }
-
- var video = e.Item as Video;
- if (video != null && video.IsThemeMedia)
- {
- return;
- }
-
- var type = GetPlaybackStoppedNotificationType(item.MediaType);
-
- SendPlaybackNotification(type, e);
- }
-
- private async void SendPlaybackNotification(string type, PlaybackProgressEventArgs e)
- {
- var user = e.Users.FirstOrDefault();
-
- if (user != null && !GetOptions().IsEnabledToMonitorUser(type, user.Id.ToString("N")))
- {
- return;
- }
-
- var item = e.MediaInfo;
-
- if ( item.IsThemeMedia)
- {
- // Don't report theme song or local trailer playback
- return;
- }
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- if (e.Item != null)
- {
- notification.Variables["ItemName"] = GetItemName(e.Item);
- }
- else
- {
- notification.Variables["ItemName"] = item.Name;
- }
-
- notification.Variables["UserName"] = user == null ? "Unknown user" : user.Name;
- notification.Variables["AppName"] = e.ClientName;
- notification.Variables["DeviceName"] = e.DeviceName;
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- private string GetPlaybackNotificationType(string mediaType)
- {
- if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.AudioPlayback.ToString();
- }
- if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.GamePlayback.ToString();
- }
- if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.VideoPlayback.ToString();
- }
-
- return null;
- }
-
- private string GetPlaybackStoppedNotificationType(string mediaType)
- {
- if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.AudioPlaybackStopped.ToString();
- }
- if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.GamePlaybackStopped.ToString();
- }
- if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
- {
- return NotificationType.VideoPlaybackStopped.ToString();
- }
-
- return null;
- }
-
- private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
- void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
- {
- if (!FilterItem(e.Item))
- {
- return;
- }
-
- lock (_libraryChangedSyncLock)
- {
- if (LibraryUpdateTimer == null)
- {
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, 5000,
- Timeout.Infinite);
- }
- else
- {
- LibraryUpdateTimer.Change(5000, Timeout.Infinite);
- }
-
- _itemsAdded.Add(e.Item);
- }
- }
-
- private bool FilterItem(BaseItem item)
- {
- if (item.IsFolder)
- {
- return false;
- }
-
- if (item.LocationType == LocationType.Virtual)
- {
- return false;
- }
-
- if (item is IItemByName)
- {
- return false;
- }
-
- return item.SourceType == SourceType.Library;
- }
-
- private async void LibraryUpdateTimerCallback(object state)
- {
- List<BaseItem> items;
-
- lock (_libraryChangedSyncLock)
- {
- items = _itemsAdded.ToList();
- _itemsAdded.Clear();
- DisposeLibraryUpdateTimer();
- }
-
- items = items.Take(10).ToList();
-
- foreach (var item in items)
- {
- var notification = new NotificationRequest
- {
- NotificationType = NotificationType.NewLibraryContent.ToString()
- };
-
- notification.Variables["Name"] = GetItemName(item);
-
- await SendNotification(notification).ConfigureAwait(false);
- }
- }
-
- public static string GetItemName(BaseItem item)
- {
- var name = item.Name;
- var episode = item as Episode;
- if (episode != null)
- {
- if (episode.IndexNumber.HasValue)
- {
- name = string.Format("Ep{0} - {1}", episode.IndexNumber.Value.ToString(CultureInfo.InvariantCulture), name);
- }
- if (episode.ParentIndexNumber.HasValue)
- {
- name = string.Format("S{0}, {1}", episode.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture), name);
- }
- }
-
- var hasSeries = item as IHasSeries;
-
- if (hasSeries != null)
- {
- name = hasSeries.SeriesName + " - " + name;
- }
-
- var hasArtist = item as IHasArtist;
- if (hasArtist != null)
- {
- var artists = hasArtist.AllArtists;
-
- if (artists.Count > 0)
- {
- name = hasArtist.AllArtists[0] + " - " + name;
- }
- }
-
- return name;
- }
-
- async void _userManager_UserCreated(object sender, GenericEventArgs<User> e)
- {
- var notification = new NotificationRequest
- {
- UserIds = new List<string> { e.Argument.Id.ToString("N") },
- Name = "Welcome to Emby!",
- Description = "Check back here for more notifications."
- };
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- async void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
- {
- var result = e.Result;
-
- if (result.Status == TaskCompletionStatus.Failed)
- {
- var type = NotificationType.TaskFailed.ToString();
-
- var notification = new NotificationRequest
- {
- Description = result.ErrorMessage,
- Level = NotificationLevel.Error,
- NotificationType = type
- };
-
- notification.Variables["Name"] = result.Name;
- notification.Variables["ErrorMessage"] = result.ErrorMessage;
-
- await SendNotification(notification).ConfigureAwait(false);
- }
- }
-
- async void _installationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
- {
- var type = NotificationType.PluginUninstalled.ToString();
-
- var plugin = e.Argument;
-
- var notification = new NotificationRequest
- {
- NotificationType = type
- };
-
- notification.Variables["Name"] = plugin.Name;
- notification.Variables["Version"] = plugin.Version.ToString();
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- async void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
- {
- var installationInfo = e.InstallationInfo;
-
- var type = NotificationType.InstallationFailed.ToString();
-
- var notification = new NotificationRequest
- {
- Level = NotificationLevel.Error,
- Description = e.Exception.Message,
- NotificationType = type
- };
-
- notification.Variables["Name"] = installationInfo.Name;
- notification.Variables["Version"] = installationInfo.Version;
-
- await SendNotification(notification).ConfigureAwait(false);
- }
-
- private async Task SendNotification(NotificationRequest notification)
- {
- try
- {
- await _notificationManager.SendNotification(notification, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending notification", ex);
- }
- }
-
- public void Dispose()
- {
- DisposeLibraryUpdateTimer();
-
- _installationManager.PluginInstalled -= _installationManager_PluginInstalled;
- _installationManager.PluginUpdated -= _installationManager_PluginUpdated;
- _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed;
- _installationManager.PluginUninstalled -= _installationManager_PluginUninstalled;
-
- _taskManager.TaskCompleted -= _taskManager_TaskCompleted;
-
- _userManager.UserCreated -= _userManager_UserCreated;
- _libraryManager.ItemAdded -= _libraryManager_ItemAdded;
- _sessionManager.PlaybackStart -= _sessionManager_PlaybackStart;
-
- _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
- _appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged;
- _appHost.ApplicationUpdated -= _appHost_ApplicationUpdated;
-
- _deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded;
- _userManager.UserLockedOut -= _userManager_UserLockedOut;
- }
-
- private void DisposeLibraryUpdateTimer()
- {
- if (LibraryUpdateTimer != null)
- {
- LibraryUpdateTimer.Dispose();
- LibraryUpdateTimer = null;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs
deleted file mode 100644
index 916b4a622..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/WebSocketNotifier.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Controller.Plugins;
-using System.Linq;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
-{
- /// <summary>
- /// Notifies clients anytime a notification is added or udpated
- /// </summary>
- public class WebSocketNotifier : IServerEntryPoint
- {
- private readonly INotificationsRepository _notificationsRepo;
-
- private readonly IServerManager _serverManager;
-
- public WebSocketNotifier(INotificationsRepository notificationsRepo, IServerManager serverManager)
- {
- _notificationsRepo = notificationsRepo;
- _serverManager = serverManager;
- }
-
- public void Run()
- {
- _notificationsRepo.NotificationAdded += _notificationsRepo_NotificationAdded;
-
- _notificationsRepo.NotificationsMarkedRead += _notificationsRepo_NotificationsMarkedRead;
- }
-
- void _notificationsRepo_NotificationsMarkedRead(object sender, NotificationReadEventArgs e)
- {
- var list = e.IdList.ToList();
-
- list.Add(e.UserId);
- list.Add(e.IsRead.ToString().ToLower());
-
- var msg = string.Join("|", list.ToArray());
-
- _serverManager.SendWebSocketMessage("NotificationsMarkedRead", msg);
- }
-
- void _notificationsRepo_NotificationAdded(object sender, NotificationUpdateEventArgs e)
- {
- var msg = e.Notification.UserId + "|" + e.Notification.Id;
-
- _serverManager.SendWebSocketMessage("NotificationAdded", msg);
- }
-
- public void Dispose()
- {
- _notificationsRepo.NotificationAdded -= _notificationsRepo_NotificationAdded;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs
deleted file mode 100644
index 414fda400..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/RecordingNotifier.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System;
-using System.Linq;
-using System.Threading;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- public class RecordingNotifier : IServerEntryPoint
- {
- private readonly ILiveTvManager _liveTvManager;
- private readonly ISessionManager _sessionManager;
- private readonly IUserManager _userManager;
- private readonly ILogger _logger;
-
- public RecordingNotifier(ISessionManager sessionManager, IUserManager userManager, ILogger logger, ILiveTvManager liveTvManager)
- {
- _sessionManager = sessionManager;
- _userManager = userManager;
- _logger = logger;
- _liveTvManager = liveTvManager;
- }
-
- public void Run()
- {
- _liveTvManager.TimerCancelled += _liveTvManager_TimerCancelled;
- _liveTvManager.SeriesTimerCancelled += _liveTvManager_SeriesTimerCancelled;
- _liveTvManager.TimerCreated += _liveTvManager_TimerCreated;
- _liveTvManager.SeriesTimerCreated += _liveTvManager_SeriesTimerCreated;
- }
-
- private void _liveTvManager_SeriesTimerCreated(object sender, Model.Events.GenericEventArgs<TimerEventInfo> e)
- {
- SendMessage("SeriesTimerCreated", e.Argument);
- }
-
- private void _liveTvManager_TimerCreated(object sender, Model.Events.GenericEventArgs<TimerEventInfo> e)
- {
- SendMessage("TimerCreated", e.Argument);
- }
-
- private void _liveTvManager_SeriesTimerCancelled(object sender, Model.Events.GenericEventArgs<TimerEventInfo> e)
- {
- SendMessage("SeriesTimerCancelled", e.Argument);
- }
-
- private void _liveTvManager_TimerCancelled(object sender, Model.Events.GenericEventArgs<TimerEventInfo> e)
- {
- SendMessage("TimerCancelled", e.Argument);
- }
-
- private async void SendMessage(string name, TimerEventInfo info)
- {
- var users = _userManager.Users.Where(i => i.Policy.EnableLiveTvAccess).Select(i => i.Id.ToString("N")).ToList();
-
- try
- {
- await _sessionManager.SendMessageToUserSessions<TimerEventInfo>(users, name, info, CancellationToken.None);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending message", ex);
- }
- }
-
- public void Dispose()
- {
- _liveTvManager.TimerCancelled -= _liveTvManager_TimerCancelled;
- _liveTvManager.SeriesTimerCancelled -= _liveTvManager_SeriesTimerCancelled;
- _liveTvManager.TimerCreated -= _liveTvManager_TimerCreated;
- _liveTvManager.SeriesTimerCreated -= _liveTvManager_SeriesTimerCreated;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs b/MediaBrowser.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
deleted file mode 100644
index a0b7ff515..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/RefreshUsersMetadata.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using System.Threading;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- /// <summary>
- /// Class RefreshUsersMetadata
- /// </summary>
- public class RefreshUsersMetadata : IServerEntryPoint
- {
- /// <summary>
- /// The _user manager
- /// </summary>
- private readonly IUserManager _userManager;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RefreshUsersMetadata" /> class.
- /// </summary>
- /// <param name="userManager">The user manager.</param>
- public RefreshUsersMetadata(IUserManager userManager)
- {
- _userManager = userManager;
- }
-
- /// <summary>
- /// Runs this instance.
- /// </summary>
- public async void Run()
- {
- await _userManager.RefreshUsersMetadata(CancellationToken.None).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs
deleted file mode 100644
index 0da48a2d8..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/ServerEventNotifier.cs
+++ /dev/null
@@ -1,203 +0,0 @@
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- /// <summary>
- /// Class WebSocketEvents
- /// </summary>
- public class ServerEventNotifier : IServerEntryPoint
- {
- /// <summary>
- /// The _server manager
- /// </summary>
- private readonly IServerManager _serverManager;
-
- /// <summary>
- /// The _user manager
- /// </summary>
- private readonly IUserManager _userManager;
-
- /// <summary>
- /// The _installation manager
- /// </summary>
- private readonly IInstallationManager _installationManager;
-
- /// <summary>
- /// The _kernel
- /// </summary>
- private readonly IServerApplicationHost _appHost;
-
- /// <summary>
- /// The _task manager
- /// </summary>
- private readonly ITaskManager _taskManager;
-
- private readonly ISessionManager _sessionManager;
- private readonly ISyncManager _syncManager;
-
- public ServerEventNotifier(IServerManager serverManager, IServerApplicationHost appHost, IUserManager userManager, IInstallationManager installationManager, ITaskManager taskManager, ISessionManager sessionManager, ISyncManager syncManager)
- {
- _serverManager = serverManager;
- _userManager = userManager;
- _installationManager = installationManager;
- _appHost = appHost;
- _taskManager = taskManager;
- _sessionManager = sessionManager;
- _syncManager = syncManager;
- }
-
- public void Run()
- {
- _userManager.UserDeleted += userManager_UserDeleted;
- _userManager.UserUpdated += userManager_UserUpdated;
- _userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated;
-
- _appHost.HasPendingRestartChanged += kernel_HasPendingRestartChanged;
-
- _installationManager.PluginUninstalled += InstallationManager_PluginUninstalled;
- _installationManager.PackageInstalling += _installationManager_PackageInstalling;
- _installationManager.PackageInstallationCancelled += _installationManager_PackageInstallationCancelled;
- _installationManager.PackageInstallationCompleted += _installationManager_PackageInstallationCompleted;
- _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed;
-
- _taskManager.TaskCompleted += _taskManager_TaskCompleted;
- _syncManager.SyncJobCreated += _syncManager_SyncJobCreated;
- _syncManager.SyncJobCancelled += _syncManager_SyncJobCancelled;
- }
-
- void _syncManager_SyncJobCancelled(object sender, GenericEventArgs<SyncJob> e)
- {
- _sessionManager.SendMessageToUserDeviceSessions(e.Argument.TargetId, "SyncJobCancelled", e.Argument, CancellationToken.None);
- }
-
- void _syncManager_SyncJobCreated(object sender, GenericEventArgs<SyncJobCreationResult> e)
- {
- _sessionManager.SendMessageToUserDeviceSessions(e.Argument.Job.TargetId, "SyncJobCreated", e.Argument, CancellationToken.None);
- }
-
- void _installationManager_PackageInstalling(object sender, InstallationEventArgs e)
- {
- _serverManager.SendWebSocketMessage("PackageInstalling", e.InstallationInfo);
- }
-
- void _installationManager_PackageInstallationCancelled(object sender, InstallationEventArgs e)
- {
- _serverManager.SendWebSocketMessage("PackageInstallationCancelled", e.InstallationInfo);
- }
-
- void _installationManager_PackageInstallationCompleted(object sender, InstallationEventArgs e)
- {
- _serverManager.SendWebSocketMessage("PackageInstallationCompleted", e.InstallationInfo);
- }
-
- void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
- {
- _serverManager.SendWebSocketMessage("PackageInstallationFailed", e.InstallationInfo);
- }
-
- void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
- {
- _serverManager.SendWebSocketMessage("ScheduledTaskEnded", e.Result);
- }
-
- /// <summary>
- /// Installations the manager_ plugin uninstalled.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- void InstallationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
- {
- _serverManager.SendWebSocketMessage("PluginUninstalled", e.Argument.GetPluginInfo());
- }
-
- /// <summary>
- /// Handles the HasPendingRestartChanged event of the kernel control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
- void kernel_HasPendingRestartChanged(object sender, EventArgs e)
- {
- _sessionManager.SendRestartRequiredNotification(CancellationToken.None);
- }
-
- /// <summary>
- /// Users the manager_ user updated.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- void userManager_UserUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- SendMessageToUserSession(e.Argument, "UserUpdated", dto);
- }
-
- /// <summary>
- /// Users the manager_ user deleted.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- void userManager_UserDeleted(object sender, GenericEventArgs<User> e)
- {
- SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N"));
- }
-
- void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs<User> e)
- {
- var dto = _userManager.GetUserDto(e.Argument);
-
- SendMessageToUserSession(e.Argument, "UserConfigurationUpdated", dto);
- }
-
- private async void SendMessageToUserSession<T>(User user, string name, T data)
- {
- await _sessionManager.SendMessageToUserSessions(new List<string> { user.Id.ToString("N") }, name, data, CancellationToken.None);
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (dispose)
- {
- _userManager.UserDeleted -= userManager_UserDeleted;
- _userManager.UserUpdated -= userManager_UserUpdated;
- _userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated;
-
- _installationManager.PluginUninstalled -= InstallationManager_PluginUninstalled;
- _installationManager.PackageInstalling -= _installationManager_PackageInstalling;
- _installationManager.PackageInstallationCancelled -= _installationManager_PackageInstallationCancelled;
- _installationManager.PackageInstallationCompleted -= _installationManager_PackageInstallationCompleted;
- _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed;
-
- _appHost.HasPendingRestartChanged -= kernel_HasPendingRestartChanged;
- _syncManager.SyncJobCreated -= _syncManager_SyncJobCreated;
- _syncManager.SyncJobCancelled -= _syncManager_SyncJobCancelled;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs
deleted file mode 100644
index d14bd4368..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Configuration;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- /// <summary>
- /// Class UsageEntryPoint
- /// </summary>
- public class UsageEntryPoint : IServerEntryPoint
- {
- private readonly IApplicationHost _applicationHost;
- private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
- private readonly ISessionManager _sessionManager;
- private readonly IUserManager _userManager;
- private readonly IServerConfigurationManager _config;
-
- private readonly ConcurrentDictionary<Guid, ClientInfo> _apps = new ConcurrentDictionary<Guid, ClientInfo>();
-
- public UsageEntryPoint(ILogger logger, IApplicationHost applicationHost, IHttpClient httpClient, ISessionManager sessionManager, IUserManager userManager, IServerConfigurationManager config)
- {
- _logger = logger;
- _applicationHost = applicationHost;
- _httpClient = httpClient;
- _sessionManager = sessionManager;
- _userManager = userManager;
- _config = config;
-
- _sessionManager.SessionStarted += _sessionManager_SessionStarted;
- }
-
- void _sessionManager_SessionStarted(object sender, SessionEventArgs e)
- {
- var session = e.SessionInfo;
-
- if (!string.IsNullOrEmpty(session.Client) &&
- !string.IsNullOrEmpty(session.DeviceName) &&
- !string.IsNullOrEmpty(session.DeviceId) &&
- !string.IsNullOrEmpty(session.ApplicationVersion))
- {
- var keys = new List<string>
- {
- session.Client,
- session.DeviceName,
- session.DeviceId,
- session.ApplicationVersion
- };
-
- var key = string.Join("_", keys.ToArray()).GetMD5();
-
- _apps.GetOrAdd(key, guid => GetNewClientInfo(session));
- }
- }
-
- private async void ReportNewSession(ClientInfo client)
- {
- if (!_config.Configuration.EnableAnonymousUsageReporting)
- {
- return;
- }
-
- try
- {
- await new UsageReporter(_applicationHost, _httpClient, _userManager, _logger)
- .ReportAppUsage(client, CancellationToken.None)
- .ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending anonymous usage statistics.", ex);
- }
- }
-
- private ClientInfo GetNewClientInfo(SessionInfo session)
- {
- var info = new ClientInfo
- {
- AppName = session.Client,
- AppVersion = session.ApplicationVersion,
- DeviceName = session.DeviceName,
- DeviceId = session.DeviceId
- };
-
- ReportNewSession(info);
-
- return info;
- }
-
- public async void Run()
- {
- await Task.Delay(5000).ConfigureAwait(false);
- OnTimerFired();
- }
-
- /// <summary>
- /// Called when [timer fired].
- /// </summary>
- private async void OnTimerFired()
- {
- if (!_config.Configuration.EnableAnonymousUsageReporting)
- {
- return;
- }
-
- try
- {
- await new UsageReporter(_applicationHost, _httpClient, _userManager, _logger)
- .ReportServerUsage(CancellationToken.None)
- .ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending anonymous usage statistics.", ex);
- }
- }
-
- public void Dispose()
- {
- _sessionManager.SessionStarted -= _sessionManager_SessionStarted;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs b/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs
deleted file mode 100644
index e445300e4..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs
+++ /dev/null
@@ -1,138 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Connect;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- public class UsageReporter
- {
- private readonly IApplicationHost _applicationHost;
- private readonly IHttpClient _httpClient;
- private readonly IUserManager _userManager;
- private readonly ILogger _logger;
- private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
-
- public UsageReporter(IApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager, ILogger logger)
- {
- _applicationHost = applicationHost;
- _httpClient = httpClient;
- _userManager = userManager;
- _logger = logger;
- }
-
- public async Task ReportServerUsage(CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var data = new Dictionary<string, string>
- {
- { "feature", _applicationHost.Name },
- { "mac", _applicationHost.SystemId },
- { "serverid", _applicationHost.SystemId },
- { "deviceid", _applicationHost.SystemId },
- { "ver", _applicationHost.ApplicationVersion.ToString() },
- { "platform", _applicationHost.OperatingSystemDisplayName },
- { "isservice", _applicationHost.IsRunningAsService.ToString().ToLower()}
- };
-
- var users = _userManager.Users.ToList();
-
- data["localusers"] = users.Count(i => !i.ConnectLinkType.HasValue).ToString(CultureInfo.InvariantCulture);
- data["guests"] = users.Count(i => i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.Guest).ToString(CultureInfo.InvariantCulture);
- data["linkedusers"] = users.Count(i => i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.LinkedUser).ToString(CultureInfo.InvariantCulture);
-
- data["plugins"] = string.Join(",", _applicationHost.Plugins.Select(i => i.Id).ToArray());
-
- var logErrors = false;
-#if DEBUG
- logErrors = true;
-#endif
- var options = new HttpRequestOptions
- {
- Url = MbAdminUrl + "service/registration/ping",
- CancellationToken = cancellationToken,
-
- // Seeing block length errors
- EnableHttpCompression = false,
-
- LogRequest = false,
- LogErrors = logErrors,
- BufferContent = false
- };
-
- options.SetPostData(data);
-
- using (var response = await _httpClient.SendAsync(options, "POST").ConfigureAwait(false))
- {
-
- }
- }
-
- public async Task ReportAppUsage(ClientInfo app, CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(app.DeviceId))
- {
- throw new ArgumentException("Client info must have a device Id");
- }
-
- _logger.Info("App Activity: app: {0}, version: {1}, deviceId: {2}, deviceName: {3}",
- app.AppName ?? "Unknown App",
- app.AppVersion ?? "Unknown",
- app.DeviceId,
- app.DeviceName ?? "Unknown");
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var data = new Dictionary<string, string>
- {
- { "feature", app.AppName ?? "Unknown App" },
- { "serverid", _applicationHost.SystemId },
- { "deviceid", app.DeviceId },
- { "mac", app.DeviceId },
- { "ver", app.AppVersion ?? "Unknown" },
- { "platform", app.DeviceName },
- };
-
- var logErrors = false;
-
-#if DEBUG
- logErrors = true;
-#endif
- var options = new HttpRequestOptions
- {
- Url = MbAdminUrl + "service/registration/ping",
- CancellationToken = cancellationToken,
-
- // Seeing block length errors
- EnableHttpCompression = false,
-
- LogRequest = false,
- LogErrors = logErrors,
- BufferContent = false
- };
-
- options.SetPostData(data);
-
- using (var response = await _httpClient.SendAsync(options, "POST").ConfigureAwait(false))
- {
-
- }
- }
- }
-
- public class ClientInfo
- {
- public string AppName { get; set; }
- public string AppVersion { get; set; }
- public string DeviceName { get; set; }
- public string DeviceId { get; set; }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
deleted file mode 100644
index 8262048b1..000000000
--- a/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Session;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Server.Implementations.EntryPoints
-{
- class UserDataChangeNotifier : IServerEntryPoint
- {
- private readonly ISessionManager _sessionManager;
- private readonly ILogger _logger;
- private readonly IUserDataManager _userDataManager;
- private readonly IUserManager _userManager;
-
- private readonly object _syncLock = new object();
- private Timer UpdateTimer { get; set; }
- private const int UpdateDuration = 500;
-
- private readonly Dictionary<Guid, List<IHasUserData>> _changedItems = new Dictionary<Guid, List<IHasUserData>>();
-
- public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, ILogger logger, IUserManager userManager)
- {
- _userDataManager = userDataManager;
- _sessionManager = sessionManager;
- _logger = logger;
- _userManager = userManager;
- }
-
- public void Run()
- {
- _userDataManager.UserDataSaved += _userDataManager_UserDataSaved;
- }
-
- void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
- {
- if (e.SaveReason == UserDataSaveReason.PlaybackProgress)
- {
- return;
- }
-
- lock (_syncLock)
- {
- if (UpdateTimer == null)
- {
- UpdateTimer = new Timer(UpdateTimerCallback, null, UpdateDuration,
- Timeout.Infinite);
- }
- else
- {
- UpdateTimer.Change(UpdateDuration, Timeout.Infinite);
- }
-
- List<IHasUserData> keys;
-
- if (!_changedItems.TryGetValue(e.UserId, out keys))
- {
- keys = new List<IHasUserData>();
- _changedItems[e.UserId] = keys;
- }
-
- keys.Add(e.Item);
-
- var baseItem = e.Item as BaseItem;
-
- // Go up one level for indicators
- if (baseItem != null)
- {
- var parent = baseItem.GetParent();
-
- if (parent != null)
- {
- keys.Add(parent);
- }
- }
- }
- }
-
- private void UpdateTimerCallback(object state)
- {
- lock (_syncLock)
- {
- // Remove dupes in case some were saved multiple times
- var changes = _changedItems.ToList();
- _changedItems.Clear();
-
- var task = SendNotifications(changes, CancellationToken.None);
-
- if (UpdateTimer != null)
- {
- UpdateTimer.Dispose();
- UpdateTimer = null;
- }
- }
- }
-
- private async Task SendNotifications(IEnumerable<KeyValuePair<Guid, List<IHasUserData>>> changes, CancellationToken cancellationToken)
- {
- foreach (var pair in changes)
- {
- var userId = pair.Key;
- var userSessions = _sessionManager.Sessions
- .Where(u => u.ContainsUser(userId) && u.SessionController != null && u.IsActive)
- .ToList();
-
- if (userSessions.Count > 0)
- {
- var user = _userManager.GetUserById(userId);
-
- var dtoList = pair.Value
- .DistinctBy(i => i.Id)
- .Select(i =>
- {
- var dto = _userDataManager.GetUserDataDto(i, user).Result;
- dto.ItemId = i.Id.ToString("N");
- return dto;
- })
- .ToList();
-
- var info = new UserDataChangeInfo
- {
- UserId = userId.ToString("N"),
-
- UserDataList = dtoList
- };
-
- foreach (var userSession in userSessions)
- {
- try
- {
- await userSession.SessionController.SendUserDataChangeInfo(info, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending UserDataChanged message", ex);
- }
- }
- }
-
- }
- }
-
- public void Dispose()
- {
- if (UpdateTimer != null)
- {
- UpdateTimer.Dispose();
- UpdateTimer = null;
- }
-
- _userDataManager.UserDataSaved -= _userDataManager_UserDataSaved;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
deleted file mode 100644
index 19592bc4e..000000000
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ /dev/null
@@ -1,831 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations.Library;
-using MediaBrowser.Server.Implementations.Logging;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.FileOrganization
-{
- public class EpisodeFileOrganizer
- {
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IFileOrganizationService _organizationService;
- private readonly IServerConfigurationManager _config;
- private readonly IProviderManager _providerManager;
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- public EpisodeFileOrganizer(IFileOrganizationService organizationService, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IProviderManager providerManager)
- {
- _organizationService = organizationService;
- _config = config;
- _fileSystem = fileSystem;
- _logger = logger;
- _libraryManager = libraryManager;
- _libraryMonitor = libraryMonitor;
- _providerManager = providerManager;
- }
-
- public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, AutoOrganizeOptions options, bool overwriteExisting, CancellationToken cancellationToken)
- {
- _logger.Info("Sorting file {0}", path);
-
- var result = new FileOrganizationResult
- {
- Date = DateTime.UtcNow,
- OriginalPath = path,
- OriginalFileName = Path.GetFileName(path),
- Type = FileOrganizerType.Episode,
- FileSize = new FileInfo(path).Length
- };
-
- try
- {
- if (_libraryMonitor.IsPathLocked(path))
- {
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = "Path is locked by other processes. Please try again later.";
- return result;
- }
-
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
- var resolver = new Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger());
-
- var episodeInfo = resolver.Resolve(path, false) ??
- new Naming.TV.EpisodeInfo();
-
- var seriesName = episodeInfo.SeriesName;
-
- if (!string.IsNullOrEmpty(seriesName))
- {
- var seasonNumber = episodeInfo.SeasonNumber;
-
- result.ExtractedSeasonNumber = seasonNumber;
-
- // Passing in true will include a few extra regex's
- var episodeNumber = episodeInfo.EpisodeNumber;
-
- result.ExtractedEpisodeNumber = episodeNumber;
-
- var premiereDate = episodeInfo.IsByDate ?
- new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value) :
- (DateTime?)null;
-
- if (episodeInfo.IsByDate || (seasonNumber.HasValue && episodeNumber.HasValue))
- {
- if (episodeInfo.IsByDate)
- {
- _logger.Debug("Extracted information from {0}. Series name {1}, Date {2}", path, seriesName, premiereDate.Value);
- }
- else
- {
- _logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, seasonNumber, episodeNumber);
- }
-
- var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
-
- result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
-
- await OrganizeEpisode(path,
- seriesName,
- seasonNumber,
- episodeNumber,
- endingEpisodeNumber,
- premiereDate,
- options,
- overwriteExisting,
- false,
- result,
- cancellationToken).ConfigureAwait(false);
- }
- else
- {
- var msg = string.Format("Unable to determine episode number from {0}", path);
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = msg;
- _logger.Warn(msg);
- }
- }
- else
- {
- var msg = string.Format("Unable to determine series name from {0}", path);
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = msg;
- _logger.Warn(msg);
- }
-
- var previousResult = _organizationService.GetResultBySourcePath(path);
-
- if (previousResult != null)
- {
- // Don't keep saving the same result over and over if nothing has changed
- if (previousResult.Status == result.Status && previousResult.StatusMessage == result.StatusMessage && result.Status != FileSortingStatus.Success)
- {
- return previousResult;
- }
- }
-
- await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = ex.Message;
- }
-
- return result;
- }
-
- public async Task<FileOrganizationResult> OrganizeWithCorrection(EpisodeFileOrganizationRequest request, AutoOrganizeOptions options, CancellationToken cancellationToken)
- {
- var result = _organizationService.GetResult(request.ResultId);
-
- try
- {
- Series series = null;
-
- if (request.NewSeriesProviderIds.Count > 0)
- {
- // We're having a new series here
- SeriesInfo seriesRequest = new SeriesInfo();
- seriesRequest.ProviderIds = request.NewSeriesProviderIds;
-
- var refreshOptions = new MetadataRefreshOptions(_fileSystem);
- series = new Series();
- series.Id = Guid.NewGuid();
- series.Name = request.NewSeriesName;
-
- int year;
- if (int.TryParse(request.NewSeriesYear, out year))
- {
- series.ProductionYear = year;
- }
-
- var seriesFolderName = series.Name;
- if (series.ProductionYear.HasValue)
- {
- seriesFolderName = string.Format("{0} ({1})", seriesFolderName, series.ProductionYear);
- }
-
- series.Path = Path.Combine(request.TargetFolder, seriesFolderName);
-
- series.ProviderIds = request.NewSeriesProviderIds;
-
- await series.RefreshMetadata(refreshOptions, cancellationToken);
- }
-
- if (series == null)
- {
- // Existing Series
- series = (Series)_libraryManager.GetItemById(new Guid(request.SeriesId));
- }
-
- await OrganizeEpisode(result.OriginalPath,
- series,
- request.SeasonNumber,
- request.EpisodeNumber,
- request.EndingEpisodeNumber,
- null,
- options,
- true,
- request.RememberCorrection,
- result,
- cancellationToken).ConfigureAwait(false);
-
- await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = ex.Message;
- }
-
- return result;
- }
-
- private Task OrganizeEpisode(string sourcePath,
- string seriesName,
- int? seasonNumber,
- int? episodeNumber,
- int? endingEpiosdeNumber,
- DateTime? premiereDate,
- AutoOrganizeOptions options,
- bool overwriteExisting,
- bool rememberCorrection,
- FileOrganizationResult result,
- CancellationToken cancellationToken)
- {
- var series = GetMatchingSeries(seriesName, result, options);
-
- if (series == null)
- {
- var msg = string.Format("Unable to find series in library matching name {0}", seriesName);
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = msg;
- _logger.Warn(msg);
- return Task.FromResult(true);
- }
-
- return OrganizeEpisode(sourcePath,
- series,
- seasonNumber,
- episodeNumber,
- endingEpiosdeNumber,
- premiereDate,
- options,
- overwriteExisting,
- rememberCorrection,
- result,
- cancellationToken);
- }
-
- private async Task OrganizeEpisode(string sourcePath,
- Series series,
- int? seasonNumber,
- int? episodeNumber,
- int? endingEpiosdeNumber,
- DateTime? premiereDate,
- AutoOrganizeOptions options,
- bool overwriteExisting,
- bool rememberCorrection,
- FileOrganizationResult result,
- CancellationToken cancellationToken)
- {
- _logger.Info("Sorting file {0} into series {1}", sourcePath, series.Path);
-
- var originalExtractedSeriesString = result.ExtractedName;
-
- bool isNew = string.IsNullOrWhiteSpace(result.Id);
-
- if (isNew)
- {
- await _organizationService.SaveResult(result, cancellationToken);
- }
-
- if (!_organizationService.AddToInProgressList(result, isNew))
- {
- throw new Exception("File is currently processed otherwise. Please try again later.");
- }
-
- try
- {
- // Proceed to sort the file
- var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);
-
- if (string.IsNullOrEmpty(newPath))
- {
- var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
- throw new Exception(msg);
- }
-
- _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
- result.TargetPath = newPath;
-
- var fileExists = _fileSystem.FileExists(result.TargetPath);
- var otherDuplicatePaths = GetOtherDuplicatePaths(result.TargetPath, series, seasonNumber, episodeNumber, endingEpiosdeNumber);
-
- if (!overwriteExisting)
- {
- if (options.TvOptions.CopyOriginalFile && fileExists && IsSameEpisode(sourcePath, newPath))
- {
- var msg = string.Format("File '{0}' already copied to new path '{1}', stopping organization", sourcePath, newPath);
- _logger.Info(msg);
- result.Status = FileSortingStatus.SkippedExisting;
- result.StatusMessage = msg;
- return;
- }
-
- if (fileExists)
- {
- var msg = string.Format("File '{0}' already exists as '{1}', stopping organization", sourcePath, newPath);
- _logger.Info(msg);
- result.Status = FileSortingStatus.SkippedExisting;
- result.StatusMessage = msg;
- result.TargetPath = newPath;
- return;
- }
-
- if (otherDuplicatePaths.Count > 0)
- {
- var msg = string.Format("File '{0}' already exists as these:'{1}'. Stopping organization", sourcePath, string.Join("', '", otherDuplicatePaths));
- _logger.Info(msg);
- result.Status = FileSortingStatus.SkippedExisting;
- result.StatusMessage = msg;
- result.DuplicatePaths = otherDuplicatePaths;
- return;
- }
- }
-
- PerformFileSorting(options.TvOptions, result);
-
- if (overwriteExisting)
- {
- var hasRenamedFiles = false;
-
- foreach (var path in otherDuplicatePaths)
- {
- _logger.Debug("Removing duplicate episode {0}", path);
-
- _libraryMonitor.ReportFileSystemChangeBeginning(path);
-
- var renameRelatedFiles = !hasRenamedFiles &&
- string.Equals(Path.GetDirectoryName(path), Path.GetDirectoryName(result.TargetPath), StringComparison.OrdinalIgnoreCase);
-
- if (renameRelatedFiles)
- {
- hasRenamedFiles = true;
- }
-
- try
- {
- DeleteLibraryFile(path, renameRelatedFiles, result.TargetPath);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error removing duplicate episode", ex, path);
- }
- finally
- {
- _libraryMonitor.ReportFileSystemChangeComplete(path, true);
- }
- }
- }
- }
- catch (Exception ex)
- {
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = ex.Message;
- _logger.Warn(ex.Message);
- return;
- }
- finally
- {
- _organizationService.RemoveFromInprogressList(result);
- }
-
- if (rememberCorrection)
- {
- SaveSmartMatchString(originalExtractedSeriesString, series, options);
- }
- }
-
- private void SaveSmartMatchString(string matchString, Series series, AutoOrganizeOptions options)
- {
- if (string.IsNullOrEmpty(matchString) || matchString.Length < 3)
- {
- return;
- }
-
- SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(i => string.Equals(i.ItemName, series.Name, StringComparison.OrdinalIgnoreCase));
-
- if (info == null)
- {
- info = new SmartMatchInfo();
- info.ItemName = series.Name;
- info.OrganizerType = FileOrganizerType.Episode;
- info.DisplayName = series.Name;
- var list = options.SmartMatchInfos.ToList();
- list.Add(info);
- options.SmartMatchInfos = list.ToArray();
- }
-
- if (!info.MatchStrings.Contains(matchString, StringComparer.OrdinalIgnoreCase))
- {
- var list = info.MatchStrings.ToList();
- list.Add(matchString);
- info.MatchStrings = list.ToArray();
- _config.SaveAutoOrganizeOptions(options);
- }
- }
-
- private void DeleteLibraryFile(string path, bool renameRelatedFiles, string targetPath)
- {
- _fileSystem.DeleteFile(path);
-
- if (!renameRelatedFiles)
- {
- return;
- }
-
- // Now find other files
- var originalFilenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
- var directory = Path.GetDirectoryName(path);
-
- if (!string.IsNullOrWhiteSpace(originalFilenameWithoutExtension) && !string.IsNullOrWhiteSpace(directory))
- {
- // Get all related files, e.g. metadata, images, etc
- var files = _fileSystem.GetFilePaths(directory)
- .Where(i => (Path.GetFileNameWithoutExtension(i) ?? string.Empty).StartsWith(originalFilenameWithoutExtension, StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- var targetFilenameWithoutExtension = Path.GetFileNameWithoutExtension(targetPath);
-
- foreach (var file in files)
- {
- directory = Path.GetDirectoryName(file);
- var filename = Path.GetFileName(file);
-
- filename = filename.Replace(originalFilenameWithoutExtension, targetFilenameWithoutExtension,
- StringComparison.OrdinalIgnoreCase);
-
- var destination = Path.Combine(directory, filename);
-
- _fileSystem.MoveFile(file, destination);
- }
- }
- }
-
- private List<string> GetOtherDuplicatePaths(string targetPath,
- Series series,
- int? seasonNumber,
- int? episodeNumber,
- int? endingEpisodeNumber)
- {
- // TODO: Support date-naming?
- if (!seasonNumber.HasValue || !episodeNumber.HasValue)
- {
- return new List<string>();
- }
-
- var episodePaths = series.GetRecursiveChildren()
- .OfType<Episode>()
- .Where(i =>
- {
- var locationType = i.LocationType;
-
- // Must be file system based and match exactly
- if (locationType != LocationType.Remote &&
- locationType != LocationType.Virtual &&
- i.ParentIndexNumber.HasValue &&
- i.ParentIndexNumber.Value == seasonNumber &&
- i.IndexNumber.HasValue &&
- i.IndexNumber.Value == episodeNumber)
- {
-
- if (endingEpisodeNumber.HasValue || i.IndexNumberEnd.HasValue)
- {
- return endingEpisodeNumber.HasValue && i.IndexNumberEnd.HasValue &&
- endingEpisodeNumber.Value == i.IndexNumberEnd.Value;
- }
-
- return true;
- }
-
- return false;
- })
- .Select(i => i.Path)
- .ToList();
-
- var folder = Path.GetDirectoryName(targetPath);
- var targetFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(targetPath);
-
- try
- {
- var filesOfOtherExtensions = _fileSystem.GetFilePaths(folder)
- .Where(i => _libraryManager.IsVideoFile(i) && string.Equals(_fileSystem.GetFileNameWithoutExtension(i), targetFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase));
-
- episodePaths.AddRange(filesOfOtherExtensions);
- }
- catch (DirectoryNotFoundException)
- {
- // No big deal. Maybe the season folder doesn't already exist.
- }
-
- return episodePaths.Where(i => !string.Equals(i, targetPath, StringComparison.OrdinalIgnoreCase))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
- }
-
- private void PerformFileSorting(TvFileOrganizationOptions options, FileOrganizationResult result)
- {
- _libraryMonitor.ReportFileSystemChangeBeginning(result.TargetPath);
-
- _fileSystem.CreateDirectory(Path.GetDirectoryName(result.TargetPath));
-
- var targetAlreadyExists = _fileSystem.FileExists(result.TargetPath);
-
- try
- {
- if (targetAlreadyExists || options.CopyOriginalFile)
- {
- _fileSystem.CopyFile(result.OriginalPath, result.TargetPath, true);
- }
- else
- {
- _fileSystem.MoveFile(result.OriginalPath, result.TargetPath);
- }
-
- result.Status = FileSortingStatus.Success;
- result.StatusMessage = string.Empty;
- }
- catch (Exception ex)
- {
- var errorMsg = string.Format("Failed to move file from {0} to {1}: {2}", result.OriginalPath, result.TargetPath, ex.Message);
-
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = errorMsg;
- _logger.ErrorException(errorMsg, ex);
-
- return;
- }
- finally
- {
- _libraryMonitor.ReportFileSystemChangeComplete(result.TargetPath, true);
- }
-
- if (targetAlreadyExists && !options.CopyOriginalFile)
- {
- try
- {
- _fileSystem.DeleteFile(result.OriginalPath);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath);
- }
- }
- }
-
- private Series GetMatchingSeries(string seriesName, FileOrganizationResult result, AutoOrganizeOptions options)
- {
- var parsedName = _libraryManager.ParseName(seriesName);
-
- var yearInName = parsedName.Year;
- var nameWithoutYear = parsedName.Name;
-
- result.ExtractedName = nameWithoutYear;
- result.ExtractedYear = yearInName;
-
- var series = _libraryManager.GetItemList(new Controller.Entities.InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Recursive = true
- })
- .Cast<Series>()
- .Select(i => NameUtils.GetMatchScore(nameWithoutYear, yearInName, i))
- .Where(i => i.Item2 > 0)
- .OrderByDescending(i => i.Item2)
- .Select(i => i.Item1)
- .FirstOrDefault();
-
- if (series == null)
- {
- SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(e => e.MatchStrings.Contains(nameWithoutYear, StringComparer.OrdinalIgnoreCase));
-
- if (info != null)
- {
- series = _libraryManager.GetItemList(new Controller.Entities.InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Recursive = true,
- Name = info.ItemName
-
- }).Cast<Series>().FirstOrDefault();
- }
- }
-
- return series;
- }
-
- /// <summary>
- /// Gets the new path.
- /// </summary>
- /// <param name="sourcePath">The source path.</param>
- /// <param name="series">The series.</param>
- /// <param name="seasonNumber">The season number.</param>
- /// <param name="episodeNumber">The episode number.</param>
- /// <param name="endingEpisodeNumber">The ending episode number.</param>
- /// <param name="premiereDate">The premiere date.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>System.String.</returns>
- private async Task<string> GetNewPath(string sourcePath,
- Series series,
- int? seasonNumber,
- int? episodeNumber,
- int? endingEpisodeNumber,
- DateTime? premiereDate,
- TvFileOrganizationOptions options,
- CancellationToken cancellationToken)
- {
- var episodeInfo = new EpisodeInfo
- {
- IndexNumber = episodeNumber,
- IndexNumberEnd = endingEpisodeNumber,
- MetadataCountryCode = series.GetPreferredMetadataCountryCode(),
- MetadataLanguage = series.GetPreferredMetadataLanguage(),
- ParentIndexNumber = seasonNumber,
- SeriesProviderIds = series.ProviderIds,
- PremiereDate = premiereDate
- };
-
- var searchResults = await _providerManager.GetRemoteSearchResults<Episode, EpisodeInfo>(new RemoteSearchQuery<EpisodeInfo>
- {
- SearchInfo = episodeInfo
-
- }, cancellationToken).ConfigureAwait(false);
-
- var episode = searchResults.FirstOrDefault();
-
- if (episode == null)
- {
- var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
- _logger.Warn(msg);
- throw new Exception(msg);
- }
-
- var episodeName = episode.Name;
-
- //if (string.IsNullOrWhiteSpace(episodeName))
- //{
- // var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
- // _logger.Warn(msg);
- // return null;
- //}
-
- seasonNumber = seasonNumber ?? episode.ParentIndexNumber;
- episodeNumber = episodeNumber ?? episode.IndexNumber;
-
- var newPath = GetSeasonFolderPath(series, seasonNumber.Value, options);
-
- // MAX_PATH - trailing <NULL> charachter - drive component: 260 - 1 - 3 = 256
- // Usually newPath would include the drive component, but use 256 to be sure
- var maxFilenameLength = 256 - newPath.Length;
-
- if (!newPath.EndsWith(@"\"))
- {
- // Remove 1 for missing backslash combining path and filename
- maxFilenameLength--;
- }
-
- // Remove additional 4 chars to prevent PathTooLongException for downloaded subtitles (eg. filename.ext.eng.srt)
- maxFilenameLength -= 4;
-
- var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber.Value, episodeNumber.Value, endingEpisodeNumber, episodeName, options, maxFilenameLength);
-
- if (string.IsNullOrEmpty(episodeFileName))
- {
- // cause failure
- return string.Empty;
- }
-
- newPath = Path.Combine(newPath, episodeFileName);
-
- return newPath;
- }
-
- /// <summary>
- /// Gets the season folder path.
- /// </summary>
- /// <param name="series">The series.</param>
- /// <param name="seasonNumber">The season number.</param>
- /// <param name="options">The options.</param>
- /// <returns>System.String.</returns>
- private string GetSeasonFolderPath(Series series, int seasonNumber, TvFileOrganizationOptions options)
- {
- // If there's already a season folder, use that
- var season = series
- .GetRecursiveChildren(i => i is Season && i.LocationType == LocationType.FileSystem && i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber)
- .FirstOrDefault();
-
- if (season != null)
- {
- return season.Path;
- }
-
- var path = series.Path;
-
- if (series.ContainsEpisodesWithoutSeasonFolders)
- {
- return path;
- }
-
- if (seasonNumber == 0)
- {
- return Path.Combine(path, _fileSystem.GetValidFilename(options.SeasonZeroFolderName));
- }
-
- var seasonFolderName = options.SeasonFolderPattern
- .Replace("%s", seasonNumber.ToString(_usCulture))
- .Replace("%0s", seasonNumber.ToString("00", _usCulture))
- .Replace("%00s", seasonNumber.ToString("000", _usCulture));
-
- return Path.Combine(path, _fileSystem.GetValidFilename(seasonFolderName));
- }
-
- private string GetEpisodeFileName(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, string episodeTitle, TvFileOrganizationOptions options, int? maxLength)
- {
- seriesName = _fileSystem.GetValidFilename(seriesName).Trim();
-
- if (string.IsNullOrWhiteSpace(episodeTitle))
- {
- episodeTitle = string.Empty;
- }
- else
- {
- episodeTitle = _fileSystem.GetValidFilename(episodeTitle).Trim();
- }
-
- var sourceExtension = (Path.GetExtension(sourcePath) ?? string.Empty).TrimStart('.');
-
- var pattern = endingEpisodeNumber.HasValue ? options.MultiEpisodeNamePattern : options.EpisodeNamePattern;
-
- if (string.IsNullOrWhiteSpace(pattern))
- {
- throw new Exception("GetEpisodeFileName: Configured episode name pattern is empty!");
- }
-
- var result = pattern.Replace("%sn", seriesName)
- .Replace("%s.n", seriesName.Replace(" ", "."))
- .Replace("%s_n", seriesName.Replace(" ", "_"))
- .Replace("%s", seasonNumber.ToString(_usCulture))
- .Replace("%0s", seasonNumber.ToString("00", _usCulture))
- .Replace("%00s", seasonNumber.ToString("000", _usCulture))
- .Replace("%ext", sourceExtension)
- .Replace("%en", "%#1")
- .Replace("%e.n", "%#2")
- .Replace("%e_n", "%#3");
-
- if (endingEpisodeNumber.HasValue)
- {
- result = result.Replace("%ed", endingEpisodeNumber.Value.ToString(_usCulture))
- .Replace("%0ed", endingEpisodeNumber.Value.ToString("00", _usCulture))
- .Replace("%00ed", endingEpisodeNumber.Value.ToString("000", _usCulture));
- }
-
- result = result.Replace("%e", episodeNumber.ToString(_usCulture))
- .Replace("%0e", episodeNumber.ToString("00", _usCulture))
- .Replace("%00e", episodeNumber.ToString("000", _usCulture));
-
- if (maxLength.HasValue && result.Contains("%#"))
- {
- // Substract 3 for the temp token length (%#1, %#2 or %#3)
- int maxRemainingTitleLength = maxLength.Value - result.Length + 3;
- string shortenedEpisodeTitle = string.Empty;
-
- if (maxRemainingTitleLength > 5)
- {
- // A title with fewer than 5 letters wouldn't be of much value
- shortenedEpisodeTitle = episodeTitle.Substring(0, Math.Min(maxRemainingTitleLength, episodeTitle.Length));
- }
-
- result = result.Replace("%#1", shortenedEpisodeTitle)
- .Replace("%#2", shortenedEpisodeTitle.Replace(" ", "."))
- .Replace("%#3", shortenedEpisodeTitle.Replace(" ", "_"));
- }
-
- if (maxLength.HasValue && result.Length > maxLength.Value)
- {
- // There may be cases where reducing the title length may still not be sufficient to
- // stay below maxLength
- var msg = string.Format("Unable to generate an episode file name shorter than {0} characters to constrain to the max path limit", maxLength);
- throw new Exception(msg);
- }
-
- return result;
- }
-
- private bool IsSameEpisode(string sourcePath, string newPath)
- {
- try
- {
- var sourceFileInfo = new FileInfo(sourcePath);
- var destinationFileInfo = new FileInfo(newPath);
-
- if (sourceFileInfo.Length == destinationFileInfo.Length)
- {
- return true;
- }
- }
- catch (FileNotFoundException)
- {
- return false;
- }
- catch (DirectoryNotFoundException)
- {
- return false;
- }
-
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs b/MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs
deleted file mode 100644
index c560152db..000000000
--- a/MediaBrowser.Server.Implementations/FileOrganization/Extensions.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.FileOrganization;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Server.Implementations.FileOrganization
-{
- public static class ConfigurationExtension
- {
- public static AutoOrganizeOptions GetAutoOrganizeOptions(this IConfigurationManager manager)
- {
- return manager.GetConfiguration<AutoOrganizeOptions>("autoorganize");
- }
- public static void SaveAutoOrganizeOptions(this IConfigurationManager manager, AutoOrganizeOptions options)
- {
- manager.SaveConfiguration("autoorganize", options);
- }
- }
-
- public class AutoOrganizeOptionsFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- Key = "autoorganize",
- ConfigurationType = typeof (AutoOrganizeOptions)
- }
- };
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs
deleted file mode 100644
index 141dcf9b4..000000000
--- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationNotifier.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.FileOrganization
-{
- /// <summary>
- /// Class SessionInfoWebSocketListener
- /// </summary>
- class FileOrganizationNotifier : IServerEntryPoint
- {
- private readonly IFileOrganizationService _organizationService;
- private readonly ISessionManager _sessionManager;
- private readonly ITaskManager _taskManager;
-
- public FileOrganizationNotifier(ILogger logger, IFileOrganizationService organizationService, ISessionManager sessionManager, ITaskManager taskManager)
- {
- _organizationService = organizationService;
- _sessionManager = sessionManager;
- _taskManager = taskManager;
- }
-
- public void Run()
- {
- _organizationService.ItemAdded += _organizationService_ItemAdded;
- _organizationService.ItemRemoved += _organizationService_ItemRemoved;
- _organizationService.ItemUpdated += _organizationService_ItemUpdated;
- _organizationService.LogReset += _organizationService_LogReset;
-
- //_taskManager.TaskCompleted += _taskManager_TaskCompleted;
- }
-
- private void _organizationService_LogReset(object sender, EventArgs e)
- {
- _sessionManager.SendMessageToAdminSessions("AutoOrganize_LogReset", (FileOrganizationResult)null, CancellationToken.None);
- }
-
- private void _organizationService_ItemUpdated(object sender, GenericEventArgs<FileOrganizationResult> e)
- {
- _sessionManager.SendMessageToAdminSessions("AutoOrganize_ItemUpdated", e.Argument, CancellationToken.None);
- }
-
- private void _organizationService_ItemRemoved(object sender, GenericEventArgs<FileOrganizationResult> e)
- {
- _sessionManager.SendMessageToAdminSessions("AutoOrganize_ItemRemoved", e.Argument, CancellationToken.None);
- }
-
- private void _organizationService_ItemAdded(object sender, GenericEventArgs<FileOrganizationResult> e)
- {
- _sessionManager.SendMessageToAdminSessions("AutoOrganize_ItemAdded", e.Argument, CancellationToken.None);
- }
-
- //private void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
- //{
- // var taskWithKey = e.Task.ScheduledTask as IHasKey;
- // if (taskWithKey != null && taskWithKey.Key == "AutoOrganize")
- // {
- // _sessionManager.SendMessageToAdminSessions("AutoOrganize_TaskCompleted", (FileOrganizationResult)null, CancellationToken.None);
- // }
- //}
-
- public void Dispose()
- {
- _organizationService.ItemAdded -= _organizationService_ItemAdded;
- _organizationService.ItemRemoved -= _organizationService_ItemRemoved;
- _organizationService.ItemUpdated -= _organizationService_ItemUpdated;
- _organizationService.LogReset -= _organizationService_LogReset;
-
- //_taskManager.TaskCompleted -= _taskManager_TaskCompleted;
- }
-
-
- }
-}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
deleted file mode 100644
index de33c39e6..000000000
--- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
+++ /dev/null
@@ -1,283 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Concurrent;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.FileOrganization
-{
- public class FileOrganizationService : IFileOrganizationService
- {
- private readonly ITaskManager _taskManager;
- private readonly IFileOrganizationRepository _repo;
- private readonly ILogger _logger;
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
- private readonly IProviderManager _providerManager;
- private readonly ConcurrentDictionary<string, bool> _inProgressItemIds = new ConcurrentDictionary<string, bool>();
-
- public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemAdded;
- public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemUpdated;
- public event EventHandler<GenericEventArgs<FileOrganizationResult>> ItemRemoved;
- public event EventHandler LogReset;
-
- public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem, IProviderManager providerManager)
- {
- _taskManager = taskManager;
- _repo = repo;
- _logger = logger;
- _libraryMonitor = libraryMonitor;
- _libraryManager = libraryManager;
- _config = config;
- _fileSystem = fileSystem;
- _providerManager = providerManager;
- }
-
- public void BeginProcessNewFiles()
- {
- _taskManager.CancelIfRunningAndQueue<OrganizerScheduledTask>();
- }
-
- public Task SaveResult(FileOrganizationResult result, CancellationToken cancellationToken)
- {
- if (result == null || string.IsNullOrEmpty(result.OriginalPath))
- {
- throw new ArgumentNullException("result");
- }
-
- result.Id = result.OriginalPath.GetMD5().ToString("N");
-
- return _repo.SaveResult(result, cancellationToken);
- }
-
- public QueryResult<FileOrganizationResult> GetResults(FileOrganizationResultQuery query)
- {
- var results = _repo.GetResults(query);
-
- foreach (var result in results.Items)
- {
- result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
- }
-
- return results;
- }
-
- public FileOrganizationResult GetResult(string id)
- {
- var result = _repo.GetResult(id);
-
- if (result != null)
- {
- result.IsInProgress = _inProgressItemIds.ContainsKey(result.Id);
- }
-
- return result;
- }
-
- public FileOrganizationResult GetResultBySourcePath(string path)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException("path");
- }
-
- var id = path.GetMD5().ToString("N");
-
- return GetResult(id);
- }
-
- public async Task DeleteOriginalFile(string resultId)
- {
- var result = _repo.GetResult(resultId);
-
- _logger.Info("Requested to delete {0}", result.OriginalPath);
-
- if (!AddToInProgressList(result, false))
- {
- throw new Exception("Path is currently processed otherwise. Please try again later.");
- }
-
- try
- {
- _fileSystem.DeleteFile(result.OriginalPath);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting {0}", ex, result.OriginalPath);
- }
- finally
- {
- RemoveFromInprogressList(result);
- }
-
- await _repo.Delete(resultId);
-
- EventHelper.FireEventIfNotNull(ItemRemoved, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
- }
-
- private AutoOrganizeOptions GetAutoOrganizeOptions()
- {
- return _config.GetAutoOrganizeOptions();
- }
-
- public async Task PerformOrganization(string resultId)
- {
- var result = _repo.GetResult(resultId);
-
- if (string.IsNullOrEmpty(result.TargetPath))
- {
- throw new ArgumentException("No target path available.");
- }
-
- var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
- _libraryMonitor, _providerManager);
-
- var organizeResult = await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeOptions(), true, CancellationToken.None)
- .ConfigureAwait(false);
-
- if (organizeResult.Status != FileSortingStatus.Success)
- {
- throw new Exception(result.StatusMessage);
- }
- }
-
- public async Task ClearLog()
- {
- await _repo.DeleteAll();
- EventHelper.FireEventIfNotNull(LogReset, this, EventArgs.Empty, _logger);
- }
-
- public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request)
- {
- var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
- _libraryMonitor, _providerManager);
-
- var result = await organizer.OrganizeWithCorrection(request, GetAutoOrganizeOptions(), CancellationToken.None).ConfigureAwait(false);
-
- if (result.Status != FileSortingStatus.Success)
- {
- throw new Exception(result.StatusMessage);
- }
- }
-
- public QueryResult<SmartMatchInfo> GetSmartMatchInfos(FileOrganizationResultQuery query)
- {
- if (query == null)
- {
- throw new ArgumentNullException("query");
- }
-
- var options = GetAutoOrganizeOptions();
-
- var items = options.SmartMatchInfos.Skip(query.StartIndex ?? 0).Take(query.Limit ?? Int32.MaxValue).ToArray();
-
- return new QueryResult<SmartMatchInfo>()
- {
- Items = items,
- TotalRecordCount = options.SmartMatchInfos.Length
- };
- }
-
- public void DeleteSmartMatchEntry(string itemName, string matchString)
- {
- if (string.IsNullOrEmpty(itemName))
- {
- throw new ArgumentNullException("itemName");
- }
-
- if (string.IsNullOrEmpty(matchString))
- {
- throw new ArgumentNullException("matchString");
- }
-
- var options = GetAutoOrganizeOptions();
-
- SmartMatchInfo info = options.SmartMatchInfos.FirstOrDefault(i => string.Equals(i.ItemName, itemName));
-
- if (info != null && info.MatchStrings.Contains(matchString))
- {
- var list = info.MatchStrings.ToList();
- list.Remove(matchString);
- info.MatchStrings = list.ToArray();
-
- if (info.MatchStrings.Length == 0)
- {
- var infos = options.SmartMatchInfos.ToList();
- infos.Remove(info);
- options.SmartMatchInfos = infos.ToArray();
- }
-
- _config.SaveAutoOrganizeOptions(options);
- }
- }
-
- /// <summary>
- /// Attempts to add a an item to the list of currently processed items.
- /// </summary>
- /// <param name="result">The result item.</param>
- /// <param name="isNewItem">Passing true will notify the client to reload all items, otherwise only a single item will be refreshed.</param>
- /// <returns>True if the item was added, False if the item is already contained in the list.</returns>
- public bool AddToInProgressList(FileOrganizationResult result, bool isNewItem)
- {
- if (string.IsNullOrWhiteSpace(result.Id))
- {
- result.Id = result.OriginalPath.GetMD5().ToString("N");
- }
-
- if (!_inProgressItemIds.TryAdd(result.Id, false))
- {
- return false;
- }
-
- result.IsInProgress = true;
-
- if (isNewItem)
- {
- EventHelper.FireEventIfNotNull(ItemAdded, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
- }
- else
- {
- EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
- }
-
- return true;
- }
-
- /// <summary>
- /// Removes an item from the list of currently processed items.
- /// </summary>
- /// <param name="result">The result item.</param>
- /// <returns>True if the item was removed, False if the item was not contained in the list.</returns>
- public bool RemoveFromInprogressList(FileOrganizationResult result)
- {
- bool itemValue;
- var retval = _inProgressItemIds.TryRemove(result.Id, out itemValue);
-
- result.IsInProgress = false;
-
- EventHelper.FireEventIfNotNull(ItemUpdated, this, new GenericEventArgs<FileOrganizationResult>(result), _logger);
-
- return retval;
- }
-
- }
-}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs b/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs
deleted file mode 100644
index 624133d4f..000000000
--- a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Controller.Entities;
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-
-namespace MediaBrowser.Server.Implementations.FileOrganization
-{
- public static class NameUtils
- {
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- internal static Tuple<T, int> GetMatchScore<T>(string sortedName, int? year, T series)
- where T : BaseItem
- {
- var score = 0;
-
- var seriesNameWithoutYear = series.Name;
- if (series.ProductionYear.HasValue)
- {
- seriesNameWithoutYear = seriesNameWithoutYear.Replace(series.ProductionYear.Value.ToString(UsCulture), String.Empty);
- }
-
- if (IsNameMatch(sortedName, seriesNameWithoutYear))
- {
- score++;
-
- if (year.HasValue && series.ProductionYear.HasValue)
- {
- if (year.Value == series.ProductionYear.Value)
- {
- score++;
- }
- else
- {
- // Regardless of name, return a 0 score if the years don't match
- return new Tuple<T, int>(series, 0);
- }
- }
- }
-
- return new Tuple<T, int>(series, score);
- }
-
-
- private static bool IsNameMatch(string name1, string name2)
- {
- name1 = GetComparableName(name1);
- name2 = GetComparableName(name2);
-
- return String.Equals(name1, name2, StringComparison.OrdinalIgnoreCase);
- }
-
- private static string GetComparableName(string name)
- {
- name = RemoveDiacritics(name);
-
- name = " " + name + " ";
-
- name = name.Replace(".", " ")
- .Replace("_", " ")
- .Replace(" and ", " ")
- .Replace(".and.", " ")
- .Replace("&", " ")
- .Replace("!", " ")
- .Replace("(", " ")
- .Replace(")", " ")
- .Replace(":", " ")
- .Replace(",", " ")
- .Replace("-", " ")
- .Replace("'", " ")
- .Replace("[", " ")
- .Replace("]", " ")
- .Replace(" a ", String.Empty, StringComparison.OrdinalIgnoreCase)
- .Replace(" the ", String.Empty, StringComparison.OrdinalIgnoreCase)
- .Replace(" ", String.Empty);
-
- return name.Trim();
- }
-
- /// <summary>
- /// Removes the diacritics.
- /// </summary>
- /// <param name="text">The text.</param>
- /// <returns>System.String.</returns>
- private static string RemoveDiacritics(string text)
- {
- return String.Concat(
- text.Normalize(NormalizationForm.FormD)
- .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
- UnicodeCategory.NonSpacingMark)
- ).Normalize(NormalizationForm.FormC);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs b/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
deleted file mode 100644
index ca41db80c..000000000
--- a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.FileOrganization
-{
- public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask
- {
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IServerConfigurationManager _config;
- private readonly IFileOrganizationService _organizationService;
- private readonly IProviderManager _providerManager;
-
- public OrganizerScheduledTask(ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IServerConfigurationManager config, IFileOrganizationService organizationService, IProviderManager providerManager)
- {
- _libraryMonitor = libraryMonitor;
- _libraryManager = libraryManager;
- _logger = logger;
- _fileSystem = fileSystem;
- _config = config;
- _organizationService = organizationService;
- _providerManager = providerManager;
- }
-
- public string Name
- {
- get { return "Organize new media files"; }
- }
-
- public string Description
- {
- get { return "Processes new files available in the configured watch folder."; }
- }
-
- public string Category
- {
- get { return "Library"; }
- }
-
- private AutoOrganizeOptions GetAutoOrganizeOptions()
- {
- return _config.GetAutoOrganizeOptions();
- }
-
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- if (GetAutoOrganizeOptions().TvOptions.IsEnabled)
- {
- await new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _libraryMonitor, _organizationService, _config, _providerManager)
- .Organize(GetAutoOrganizeOptions(), cancellationToken, progress).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromMinutes(5).Ticks}
- };
- }
-
- public bool IsHidden
- {
- get { return !GetAutoOrganizeOptions().TvOptions.IsEnabled; }
- }
-
- public bool IsEnabled
- {
- get { return GetAutoOrganizeOptions().TvOptions.IsEnabled; }
- }
-
- public bool IsLogged
- {
- get { return false; }
- }
-
- public string Key
- {
- get { return "AutoOrganize"; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
deleted file mode 100644
index d83aee25b..000000000
--- a/MediaBrowser.Server.Implementations/FileOrganization/TvFolderOrganizer.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.FileOrganization;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.FileOrganization
-{
- public class TvFolderOrganizer
- {
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IFileOrganizationService _organizationService;
- private readonly IServerConfigurationManager _config;
- private readonly IProviderManager _providerManager;
-
- public TvFolderOrganizer(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IFileOrganizationService organizationService, IServerConfigurationManager config, IProviderManager providerManager)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _fileSystem = fileSystem;
- _libraryMonitor = libraryMonitor;
- _organizationService = organizationService;
- _config = config;
- _providerManager = providerManager;
- }
-
- private bool EnableOrganization(FileSystemMetadata fileInfo, TvFileOrganizationOptions options)
- {
- var minFileBytes = options.MinFileSizeMb * 1024 * 1024;
-
- try
- {
- return _libraryManager.IsVideoFile(fileInfo.FullName) && fileInfo.Length >= minFileBytes;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error organizing file {0}", ex, fileInfo.Name);
- }
-
- return false;
- }
-
- public async Task Organize(AutoOrganizeOptions options, CancellationToken cancellationToken, IProgress<double> progress)
- {
- var watchLocations = options.TvOptions.WatchLocations.ToList();
-
- var eligibleFiles = watchLocations.SelectMany(GetFilesToOrganize)
- .OrderBy(_fileSystem.GetCreationTimeUtc)
- .Where(i => EnableOrganization(i, options.TvOptions))
- .ToList();
-
- var processedFolders = new HashSet<string>();
-
- progress.Report(10);
-
- if (eligibleFiles.Count > 0)
- {
- var numComplete = 0;
-
- foreach (var file in eligibleFiles)
- {
- var organizer = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager,
- _libraryMonitor, _providerManager);
-
- try
- {
- var result = await organizer.OrganizeEpisodeFile(file.FullName, options, options.TvOptions.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false);
- if (result.Status == FileSortingStatus.Success && !processedFolders.Contains(file.DirectoryName, StringComparer.OrdinalIgnoreCase))
- {
- processedFolders.Add(file.DirectoryName);
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error organizing episode {0}", ex, file.FullName);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= eligibleFiles.Count;
-
- progress.Report(10 + 89 * percent);
- }
- }
-
- cancellationToken.ThrowIfCancellationRequested();
- progress.Report(99);
-
- foreach (var path in processedFolders)
- {
- var deleteExtensions = options.TvOptions.LeftOverFileExtensionsToDelete
- .Select(i => i.Trim().TrimStart('.'))
- .Where(i => !string.IsNullOrEmpty(i))
- .Select(i => "." + i)
- .ToList();
-
- if (deleteExtensions.Count > 0)
- {
- DeleteLeftOverFiles(path, deleteExtensions);
- }
-
- if (options.TvOptions.DeleteEmptyFolders)
- {
- if (!IsWatchFolder(path, watchLocations))
- {
- DeleteEmptyFolders(path);
- }
- }
- }
-
- progress.Report(100);
- }
-
- /// <summary>
- /// Gets the files to organize.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>IEnumerable{FileInfo}.</returns>
- private List<FileSystemMetadata> GetFilesToOrganize(string path)
- {
- try
- {
- return _fileSystem.GetFiles(path, true)
- .ToList();
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error getting files from {0}", ex, path);
-
- return new List<FileSystemMetadata>();
- }
- }
-
- /// <summary>
- /// Deletes the left over files.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="extensions">The extensions.</param>
- private void DeleteLeftOverFiles(string path, IEnumerable<string> extensions)
- {
- var eligibleFiles = _fileSystem.GetFiles(path, true)
- .Where(i => extensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase))
- .ToList();
-
- foreach (var file in eligibleFiles)
- {
- try
- {
- _fileSystem.DeleteFile(file.FullName);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting file {0}", ex, file.FullName);
- }
- }
- }
-
- /// <summary>
- /// Deletes the empty folders.
- /// </summary>
- /// <param name="path">The path.</param>
- private void DeleteEmptyFolders(string path)
- {
- try
- {
- foreach (var d in _fileSystem.GetDirectoryPaths(path))
- {
- DeleteEmptyFolders(d);
- }
-
- var entries = _fileSystem.GetFileSystemEntryPaths(path);
-
- if (!entries.Any())
- {
- try
- {
- _logger.Debug("Deleting empty directory {0}", path);
- _fileSystem.DeleteDirectory(path, false);
- }
- catch (UnauthorizedAccessException) { }
- catch (DirectoryNotFoundException) { }
- }
- }
- catch (UnauthorizedAccessException) { }
- }
-
- /// <summary>
- /// Determines if a given folder path is contained in a folder list
- /// </summary>
- /// <param name="path">The folder path to check.</param>
- /// <param name="watchLocations">A list of folders.</param>
- private bool IsWatchFolder(string path, IEnumerable<string> watchLocations)
- {
- return watchLocations.Contains(path, StringComparer.OrdinalIgnoreCase);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs
index eeefdd65a..2742e1a26 100644
--- a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs
+++ b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs
@@ -14,7 +14,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Tasks;
-using MediaBrowser.Server.Implementations.ScheduledTasks;
+using MediaBrowser.Model.Threading;
namespace MediaBrowser.Server.Implementations.IO
{
@@ -26,13 +26,14 @@ namespace MediaBrowser.Server.Implementations.IO
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem;
private readonly List<string> _affectedPaths = new List<string>();
- private Timer _timer;
+ private ITimer _timer;
+ private readonly ITimerFactory _timerFactory;
private readonly object _timerLock = new object();
public string Path { get; private set; }
public event EventHandler<EventArgs> Completed;
- public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger)
+ public FileRefresher(string path, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ITaskManager taskManager, ILogger logger, ITimerFactory timerFactory)
{
logger.Debug("New file refresher created for {0}", path);
Path = path;
@@ -42,6 +43,7 @@ namespace MediaBrowser.Server.Implementations.IO
LibraryManager = libraryManager;
TaskManager = taskManager;
Logger = logger;
+ _timerFactory = timerFactory;
AddPath(path);
}
@@ -88,7 +90,7 @@ namespace MediaBrowser.Server.Implementations.IO
if (_timer == null)
{
- _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
+ _timer = _timerFactory.Create(OnTimerCallback, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1));
}
else
{
@@ -163,7 +165,7 @@ namespace MediaBrowser.Server.Implementations.IO
// If the root folder changed, run the library task so the user can see it
if (itemsToRefresh.Any(i => i is AggregateFolder))
{
- TaskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
+ LibraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None);
return;
}
@@ -268,11 +270,11 @@ namespace MediaBrowser.Server.Implementations.IO
return false;
}
}
- catch (DirectoryNotFoundException)
- {
- // File may have been deleted
- return false;
- }
+ //catch (DirectoryNotFoundException)
+ //{
+ // // File may have been deleted
+ // return false;
+ //}
catch (FileNotFoundException)
{
// File may have been deleted
diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
index 77981b528..49cb1e75f 100644
--- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
+++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging;
-using Microsoft.Win32;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -16,6 +15,8 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Controller;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.Tasks;
+using MediaBrowser.Model.Threading;
+using Microsoft.Win32;
namespace MediaBrowser.Server.Implementations.IO
{
@@ -136,12 +137,12 @@ namespace MediaBrowser.Server.Implementations.IO
private IServerConfigurationManager ConfigurationManager { get; set; }
private readonly IFileSystem _fileSystem;
- private readonly IServerApplicationHost _appHost;
+ private readonly ITimerFactory _timerFactory;
/// <summary>
/// Initializes a new instance of the <see cref="LibraryMonitor" /> class.
/// </summary>
- public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IServerApplicationHost appHost)
+ public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ITimerFactory timerFactory)
{
if (taskManager == null)
{
@@ -153,7 +154,7 @@ namespace MediaBrowser.Server.Implementations.IO
Logger = logManager.GetLogger(GetType().Name);
ConfigurationManager = configurationManager;
_fileSystem = fileSystem;
- _appHost = appHost;
+ _timerFactory = timerFactory;
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
}
@@ -528,7 +529,7 @@ namespace MediaBrowser.Server.Implementations.IO
}
}
- var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger);
+ var newRefresher = new FileRefresher(path, _fileSystem, ConfigurationManager, LibraryManager, TaskManager, Logger, _timerFactory);
newRefresher.Completed += NewRefresher_Completed;
_activeRefreshers.Add(newRefresher);
}
diff --git a/MediaBrowser.Server.Implementations/IO/ThrottledStream.cs b/MediaBrowser.Server.Implementations/IO/ThrottledStream.cs
deleted file mode 100644
index 538812dc0..000000000
--- a/MediaBrowser.Server.Implementations/IO/ThrottledStream.cs
+++ /dev/null
@@ -1,393 +0,0 @@
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.IO
-{
- /// <summary>
- /// Class for streaming data with throttling support.
- /// </summary>
- public class ThrottledStream : Stream
- {
- /// <summary>
- /// A constant used to specify an infinite number of bytes that can be transferred per second.
- /// </summary>
- public const long Infinite = 0;
-
- #region Private members
- /// <summary>
- /// The base stream.
- /// </summary>
- private readonly Stream _baseStream;
-
- /// <summary>
- /// The maximum bytes per second that can be transferred through the base stream.
- /// </summary>
- private long _maximumBytesPerSecond;
-
- /// <summary>
- /// The number of bytes that has been transferred since the last throttle.
- /// </summary>
- private long _byteCount;
-
- /// <summary>
- /// The start time in milliseconds of the last throttle.
- /// </summary>
- private long _start;
- #endregion
-
- #region Properties
- /// <summary>
- /// Gets the current milliseconds.
- /// </summary>
- /// <value>The current milliseconds.</value>
- protected long CurrentMilliseconds
- {
- get
- {
- return Environment.TickCount;
- }
- }
-
- /// <summary>
- /// Gets or sets the maximum bytes per second that can be transferred through the base stream.
- /// </summary>
- /// <value>The maximum bytes per second.</value>
- public long MaximumBytesPerSecond
- {
- get
- {
- return _maximumBytesPerSecond;
- }
- set
- {
- if (MaximumBytesPerSecond != value)
- {
- _maximumBytesPerSecond = value;
- Reset();
- }
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether the current stream supports reading.
- /// </summary>
- /// <returns>true if the stream supports reading; otherwise, false.</returns>
- public override bool CanRead
- {
- get
- {
- return _baseStream.CanRead;
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether the current stream supports seeking.
- /// </summary>
- /// <value></value>
- /// <returns>true if the stream supports seeking; otherwise, false.</returns>
- public override bool CanSeek
- {
- get
- {
- return _baseStream.CanSeek;
- }
- }
-
- /// <summary>
- /// Gets a value indicating whether the current stream supports writing.
- /// </summary>
- /// <value></value>
- /// <returns>true if the stream supports writing; otherwise, false.</returns>
- public override bool CanWrite
- {
- get
- {
- return _baseStream.CanWrite;
- }
- }
-
- /// <summary>
- /// Gets the length in bytes of the stream.
- /// </summary>
- /// <value></value>
- /// <returns>A long value representing the length of the stream in bytes.</returns>
- /// <exception cref="T:System.NotSupportedException">The base stream does not support seeking. </exception>
- /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
- public override long Length
- {
- get
- {
- return _baseStream.Length;
- }
- }
-
- /// <summary>
- /// Gets or sets the position within the current stream.
- /// </summary>
- /// <value></value>
- /// <returns>The current position within the stream.</returns>
- /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
- /// <exception cref="T:System.NotSupportedException">The base stream does not support seeking. </exception>
- /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
- public override long Position
- {
- get
- {
- return _baseStream.Position;
- }
- set
- {
- _baseStream.Position = value;
- }
- }
- #endregion
-
- public long MinThrottlePosition;
-
- #region Ctor
- /// <summary>
- /// Initializes a new instance of the <see cref="T:ThrottledStream"/> class.
- /// </summary>
- /// <param name="baseStream">The base stream.</param>
- /// <param name="maximumBytesPerSecond">The maximum bytes per second that can be transferred through the base stream.</param>
- /// <exception cref="ArgumentNullException">Thrown when <see cref="baseStream"/> is a null reference.</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="maximumBytesPerSecond"/> is a negative value.</exception>
- public ThrottledStream(Stream baseStream, long maximumBytesPerSecond)
- {
- if (baseStream == null)
- {
- throw new ArgumentNullException("baseStream");
- }
-
- if (maximumBytesPerSecond < 0)
- {
- throw new ArgumentOutOfRangeException("maximumBytesPerSecond",
- maximumBytesPerSecond, "The maximum number of bytes per second can't be negative.");
- }
-
- _baseStream = baseStream;
- _maximumBytesPerSecond = maximumBytesPerSecond;
- _start = CurrentMilliseconds;
- _byteCount = 0;
- }
- #endregion
-
- #region Public methods
- /// <summary>
- /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
- /// </summary>
- /// <exception cref="T:System.IO.IOException">An I/O error occurs.</exception>
- public override void Flush()
- {
- _baseStream.Flush();
- }
-
- /// <summary>
- /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
- /// </summary>
- /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
- /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
- /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
- /// <returns>
- /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
- /// </returns>
- /// <exception cref="T:System.ArgumentException">The sum of offset and count is larger than the buffer length. </exception>
- /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
- /// <exception cref="T:System.NotSupportedException">The base stream does not support reading. </exception>
- /// <exception cref="T:System.ArgumentNullException">buffer is null. </exception>
- /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
- /// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception>
- public override int Read(byte[] buffer, int offset, int count)
- {
- Throttle(count);
-
- return _baseStream.Read(buffer, offset, count);
- }
-
- /// <summary>
- /// Sets the position within the current stream.
- /// </summary>
- /// <param name="offset">A byte offset relative to the origin parameter.</param>
- /// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin"></see> indicating the reference point used to obtain the new position.</param>
- /// <returns>
- /// The new position within the current stream.
- /// </returns>
- /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
- /// <exception cref="T:System.NotSupportedException">The base stream does not support seeking, such as if the stream is constructed from a pipe or console output. </exception>
- /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
- public override long Seek(long offset, SeekOrigin origin)
- {
- return _baseStream.Seek(offset, origin);
- }
-
- /// <summary>
- /// Sets the length of the current stream.
- /// </summary>
- /// <param name="value">The desired length of the current stream in bytes.</param>
- /// <exception cref="T:System.NotSupportedException">The base stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output. </exception>
- /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
- /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
- public override void SetLength(long value)
- {
- _baseStream.SetLength(value);
- }
-
- private long _bytesWritten;
-
- /// <summary>
- /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
- /// </summary>
- /// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current stream.</param>
- /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
- /// <param name="count">The number of bytes to be written to the current stream.</param>
- /// <exception cref="T:System.IO.IOException">An I/O error occurs. </exception>
- /// <exception cref="T:System.NotSupportedException">The base stream does not support writing. </exception>
- /// <exception cref="T:System.ObjectDisposedException">Methods were called after the stream was closed. </exception>
- /// <exception cref="T:System.ArgumentNullException">buffer is null. </exception>
- /// <exception cref="T:System.ArgumentException">The sum of offset and count is greater than the buffer length. </exception>
- /// <exception cref="T:System.ArgumentOutOfRangeException">offset or count is negative. </exception>
- public override void Write(byte[] buffer, int offset, int count)
- {
- Throttle(count);
-
- _baseStream.Write(buffer, offset, count);
-
- _bytesWritten += count;
- }
-
- public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- await ThrottleAsync(count, cancellationToken).ConfigureAwait(false);
-
- await _baseStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
-
- _bytesWritten += count;
- }
-
- /// <summary>
- /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
- /// </returns>
- public override string ToString()
- {
- return _baseStream.ToString();
- }
- #endregion
-
- private bool ThrottleCheck(int bufferSizeInBytes)
- {
- if (_bytesWritten < MinThrottlePosition)
- {
- return false;
- }
-
- // Make sure the buffer isn't empty.
- if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0)
- {
- return false;
- }
-
- return true;
- }
-
- #region Protected methods
- /// <summary>
- /// Throttles for the specified buffer size in bytes.
- /// </summary>
- /// <param name="bufferSizeInBytes">The buffer size in bytes.</param>
- protected void Throttle(int bufferSizeInBytes)
- {
- if (!ThrottleCheck(bufferSizeInBytes))
- {
- return ;
- }
-
- _byteCount += bufferSizeInBytes;
- long elapsedMilliseconds = CurrentMilliseconds - _start;
-
- if (elapsedMilliseconds > 0)
- {
- // Calculate the current bps.
- long bps = _byteCount * 1000L / elapsedMilliseconds;
-
- // If the bps are more then the maximum bps, try to throttle.
- if (bps > _maximumBytesPerSecond)
- {
- // Calculate the time to sleep.
- long wakeElapsed = _byteCount * 1000L / _maximumBytesPerSecond;
- int toSleep = (int)(wakeElapsed - elapsedMilliseconds);
-
- if (toSleep > 1)
- {
- try
- {
- // The time to sleep is more then a millisecond, so sleep.
- Thread.Sleep(toSleep);
- }
- catch (ThreadAbortException)
- {
- // Eatup ThreadAbortException.
- }
-
- // A sleep has been done, reset.
- Reset();
- }
- }
- }
- }
-
- protected async Task ThrottleAsync(int bufferSizeInBytes, CancellationToken cancellationToken)
- {
- if (!ThrottleCheck(bufferSizeInBytes))
- {
- return;
- }
-
- _byteCount += bufferSizeInBytes;
- long elapsedMilliseconds = CurrentMilliseconds - _start;
-
- if (elapsedMilliseconds > 0)
- {
- // Calculate the current bps.
- long bps = _byteCount * 1000L / elapsedMilliseconds;
-
- // If the bps are more then the maximum bps, try to throttle.
- if (bps > _maximumBytesPerSecond)
- {
- // Calculate the time to sleep.
- long wakeElapsed = _byteCount * 1000L / _maximumBytesPerSecond;
- int toSleep = (int)(wakeElapsed - elapsedMilliseconds);
-
- if (toSleep > 1)
- {
- // The time to sleep is more then a millisecond, so sleep.
- await Task.Delay(toSleep, cancellationToken).ConfigureAwait(false);
-
- // A sleep has been done, reset.
- Reset();
- }
- }
- }
- }
-
- /// <summary>
- /// Will reset the bytecount to 0 and reset the start time to the current time.
- /// </summary>
- protected void Reset()
- {
- long difference = CurrentMilliseconds - _start;
-
- // Only reset counters when a known history is available of more then 1 second.
- if (difference > 1000)
- {
- _byteCount = 0;
- _start = CurrentMilliseconds;
- }
- }
- #endregion
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs
deleted file mode 100644
index 72a63c547..000000000
--- a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs
+++ /dev/null
@@ -1,386 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Globalization;
-
-namespace MediaBrowser.Server.Implementations.Intros
-{
- public class DefaultIntroProvider : IIntroProvider
- {
- private readonly ISecurityManager _security;
- private readonly ILocalizationManager _localization;
- private readonly IConfigurationManager _serverConfig;
- private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
- private readonly IMediaSourceManager _mediaSourceManager;
-
- public DefaultIntroProvider(ISecurityManager security, ILocalizationManager localization, IConfigurationManager serverConfig, ILibraryManager libraryManager, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager)
- {
- _security = security;
- _localization = localization;
- _serverConfig = serverConfig;
- _libraryManager = libraryManager;
- _fileSystem = fileSystem;
- _mediaSourceManager = mediaSourceManager;
- }
-
- public async Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user)
- {
- var config = GetOptions();
-
- if (item is Movie)
- {
- if (!config.EnableIntrosForMovies)
- {
- return new List<IntroInfo>();
- }
- }
- else if (item is Episode)
- {
- if (!config.EnableIntrosForEpisodes)
- {
- return new List<IntroInfo>();
- }
- }
- else
- {
- return new List<IntroInfo>();
- }
-
- var ratingLevel = string.IsNullOrWhiteSpace(item.OfficialRating)
- ? null
- : _localization.GetRatingLevel(item.OfficialRating);
-
- var candidates = new List<ItemWithTrailer>();
-
- var trailerTypes = new List<TrailerType>();
- var sourceTypes = new List<SourceType>();
-
- if (config.EnableIntrosFromMoviesInLibrary)
- {
- trailerTypes.Add(TrailerType.LocalTrailer);
- sourceTypes.Add(SourceType.Library);
- }
-
- if (IsSupporter)
- {
- if (config.EnableIntrosFromUpcomingTrailers)
- {
- trailerTypes.Add(TrailerType.ComingSoonToTheaters);
- sourceTypes.Clear();
- }
- if (config.EnableIntrosFromUpcomingDvdMovies)
- {
- trailerTypes.Add(TrailerType.ComingSoonToDvd);
- sourceTypes.Clear();
- }
- if (config.EnableIntrosFromUpcomingStreamingMovies)
- {
- trailerTypes.Add(TrailerType.ComingSoonToStreaming);
- sourceTypes.Clear();
- }
- if (config.EnableIntrosFromSimilarMovies)
- {
- trailerTypes.Add(TrailerType.Archive);
- sourceTypes.Clear();
- }
- }
-
- if (trailerTypes.Count > 0)
- {
- var trailerResult = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Trailer).Name },
- TrailerTypes = trailerTypes.ToArray(),
- SimilarTo = item,
- IsPlayed = config.EnableIntrosForWatchedContent ? (bool?)null : false,
- MaxParentalRating = config.EnableIntrosParentalControl ? ratingLevel : null,
- BlockUnratedItems = config.EnableIntrosParentalControl ? new[] { UnratedItem.Trailer } : new UnratedItem[] { },
-
- // Account for duplicates by imdb id, since the database doesn't support this yet
- Limit = config.TrailerLimit * 2,
- SourceTypes = sourceTypes.ToArray()
-
- }).Where(i => string.IsNullOrWhiteSpace(i.GetProviderId(MetadataProviders.Imdb)) || !string.Equals(i.GetProviderId(MetadataProviders.Imdb), item.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase)).Take(config.TrailerLimit);
-
- candidates.AddRange(trailerResult.Select(i => new ItemWithTrailer
- {
- Item = i,
- Type = i.SourceType == SourceType.Channel ? ItemWithTrailerType.ChannelTrailer : ItemWithTrailerType.ItemWithTrailer,
- LibraryManager = _libraryManager
- }));
- }
-
- return GetResult(item, candidates, config);
- }
-
- private IEnumerable<IntroInfo> GetResult(BaseItem item, IEnumerable<ItemWithTrailer> candidates, CinemaModeConfiguration config)
- {
- var customIntros = !string.IsNullOrWhiteSpace(config.CustomIntroPath) ?
- GetCustomIntros(config) :
- new List<IntroInfo>();
-
- var mediaInfoIntros = !string.IsNullOrWhiteSpace(config.MediaInfoIntroPath) ?
- GetMediaInfoIntros(config, item) :
- new List<IntroInfo>();
-
- // Avoid implicitly captured closure
- return candidates.Select(i => i.IntroInfo)
- .Concat(customIntros.Take(1))
- .Concat(mediaInfoIntros);
- }
-
- private CinemaModeConfiguration GetOptions()
- {
- return _serverConfig.GetConfiguration<CinemaModeConfiguration>("cinemamode");
- }
-
- private List<IntroInfo> GetCustomIntros(CinemaModeConfiguration options)
- {
- try
- {
- return GetCustomIntroFiles(options, true, false)
- .OrderBy(i => Guid.NewGuid())
- .Select(i => new IntroInfo
- {
- Path = i
-
- }).ToList();
- }
- catch (IOException)
- {
- return new List<IntroInfo>();
- }
- }
-
- private IEnumerable<IntroInfo> GetMediaInfoIntros(CinemaModeConfiguration options, BaseItem item)
- {
- try
- {
- var hasMediaSources = item as IHasMediaSources;
-
- if (hasMediaSources == null)
- {
- return new List<IntroInfo>();
- }
-
- var mediaSource = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false)
- .FirstOrDefault();
-
- if (mediaSource == null)
- {
- return new List<IntroInfo>();
- }
-
- var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
- var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
-
- var allIntros = GetCustomIntroFiles(options, false, true)
- .OrderBy(i => Guid.NewGuid())
- .Select(i => new IntroInfo
- {
- Path = i
-
- }).ToList();
-
- var returnResult = new List<IntroInfo>();
-
- if (videoStream != null)
- {
- returnResult.AddRange(GetMediaInfoIntrosByVideoStream(allIntros, videoStream).Take(1));
- }
-
- if (audioStream != null)
- {
- returnResult.AddRange(GetMediaInfoIntrosByAudioStream(allIntros, audioStream).Take(1));
- }
-
- returnResult.AddRange(GetMediaInfoIntrosByTags(allIntros, item.Tags).Take(1));
-
- return returnResult.DistinctBy(i => i.Path, StringComparer.OrdinalIgnoreCase);
- }
- catch (IOException)
- {
- return new List<IntroInfo>();
- }
- }
-
- private IEnumerable<IntroInfo> GetMediaInfoIntrosByVideoStream(List<IntroInfo> allIntros, MediaStream stream)
- {
- var codec = stream.Codec;
-
- if (string.IsNullOrWhiteSpace(codec))
- {
- return new List<IntroInfo>();
- }
-
- return allIntros
- .Where(i => IsMatch(i.Path, codec))
- .OrderBy(i => Guid.NewGuid());
- }
-
- private IEnumerable<IntroInfo> GetMediaInfoIntrosByAudioStream(List<IntroInfo> allIntros, MediaStream stream)
- {
- var codec = stream.Codec;
-
- if (string.IsNullOrWhiteSpace(codec))
- {
- return new List<IntroInfo>();
- }
-
- return allIntros
- .Where(i => IsAudioMatch(i.Path, stream))
- .OrderBy(i => Guid.NewGuid());
- }
-
- private IEnumerable<IntroInfo> GetMediaInfoIntrosByTags(List<IntroInfo> allIntros, List<string> tags)
- {
- return allIntros
- .Where(i => tags.Any(t => IsMatch(i.Path, t)))
- .OrderBy(i => Guid.NewGuid());
- }
-
- private bool IsMatch(string file, string attribute)
- {
- var filename = Path.GetFileNameWithoutExtension(file) ?? string.Empty;
- filename = Normalize(filename);
-
- if (string.IsNullOrWhiteSpace(filename))
- {
- return false;
- }
-
- attribute = Normalize(attribute);
- if (string.IsNullOrWhiteSpace(attribute))
- {
- return false;
- }
-
- return string.Equals(filename, attribute, StringComparison.OrdinalIgnoreCase);
- }
-
- private string Normalize(string value)
- {
- return value;
- }
-
- private bool IsAudioMatch(string path, MediaStream stream)
- {
- if (!string.IsNullOrWhiteSpace(stream.Codec))
- {
- if (IsMatch(path, stream.Codec))
- {
- return true;
- }
- }
- if (!string.IsNullOrWhiteSpace(stream.Profile))
- {
- if (IsMatch(path, stream.Profile))
- {
- return true;
- }
- }
-
- return false;
- }
-
- private IEnumerable<string> GetCustomIntroFiles(CinemaModeConfiguration options, bool enableCustomIntros, bool enableMediaInfoIntros)
- {
- var list = new List<string>();
-
- if (enableCustomIntros && !string.IsNullOrWhiteSpace(options.CustomIntroPath))
- {
- list.AddRange(_fileSystem.GetFilePaths(options.CustomIntroPath, true)
- .Where(_libraryManager.IsVideoFile));
- }
-
- if (enableMediaInfoIntros && !string.IsNullOrWhiteSpace(options.MediaInfoIntroPath))
- {
- list.AddRange(_fileSystem.GetFilePaths(options.MediaInfoIntroPath, true)
- .Where(_libraryManager.IsVideoFile));
- }
-
- return list.Distinct(StringComparer.OrdinalIgnoreCase);
- }
-
- public IEnumerable<string> GetAllIntroFiles()
- {
- return GetCustomIntroFiles(GetOptions(), true, true);
- }
-
- private bool IsSupporter
- {
- get { return _security.IsMBSupporter; }
- }
-
- public string Name
- {
- get { return "Default"; }
- }
-
- internal class ItemWithTrailer
- {
- internal BaseItem Item;
- internal ItemWithTrailerType Type;
- internal ILibraryManager LibraryManager;
-
- public IntroInfo IntroInfo
- {
- get
- {
- var id = Item.Id;
-
- if (Type == ItemWithTrailerType.ItemWithTrailer)
- {
- var hasTrailers = Item as IHasTrailers;
-
- if (hasTrailers != null)
- {
- id = hasTrailers.LocalTrailerIds.FirstOrDefault();
- }
- }
- return new IntroInfo
- {
- ItemId = id
- };
- }
- }
- }
-
- internal enum ItemWithTrailerType
- {
- ChannelTrailer,
- ItemWithTrailer
- }
- }
-
- public class CinemaModeConfigurationFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new[]
- {
- new ConfigurationStore
- {
- ConfigurationType = typeof(CinemaModeConfiguration),
- Key = "cinemamode"
- }
- };
- }
- }
-
-}
diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
deleted file mode 100644
index f20d87c4b..000000000
--- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
+++ /dev/null
@@ -1,148 +0,0 @@
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- /// <summary>
- /// Provides the core resolver ignore rules
- /// </summary>
- public class CoreResolutionIgnoreRule : IResolverIgnoreRule
- {
- private readonly IFileSystem _fileSystem;
- private readonly ILibraryManager _libraryManager;
-
- /// <summary>
- /// Any folder named in this list will be ignored - can be added to at runtime for extensibility
- /// </summary>
- public static readonly List<string> IgnoreFolders = new List<string>
- {
- "metadata",
- "ps3_update",
- "ps3_vprm",
- "extrafanart",
- "extrathumbs",
- ".actors",
- ".wd_tv",
-
- // Synology
- "@eaDir",
- "eaDir",
- "#recycle"
-
- };
-
- public CoreResolutionIgnoreRule(IFileSystem fileSystem, ILibraryManager libraryManager)
- {
- _fileSystem = fileSystem;
- _libraryManager = libraryManager;
- }
-
- /// <summary>
- /// Shoulds the ignore.
- /// </summary>
- /// <param name="fileInfo">The file information.</param>
- /// <param name="parent">The parent.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
- {
- var filename = fileInfo.Name;
- var isHidden = fileInfo.IsHidden;
- var path = fileInfo.FullName;
-
- // Handle mac .DS_Store
- // https://github.com/MediaBrowser/MediaBrowser/issues/427
- if (filename.IndexOf("._", StringComparison.OrdinalIgnoreCase) == 0)
- {
- return true;
- }
-
- // Ignore hidden files and folders
- if (isHidden)
- {
- if (parent == null)
- {
- var parentFolderName = Path.GetFileName(Path.GetDirectoryName(path));
-
- if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- }
-
- // Sometimes these are marked hidden
- if (_fileSystem.IsRootPath(path))
- {
- return false;
- }
-
- return true;
- }
-
- if (fileInfo.IsDirectory)
- {
- // Ignore any folders in our list
- if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
- {
- return true;
- }
-
- if (parent != null)
- {
- // Ignore trailer folders but allow it at the collection level
- if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) &&
- !(parent is AggregateFolder) && !(parent is UserRootFolder))
- {
- return true;
- }
-
- if (string.Equals(filename, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
-
- if (string.Equals(filename, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
- }
- else
- {
- if (parent != null)
- {
- // Don't resolve these into audio files
- if (string.Equals(_fileSystem.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && _libraryManager.IsAudioFile(filename))
- {
- return true;
- }
- }
-
- // Ignore samples
- var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase)
- .Replace("-", " ", StringComparison.OrdinalIgnoreCase)
- .Replace("_", " ", StringComparison.OrdinalIgnoreCase)
- .Replace("!", " ", StringComparison.OrdinalIgnoreCase);
-
- if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return true;
- }
- }
-
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
deleted file mode 100644
index 18feaa849..000000000
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ /dev/null
@@ -1,3067 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Naming.Audio;
-using MediaBrowser.Naming.Common;
-using MediaBrowser.Naming.TV;
-using MediaBrowser.Naming.Video;
-using MediaBrowser.Server.Implementations.Library.Validators;
-using MediaBrowser.Server.Implementations.Logging;
-using MediaBrowser.Server.Implementations.ScheduledTasks;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Library;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Server.Implementations.Library.Resolvers;
-using SortOrder = MediaBrowser.Model.Entities.SortOrder;
-using VideoResolver = MediaBrowser.Naming.Video.VideoResolver;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- /// <summary>
- /// Class LibraryManager
- /// </summary>
- public class LibraryManager : ILibraryManager
- {
- /// <summary>
- /// Gets or sets the postscan tasks.
- /// </summary>
- /// <value>The postscan tasks.</value>
- private ILibraryPostScanTask[] PostscanTasks { get; set; }
-
- /// <summary>
- /// Gets the intro providers.
- /// </summary>
- /// <value>The intro providers.</value>
- private IIntroProvider[] IntroProviders { get; set; }
-
- /// <summary>
- /// Gets the list of entity resolution ignore rules
- /// </summary>
- /// <value>The entity resolution ignore rules.</value>
- private IResolverIgnoreRule[] EntityResolutionIgnoreRules { get; set; }
-
- /// <summary>
- /// Gets the list of BasePluginFolders added by plugins
- /// </summary>
- /// <value>The plugin folders.</value>
- private IVirtualFolderCreator[] PluginFolderCreators { get; set; }
-
- /// <summary>
- /// Gets the list of currently registered entity resolvers
- /// </summary>
- /// <value>The entity resolvers enumerable.</value>
- private IItemResolver[] EntityResolvers { get; set; }
- private IMultiItemResolver[] MultiItemResolvers { get; set; }
-
- /// <summary>
- /// Gets or sets the comparers.
- /// </summary>
- /// <value>The comparers.</value>
- private IBaseItemComparer[] Comparers { get; set; }
-
- /// <summary>
- /// Gets the active item repository
- /// </summary>
- /// <value>The item repository.</value>
- public IItemRepository ItemRepository { get; set; }
-
- /// <summary>
- /// Occurs when [item added].
- /// </summary>
- public event EventHandler<ItemChangeEventArgs> ItemAdded;
-
- /// <summary>
- /// Occurs when [item updated].
- /// </summary>
- public event EventHandler<ItemChangeEventArgs> ItemUpdated;
-
- /// <summary>
- /// Occurs when [item removed].
- /// </summary>
- public event EventHandler<ItemChangeEventArgs> ItemRemoved;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// The _task manager
- /// </summary>
- private readonly ITaskManager _taskManager;
-
- /// <summary>
- /// The _user manager
- /// </summary>
- private readonly IUserManager _userManager;
-
- /// <summary>
- /// The _user data repository
- /// </summary>
- private readonly IUserDataManager _userDataRepository;
-
- /// <summary>
- /// Gets or sets the configuration manager.
- /// </summary>
- /// <value>The configuration manager.</value>
- private IServerConfigurationManager ConfigurationManager { get; set; }
-
- /// <summary>
- /// A collection of items that may be referenced from multiple physical places in the library
- /// (typically, multiple user roots). We store them here and be sure they all reference a
- /// single instance.
- /// </summary>
- /// <value>The by reference items.</value>
- private ConcurrentDictionary<Guid, BaseItem> ByReferenceItems { get; set; }
-
- private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
- private readonly Func<IProviderManager> _providerManagerFactory;
- private readonly Func<IUserViewManager> _userviewManager;
- public bool IsScanRunning { get; private set; }
-
- /// <summary>
- /// The _library items cache
- /// </summary>
- private readonly ConcurrentDictionary<Guid, BaseItem> _libraryItemsCache;
- /// <summary>
- /// Gets the library items cache.
- /// </summary>
- /// <value>The library items cache.</value>
- private ConcurrentDictionary<Guid, BaseItem> LibraryItemsCache
- {
- get
- {
- return _libraryItemsCache;
- }
- }
-
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LibraryManager" /> class.
- /// </summary>
- /// <param name="logger">The logger.</param>
- /// <param name="taskManager">The task manager.</param>
- /// <param name="userManager">The user manager.</param>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <param name="userDataRepository">The user data repository.</param>
- public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem, Func<IProviderManager> providerManagerFactory, Func<IUserViewManager> userviewManager)
- {
- _logger = logger;
- _taskManager = taskManager;
- _userManager = userManager;
- ConfigurationManager = configurationManager;
- _userDataRepository = userDataRepository;
- _libraryMonitorFactory = libraryMonitorFactory;
- _fileSystem = fileSystem;
- _providerManagerFactory = providerManagerFactory;
- _userviewManager = userviewManager;
- ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>();
- _libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
-
- ConfigurationManager.ConfigurationUpdated += ConfigurationUpdated;
-
- RecordConfigurationValues(configurationManager.Configuration);
- }
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="rules">The rules.</param>
- /// <param name="pluginFolders">The plugin folders.</param>
- /// <param name="resolvers">The resolvers.</param>
- /// <param name="introProviders">The intro providers.</param>
- /// <param name="itemComparers">The item comparers.</param>
- /// <param name="postscanTasks">The postscan tasks.</param>
- public void AddParts(IEnumerable<IResolverIgnoreRule> rules,
- IEnumerable<IVirtualFolderCreator> pluginFolders,
- IEnumerable<IItemResolver> resolvers,
- IEnumerable<IIntroProvider> introProviders,
- IEnumerable<IBaseItemComparer> itemComparers,
- IEnumerable<ILibraryPostScanTask> postscanTasks)
- {
- EntityResolutionIgnoreRules = rules.ToArray();
- PluginFolderCreators = pluginFolders.ToArray();
- EntityResolvers = resolvers.OrderBy(i => i.Priority).ToArray();
- MultiItemResolvers = EntityResolvers.OfType<IMultiItemResolver>().ToArray();
- IntroProviders = introProviders.ToArray();
- Comparers = itemComparers.ToArray();
-
- PostscanTasks = postscanTasks.OrderBy(i =>
- {
- var hasOrder = i as IHasOrder;
-
- return hasOrder == null ? 0 : hasOrder.Order;
-
- }).ToArray();
- }
-
- /// <summary>
- /// The _root folder
- /// </summary>
- private volatile AggregateFolder _rootFolder;
- /// <summary>
- /// The _root folder sync lock
- /// </summary>
- private readonly object _rootFolderSyncLock = new object();
- /// <summary>
- /// Gets the root folder.
- /// </summary>
- /// <value>The root folder.</value>
- public AggregateFolder RootFolder
- {
- get
- {
- if (_rootFolder == null)
- {
- lock (_rootFolderSyncLock)
- {
- if (_rootFolder == null)
- {
- _rootFolder = CreateRootFolder();
- }
- }
- }
- return _rootFolder;
- }
- }
-
- /// <summary>
- /// The _season zero display name
- /// </summary>
- private string _seasonZeroDisplayName;
-
- private bool _wizardCompleted;
- /// <summary>
- /// Records the configuration values.
- /// </summary>
- /// <param name="configuration">The configuration.</param>
- private void RecordConfigurationValues(ServerConfiguration configuration)
- {
- _seasonZeroDisplayName = configuration.SeasonZeroDisplayName;
- _wizardCompleted = configuration.IsStartupWizardCompleted;
- }
-
- /// <summary>
- /// Configurations the updated.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
- void ConfigurationUpdated(object sender, EventArgs e)
- {
- var config = ConfigurationManager.Configuration;
-
- var newSeasonZeroName = ConfigurationManager.Configuration.SeasonZeroDisplayName;
- var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.Ordinal);
- var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
-
- RecordConfigurationValues(config);
-
- if (seasonZeroNameChanged || wizardChanged)
- {
- _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
- }
-
- if (seasonZeroNameChanged)
- {
- Task.Run(async () =>
- {
- await UpdateSeasonZeroNames(newSeasonZeroName, CancellationToken.None).ConfigureAwait(false);
-
- });
- }
- }
-
- /// <summary>
- /// Updates the season zero names.
- /// </summary>
- /// <param name="newName">The new name.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task UpdateSeasonZeroNames(string newName, CancellationToken cancellationToken)
- {
- var seasons = GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Season).Name },
- Recursive = true,
- IndexNumber = 0
-
- }).Cast<Season>()
- .Where(i => !string.Equals(i.Name, newName, StringComparison.Ordinal))
- .ToList();
-
- foreach (var season in seasons)
- {
- season.Name = newName;
-
- try
- {
- await UpdateItem(season, ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving {0}", ex, season.Path);
- }
- }
- }
-
- public void RegisterItem(BaseItem item)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
- RegisterItem(item.Id, item);
- }
-
- private void RegisterItem(Guid id, BaseItem item)
- {
- if (item is IItemByName)
- {
- if (!(item is MusicArtist))
- {
- return;
- }
- }
-
- if (item.IsFolder)
- {
- if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel) && !(item is AggregateFolder))
- {
- if (item.SourceType != SourceType.Library)
- {
- return;
- }
- }
- }
- else
- {
- if (item is Photo)
- {
- return;
- }
- }
-
- LibraryItemsCache.AddOrUpdate(id, item, delegate { return item; });
- }
-
- public async Task DeleteItem(BaseItem item, DeleteOptions options)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- _logger.Debug("Deleting item, Type: {0}, Name: {1}, Path: {2}, Id: {3}",
- item.GetType().Name,
- item.Name ?? "Unknown name",
- item.Path ?? string.Empty,
- item.Id);
-
- var parent = item.Parent;
-
- var locationType = item.LocationType;
-
- var children = item.IsFolder
- ? ((Folder)item).GetRecursiveChildren(false).ToList()
- : new List<BaseItem>();
-
- foreach (var metadataPath in GetMetadataPaths(item, children))
- {
- _logger.Debug("Deleting path {0}", metadataPath);
-
- try
- {
- _fileSystem.DeleteDirectory(metadataPath, true);
- }
- catch (DirectoryNotFoundException)
- {
-
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting {0}", ex, metadataPath);
- }
- }
-
- if (options.DeleteFileLocation && locationType != LocationType.Remote && locationType != LocationType.Virtual)
- {
- foreach (var path in item.GetDeletePaths().ToList())
- {
- if (_fileSystem.DirectoryExists(path))
- {
- _logger.Debug("Deleting path {0}", path);
- _fileSystem.DeleteDirectory(path, true);
- }
- else if (_fileSystem.FileExists(path))
- {
- _logger.Debug("Deleting path {0}", path);
- _fileSystem.DeleteFile(path);
- }
- }
-
- if (parent != null)
- {
- await parent.ValidateChildren(new Progress<double>(), CancellationToken.None)
- .ConfigureAwait(false);
- }
- }
- else if (parent != null)
- {
- parent.RemoveChild(item);
- }
-
- await ItemRepository.DeleteItem(item.Id, CancellationToken.None).ConfigureAwait(false);
- foreach (var child in children)
- {
- await ItemRepository.DeleteItem(child.Id, CancellationToken.None).ConfigureAwait(false);
- }
-
- BaseItem removed;
- _libraryItemsCache.TryRemove(item.Id, out removed);
-
- ReportItemRemoved(item);
- }
-
- private IEnumerable<string> GetMetadataPaths(BaseItem item, IEnumerable<BaseItem> children)
- {
- var list = new List<string>
- {
- item.GetInternalMetadataPath()
- };
-
- list.AddRange(children.Select(i => i.GetInternalMetadataPath()));
-
- return list;
- }
-
- /// <summary>
- /// Resolves the item.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <param name="resolvers">The resolvers.</param>
- /// <returns>BaseItem.</returns>
- private BaseItem ResolveItem(ItemResolveArgs args, IItemResolver[] resolvers)
- {
- var item = (resolvers ?? EntityResolvers).Select(r => Resolve(args, r))
- .FirstOrDefault(i => i != null);
-
- if (item != null)
- {
- ResolverHelper.SetInitialItemValues(item, args, _fileSystem, this);
- }
-
- return item;
- }
-
- private BaseItem Resolve(ItemResolveArgs args, IItemResolver resolver)
- {
- try
- {
- return resolver.ResolvePath(args);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in {0} resolving {1}", ex, resolver.GetType().Name, args.Path);
- return null;
- }
- }
-
- public Guid GetNewItemId(string key, Type type)
- {
- if (string.IsNullOrWhiteSpace(key))
- {
- throw new ArgumentNullException("key");
- }
- if (type == null)
- {
- throw new ArgumentNullException("type");
- }
-
- if (ConfigurationManager.Configuration.EnableLocalizedGuids && key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath))
- {
- // Try to normalize paths located underneath program-data in an attempt to make them more portable
- key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length)
- .TrimStart(new[] { '/', '\\' })
- .Replace("/", "\\");
- }
-
- if (!ConfigurationManager.Configuration.EnableCaseSensitiveItemIds)
- {
- key = key.ToLower();
- }
-
- key = type.FullName + key;
-
- return key.GetMD5();
- }
-
- /// <summary>
- /// Ensure supplied item has only one instance throughout
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>The proper instance to the item</returns>
- public BaseItem GetOrAddByReferenceItem(BaseItem item)
- {
- // Add this item to our list if not there already
- if (!ByReferenceItems.TryAdd(item.Id, item))
- {
- // Already there - return the existing reference
- item = ByReferenceItems[item.Id];
- }
- return item;
- }
-
- public BaseItem ResolvePath(FileSystemMetadata fileInfo,
- Folder parent = null)
- {
- return ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent);
- }
-
- private BaseItem ResolvePath(FileSystemMetadata fileInfo,
- IDirectoryService directoryService,
- IItemResolver[] resolvers,
- Folder parent = null,
- string collectionType = null,
- LibraryOptions libraryOptions = null)
- {
- if (fileInfo == null)
- {
- throw new ArgumentNullException("fileInfo");
- }
-
- var fullPath = fileInfo.FullName;
-
- if (string.IsNullOrWhiteSpace(collectionType) && parent != null)
- {
- collectionType = GetContentTypeOverride(fullPath, true);
- }
-
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
- {
- Parent = parent,
- Path = fullPath,
- FileInfo = fileInfo,
- CollectionType = collectionType,
- LibraryOptions = libraryOptions
- };
-
- // Return null if ignore rules deem that we should do so
- if (IgnoreFile(args.FileInfo, args.Parent))
- {
- return null;
- }
-
- // Gather child folder and files
- if (args.IsDirectory)
- {
- var isPhysicalRoot = args.IsPhysicalRoot;
-
- // When resolving the root, we need it's grandchildren (children of user views)
- var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
-
- var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
-
- // Need to remove subpaths that may have been resolved from shortcuts
- // Example: if \\server\movies exists, then strip out \\server\movies\action
- if (isPhysicalRoot)
- {
- var paths = NormalizeRootPathList(fileSystemDictionary.Values);
-
- fileSystemDictionary = paths.ToDictionary(i => i.FullName);
- }
-
- args.FileSystemDictionary = fileSystemDictionary;
- }
-
- // Check to see if we should resolve based on our contents
- if (args.IsDirectory && !ShouldResolvePathContents(args))
- {
- return null;
- }
-
- return ResolveItem(args, resolvers);
- }
-
- private readonly List<string> _ignoredPaths = new List<string>();
-
- public void RegisterIgnoredPath(string path)
- {
- lock (_ignoredPaths)
- {
- _ignoredPaths.Add(path);
- }
- }
- public void UnRegisterIgnoredPath(string path)
- {
- lock (_ignoredPaths)
- {
- _ignoredPaths.Remove(path);
- }
- }
-
- public bool IgnoreFile(FileSystemMetadata file, BaseItem parent)
- {
- if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent)))
- {
- return true;
- }
-
- //lock (_ignoredPaths)
- {
- if (_ignoredPaths.Contains(file.FullName, StringComparer.OrdinalIgnoreCase))
- {
- return true;
- }
- }
- return false;
- }
-
- public IEnumerable<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths)
- {
- var originalList = paths.ToList();
-
- var list = originalList.Where(i => i.IsDirectory)
- .Select(i => _fileSystem.NormalizePath(i.FullName))
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .ToList();
-
- var dupes = list.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && list.Any(i => _fileSystem.ContainsSubPath(i, subPath)))
- .ToList();
-
- foreach (var dupe in dupes)
- {
- _logger.Info("Found duplicate path: {0}", dupe);
- }
-
- var newList = list.Except(dupes, StringComparer.OrdinalIgnoreCase).Select(_fileSystem.GetDirectoryInfo).ToList();
- newList.AddRange(originalList.Where(i => !i.IsDirectory));
- return newList;
- }
-
- /// <summary>
- /// Determines whether a path should be ignored based on its contents - called after the contents have been read
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- private static bool ShouldResolvePathContents(ItemResolveArgs args)
- {
- // Ignore any folders containing a file called .ignore
- return !args.ContainsFileSystemEntryByName(".ignore");
- }
-
- public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, string collectionType)
- {
- return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers);
- }
-
- public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
- IDirectoryService directoryService,
- Folder parent,
- LibraryOptions libraryOptions,
- string collectionType,
- IItemResolver[] resolvers)
- {
- var fileList = files.Where(i => !IgnoreFile(i, parent)).ToList();
-
- if (parent != null)
- {
- var multiItemResolvers = resolvers == null ? MultiItemResolvers : resolvers.OfType<IMultiItemResolver>().ToArray();
-
- foreach (var resolver in multiItemResolvers)
- {
- var result = resolver.ResolveMultiple(parent, fileList, collectionType, directoryService);
-
- if (result != null && result.Items.Count > 0)
- {
- var items = new List<BaseItem>();
- items.AddRange(result.Items);
-
- foreach (var item in items)
- {
- ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService);
- }
- items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions));
- return items;
- }
- }
- }
-
- return ResolveFileList(fileList, directoryService, parent, collectionType, resolvers, libraryOptions);
- }
-
- private IEnumerable<BaseItem> ResolveFileList(IEnumerable<FileSystemMetadata> fileList,
- IDirectoryService directoryService,
- Folder parent,
- string collectionType,
- IItemResolver[] resolvers,
- LibraryOptions libraryOptions)
- {
- return fileList.Select(f =>
- {
- try
- {
- return ResolvePath(f, directoryService, resolvers, parent, collectionType, libraryOptions);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error resolving path {0}", ex, f.FullName);
- return null;
- }
- }).Where(i => i != null);
- }
-
- /// <summary>
- /// Creates the root media folder
- /// </summary>
- /// <returns>AggregateFolder.</returns>
- /// <exception cref="System.InvalidOperationException">Cannot create the root folder until plugins have loaded</exception>
- public AggregateFolder CreateRootFolder()
- {
- var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath;
-
- _fileSystem.CreateDirectory(rootFolderPath);
-
- var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath));
-
- // Add in the plug-in folders
- foreach (var child in PluginFolderCreators)
- {
- var folder = child.GetFolder();
-
- if (folder != null)
- {
- if (folder.Id == Guid.Empty)
- {
- if (string.IsNullOrWhiteSpace(folder.Path))
- {
- folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType());
- }
- else
- {
- folder.Id = GetNewItemId(folder.Path, folder.GetType());
- }
- }
-
- var dbItem = GetItemById(folder.Id) as BasePluginFolder;
-
- if (dbItem != null && string.Equals(dbItem.Path, folder.Path, StringComparison.OrdinalIgnoreCase))
- {
- folder = dbItem;
- }
-
- if (folder.ParentId != rootFolder.Id)
- {
- folder.ParentId = rootFolder.Id;
- var task = folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
- Task.WaitAll(task);
- }
-
- rootFolder.AddVirtualChild(folder);
-
- RegisterItem(folder);
- }
- }
-
- return rootFolder;
- }
-
- private volatile UserRootFolder _userRootFolder;
- private readonly object _syncLock = new object();
- public Folder GetUserRootFolder()
- {
- if (_userRootFolder == null)
- {
- lock (_syncLock)
- {
- if (_userRootFolder == null)
- {
- var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
-
- _fileSystem.CreateDirectory(userRootPath);
-
- var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder;
-
- if (tmpItem == null)
- {
- tmpItem = (UserRootFolder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath));
- }
-
- _userRootFolder = tmpItem;
- }
- }
- }
-
- return _userRootFolder;
- }
-
- public BaseItem FindByPath(string path, bool? isFolder)
- {
- // If this returns multiple items it could be tricky figuring out which one is correct.
- // In most cases, the newest one will be and the others obsolete but not yet cleaned up
-
- var query = new InternalItemsQuery
- {
- Path = path,
- IsFolder = isFolder,
- SortBy = new[] { ItemSortBy.DateCreated },
- SortOrder = SortOrder.Descending,
- Limit = 1
- };
-
- return GetItemList(query)
- .FirstOrDefault();
- }
-
- /// <summary>
- /// Gets a Person
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{Person}.</returns>
- public Person GetPerson(string name)
- {
- return CreateItemByName<Person>(Person.GetPath(name), name);
- }
-
- /// <summary>
- /// Gets a Studio
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{Studio}.</returns>
- public Studio GetStudio(string name)
- {
- return CreateItemByName<Studio>(Studio.GetPath(name), name);
- }
-
- /// <summary>
- /// Gets a Genre
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{Genre}.</returns>
- public Genre GetGenre(string name)
- {
- return CreateItemByName<Genre>(Genre.GetPath(name), name);
- }
-
- /// <summary>
- /// Gets the genre.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{MusicGenre}.</returns>
- public MusicGenre GetMusicGenre(string name)
- {
- return CreateItemByName<MusicGenre>(MusicGenre.GetPath(name), name);
- }
-
- /// <summary>
- /// Gets the game genre.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{GameGenre}.</returns>
- public GameGenre GetGameGenre(string name)
- {
- return CreateItemByName<GameGenre>(GameGenre.GetPath(name), name);
- }
-
- /// <summary>
- /// Gets a Year
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>Task{Year}.</returns>
- /// <exception cref="System.ArgumentOutOfRangeException"></exception>
- public Year GetYear(int value)
- {
- if (value <= 0)
- {
- throw new ArgumentOutOfRangeException("Years less than or equal to 0 are invalid.");
- }
-
- var name = value.ToString(CultureInfo.InvariantCulture);
-
- return CreateItemByName<Year>(Year.GetPath(name), name);
- }
-
- /// <summary>
- /// Gets a Genre
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>Task{Genre}.</returns>
- public MusicArtist GetArtist(string name)
- {
- return CreateItemByName<MusicArtist>(MusicArtist.GetPath(name), name);
- }
-
- private T CreateItemByName<T>(string path, string name)
- where T : BaseItem, new()
- {
- if (typeof(T) == typeof(MusicArtist))
- {
- var existing = GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(T).Name },
- Name = name
-
- }).Cast<MusicArtist>()
- .OrderBy(i => i.IsAccessedByName ? 1 : 0)
- .Cast<T>()
- .FirstOrDefault();
-
- if (existing != null)
- {
- return existing;
- }
- }
-
- var id = GetNewItemId(path, typeof(T));
-
- var item = GetItemById(id) as T;
-
- if (item == null)
- {
- item = new T
- {
- Name = name,
- Id = id,
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow,
- Path = path
- };
-
- var task = CreateItem(item, CancellationToken.None);
- Task.WaitAll(task);
- }
-
- return item;
- }
-
- public IEnumerable<MusicArtist> GetAlbumArtists(IEnumerable<IHasAlbumArtist> items)
- {
- var names = items
- .SelectMany(i => i.AlbumArtists)
- .DistinctNames()
- .Select(i =>
- {
- try
- {
- var artist = GetArtist(i);
-
- return artist;
- }
- catch
- {
- // Already logged at lower levels
- return null;
- }
- })
- .Where(i => i != null);
-
- return names;
- }
-
- public IEnumerable<MusicArtist> GetArtists(IEnumerable<IHasArtist> items)
- {
- var names = items
- .SelectMany(i => i.AllArtists)
- .DistinctNames()
- .Select(i =>
- {
- try
- {
- var artist = GetArtist(i);
-
- return artist;
- }
- catch
- {
- // Already logged at lower levels
- return null;
- }
- })
- .Where(i => i != null);
-
- return names;
- }
-
- /// <summary>
- /// Validate and refresh the People sub-set of the IBN.
- /// The items are stored in the db but not loaded into memory until actually requested by an operation.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
- {
- // Ensure the location is available.
- _fileSystem.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath);
-
- return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress);
- }
-
- /// <summary>
- /// Reloads the root media folder
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task ValidateMediaLibrary(IProgress<double> progress, CancellationToken cancellationToken)
- {
- // Just run the scheduled task so that the user can see it
- _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
-
- return Task.FromResult(true);
- }
-
- /// <summary>
- /// Queues the library scan.
- /// </summary>
- public void QueueLibraryScan()
- {
- // Just run the scheduled task so that the user can see it
- _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>();
- }
-
- /// <summary>
- /// Validates the media library internal.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
- {
- IsScanRunning = true;
- _libraryMonitorFactory().Stop();
-
- try
- {
- await PerformLibraryValidation(progress, cancellationToken).ConfigureAwait(false);
- }
- finally
- {
- _libraryMonitorFactory().Start();
- IsScanRunning = false;
- }
- }
-
- private async Task PerformLibraryValidation(IProgress<double> progress, CancellationToken cancellationToken)
- {
- _logger.Info("Validating media library");
-
- await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
-
- progress.Report(.5);
-
- // Start by just validating the children of the root, but go no further
- await RootFolder.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false);
-
- progress.Report(1);
-
- var userRoot = GetUserRootFolder();
-
- await userRoot.RefreshMetadata(cancellationToken).ConfigureAwait(false);
-
- await userRoot.ValidateChildren(new Progress<double>(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false);
- progress.Report(2);
-
- var innerProgress = new ActionableProgress<double>();
-
- innerProgress.RegisterAction(pct => progress.Report(2 + pct * .73));
-
- // Now validate the entire media library
- await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: true).ConfigureAwait(false);
-
- progress.Report(75);
-
- innerProgress = new ActionableProgress<double>();
-
- innerProgress.RegisterAction(pct => progress.Report(75 + pct * .25));
-
- // Run post-scan tasks
- await RunPostScanTasks(innerProgress, cancellationToken).ConfigureAwait(false);
-
- progress.Report(100);
- }
-
- /// <summary>
- /// Runs the post scan tasks.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task RunPostScanTasks(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var tasks = PostscanTasks.ToList();
-
- var numComplete = 0;
- var numTasks = tasks.Count;
-
- foreach (var task in tasks)
- {
- var innerProgress = new ActionableProgress<double>();
-
- // Prevent access to modified closure
- var currentNumComplete = numComplete;
-
- innerProgress.RegisterAction(pct =>
- {
- double innerPercent = currentNumComplete * 100 + pct;
- innerPercent /= numTasks;
- progress.Report(innerPercent);
- });
-
- _logger.Debug("Running post-scan task {0}", task.GetType().Name);
-
- try
- {
- await task.Run(innerProgress, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- _logger.Info("Post-scan task cancelled: {0}", task.GetType().Name);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error running postscan task", ex);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= numTasks;
- progress.Report(percent * 100);
- }
-
- progress.Report(100);
- }
-
- /// <summary>
- /// Gets the default view.
- /// </summary>
- /// <returns>IEnumerable{VirtualFolderInfo}.</returns>
- public IEnumerable<VirtualFolderInfo> GetVirtualFolders()
- {
- return GetView(ConfigurationManager.ApplicationPaths.DefaultUserViewsPath);
- }
-
- /// <summary>
- /// Gets the view.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>IEnumerable{VirtualFolderInfo}.</returns>
- private IEnumerable<VirtualFolderInfo> GetView(string path)
- {
- var topLibraryFolders = GetUserRootFolder().Children.ToList();
-
- return _fileSystem.GetDirectoryPaths(path)
- .Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders));
- }
-
- private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> allCollectionFolders)
- {
- var info = new VirtualFolderInfo
- {
- Name = Path.GetFileName(dir),
-
- Locations = Directory.EnumerateFiles(dir, "*.mblink", SearchOption.TopDirectoryOnly)
- .Select(_fileSystem.ResolveShortcut)
- .OrderBy(i => i)
- .ToList(),
-
- CollectionType = GetCollectionType(dir)
- };
-
- var libraryFolder = allCollectionFolders.FirstOrDefault(i => string.Equals(i.Path, dir, StringComparison.OrdinalIgnoreCase));
-
- if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary))
- {
- info.PrimaryImageItemId = libraryFolder.Id.ToString("N");
- }
-
- if (libraryFolder != null)
- {
- info.ItemId = libraryFolder.Id.ToString("N");
- info.LibraryOptions = GetLibraryOptions(libraryFolder);
- }
-
- return info;
- }
-
- private string GetCollectionType(string path)
- {
- return _fileSystem.GetFiles(path, false)
- .Where(i => string.Equals(i.Extension, ".collection", StringComparison.OrdinalIgnoreCase))
- .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
- .FirstOrDefault();
- }
-
- /// <summary>
- /// Gets the item by id.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>BaseItem.</returns>
- /// <exception cref="System.ArgumentNullException">id</exception>
- public BaseItem GetItemById(Guid id)
- {
- if (id == Guid.Empty)
- {
- throw new ArgumentNullException("id");
- }
-
- BaseItem item;
-
- if (LibraryItemsCache.TryGetValue(id, out item))
- {
- return item;
- }
-
- item = RetrieveItem(id);
-
- //_logger.Debug("GetitemById {0}", id);
-
- if (item != null)
- {
- RegisterItem(item);
- }
-
- return item;
- }
-
- public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query)
- {
- if (query.Recursive && query.ParentId.HasValue)
- {
- var parent = GetItemById(query.ParentId.Value);
- if (parent != null)
- {
- SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
- query.ParentId = null;
- }
- }
-
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- return ItemRepository.GetItemList(query);
- }
-
- public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, IEnumerable<string> parentIds)
- {
- var parents = parentIds.Select(i => GetItemById(new Guid(i))).Where(i => i != null).ToList();
-
- SetTopParentIdsOrAncestors(query, parents);
-
- if (query.AncestorIds.Length == 0 && query.TopParentIds.Length == 0)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
- }
-
- return ItemRepository.GetItemList(query);
- }
-
- public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- if (query.EnableTotalRecordCount)
- {
- return ItemRepository.GetItems(query);
- }
-
- return new QueryResult<BaseItem>
- {
- Items = ItemRepository.GetItemList(query).ToArray()
- };
- }
-
- public List<Guid> GetItemIds(InternalItemsQuery query)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- return ItemRepository.GetItemIdsList(query);
- }
-
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- SetTopParentOrAncestorIds(query);
- return ItemRepository.GetStudios(query);
- }
-
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- SetTopParentOrAncestorIds(query);
- return ItemRepository.GetGenres(query);
- }
-
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- SetTopParentOrAncestorIds(query);
- return ItemRepository.GetGameGenres(query);
- }
-
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- SetTopParentOrAncestorIds(query);
- return ItemRepository.GetMusicGenres(query);
- }
-
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- SetTopParentOrAncestorIds(query);
- return ItemRepository.GetAllArtists(query);
- }
-
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- SetTopParentOrAncestorIds(query);
- return ItemRepository.GetArtists(query);
- }
-
- private void SetTopParentOrAncestorIds(InternalItemsQuery query)
- {
- if (query.AncestorIds.Length == 0)
- {
- return;
- }
-
- var parents = query.AncestorIds.Select(i => GetItemById(new Guid(i))).ToList();
-
- if (parents.All(i =>
- {
- if (i is ICollectionFolder || i is UserView)
- {
- return true;
- }
-
- //_logger.Debug("Query requires ancestor query due to type: " + i.GetType().Name);
- return false;
-
- }))
- {
- // Optimize by querying against top level views
- query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray();
- query.AncestorIds = new string[] { };
- }
- }
-
- public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
- {
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- SetTopParentOrAncestorIds(query);
- return ItemRepository.GetAlbumArtists(query);
- }
-
- public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query)
- {
- if (query.Recursive && query.ParentId.HasValue)
- {
- var parent = GetItemById(query.ParentId.Value);
- if (parent != null)
- {
- SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent });
- query.ParentId = null;
- }
- }
-
- if (query.User != null)
- {
- AddUserToQuery(query, query.User);
- }
-
- if (query.EnableTotalRecordCount)
- {
- return ItemRepository.GetItems(query);
- }
-
- return new QueryResult<BaseItem>
- {
- Items = ItemRepository.GetItemList(query).ToArray()
- };
- }
-
- private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents)
- {
- if (parents.All(i =>
- {
- if (i is ICollectionFolder || i is UserView)
- {
- return true;
- }
-
- //_logger.Debug("Query requires ancestor query due to type: " + i.GetType().Name);
- return false;
-
- }))
- {
- // Optimize by querying against top level views
- query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray();
- }
- else
- {
- // We need to be able to query from any arbitrary ancestor up the tree
- query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray();
- }
- }
-
- private void AddUserToQuery(InternalItemsQuery query, User user)
- {
- if (query.AncestorIds.Length == 0 &&
- !query.ParentId.HasValue &&
- query.ChannelIds.Length == 0 &&
- query.TopParentIds.Length == 0 &&
- string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)
- && query.ItemIds.Length == 0)
- {
- var userViews = _userviewManager().GetUserViews(new UserViewQuery
- {
- UserId = user.Id.ToString("N"),
- IncludeHidden = true
-
- }, CancellationToken.None).Result.ToList();
-
- query.TopParentIds = userViews.SelectMany(i => GetTopParentsForQuery(i, user)).Select(i => i.Id.ToString("N")).ToArray();
- }
- }
-
- private IEnumerable<BaseItem> GetTopParentsForQuery(BaseItem item, User user)
- {
- var view = item as UserView;
-
- if (view != null)
- {
- if (string.Equals(view.ViewType, CollectionType.LiveTv))
- {
- return new[] { view };
- }
- if (string.Equals(view.ViewType, CollectionType.Channels))
- {
- var channelResult = BaseItem.ChannelManager.GetChannelsInternal(new ChannelQuery
- {
- UserId = user.Id.ToString("N")
-
- }, CancellationToken.None).Result;
-
- return channelResult.Items;
- }
-
- // Translate view into folders
- if (view.DisplayParentId != Guid.Empty)
- {
- var displayParent = GetItemById(view.DisplayParentId);
- if (displayParent != null)
- {
- return GetTopParentsForQuery(displayParent, user);
- }
- return new BaseItem[] { };
- }
- if (view.ParentId != Guid.Empty)
- {
- var displayParent = GetItemById(view.ParentId);
- if (displayParent != null)
- {
- return GetTopParentsForQuery(displayParent, user);
- }
- return new BaseItem[] { };
- }
-
- // Handle grouping
- if (user != null && !string.IsNullOrWhiteSpace(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType))
- {
- return user.RootFolder
- .GetChildren(user, true)
- .OfType<CollectionFolder>()
- .Where(i => string.IsNullOrWhiteSpace(i.CollectionType) || string.Equals(i.CollectionType, view.ViewType, StringComparison.OrdinalIgnoreCase))
- .Where(i => user.IsFolderGrouped(i.Id))
- .SelectMany(i => GetTopParentsForQuery(i, user));
- }
- return new BaseItem[] { };
- }
-
- var collectionFolder = item as CollectionFolder;
- if (collectionFolder != null)
- {
- return collectionFolder.GetPhysicalParents();
- }
-
- var topParent = item.GetTopParent();
- if (topParent != null)
- {
- return new[] { topParent };
- }
- return new BaseItem[] { };
- }
-
- /// <summary>
- /// Gets the intros.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="user">The user.</param>
- /// <returns>IEnumerable{System.String}.</returns>
- public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user)
- {
- var tasks = IntroProviders
- .OrderBy(i => i.GetType().Name.IndexOf("Default", StringComparison.OrdinalIgnoreCase) == -1 ? 0 : 1)
- .Take(1)
- .Select(i => GetIntros(i, item, user));
-
- var items = await Task.WhenAll(tasks).ConfigureAwait(false);
-
- return items
- .SelectMany(i => i.ToArray())
- .Select(ResolveIntro)
- .Where(i => i != null);
- }
-
- /// <summary>
- /// Gets the intros.
- /// </summary>
- /// <param name="provider">The provider.</param>
- /// <param name="item">The item.</param>
- /// <param name="user">The user.</param>
- /// <returns>Task&lt;IEnumerable&lt;IntroInfo&gt;&gt;.</returns>
- private async Task<IEnumerable<IntroInfo>> GetIntros(IIntroProvider provider, BaseItem item, User user)
- {
- try
- {
- return await provider.GetIntros(item, user).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting intros", ex);
-
- return new List<IntroInfo>();
- }
- }
-
- /// <summary>
- /// Gets all intro files.
- /// </summary>
- /// <returns>IEnumerable{System.String}.</returns>
- public IEnumerable<string> GetAllIntroFiles()
- {
- return IntroProviders.SelectMany(i =>
- {
- try
- {
- return i.GetAllIntroFiles().ToList();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting intro files", ex);
-
- return new List<string>();
- }
- });
- }
-
- /// <summary>
- /// Resolves the intro.
- /// </summary>
- /// <param name="info">The info.</param>
- /// <returns>Video.</returns>
- private Video ResolveIntro(IntroInfo info)
- {
- Video video = null;
-
- if (info.ItemId.HasValue)
- {
- // Get an existing item by Id
- video = GetItemById(info.ItemId.Value) as Video;
-
- if (video == null)
- {
- _logger.Error("Unable to locate item with Id {0}.", info.ItemId.Value);
- }
- }
- else if (!string.IsNullOrEmpty(info.Path))
- {
- try
- {
- // Try to resolve the path into a video
- video = ResolvePath(_fileSystem.GetFileSystemInfo(info.Path)) as Video;
-
- if (video == null)
- {
- _logger.Error("Intro resolver returned null for {0}.", info.Path);
- }
- else
- {
- // Pull the saved db item that will include metadata
- var dbItem = GetItemById(video.Id) as Video;
-
- if (dbItem != null)
- {
- video = dbItem;
- }
- else
- {
- return null;
- }
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error resolving path {0}.", ex, info.Path);
- }
- }
- else
- {
- _logger.Error("IntroProvider returned an IntroInfo with null Path and ItemId.");
- }
-
- return video;
- }
-
- /// <summary>
- /// Sorts the specified sort by.
- /// </summary>
- /// <param name="items">The items.</param>
- /// <param name="user">The user.</param>
- /// <param name="sortBy">The sort by.</param>
- /// <param name="sortOrder">The sort order.</param>
- /// <returns>IEnumerable{BaseItem}.</returns>
- public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder)
- {
- var isFirst = true;
-
- IOrderedEnumerable<BaseItem> orderedItems = null;
-
- foreach (var orderBy in sortBy.Select(o => GetComparer(o, user)).Where(c => c != null))
- {
- if (isFirst)
- {
- orderedItems = sortOrder == SortOrder.Descending ? items.OrderByDescending(i => i, orderBy) : items.OrderBy(i => i, orderBy);
- }
- else
- {
- orderedItems = sortOrder == SortOrder.Descending ? orderedItems.ThenByDescending(i => i, orderBy) : orderedItems.ThenBy(i => i, orderBy);
- }
-
- isFirst = false;
- }
-
- return orderedItems ?? items;
- }
-
- /// <summary>
- /// Gets the comparer.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="user">The user.</param>
- /// <returns>IBaseItemComparer.</returns>
- private IBaseItemComparer GetComparer(string name, User user)
- {
- var comparer = Comparers.FirstOrDefault(c => string.Equals(name, c.Name, StringComparison.OrdinalIgnoreCase));
-
- if (comparer != null)
- {
- // If it requires a user, create a new one, and assign the user
- if (comparer is IUserBaseItemComparer)
- {
- var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType());
-
- userComparer.User = user;
- userComparer.UserManager = _userManager;
- userComparer.UserDataRepository = _userDataRepository;
-
- return userComparer;
- }
- }
-
- return comparer;
- }
-
- /// <summary>
- /// Creates the item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task CreateItem(BaseItem item, CancellationToken cancellationToken)
- {
- return CreateItems(new[] { item }, cancellationToken);
- }
-
- /// <summary>
- /// Creates the items.
- /// </summary>
- /// <param name="items">The items.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task CreateItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
- {
- var list = items.ToList();
-
- await ItemRepository.SaveItems(list, cancellationToken).ConfigureAwait(false);
-
- foreach (var item in list)
- {
- RegisterItem(item);
- }
-
- if (ItemAdded != null)
- {
- foreach (var item in list)
- {
- try
- {
- ItemAdded(this, new ItemChangeEventArgs { Item = item });
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in ItemAdded event handler", ex);
- }
- }
- }
- }
-
- /// <summary>
- /// Updates the item.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="updateReason">The update reason.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task UpdateItem(BaseItem item, ItemUpdateType updateReason, CancellationToken cancellationToken)
- {
- var locationType = item.LocationType;
- if (locationType != LocationType.Remote && locationType != LocationType.Virtual)
- {
- await _providerManagerFactory().SaveMetadata(item, updateReason).ConfigureAwait(false);
- }
-
- item.DateLastSaved = DateTime.UtcNow;
-
- var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
- _logger.Debug("Saving {0} to database.", logName);
-
- await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false);
-
- RegisterItem(item);
-
- if (ItemUpdated != null)
- {
- try
- {
- ItemUpdated(this, new ItemChangeEventArgs
- {
- Item = item,
- UpdateReason = updateReason
- });
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in ItemUpdated event handler", ex);
- }
- }
- }
-
- /// <summary>
- /// Reports the item removed.
- /// </summary>
- /// <param name="item">The item.</param>
- public void ReportItemRemoved(BaseItem item)
- {
- if (ItemRemoved != null)
- {
- try
- {
- ItemRemoved(this, new ItemChangeEventArgs { Item = item });
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in ItemRemoved event handler", ex);
- }
- }
- }
-
- /// <summary>
- /// Retrieves the item.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>BaseItem.</returns>
- public BaseItem RetrieveItem(Guid id)
- {
- return ItemRepository.RetrieveItem(id);
- }
-
- public IEnumerable<Folder> GetCollectionFolders(BaseItem item)
- {
- while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null)
- {
- item = item.GetParent();
- }
-
- if (item == null)
- {
- return new List<Folder>();
- }
-
- return GetUserRootFolder().Children
- .OfType<Folder>()
- .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase));
- }
-
- public LibraryOptions GetLibraryOptions(BaseItem item)
- {
- var collectionFolder = item as CollectionFolder;
- if (collectionFolder == null)
- {
- collectionFolder = GetCollectionFolders(item)
- .OfType<CollectionFolder>()
- .FirstOrDefault();
- }
-
- var options = collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
-
- if (options.SchemaVersion < 3)
- {
- options.SaveLocalMetadata = ConfigurationManager.Configuration.SaveLocalMeta;
- options.EnableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders;
- }
-
- if (options.SchemaVersion < 2)
- {
- var chapterOptions = ConfigurationManager.GetConfiguration<ChapterOptions>("chapters");
- options.ExtractChapterImagesDuringLibraryScan = chapterOptions.ExtractDuringLibraryScan;
-
- if (collectionFolder != null)
- {
- if (string.Equals(collectionFolder.CollectionType, "movies", StringComparison.OrdinalIgnoreCase))
- {
- options.EnableChapterImageExtraction = chapterOptions.EnableMovieChapterImageExtraction;
- }
- else if (string.Equals(collectionFolder.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
- {
- options.EnableChapterImageExtraction = chapterOptions.EnableEpisodeChapterImageExtraction;
- }
- }
- }
-
- return options;
- }
-
- public string GetContentType(BaseItem item)
- {
- string configuredContentType = GetConfiguredContentType(item, false);
- if (!string.IsNullOrWhiteSpace(configuredContentType))
- {
- return configuredContentType;
- }
- configuredContentType = GetConfiguredContentType(item, true);
- if (!string.IsNullOrWhiteSpace(configuredContentType))
- {
- return configuredContentType;
- }
- return GetInheritedContentType(item);
- }
-
- public string GetInheritedContentType(BaseItem item)
- {
- var type = GetTopFolderContentType(item);
-
- if (!string.IsNullOrWhiteSpace(type))
- {
- return type;
- }
-
- return item.GetParents()
- .Select(GetConfiguredContentType)
- .LastOrDefault(i => !string.IsNullOrWhiteSpace(i));
- }
-
- public string GetConfiguredContentType(BaseItem item)
- {
- return GetConfiguredContentType(item, false);
- }
-
- public string GetConfiguredContentType(string path)
- {
- return GetContentTypeOverride(path, false);
- }
-
- public string GetConfiguredContentType(BaseItem item, bool inheritConfiguredPath)
- {
- ICollectionFolder collectionFolder = item as ICollectionFolder;
- if (collectionFolder != null)
- {
- return collectionFolder.CollectionType;
- }
- return GetContentTypeOverride(item.ContainingFolderPath, inheritConfiguredPath);
- }
-
- private string GetContentTypeOverride(string path, bool inherit)
- {
- var nameValuePair = ConfigurationManager.Configuration.ContentTypes.FirstOrDefault(i => string.Equals(i.Name, path, StringComparison.OrdinalIgnoreCase) || (inherit && !string.IsNullOrWhiteSpace(i.Name) && _fileSystem.ContainsSubPath(i.Name, path)));
- if (nameValuePair != null)
- {
- return nameValuePair.Value;
- }
- return null;
- }
-
- private string GetTopFolderContentType(BaseItem item)
- {
- if (item == null)
- {
- return null;
- }
-
- while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null)
- {
- item = item.GetParent();
- }
-
- return GetUserRootFolder().Children
- .OfType<ICollectionFolder>()
- .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path))
- .Select(i => i.CollectionType)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
- }
-
- private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
- //private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromMinutes(1);
-
- public Task<UserView> GetNamedView(User user,
- string name,
- string viewType,
- string sortName,
- CancellationToken cancellationToken)
- {
- return GetNamedView(user, name, null, viewType, sortName, cancellationToken);
- }
-
- public async Task<UserView> GetNamedView(string name,
- string viewType,
- string sortName,
- CancellationToken cancellationToken)
- {
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "views");
-
- path = Path.Combine(path, _fileSystem.GetValidFilename(viewType));
-
- var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView));
-
- var item = GetItemById(id) as UserView;
-
- var refresh = false;
-
- if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
- {
- _fileSystem.CreateDirectory(path);
-
- item = new UserView
- {
- Path = path,
- Id = id,
- DateCreated = DateTime.UtcNow,
- Name = name,
- ViewType = viewType,
- ForcedSortName = sortName
- };
-
- await CreateItem(item, cancellationToken).ConfigureAwait(false);
-
- refresh = true;
- }
-
- if (!refresh)
- {
- refresh = DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
- }
-
- if (!refresh && item.DisplayParentId != Guid.Empty)
- {
- var displayParent = GetItemById(item.DisplayParentId);
- refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
- }
-
- if (refresh)
- {
- await item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None).ConfigureAwait(false);
- _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)
- {
- // Not sure why this is necessary but need to figure it out
- // View images are not getting utilized without this
- ForceSave = true
- });
- }
-
- return item;
- }
-
- public async Task<UserView> GetNamedView(User user,
- string name,
- string parentId,
- string viewType,
- string sortName,
- CancellationToken cancellationToken)
- {
- var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty) + (viewType ?? string.Empty);
-
- var id = GetNewItemId(idValues, typeof(UserView));
-
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
-
- var item = GetItemById(id) as UserView;
-
- var isNew = false;
-
- if (item == null)
- {
- _fileSystem.CreateDirectory(path);
-
- item = new UserView
- {
- Path = path,
- Id = id,
- DateCreated = DateTime.UtcNow,
- Name = name,
- ViewType = viewType,
- ForcedSortName = sortName,
- UserId = user.Id
- };
-
- if (!string.IsNullOrWhiteSpace(parentId))
- {
- item.DisplayParentId = new Guid(parentId);
- }
-
- await CreateItem(item, cancellationToken).ConfigureAwait(false);
-
- isNew = true;
- }
-
- var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
-
- if (!refresh && item.DisplayParentId != Guid.Empty)
- {
- var displayParent = GetItemById(item.DisplayParentId);
- refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
- }
-
- if (refresh)
- {
- _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)
- {
- // Need to force save to increment DateLastSaved
- ForceSave = true
- });
- }
-
- return item;
- }
-
- public async Task<UserView> GetShadowView(BaseItem parent,
- string viewType,
- string sortName,
- CancellationToken cancellationToken)
- {
- if (parent == null)
- {
- throw new ArgumentNullException("parent");
- }
-
- var name = parent.Name;
- var parentId = parent.Id;
-
- var idValues = "38_namedview_" + name + parentId + (viewType ?? string.Empty);
-
- var id = GetNewItemId(idValues, typeof(UserView));
-
- var path = parent.Path;
-
- var item = GetItemById(id) as UserView;
-
- var isNew = false;
-
- if (item == null)
- {
- _fileSystem.CreateDirectory(path);
-
- item = new UserView
- {
- Path = path,
- Id = id,
- DateCreated = DateTime.UtcNow,
- Name = name,
- ViewType = viewType,
- ForcedSortName = sortName
- };
-
- item.DisplayParentId = parentId;
-
- await CreateItem(item, cancellationToken).ConfigureAwait(false);
-
- isNew = true;
- }
-
- var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
-
- if (!refresh && item.DisplayParentId != Guid.Empty)
- {
- var displayParent = GetItemById(item.DisplayParentId);
- refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
- }
-
- if (refresh)
- {
- _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)
- {
- // Need to force save to increment DateLastSaved
- ForceSave = true
- });
- }
-
- return item;
- }
-
- public async Task<UserView> GetNamedView(string name,
- string parentId,
- string viewType,
- string sortName,
- string uniqueId,
- CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- var idValues = "37_namedview_" + name + (parentId ?? string.Empty) + (viewType ?? string.Empty);
- if (!string.IsNullOrWhiteSpace(uniqueId))
- {
- idValues += uniqueId;
- }
-
- var id = GetNewItemId(idValues, typeof(UserView));
-
- var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
-
- var item = GetItemById(id) as UserView;
-
- var isNew = false;
-
- if (item == null)
- {
- _fileSystem.CreateDirectory(path);
-
- item = new UserView
- {
- Path = path,
- Id = id,
- DateCreated = DateTime.UtcNow,
- Name = name,
- ViewType = viewType,
- ForcedSortName = sortName
- };
-
- if (!string.IsNullOrWhiteSpace(parentId))
- {
- item.DisplayParentId = new Guid(parentId);
- }
-
- await CreateItem(item, cancellationToken).ConfigureAwait(false);
-
- isNew = true;
- }
-
- if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
- {
- item.ViewType = viewType;
- await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
- }
-
- var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
-
- if (!refresh && item.DisplayParentId != Guid.Empty)
- {
- var displayParent = GetItemById(item.DisplayParentId);
- refresh = displayParent != null && displayParent.DateLastSaved > item.DateLastRefreshed;
- }
-
- if (refresh)
- {
- _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)
- {
- // Need to force save to increment DateLastSaved
- ForceSave = true
- });
- }
-
- return item;
- }
-
- public bool IsVideoFile(string path, LibraryOptions libraryOptions)
- {
- var resolver = new VideoResolver(GetNamingOptions(libraryOptions), new PatternsLogger());
- return resolver.IsVideoFile(path);
- }
-
- public bool IsVideoFile(string path)
- {
- return IsVideoFile(path, new LibraryOptions());
- }
-
- public bool IsAudioFile(string path, LibraryOptions libraryOptions)
- {
- var parser = new AudioFileParser(GetNamingOptions(libraryOptions));
- return parser.IsAudioFile(path);
- }
-
- public bool IsAudioFile(string path)
- {
- return IsAudioFile(path, new LibraryOptions());
- }
-
- public int? GetSeasonNumberFromPath(string path)
- {
- return new SeasonPathParser(GetNamingOptions(), new RegexProvider()).Parse(path, true, true).SeasonNumber;
- }
-
- public bool FillMissingEpisodeNumbersFromPath(Episode episode)
- {
- var resolver = new EpisodeResolver(GetNamingOptions(),
- new PatternsLogger());
-
- var isFolder = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd ||
- episode.VideoType == VideoType.HdDvd;
-
- var locationType = episode.LocationType;
-
- var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
- resolver.Resolve(episode.Path, isFolder) :
- new Naming.TV.EpisodeInfo();
-
- if (episodeInfo == null)
- {
- episodeInfo = new Naming.TV.EpisodeInfo();
- }
-
- var changed = false;
-
- if (episodeInfo.IsByDate)
- {
- if (episode.IndexNumber.HasValue)
- {
- episode.IndexNumber = null;
- changed = true;
- }
-
- if (episode.IndexNumberEnd.HasValue)
- {
- episode.IndexNumberEnd = null;
- changed = true;
- }
-
- if (!episode.PremiereDate.HasValue)
- {
- if (episodeInfo.Year.HasValue && episodeInfo.Month.HasValue && episodeInfo.Day.HasValue)
- {
- episode.PremiereDate = new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value).ToUniversalTime();
- }
-
- if (episode.PremiereDate.HasValue)
- {
- changed = true;
- }
- }
-
- if (!episode.ProductionYear.HasValue)
- {
- episode.ProductionYear = episodeInfo.Year;
-
- if (episode.ProductionYear.HasValue)
- {
- changed = true;
- }
- }
-
- if (!episode.ParentIndexNumber.HasValue)
- {
- var season = episode.Season;
-
- if (season != null)
- {
- episode.ParentIndexNumber = season.IndexNumber;
- }
-
- if (episode.ParentIndexNumber.HasValue)
- {
- changed = true;
- }
- }
- }
- else
- {
- if (!episode.IndexNumber.HasValue)
- {
- episode.IndexNumber = episodeInfo.EpisodeNumber;
-
- if (episode.IndexNumber.HasValue)
- {
- changed = true;
- }
- }
-
- if (!episode.IndexNumberEnd.HasValue)
- {
- episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber;
-
- if (episode.IndexNumberEnd.HasValue)
- {
- changed = true;
- }
- }
-
- if (!episode.ParentIndexNumber.HasValue)
- {
- episode.ParentIndexNumber = episodeInfo.SeasonNumber;
-
- if (!episode.ParentIndexNumber.HasValue)
- {
- var season = episode.Season;
-
- if (season != null)
- {
- episode.ParentIndexNumber = season.IndexNumber;
- }
- }
-
- if (episode.ParentIndexNumber.HasValue)
- {
- changed = true;
- }
- }
- }
-
- return changed;
- }
-
- public NamingOptions GetNamingOptions()
- {
- return GetNamingOptions(new LibraryOptions());
- }
-
- public NamingOptions GetNamingOptions(LibraryOptions libraryOptions)
- {
- var options = new ExtendedNamingOptions();
-
- // These cause apps to have problems
- options.AudioFileExtensions.Remove(".m3u");
- options.AudioFileExtensions.Remove(".wpl");
-
- if (!libraryOptions.EnableArchiveMediaFiles)
- {
- options.AudioFileExtensions.Remove(".rar");
- options.AudioFileExtensions.Remove(".zip");
- }
-
- if (!libraryOptions.EnableArchiveMediaFiles)
- {
- options.VideoFileExtensions.Remove(".rar");
- options.VideoFileExtensions.Remove(".zip");
- }
-
- return options;
- }
-
- public ItemLookupInfo ParseName(string name)
- {
- var resolver = new VideoResolver(GetNamingOptions(), new PatternsLogger());
-
- var result = resolver.CleanDateTime(name);
- var cleanName = resolver.CleanString(result.Name);
-
- return new ItemLookupInfo
- {
- Name = cleanName.Name,
- Year = result.Year
- };
- }
-
- public IEnumerable<Video> FindTrailers(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
- {
- var files = owner.DetectIsInMixedFolder() ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
- .Where(i => string.Equals(i.Name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase))
- .SelectMany(i => _fileSystem.GetFiles(i.FullName, false))
- .ToList();
-
- var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger());
-
- var videos = videoListResolver.Resolve(fileSystemChildren);
-
- var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
-
- if (currentVideo != null)
- {
- files.AddRange(currentVideo.Extras.Where(i => string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path)));
- }
-
- var resolvers = new IItemResolver[]
- {
- new GenericVideoResolver<Trailer>(this)
- };
-
- return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers)
- .OfType<Trailer>()
- .Select(video =>
- {
- // Try to retrieve it from the db. If we don't find it, use the resolved version
- var dbItem = GetItemById(video.Id) as Trailer;
-
- if (dbItem != null)
- {
- video = dbItem;
- }
-
- video.ExtraType = ExtraType.Trailer;
- video.TrailerTypes = new List<TrailerType> { TrailerType.LocalTrailer };
-
- return video;
-
- // Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path).ToList();
- }
-
- public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
- {
- var files = fileSystemChildren.Where(i => i.IsDirectory)
- .Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase))
- .SelectMany(i => _fileSystem.GetFiles(i.FullName, false))
- .ToList();
-
- var videoListResolver = new VideoListResolver(GetNamingOptions(), new PatternsLogger());
-
- var videos = videoListResolver.Resolve(fileSystemChildren);
-
- var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files.First().Path, StringComparison.OrdinalIgnoreCase));
-
- if (currentVideo != null)
- {
- files.AddRange(currentVideo.Extras.Where(i => !string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path)));
- }
-
- return ResolvePaths(files, directoryService, null, new LibraryOptions(), null)
- .OfType<Video>()
- .Select(video =>
- {
- // Try to retrieve it from the db. If we don't find it, use the resolved version
- var dbItem = GetItemById(video.Id) as Video;
-
- if (dbItem != null)
- {
- video = dbItem;
- }
-
- SetExtraTypeFromFilename(video);
-
- return video;
-
- // Sort them so that the list can be easily compared for changes
- }).OrderBy(i => i.Path).ToList();
- }
-
- public string GetPathAfterNetworkSubstitution(string path, BaseItem ownerItem)
- {
- if (ownerItem != null)
- {
- var libraryOptions = GetLibraryOptions(ownerItem);
- if (libraryOptions != null)
- {
- foreach (var pathInfo in libraryOptions.PathInfos)
- {
- if (string.IsNullOrWhiteSpace(pathInfo.NetworkPath))
- {
- continue;
- }
-
- var substitutionResult = SubstitutePathInternal(path, pathInfo.Path, pathInfo.NetworkPath);
- if (substitutionResult.Item2)
- {
- return substitutionResult.Item1;
- }
- }
- }
- }
-
- var metadataPath = ConfigurationManager.Configuration.MetadataPath;
- var metadataNetworkPath = ConfigurationManager.Configuration.MetadataNetworkPath;
-
- if (!string.IsNullOrWhiteSpace(metadataPath) && !string.IsNullOrWhiteSpace(metadataNetworkPath))
- {
- var metadataSubstitutionResult = SubstitutePathInternal(path, metadataPath, metadataNetworkPath);
- if (metadataSubstitutionResult.Item2)
- {
- return metadataSubstitutionResult.Item1;
- }
- }
-
- foreach (var map in ConfigurationManager.Configuration.PathSubstitutions)
- {
- var substitutionResult = SubstitutePathInternal(path, map.From, map.To);
- if (substitutionResult.Item2)
- {
- return substitutionResult.Item1;
- }
- }
-
- return path;
- }
-
- public string SubstitutePath(string path, string from, string to)
- {
- return SubstitutePathInternal(path, from, to).Item1;
- }
-
- private Tuple<string, bool> SubstitutePathInternal(string path, string from, string to)
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException("path");
- }
- if (string.IsNullOrWhiteSpace(from))
- {
- throw new ArgumentNullException("from");
- }
- if (string.IsNullOrWhiteSpace(to))
- {
- throw new ArgumentNullException("to");
- }
-
- from = from.Trim();
- to = to.Trim();
-
- var newPath = path.Replace(from, to, StringComparison.OrdinalIgnoreCase);
- var changed = false;
-
- if (!string.Equals(newPath, path))
- {
- if (to.IndexOf('/') != -1)
- {
- newPath = newPath.Replace('\\', '/');
- }
- else
- {
- newPath = newPath.Replace('/', '\\');
- }
-
- changed = true;
- }
-
- return new Tuple<string, bool>(newPath, changed);
- }
-
- private void SetExtraTypeFromFilename(Video item)
- {
- var resolver = new ExtraResolver(GetNamingOptions(), new PatternsLogger(), new RegexProvider());
-
- var result = resolver.GetExtraInfo(item.Path);
-
- if (string.Equals(result.ExtraType, "deletedscene", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.DeletedScene;
- }
- else if (string.Equals(result.ExtraType, "behindthescenes", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.BehindTheScenes;
- }
- else if (string.Equals(result.ExtraType, "interview", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.Interview;
- }
- else if (string.Equals(result.ExtraType, "scene", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.Scene;
- }
- else if (string.Equals(result.ExtraType, "sample", StringComparison.OrdinalIgnoreCase))
- {
- item.ExtraType = ExtraType.Sample;
- }
- else
- {
- item.ExtraType = ExtraType.Clip;
- }
- }
-
- public List<PersonInfo> GetPeople(InternalPeopleQuery query)
- {
- return ItemRepository.GetPeople(query);
- }
-
- public List<PersonInfo> GetPeople(BaseItem item)
- {
- if (item.SupportsPeople)
- {
- var people = GetPeople(new InternalPeopleQuery
- {
- ItemId = item.Id
- });
-
- if (people.Count > 0)
- {
- return people;
- }
- }
-
- return new List<PersonInfo>();
- }
-
- public List<Person> GetPeopleItems(InternalPeopleQuery query)
- {
- return ItemRepository.GetPeopleNames(query).Select(i =>
- {
- try
- {
- return GetPerson(i);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting person", ex);
- return null;
- }
-
- }).Where(i => i != null).ToList();
- }
-
- public List<string> GetPeopleNames(InternalPeopleQuery query)
- {
- return ItemRepository.GetPeopleNames(query);
- }
-
- public Task UpdatePeople(BaseItem item, List<PersonInfo> people)
- {
- if (!item.SupportsPeople)
- {
- return Task.FromResult(true);
- }
-
- return ItemRepository.UpdatePeople(item.Id, people);
- }
-
- private readonly SemaphoreSlim _dynamicImageResourcePool = new SemaphoreSlim(1, 1);
- public async Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex)
- {
- foreach (var url in image.Path.Split('|'))
- {
- try
- {
- _logger.Debug("ConvertImageToLocal item {0} - image url: {1}", item.Id, url);
-
- await _providerManagerFactory().SaveImage(item, url, _dynamicImageResourcePool, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
-
- var newImage = item.GetImageInfo(image.Type, imageIndex);
-
- if (newImage != null)
- {
- newImage.IsPlaceholder = image.IsPlaceholder;
- }
-
- await item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
-
- return item.GetImageInfo(image.Type, imageIndex);
- }
- catch (HttpException ex)
- {
- if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
- {
- continue;
- }
- throw;
- }
- }
-
- // Remove this image to prevent it from retrying over and over
- item.RemoveImage(image);
- await item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
-
- throw new InvalidOperationException();
- }
-
- public void AddVirtualFolder(string name, string collectionType, LibraryOptions options, bool refreshLibrary)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- name = _fileSystem.GetValidFilename(name);
-
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
-
- var virtualFolderPath = Path.Combine(rootFolderPath, name);
- while (_fileSystem.DirectoryExists(virtualFolderPath))
- {
- name += "1";
- virtualFolderPath = Path.Combine(rootFolderPath, name);
- }
-
- var mediaPathInfos = options.PathInfos;
- if (mediaPathInfos != null)
- {
- var invalidpath = mediaPathInfos.FirstOrDefault(i => !_fileSystem.DirectoryExists(i.Path));
- if (invalidpath != null)
- {
- throw new ArgumentException("The specified path does not exist: " + invalidpath.Path + ".");
- }
- }
-
- _libraryMonitorFactory().Stop();
-
- try
- {
- _fileSystem.CreateDirectory(virtualFolderPath);
-
- if (!string.IsNullOrEmpty(collectionType))
- {
- var path = Path.Combine(virtualFolderPath, collectionType + ".collection");
-
- using (File.Create(path))
- {
-
- }
- }
-
- CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
-
- if (mediaPathInfos != null)
- {
- foreach (var path in mediaPathInfos)
- {
- AddMediaPathInternal(name, path, false);
- }
- }
- }
- finally
- {
- Task.Run(() =>
- {
- // No need to start if scanning the library because it will handle it
- if (refreshLibrary)
- {
- ValidateMediaLibrary(new Progress<double>(), CancellationToken.None);
- }
- else
- {
- // Need to add a delay here or directory watchers may still pick up the changes
- var task = Task.Delay(1000);
- // Have to block here to allow exceptions to bubble
- Task.WaitAll(task);
-
- _libraryMonitorFactory().Start();
- }
- });
- }
- }
-
- private bool ValidateNetworkPath(string path)
- {
- if (Environment.OSVersion.Platform == PlatformID.Win32NT)
- {
- // We can't validate protocol-based paths, so just allow them
- if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
- {
- return Directory.Exists(path);
- }
- }
-
- // Without native support for unc, we cannot validate this when running under mono
- return true;
- }
-
- private const string ShortcutFileExtension = ".mblink";
- private const string ShortcutFileSearch = "*" + ShortcutFileExtension;
- public void AddMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
- {
- AddMediaPathInternal(virtualFolderName, pathInfo, true);
- }
-
- private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions)
- {
- if (pathInfo == null)
- {
- throw new ArgumentNullException("path");
- }
-
- var path = pathInfo.Path;
-
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException("path");
- }
-
- if (!_fileSystem.DirectoryExists(path))
- {
- throw new DirectoryNotFoundException("The path does not exist.");
- }
-
- if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
- {
- throw new DirectoryNotFoundException("The network path does not exist.");
- }
-
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
- var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
-
- var shortcutFilename = _fileSystem.GetFileNameWithoutExtension(path);
-
- var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
-
- while (_fileSystem.FileExists(lnk))
- {
- shortcutFilename += "1";
- lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
- }
-
- _fileSystem.CreateShortcut(lnk, path);
-
- RemoveContentTypeOverrides(path);
-
- if (saveLibraryOptions)
- {
- var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
-
- var list = libraryOptions.PathInfos.ToList();
- list.Add(pathInfo);
- libraryOptions.PathInfos = list.ToArray();
-
- SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions);
-
- CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions);
- }
- }
-
- public void UpdateMediaPath(string virtualFolderName, MediaPathInfo pathInfo)
- {
- if (pathInfo == null)
- {
- throw new ArgumentNullException("path");
- }
-
- if (!string.IsNullOrWhiteSpace(pathInfo.NetworkPath) && !ValidateNetworkPath(pathInfo.NetworkPath))
- {
- throw new DirectoryNotFoundException("The network path does not exist.");
- }
-
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
- var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
-
- var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
-
- SyncLibraryOptionsToLocations(virtualFolderPath, libraryOptions);
-
- var list = libraryOptions.PathInfos.ToList();
- foreach (var originalPathInfo in list)
- {
- if (string.Equals(pathInfo.Path, originalPathInfo.Path, StringComparison.Ordinal))
- {
- originalPathInfo.NetworkPath = pathInfo.NetworkPath;
- break;
- }
- }
-
- libraryOptions.PathInfos = list.ToArray();
-
- CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions);
- }
-
- private void SyncLibraryOptionsToLocations(string virtualFolderPath, LibraryOptions options)
- {
- var topLibraryFolders = GetUserRootFolder().Children.ToList();
- var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders);
-
- if (info.Locations.Count > 0 && info.Locations.Count != options.PathInfos.Length)
- {
- var list = options.PathInfos.ToList();
-
- foreach (var location in info.Locations)
- {
- if (!list.Any(i => string.Equals(i.Path, location, StringComparison.Ordinal)))
- {
- list.Add(new MediaPathInfo
- {
- Path = location
- });
- }
- }
-
- options.PathInfos = list.ToArray();
- }
- }
-
- public void RemoveVirtualFolder(string name, bool refreshLibrary)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
-
- var path = Path.Combine(rootFolderPath, name);
-
- if (!_fileSystem.DirectoryExists(path))
- {
- throw new DirectoryNotFoundException("The media folder does not exist");
- }
-
- _libraryMonitorFactory().Stop();
-
- try
- {
- _fileSystem.DeleteDirectory(path, true);
- }
- finally
- {
- Task.Run(() =>
- {
- // No need to start if scanning the library because it will handle it
- if (refreshLibrary)
- {
- ValidateMediaLibrary(new Progress<double>(), CancellationToken.None);
- }
- else
- {
- // Need to add a delay here or directory watchers may still pick up the changes
- var task = Task.Delay(1000);
- // Have to block here to allow exceptions to bubble
- Task.WaitAll(task);
-
- _libraryMonitorFactory().Start();
- }
- });
- }
- }
-
- private void RemoveContentTypeOverrides(string path)
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException("path");
- }
-
- var removeList = new List<NameValuePair>();
-
- foreach (var contentType in ConfigurationManager.Configuration.ContentTypes)
- {
- if (string.Equals(path, contentType.Name, StringComparison.OrdinalIgnoreCase)
- || _fileSystem.ContainsSubPath(path, contentType.Name))
- {
- removeList.Add(contentType);
- }
- }
-
- if (removeList.Count > 0)
- {
- ConfigurationManager.Configuration.ContentTypes = ConfigurationManager.Configuration.ContentTypes
- .Except(removeList)
- .ToArray();
-
- ConfigurationManager.SaveConfiguration();
- }
- }
-
- public void RemoveMediaPath(string virtualFolderName, string mediaPath)
- {
- if (string.IsNullOrWhiteSpace(mediaPath))
- {
- throw new ArgumentNullException("mediaPath");
- }
-
- var rootFolderPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
- var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
-
- if (!_fileSystem.DirectoryExists(virtualFolderPath))
- {
- throw new DirectoryNotFoundException(string.Format("The media collection {0} does not exist", virtualFolderName));
- }
-
- var shortcut = Directory.EnumerateFiles(virtualFolderPath, ShortcutFileSearch, SearchOption.AllDirectories).FirstOrDefault(f => _fileSystem.ResolveShortcut(f).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
-
- if (!string.IsNullOrEmpty(shortcut))
- {
- _fileSystem.DeleteFile(shortcut);
- }
-
- var libraryOptions = CollectionFolder.GetLibraryOptions(virtualFolderPath);
-
- libraryOptions.PathInfos = libraryOptions
- .PathInfos
- .Where(i => !string.Equals(i.Path, mediaPath, StringComparison.Ordinal))
- .ToArray();
-
- CollectionFolder.SaveLibraryOptions(virtualFolderPath, libraryOptions);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs
deleted file mode 100644
index 78107b82d..000000000
--- a/MediaBrowser.Server.Implementations/Library/LocalTrailerPostScanTask.cs
+++ /dev/null
@@ -1,102 +0,0 @@
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- public class LocalTrailerPostScanTask : ILibraryPostScanTask
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IChannelManager _channelManager;
-
- public LocalTrailerPostScanTask(ILibraryManager libraryManager, IChannelManager channelManager)
- {
- _libraryManager = libraryManager;
- _channelManager = channelManager;
- }
-
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var items = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(BoxSet).Name, typeof(Game).Name, typeof(Movie).Name, typeof(Series).Name },
- Recursive = true
-
- }).OfType<IHasTrailers>().ToList();
-
- var trailerTypes = Enum.GetNames(typeof(TrailerType))
- .Select(i => (TrailerType)Enum.Parse(typeof(TrailerType), i, true))
- .Except(new[] { TrailerType.LocalTrailer })
- .ToArray();
-
- var trailers = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Trailer).Name },
- TrailerTypes = trailerTypes,
- Recursive = true
-
- }).ToArray();
-
- var numComplete = 0;
-
- foreach (var item in items)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- await AssignTrailers(item, trailers).ConfigureAwait(false);
-
- numComplete++;
- double percent = numComplete;
- percent /= items.Count;
- progress.Report(percent * 100);
- }
-
- progress.Report(100);
- }
-
- private async Task AssignTrailers(IHasTrailers item, BaseItem[] channelTrailers)
- {
- if (item is Game)
- {
- return;
- }
-
- var imdbId = item.GetProviderId(MetadataProviders.Imdb);
- var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
-
- var trailers = channelTrailers.Where(i =>
- {
- if (!string.IsNullOrWhiteSpace(imdbId) &&
- string.Equals(imdbId, i.GetProviderId(MetadataProviders.Imdb), StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- if (!string.IsNullOrWhiteSpace(tmdbId) &&
- string.Equals(tmdbId, i.GetProviderId(MetadataProviders.Tmdb), StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- return false;
- });
-
- var trailerIds = trailers.Select(i => i.Id)
- .ToList();
-
- if (!trailerIds.SequenceEqual(item.RemoteTrailerIds))
- {
- item.RemoteTrailerIds = trailerIds;
-
- var baseItem = (BaseItem)item;
- await baseItem.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None)
- .ConfigureAwait(false);
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
deleted file mode 100644
index 0f1931dda..000000000
--- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs
+++ /dev/null
@@ -1,648 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- public class MediaSourceManager : IMediaSourceManager, IDisposable
- {
- private readonly IItemRepository _itemRepo;
- private readonly IUserManager _userManager;
- private readonly ILibraryManager _libraryManager;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly IFileSystem _fileSystem;
-
- private IMediaSourceProvider[] _providers;
- private readonly ILogger _logger;
- private readonly IUserDataManager _userDataManager;
-
- public MediaSourceManager(IItemRepository itemRepo, IUserManager userManager, ILibraryManager libraryManager, ILogger logger, IJsonSerializer jsonSerializer, IFileSystem fileSystem, IUserDataManager userDataManager)
- {
- _itemRepo = itemRepo;
- _userManager = userManager;
- _libraryManager = libraryManager;
- _logger = logger;
- _jsonSerializer = jsonSerializer;
- _fileSystem = fileSystem;
- _userDataManager = userDataManager;
- }
-
- public void AddParts(IEnumerable<IMediaSourceProvider> providers)
- {
- _providers = providers.ToArray();
- }
-
- public IEnumerable<MediaStream> GetMediaStreams(MediaStreamQuery query)
- {
- var list = _itemRepo.GetMediaStreams(query)
- .ToList();
-
- foreach (var stream in list)
- {
- stream.SupportsExternalStream = StreamSupportsExternalStream(stream);
- }
-
- return list;
- }
-
- private bool StreamSupportsExternalStream(MediaStream stream)
- {
- if (stream.IsExternal)
- {
- return true;
- }
-
- if (stream.IsTextSubtitleStream)
- {
- return true;
- }
-
- return false;
- }
-
- public IEnumerable<MediaStream> GetMediaStreams(string mediaSourceId)
- {
- var list = GetMediaStreams(new MediaStreamQuery
- {
- ItemId = new Guid(mediaSourceId)
- });
-
- return GetMediaStreamsForItem(list);
- }
-
- public IEnumerable<MediaStream> GetMediaStreams(Guid itemId)
- {
- var list = GetMediaStreams(new MediaStreamQuery
- {
- ItemId = itemId
- });
-
- return GetMediaStreamsForItem(list);
- }
-
- private IEnumerable<MediaStream> GetMediaStreamsForItem(IEnumerable<MediaStream> streams)
- {
- var list = streams.ToList();
-
- var subtitleStreams = list
- .Where(i => i.Type == MediaStreamType.Subtitle)
- .ToList();
-
- if (subtitleStreams.Count > 0)
- {
- foreach (var subStream in subtitleStreams)
- {
- subStream.SupportsExternalStream = StreamSupportsExternalStream(subStream);
- }
- }
-
- return list;
- }
-
- public async Task<IEnumerable<MediaSourceInfo>> GetPlayackMediaSources(string id, string userId, bool enablePathSubstitution, string[] supportedLiveMediaTypes, CancellationToken cancellationToken)
- {
- var item = _libraryManager.GetItemById(id);
-
- var hasMediaSources = (IHasMediaSources)item;
- User user = null;
-
- if (!string.IsNullOrWhiteSpace(userId))
- {
- user = _userManager.GetUserById(userId);
- }
-
- var mediaSources = GetStaticMediaSources(hasMediaSources, enablePathSubstitution, user);
- var dynamicMediaSources = await GetDynamicMediaSources(hasMediaSources, cancellationToken).ConfigureAwait(false);
-
- var list = new List<MediaSourceInfo>();
-
- list.AddRange(mediaSources);
-
- foreach (var source in dynamicMediaSources)
- {
- if (user != null)
- {
- SetUserProperties(hasMediaSources, source, user);
- }
- if (source.Protocol == MediaProtocol.File)
- {
- // TODO: Path substitution
- if (!_fileSystem.FileExists(source.Path))
- {
- source.SupportsDirectStream = false;
- }
- }
- else if (source.Protocol == MediaProtocol.Http)
- {
- // TODO: Allow this when the source is plain http, e.g. not HLS or Mpeg Dash
- source.SupportsDirectStream = false;
- }
- else
- {
- source.SupportsDirectStream = false;
- }
-
- list.Add(source);
- }
-
- foreach (var source in list)
- {
- if (user != null)
- {
- if (string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
- {
- if (!user.Policy.EnableAudioPlaybackTranscoding)
- {
- source.SupportsTranscoding = false;
- }
- }
- }
- }
-
- return SortMediaSources(list).Where(i => i.Type != MediaSourceType.Placeholder);
- }
-
- private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
- {
- var tasks = _providers.Select(i => GetDynamicMediaSources(item, i, cancellationToken));
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
-
- return results.SelectMany(i => i.ToList());
- }
-
- private async Task<IEnumerable<MediaSourceInfo>> GetDynamicMediaSources(IHasMediaSources item, IMediaSourceProvider provider, CancellationToken cancellationToken)
- {
- try
- {
- var sources = await provider.GetMediaSources(item, cancellationToken).ConfigureAwait(false);
- var list = sources.ToList();
-
- foreach (var mediaSource in list)
- {
- SetKeyProperties(provider, mediaSource);
- }
-
- return list;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting media sources", ex);
- return new List<MediaSourceInfo>();
- }
- }
-
- private void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource)
- {
- var prefix = provider.GetType().FullName.GetMD5().ToString("N") + LiveStreamIdDelimeter;
-
- if (!string.IsNullOrWhiteSpace(mediaSource.OpenToken) && !mediaSource.OpenToken.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
- {
- mediaSource.OpenToken = prefix + mediaSource.OpenToken;
- }
-
- if (!string.IsNullOrWhiteSpace(mediaSource.LiveStreamId) && !mediaSource.LiveStreamId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
- {
- mediaSource.LiveStreamId = prefix + mediaSource.LiveStreamId;
- }
- }
-
- public async Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken)
- {
- if (!string.IsNullOrWhiteSpace(liveStreamId))
- {
- return await GetLiveStream(liveStreamId, cancellationToken).ConfigureAwait(false);
- }
- //await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- //try
- //{
- // var stream = _openStreams.Values.FirstOrDefault(i => string.Equals(i.MediaSource.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
-
- // if (stream != null)
- // {
- // return stream.MediaSource;
- // }
- //}
- //finally
- //{
- // _liveStreamSemaphore.Release();
- //}
-
- var sources = await GetPlayackMediaSources(item.Id.ToString("N"), null, enablePathSubstitution, new[] { MediaType.Audio, MediaType.Video },
- CancellationToken.None).ConfigureAwait(false);
-
- return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
- }
-
- public IEnumerable<MediaSourceInfo> GetStaticMediaSources(IHasMediaSources item, bool enablePathSubstitution, User user = null)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- if (!(item is Video))
- {
- return item.GetMediaSources(enablePathSubstitution);
- }
-
- var sources = item.GetMediaSources(enablePathSubstitution).ToList();
-
- if (user != null)
- {
- foreach (var source in sources)
- {
- SetUserProperties(item, source, user);
- }
- }
-
- return sources;
- }
-
- private void SetUserProperties(IHasUserData item, MediaSourceInfo source, User user)
- {
- var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user, item);
-
- var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections;
-
- SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection);
- SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection);
- }
-
- private void SetDefaultSubtitleStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
- {
- if (userData.SubtitleStreamIndex.HasValue && user.Configuration.RememberSubtitleSelections && user.Configuration.SubtitleMode != SubtitlePlaybackMode.None && allowRememberingSelection)
- {
- var index = userData.SubtitleStreamIndex.Value;
- // Make sure the saved index is still valid
- if (index == -1 || source.MediaStreams.Any(i => i.Type == MediaStreamType.Subtitle && i.Index == index))
- {
- source.DefaultSubtitleStreamIndex = index;
- return;
- }
- }
-
- var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference)
- ? new List<string>() : new List<string> { user.Configuration.SubtitleLanguagePreference };
-
- var defaultAudioIndex = source.DefaultAudioStreamIndex;
- var audioLangage = defaultAudioIndex == null
- ? null
- : source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
-
- source.DefaultSubtitleStreamIndex = MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams,
- preferredSubs,
- user.Configuration.SubtitleMode,
- audioLangage);
-
- MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs,
- user.Configuration.SubtitleMode, audioLangage);
- }
-
- private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
- {
- if (userData.AudioStreamIndex.HasValue && user.Configuration.RememberAudioSelections && allowRememberingSelection)
- {
- var index = userData.AudioStreamIndex.Value;
- // Make sure the saved index is still valid
- if (source.MediaStreams.Any(i => i.Type == MediaStreamType.Audio && i.Index == index))
- {
- source.DefaultAudioStreamIndex = index;
- return;
- }
- }
-
- var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference)
- ? new string[] { }
- : new[] { user.Configuration.AudioLanguagePreference };
-
- source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack);
- }
-
- private IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources)
- {
- return sources.OrderBy(i =>
- {
- if (i.VideoType.HasValue && i.VideoType.Value == VideoType.VideoFile)
- {
- return 0;
- }
-
- return 1;
-
- }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
- .ThenByDescending(i =>
- {
- var stream = i.VideoStream;
-
- return stream == null || stream.Width == null ? 0 : stream.Width.Value;
- })
- .ToList();
- }
-
- private readonly Dictionary<string, LiveStreamInfo> _openStreams = new Dictionary<string, LiveStreamInfo>(StringComparer.OrdinalIgnoreCase);
- private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
-
- public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken)
- {
- await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var tuple = GetProvider(request.OpenToken);
- var provider = tuple.Item1;
-
- var mediaSourceTuple = await provider.OpenMediaSource(tuple.Item2, cancellationToken).ConfigureAwait(false);
-
- var mediaSource = mediaSourceTuple.Item1;
-
- if (string.IsNullOrWhiteSpace(mediaSource.LiveStreamId))
- {
- throw new InvalidOperationException(string.Format("{0} returned null LiveStreamId", provider.GetType().Name));
- }
-
- SetKeyProperties(provider, mediaSource);
-
- var info = new LiveStreamInfo
- {
- Date = DateTime.UtcNow,
- EnableCloseTimer = enableAutoClose,
- Id = mediaSource.LiveStreamId,
- MediaSource = mediaSource,
- DirectStreamProvider = mediaSourceTuple.Item2
- };
-
- _openStreams[mediaSource.LiveStreamId] = info;
-
- if (enableAutoClose)
- {
- StartCloseTimer();
- }
-
- var json = _jsonSerializer.SerializeToString(mediaSource);
- _logger.Debug("Live stream opened: " + json);
- var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
-
- if (!string.IsNullOrWhiteSpace(request.UserId))
- {
- var user = _userManager.GetUserById(request.UserId);
- var item = string.IsNullOrWhiteSpace(request.ItemId)
- ? null
- : _libraryManager.GetItemById(request.ItemId);
- SetUserProperties(item, clone, user);
- }
-
- return new LiveStreamResponse
- {
- MediaSource = clone
- };
- }
- finally
- {
- _liveStreamSemaphore.Release();
- }
- }
-
- public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStreamWithDirectStreamProvider(string id, CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- throw new ArgumentNullException("id");
- }
-
- _logger.Debug("Getting already opened live stream {0}", id);
-
- await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- LiveStreamInfo info;
- if (_openStreams.TryGetValue(id, out info))
- {
- return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info.DirectStreamProvider);
- }
- else
- {
- throw new ResourceNotFoundException();
- }
- }
- finally
- {
- _liveStreamSemaphore.Release();
- }
- }
-
- public async Task<MediaSourceInfo> GetLiveStream(string id, CancellationToken cancellationToken)
- {
- var result = await GetLiveStreamWithDirectStreamProvider(id, cancellationToken).ConfigureAwait(false);
- return result.Item1;
- }
-
- public async Task PingLiveStream(string id, CancellationToken cancellationToken)
- {
- await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- LiveStreamInfo info;
- if (_openStreams.TryGetValue(id, out info))
- {
- info.Date = DateTime.UtcNow;
- }
- else
- {
- _logger.Error("Failed to ping live stream {0}", id);
- }
- }
- finally
- {
- _liveStreamSemaphore.Release();
- }
- }
-
- private async Task CloseLiveStreamWithProvider(IMediaSourceProvider provider, string streamId)
- {
- _logger.Info("Closing live stream {0} with provider {1}", streamId, provider.GetType().Name);
-
- try
- {
- await provider.CloseMediaSource(streamId).ConfigureAwait(false);
- }
- catch (NotImplementedException)
- {
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing live stream {0}", ex, streamId);
- }
- }
-
- public async Task CloseLiveStream(string id)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- throw new ArgumentNullException("id");
- }
-
- await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false);
-
- try
- {
- LiveStreamInfo current;
-
- if (_openStreams.TryGetValue(id, out current))
- {
- _openStreams.Remove(id);
- current.Closed = true;
-
- if (current.MediaSource.RequiresClosing)
- {
- var tuple = GetProvider(id);
-
- await CloseLiveStreamWithProvider(tuple.Item1, tuple.Item2).ConfigureAwait(false);
- }
-
- if (_openStreams.Count == 0)
- {
- StopCloseTimer();
- }
- }
- }
- finally
- {
- _liveStreamSemaphore.Release();
- }
- }
-
- // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
- private const char LiveStreamIdDelimeter = '_';
-
- private Tuple<IMediaSourceProvider, string> GetProvider(string key)
- {
- if (string.IsNullOrWhiteSpace(key))
- {
- throw new ArgumentException("key");
- }
-
- var keys = key.Split(new[] { LiveStreamIdDelimeter }, 2);
-
- var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), keys[0], StringComparison.OrdinalIgnoreCase));
-
- var splitIndex = key.IndexOf(LiveStreamIdDelimeter);
- var keyId = key.Substring(splitIndex + 1);
-
- return new Tuple<IMediaSourceProvider, string>(provider, keyId);
- }
-
- private Timer _closeTimer;
- private readonly TimeSpan _openStreamMaxAge = TimeSpan.FromSeconds(180);
-
- private void StartCloseTimer()
- {
- StopCloseTimer();
-
- _closeTimer = new Timer(CloseTimerCallback, null, _openStreamMaxAge, _openStreamMaxAge);
- }
-
- private void StopCloseTimer()
- {
- var timer = _closeTimer;
-
- if (timer != null)
- {
- _closeTimer = null;
- timer.Dispose();
- }
- }
-
- private async void CloseTimerCallback(object state)
- {
- List<LiveStreamInfo> infos;
- await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false);
-
- try
- {
- infos = _openStreams
- .Values
- .Where(i => i.EnableCloseTimer && DateTime.UtcNow - i.Date > _openStreamMaxAge)
- .ToList();
- }
- finally
- {
- _liveStreamSemaphore.Release();
- }
-
- foreach (var info in infos)
- {
- if (!info.Closed)
- {
- try
- {
- await CloseLiveStream(info.Id).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing media source", ex);
- }
- }
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- StopCloseTimer();
- Dispose(true);
- }
-
- private readonly object _disposeLock = new object();
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (dispose)
- {
- lock (_disposeLock)
- {
- foreach (var key in _openStreams.Keys.ToList())
- {
- var task = CloseLiveStream(key);
-
- Task.WaitAll(task);
- }
- }
- }
- }
-
- private class LiveStreamInfo
- {
- public DateTime Date;
- public bool EnableCloseTimer;
- public string Id;
- public bool Closed;
- public MediaSourceInfo MediaSource;
- public IDirectStreamProvider DirectStreamProvider;
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs
deleted file mode 100644
index 3ff434898..000000000
--- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs
+++ /dev/null
@@ -1,157 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Playlists;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- public class MusicManager : IMusicManager
- {
- private readonly ILibraryManager _libraryManager;
-
- public MusicManager(ILibraryManager libraryManager)
- {
- _libraryManager = libraryManager;
- }
-
- public IEnumerable<Audio> GetInstantMixFromSong(Audio item, User user)
- {
- var list = new List<Audio>
- {
- item
- };
-
- return list.Concat(GetInstantMixFromGenres(item.Genres, user));
- }
-
- public IEnumerable<Audio> GetInstantMixFromArtist(MusicArtist artist, User user)
- {
- var genres = user.RootFolder
- .GetRecursiveChildren(user, new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(Audio).Name }
- })
- .Cast<Audio>()
- .Where(i => i.HasAnyArtist(artist.Name))
- .SelectMany(i => i.Genres)
- .Concat(artist.Genres)
- .Distinct(StringComparer.OrdinalIgnoreCase);
-
- return GetInstantMixFromGenres(genres, user);
- }
-
- public IEnumerable<Audio> GetInstantMixFromAlbum(MusicAlbum item, User user)
- {
- var genres = item
- .GetRecursiveChildren(user, new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(Audio).Name }
- })
- .Cast<Audio>()
- .SelectMany(i => i.Genres)
- .Concat(item.Genres)
- .DistinctNames();
-
- return GetInstantMixFromGenres(genres, user);
- }
-
- public IEnumerable<Audio> GetInstantMixFromFolder(Folder item, User user)
- {
- var genres = item
- .GetRecursiveChildren(user, new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] {typeof(Audio).Name}
- })
- .Cast<Audio>()
- .SelectMany(i => i.Genres)
- .Concat(item.Genres)
- .DistinctNames();
-
- return GetInstantMixFromGenres(genres, user);
- }
-
- public IEnumerable<Audio> GetInstantMixFromPlaylist(Playlist item, User user)
- {
- var genres = item
- .GetRecursiveChildren(user, new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(Audio).Name }
- })
- .Cast<Audio>()
- .SelectMany(i => i.Genres)
- .Concat(item.Genres)
- .DistinctNames();
-
- return GetInstantMixFromGenres(genres, user);
- }
-
- public IEnumerable<Audio> GetInstantMixFromGenres(IEnumerable<string> genres, User user)
- {
- var genreList = genres.ToList();
-
- var inputItems = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(Audio).Name },
-
- Genres = genreList.ToArray()
-
- });
-
- var genresDictionary = genreList.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
-
- return inputItems
- .Cast<Audio>()
- .Select(i => new Tuple<Audio, int>(i, i.Genres.Count(genresDictionary.ContainsKey)))
- .Where(i => i.Item2 > 0)
- .OrderByDescending(i => i.Item2)
- .ThenBy(i => Guid.NewGuid())
- .Select(i => i.Item1)
- .Take(100)
- .OrderBy(i => Guid.NewGuid());
- }
-
- public IEnumerable<Audio> GetInstantMixFromItem(BaseItem item, User user)
- {
- var genre = item as MusicGenre;
- if (genre != null)
- {
- return GetInstantMixFromGenres(new[] { item.Name }, user);
- }
-
- var playlist = item as Playlist;
- if (playlist != null)
- {
- return GetInstantMixFromPlaylist(playlist, user);
- }
-
- var album = item as MusicAlbum;
- if (album != null)
- {
- return GetInstantMixFromAlbum(album, user);
- }
-
- var artist = item as MusicArtist;
- if (artist != null)
- {
- return GetInstantMixFromArtist(artist, user);
- }
-
- var song = item as Audio;
- if (song != null)
- {
- return GetInstantMixFromSong(song, user);
- }
-
- var folder = item as Folder;
- if (folder != null)
- {
- return GetInstantMixFromFolder(folder, user);
- }
-
- return new Audio[] { };
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/PathExtensions.cs b/MediaBrowser.Server.Implementations/Library/PathExtensions.cs
deleted file mode 100644
index 6c0e3237e..000000000
--- a/MediaBrowser.Server.Implementations/Library/PathExtensions.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using System;
-using System.Text.RegularExpressions;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- public static class PathExtensions
- {
- /// <summary>
- /// Gets the attribute value.
- /// </summary>
- /// <param name="str">The STR.</param>
- /// <param name="attrib">The attrib.</param>
- /// <returns>System.String.</returns>
- /// <exception cref="System.ArgumentNullException">attrib</exception>
- public static string GetAttributeValue(this string str, string attrib)
- {
- if (string.IsNullOrEmpty(str))
- {
- throw new ArgumentNullException("str");
- }
-
- if (string.IsNullOrEmpty(attrib))
- {
- throw new ArgumentNullException("attrib");
- }
-
- string srch = "[" + attrib + "=";
- int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
- if (start > -1)
- {
- start += srch.Length;
- int end = str.IndexOf(']', start);
- return str.Substring(start, end - start);
- }
- // for imdbid we also accept pattern matching
- if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase))
- {
- var m = Regex.Match(str, "tt\\d{7}", RegexOptions.IgnoreCase);
- return m.Success ? m.Value : null;
- }
-
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
deleted file mode 100644
index c7d9d39b8..000000000
--- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
+++ /dev/null
@@ -1,183 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using System;
-using System.IO;
-using System.Linq;
-using System.Text.RegularExpressions;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- /// <summary>
- /// Class ResolverHelper
- /// </summary>
- public static class ResolverHelper
- {
- /// <summary>
- /// Sets the initial item values.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="parent">The parent.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <exception cref="System.ArgumentException">Item must have a path</exception>
- public static void SetInitialItemValues(BaseItem item, Folder parent, IFileSystem fileSystem, ILibraryManager libraryManager, IDirectoryService directoryService)
- {
- // This version of the below method has no ItemResolveArgs, so we have to require the path already being set
- if (string.IsNullOrWhiteSpace(item.Path))
- {
- throw new ArgumentException("Item must have a Path");
- }
-
- // If the resolver didn't specify this
- if (parent != null)
- {
- item.SetParent(parent);
- }
-
- item.Id = libraryManager.GetNewItemId(item.Path, item.GetType());
-
- item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 ||
- item.GetParents().Any(i => i.IsLocked);
-
- // Make sure DateCreated and DateModified have values
- var fileInfo = directoryService.GetFile(item.Path);
- SetDateCreated(item, fileSystem, fileInfo);
-
- EnsureName(item, fileInfo);
- }
-
- /// <summary>
- /// Sets the initial item values.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="libraryManager">The library manager.</param>
- public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args, IFileSystem fileSystem, ILibraryManager libraryManager)
- {
- // If the resolver didn't specify this
- if (string.IsNullOrEmpty(item.Path))
- {
- item.Path = args.Path;
- }
-
- // If the resolver didn't specify this
- if (args.Parent != null)
- {
- item.SetParent(args.Parent);
- }
-
- item.Id = libraryManager.GetNewItemId(item.Path, item.GetType());
-
- // Make sure the item has a name
- EnsureName(item, args.FileInfo);
-
- item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 ||
- item.GetParents().Any(i => i.IsLocked);
-
- // Make sure DateCreated and DateModified have values
- EnsureDates(fileSystem, item, args);
- }
-
- /// <summary>
- /// Ensures the name.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="fileInfo">The file information.</param>
- private static void EnsureName(BaseItem item, FileSystemMetadata fileInfo)
- {
- // If the subclass didn't supply a name, add it here
- if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path))
- {
- item.Name = GetDisplayName(fileInfo.Name, fileInfo.IsDirectory);
- }
- }
-
- /// <summary>
- /// Gets the display name.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="isDirectory">if set to <c>true</c> [is directory].</param>
- /// <returns>System.String.</returns>
- private static string GetDisplayName(string path, bool isDirectory)
- {
- return isDirectory ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path);
- }
-
- /// <summary>
- /// The MB name regex
- /// </summary>
- private static readonly Regex MbNameRegex = new Regex(@"(\[.*?\])", RegexOptions.Compiled);
-
- internal static string StripBrackets(string inputString)
- {
- var output = MbNameRegex.Replace(inputString, string.Empty).Trim();
- return Regex.Replace(output, @"\s+", " ");
- }
-
- /// <summary>
- /// Ensures DateCreated and DateModified have values
- /// </summary>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- private static void EnsureDates(IFileSystem fileSystem, BaseItem item, ItemResolveArgs args)
- {
- if (fileSystem == null)
- {
- throw new ArgumentNullException("fileSystem");
- }
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
- if (args == null)
- {
- throw new ArgumentNullException("args");
- }
-
- // See if a different path came out of the resolver than what went in
- if (!string.Equals(args.Path, item.Path, StringComparison.OrdinalIgnoreCase))
- {
- var childData = args.IsDirectory ? args.GetFileSystemEntryByPath(item.Path) : null;
-
- if (childData != null)
- {
- SetDateCreated(item, fileSystem, childData);
- }
- else
- {
- var fileData = fileSystem.GetFileSystemInfo(item.Path);
-
- if (fileData.Exists)
- {
- SetDateCreated(item, fileSystem, fileData);
- }
- }
- }
- else
- {
- SetDateCreated(item, fileSystem, args.FileInfo);
- }
- }
-
- private static void SetDateCreated(BaseItem item, IFileSystem fileSystem, FileSystemMetadata info)
- {
- var config = BaseItem.ConfigurationManager.GetMetadataConfiguration();
-
- if (config.UseFileCreationTimeForDateAdded)
- {
- item.DateCreated = fileSystem.GetCreationTimeUtc(info);
- }
- else
- {
- item.DateCreated = DateTime.UtcNow;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
deleted file mode 100644
index 039a17100..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Entities;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
-{
- /// <summary>
- /// Class AudioResolver
- /// </summary>
- public class AudioResolver : ItemResolver<Controller.Entities.Audio.Audio>
- {
- private readonly ILibraryManager _libraryManager;
-
- public AudioResolver(ILibraryManager libraryManager)
- {
- _libraryManager = libraryManager;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override ResolverPriority Priority
- {
- get { return ResolverPriority.Last; }
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>Entities.Audio.Audio.</returns>
- protected override Controller.Entities.Audio.Audio Resolve(ItemResolveArgs args)
- {
- // Return audio if the path is a file and has a matching extension
-
- if (!args.IsDirectory)
- {
- var libraryOptions = args.GetLibraryOptions();
-
- if (_libraryManager.IsAudioFile(args.Path, libraryOptions))
- {
- var collectionType = args.GetCollectionType();
-
- var isMixed = string.IsNullOrWhiteSpace(collectionType);
-
- // For conflicting extensions, give priority to videos
- if (isMixed && _libraryManager.IsVideoFile(args.Path, libraryOptions))
- {
- return null;
- }
-
- var isStandalone = args.Parent == null;
-
- if (isStandalone ||
- string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) ||
- isMixed)
- {
- return new Controller.Entities.Audio.Audio();
- }
- }
- }
-
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
deleted file mode 100644
index c1ac7d68c..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ /dev/null
@@ -1,173 +0,0 @@
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Naming.Audio;
-using MediaBrowser.Server.Implementations.Logging;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
-{
- /// <summary>
- /// Class MusicAlbumResolver
- /// </summary>
- public class MusicAlbumResolver : ItemResolver<MusicAlbum>
- {
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly ILibraryManager _libraryManager;
-
- public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager)
- {
- _logger = logger;
- _fileSystem = fileSystem;
- _libraryManager = libraryManager;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override ResolverPriority Priority
- {
- get
- {
- // Behind special folder resolver
- return ResolverPriority.Second;
- }
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>MusicAlbum.</returns>
- protected override MusicAlbum Resolve(ItemResolveArgs args)
- {
- if (!args.IsDirectory) return null;
-
- // Avoid mis-identifying top folders
- if (args.HasParent<MusicAlbum>()) return null;
- if (args.Parent.IsRoot) return null;
-
- var collectionType = args.GetCollectionType();
-
- var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase);
-
- // If there's a collection type and it's not music, don't allow it.
- if (!isMusicMediaFolder)
- {
- return null;
- }
-
- return IsMusicAlbum(args) ? new MusicAlbum() : null;
- }
-
-
- /// <summary>
- /// Determine if the supplied file data points to a music album
- /// </summary>
- public bool IsMusicAlbum(string path, IDirectoryService directoryService, LibraryOptions libraryOptions)
- {
- return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, libraryOptions, _libraryManager);
- }
-
- /// <summary>
- /// Determine if the supplied resolve args should be considered a music album
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns>
- private bool IsMusicAlbum(ItemResolveArgs args)
- {
- // Args points to an album if parent is an Artist folder or it directly contains music
- if (args.IsDirectory)
- {
- //if (args.Parent is MusicArtist) return true; //saves us from testing children twice
- if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, args.GetLibraryOptions(), _libraryManager)) return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// Determine if the supplied list contains what we should consider music
- /// </summary>
- private bool ContainsMusic(IEnumerable<FileSystemMetadata> list,
- bool allowSubfolders,
- IDirectoryService directoryService,
- ILogger logger,
- IFileSystem fileSystem,
- LibraryOptions libraryOptions,
- ILibraryManager libraryManager)
- {
- var discSubfolderCount = 0;
- var notMultiDisc = false;
-
- foreach (var fileSystemInfo in list)
- {
- if (fileSystemInfo.IsDirectory)
- {
- if (allowSubfolders)
- {
- var path = fileSystemInfo.FullName;
- var isMultiDisc = IsMultiDiscFolder(path, libraryOptions);
-
- if (isMultiDisc)
- {
- var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager);
-
- if (hasMusic)
- {
- logger.Debug("Found multi-disc folder: " + path);
- discSubfolderCount++;
- }
- }
- else
- {
- var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager);
-
- if (hasMusic)
- {
- // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album
- notMultiDisc = true;
- }
- }
- }
- }
-
- var fullName = fileSystemInfo.FullName;
-
- if (libraryManager.IsAudioFile(fullName, libraryOptions))
- {
- return true;
- }
- }
-
- if (notMultiDisc)
- {
- return false;
- }
-
- return discSubfolderCount > 0;
- }
-
- private bool IsMultiDiscFolder(string path, LibraryOptions libraryOptions)
- {
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(libraryOptions);
-
- var parser = new AlbumParser(namingOptions, new PatternsLogger());
- var result = parser.ParseMultiPart(path);
-
- return result.IsMultiPart;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
deleted file mode 100644
index be651b9c8..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
-{
- /// <summary>
- /// Class MusicArtistResolver
- /// </summary>
- public class MusicArtistResolver : ItemResolver<MusicArtist>
- {
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
-
- public MusicArtistResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager, IServerConfigurationManager config)
- {
- _logger = logger;
- _fileSystem = fileSystem;
- _libraryManager = libraryManager;
- _config = config;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override ResolverPriority Priority
- {
- get
- {
- // Behind special folder resolver
- return ResolverPriority.Second;
- }
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>MusicArtist.</returns>
- protected override MusicArtist Resolve(ItemResolveArgs args)
- {
- if (!args.IsDirectory) return null;
-
- // Don't allow nested artists
- if (args.HasParent<MusicArtist>() || args.HasParent<MusicAlbum>())
- {
- return null;
- }
-
- var collectionType = args.GetCollectionType();
-
- var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase);
-
- // If there's a collection type and it's not music, it can't be a series
- if (!isMusicMediaFolder)
- {
- return null;
- }
-
- if (args.ContainsFileSystemEntryByName("artist.nfo"))
- {
- return new MusicArtist();
- }
-
- if (_config.Configuration.EnableSimpleArtistDetection)
- {
- return null;
- }
-
- // Avoid mis-identifying top folders
- if (args.Parent.IsRoot) return null;
-
- var directoryService = args.DirectoryService;
-
- var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
-
- // If we contain an album assume we are an artist folder
- return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null;
- }
-
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
deleted file mode 100644
index 4dce16139..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ /dev/null
@@ -1,297 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Naming.Video;
-using MediaBrowser.Server.Implementations.Logging;
-using System;
-using System.IO;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
-{
- /// <summary>
- /// Resolves a Path into a Video or Video subclass
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public abstract class BaseVideoResolver<T> : Controller.Resolvers.ItemResolver<T>
- where T : Video, new()
- {
- protected readonly ILibraryManager LibraryManager;
-
- protected BaseVideoResolver(ILibraryManager libraryManager)
- {
- LibraryManager = libraryManager;
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>`0.</returns>
- protected override T Resolve(ItemResolveArgs args)
- {
- return ResolveVideo<T>(args, false);
- }
-
- /// <summary>
- /// Resolves the video.
- /// </summary>
- /// <typeparam name="TVideoType">The type of the T video type.</typeparam>
- /// <param name="args">The args.</param>
- /// <param name="parseName">if set to <c>true</c> [parse name].</param>
- /// <returns>``0.</returns>
- protected TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName)
- where TVideoType : Video, new()
- {
- var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
-
- // If the path is a file check for a matching extensions
- var parser = new Naming.Video.VideoResolver(namingOptions, new PatternsLogger());
-
- if (args.IsDirectory)
- {
- TVideoType video = null;
- VideoFileInfo videoInfo = null;
-
- // Loop through each child file/folder and see if we find a video
- foreach (var child in args.FileSystemChildren)
- {
- var filename = child.Name;
-
- if (child.IsDirectory)
- {
- if (IsDvdDirectory(filename))
- {
- videoInfo = parser.ResolveDirectory(args.Path);
-
- if (videoInfo == null)
- {
- return null;
- }
-
- video = new TVideoType
- {
- Path = args.Path,
- VideoType = VideoType.Dvd,
- ProductionYear = videoInfo.Year
- };
- break;
- }
- if (IsBluRayDirectory(filename))
- {
- videoInfo = parser.ResolveDirectory(args.Path);
-
- if (videoInfo == null)
- {
- return null;
- }
-
- video = new TVideoType
- {
- Path = args.Path,
- VideoType = VideoType.BluRay,
- ProductionYear = videoInfo.Year
- };
- break;
- }
- }
- else if (IsDvdFile(filename))
- {
- videoInfo = parser.ResolveDirectory(args.Path);
-
- if (videoInfo == null)
- {
- return null;
- }
-
- video = new TVideoType
- {
- Path = args.Path,
- VideoType = VideoType.Dvd,
- ProductionYear = videoInfo.Year
- };
- break;
- }
- }
-
- if (video != null)
- {
- video.Name = parseName ?
- videoInfo.Name :
- Path.GetFileName(args.Path);
-
- Set3DFormat(video, videoInfo);
- }
-
- return video;
- }
- else
- {
- var videoInfo = parser.Resolve(args.Path, false, false);
-
- if (videoInfo == null)
- {
- return null;
- }
-
- if (LibraryManager.IsVideoFile(args.Path, args.GetLibraryOptions()) || videoInfo.IsStub)
- {
- var path = args.Path;
-
- var video = new TVideoType
- {
- Path = path,
- IsInMixedFolder = true,
- ProductionYear = videoInfo.Year
- };
-
- SetVideoType(video, videoInfo);
-
- video.Name = parseName ?
- videoInfo.Name :
- Path.GetFileNameWithoutExtension(args.Path);
-
- Set3DFormat(video, videoInfo);
-
- return video;
- }
- }
-
- return null;
- }
-
- protected void SetVideoType(Video video, VideoFileInfo videoInfo)
- {
- var extension = Path.GetExtension(video.Path);
- video.VideoType = string.Equals(extension, ".iso", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(extension, ".img", StringComparison.OrdinalIgnoreCase) ?
- VideoType.Iso :
- VideoType.VideoFile;
-
- video.IsShortcut = string.Equals(extension, ".strm", StringComparison.OrdinalIgnoreCase);
- video.IsPlaceHolder = videoInfo.IsStub;
-
- if (videoInfo.IsStub)
- {
- if (string.Equals(videoInfo.StubType, "dvd", StringComparison.OrdinalIgnoreCase))
- {
- video.VideoType = VideoType.Dvd;
- }
- else if (string.Equals(videoInfo.StubType, "hddvd", StringComparison.OrdinalIgnoreCase))
- {
- video.VideoType = VideoType.HdDvd;
- video.IsHD = true;
- }
- else if (string.Equals(videoInfo.StubType, "bluray", StringComparison.OrdinalIgnoreCase))
- {
- video.VideoType = VideoType.BluRay;
- video.IsHD = true;
- }
- else if (string.Equals(videoInfo.StubType, "hdtv", StringComparison.OrdinalIgnoreCase))
- {
- video.IsHD = true;
- }
- }
-
- SetIsoType(video);
- }
-
- protected void SetIsoType(Video video)
- {
- if (video.VideoType == VideoType.Iso)
- {
- if (video.Path.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1)
- {
- video.IsoType = IsoType.Dvd;
- }
- else if (video.Path.IndexOf("bluray", StringComparison.OrdinalIgnoreCase) != -1)
- {
- video.IsoType = IsoType.BluRay;
- }
- }
- }
-
- protected void Set3DFormat(Video video, bool is3D, string format3D)
- {
- if (is3D)
- {
- if (string.Equals(format3D, "fsbs", StringComparison.OrdinalIgnoreCase))
- {
- video.Video3DFormat = Video3DFormat.FullSideBySide;
- }
- else if (string.Equals(format3D, "ftab", StringComparison.OrdinalIgnoreCase))
- {
- video.Video3DFormat = Video3DFormat.FullTopAndBottom;
- }
- else if (string.Equals(format3D, "hsbs", StringComparison.OrdinalIgnoreCase))
- {
- video.Video3DFormat = Video3DFormat.HalfSideBySide;
- }
- else if (string.Equals(format3D, "htab", StringComparison.OrdinalIgnoreCase))
- {
- video.Video3DFormat = Video3DFormat.HalfTopAndBottom;
- }
- else if (string.Equals(format3D, "sbs", StringComparison.OrdinalIgnoreCase))
- {
- video.Video3DFormat = Video3DFormat.HalfSideBySide;
- }
- else if (string.Equals(format3D, "sbs3d", StringComparison.OrdinalIgnoreCase))
- {
- video.Video3DFormat = Video3DFormat.HalfSideBySide;
- }
- else if (string.Equals(format3D, "tab", StringComparison.OrdinalIgnoreCase))
- {
- video.Video3DFormat = Video3DFormat.HalfTopAndBottom;
- }
- else if (string.Equals(format3D, "mvc", StringComparison.OrdinalIgnoreCase))
- {
- video.Video3DFormat = Video3DFormat.MVC;
- }
- }
- }
-
- protected void Set3DFormat(Video video, VideoFileInfo videoInfo)
- {
- Set3DFormat(video, videoInfo.Is3D, videoInfo.Format3D);
- }
-
- protected void Set3DFormat(Video video)
- {
- var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
-
- var resolver = new Format3DParser(namingOptions, new PatternsLogger());
- var result = resolver.Parse(video.Path);
-
- Set3DFormat(video, result.Is3D, result.Format3D);
- }
-
- /// <summary>
- /// Determines whether [is DVD directory] [the specified directory name].
- /// </summary>
- /// <param name="directoryName">Name of the directory.</param>
- /// <returns><c>true</c> if [is DVD directory] [the specified directory name]; otherwise, <c>false</c>.</returns>
- protected bool IsDvdDirectory(string directoryName)
- {
- return string.Equals(directoryName, "video_ts", StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Determines whether [is DVD file] [the specified name].
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns><c>true</c> if [is DVD file] [the specified name]; otherwise, <c>false</c>.</returns>
- protected bool IsDvdFile(string name)
- {
- return string.Equals(name, "video_ts.ifo", StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Determines whether [is blu ray directory] [the specified directory name].
- /// </summary>
- /// <param name="directoryName">Name of the directory.</param>
- /// <returns><c>true</c> if [is blu ray directory] [the specified directory name]; otherwise, <c>false</c>.</returns>
- protected bool IsBluRayDirectory(string directoryName)
- {
- return string.Equals(directoryName, "bdmv", StringComparison.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
deleted file mode 100644
index ff07c5282..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
-{
- /// <summary>
- /// Class FolderResolver
- /// </summary>
- public class FolderResolver : FolderResolver<Folder>
- {
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override ResolverPriority Priority
- {
- get { return ResolverPriority.Last; }
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>Folder.</returns>
- protected override Folder Resolve(ItemResolveArgs args)
- {
- if (args.IsDirectory)
- {
- return new Folder();
- }
-
- return null;
- }
- }
-
- /// <summary>
- /// Class FolderResolver
- /// </summary>
- /// <typeparam name="TItemType">The type of the T item type.</typeparam>
- public abstract class FolderResolver<TItemType> : ItemResolver<TItemType>
- where TItemType : Folder, new()
- {
- /// <summary>
- /// Sets the initial item values.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- protected override void SetInitialItemValues(TItemType item, ItemResolveArgs args)
- {
- base.SetInitialItemValues(item, args);
-
- item.IsRoot = args.Parent == null;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs
deleted file mode 100644
index a03eda263..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/ItemResolver.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
-{
- /// <summary>
- /// Class ItemResolver
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public abstract class ItemResolver<T> : IItemResolver
- where T : BaseItem, new()
- {
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>`0.</returns>
- protected virtual T Resolve(ItemResolveArgs args)
- {
- return null;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public virtual ResolverPriority Priority
- {
- get
- {
- return ResolverPriority.First;
- }
- }
-
- /// <summary>
- /// Sets initial values on the newly resolved item
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- protected virtual void SetInitialItemValues(T item, ItemResolveArgs args)
- {
- }
-
- /// <summary>
- /// Resolves the path.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>BaseItem.</returns>
- BaseItem IItemResolver.ResolvePath(ItemResolveArgs args)
- {
- var item = Resolve(args);
-
- if (item != null)
- {
- SetInitialItemValues(item, args);
- }
-
- return item;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
deleted file mode 100644
index e3447afc9..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System;
-using System.IO;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
-{
- /// <summary>
- /// Class BoxSetResolver
- /// </summary>
- public class BoxSetResolver : FolderResolver<BoxSet>
- {
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>BoxSet.</returns>
- protected override BoxSet Resolve(ItemResolveArgs args)
- {
- // It's a boxset if all of the following conditions are met:
- // Is a Directory
- // Contains [boxset] in the path
- if (args.IsDirectory)
- {
- var filename = Path.GetFileName(args.Path);
-
- if (string.IsNullOrEmpty(filename))
- {
- return null;
- }
-
- if (filename.IndexOf("[boxset]", StringComparison.OrdinalIgnoreCase) != -1 ||
- args.ContainsFileSystemEntryByName("collection.xml"))
- {
- return new BoxSet
- {
- Path = args.Path,
- Name = ResolverHelper.StripBrackets(Path.GetFileName(args.Path))
- };
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Sets the initial item values.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- protected override void SetInitialItemValues(BoxSet item, ItemResolveArgs args)
- {
- base.SetInitialItemValues(item, args);
-
- SetProviderIdFromPath(item);
- }
-
- /// <summary>
- /// Sets the provider id from path.
- /// </summary>
- /// <param name="item">The item.</param>
- private void SetProviderIdFromPath(BaseItem item)
- {
- //we need to only look at the name of this actual item (not parents)
- var justName = Path.GetFileName(item.Path);
-
- var id = justName.GetAttributeValue("tmdbid");
-
- if (!string.IsNullOrEmpty(id))
- {
- item.SetProviderId(MetadataProviders.Tmdb, id);
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
deleted file mode 100644
index bb1d57688..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ /dev/null
@@ -1,541 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Naming.Video;
-using MediaBrowser.Server.Implementations.Logging;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
-{
- /// <summary>
- /// Class MovieResolver
- /// </summary>
- public class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
- {
- public MovieResolver(ILibraryManager libraryManager)
- : base(libraryManager)
- {
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override ResolverPriority Priority
- {
- get
- {
- // Give plugins a chance to catch iso's first
- // Also since we have to loop through child files looking for videos,
- // see if we can avoid some of that by letting other resolvers claim folders first
- // Also run after series resolver
- return ResolverPriority.Third;
- }
- }
-
- public MultiItemResolverResult ResolveMultiple(Folder parent,
- List<FileSystemMetadata> files,
- string collectionType,
- IDirectoryService directoryService)
- {
- var result = ResolveMultipleInternal(parent, files, collectionType, directoryService);
-
- if (result != null)
- {
- foreach (var item in result.Items)
- {
- SetInitialItemValues((Video)item, null);
- }
- }
-
- return result;
- }
-
- private MultiItemResolverResult ResolveMultipleInternal(Folder parent,
- List<FileSystemMetadata> files,
- string collectionType,
- IDirectoryService directoryService)
- {
- if (IsInvalid(parent, collectionType))
- {
- return null;
- }
-
- if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
- {
- return ResolveVideos<MusicVideo>(parent, files, directoryService, false);
- }
-
- if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
- {
- return ResolveVideos<Video>(parent, files, directoryService, false);
- }
-
- if (string.IsNullOrEmpty(collectionType))
- {
- // Owned items should just use the plain video type
- if (parent == null)
- {
- return ResolveVideos<Video>(parent, files, directoryService, false);
- }
-
- if (parent is Series || parent.GetParents().OfType<Series>().Any())
- {
- return null;
- }
-
- return ResolveVideos<Movie>(parent, files, directoryService, false);
- }
-
- if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
- {
- return ResolveVideos<Movie>(parent, files, directoryService, true);
- }
-
- return null;
- }
-
- private MultiItemResolverResult ResolveVideos<T>(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions)
- where T : Video, new()
- {
- var files = new List<FileSystemMetadata>();
- var videos = new List<BaseItem>();
- var leftOver = new List<FileSystemMetadata>();
-
- // Loop through each child file/folder and see if we find a video
- foreach (var child in fileSystemEntries)
- {
- if (child.IsDirectory)
- {
- leftOver.Add(child);
- }
- else if (IsIgnored(child.Name))
- {
-
- }
- else
- {
- files.Add(child);
- }
- }
-
- var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
-
- var resolver = new VideoListResolver(namingOptions, new PatternsLogger());
- var resolverResult = resolver.Resolve(files, suppportMultiEditions).ToList();
-
- var result = new MultiItemResolverResult
- {
- ExtraFiles = leftOver,
- Items = videos
- };
-
- var isInMixedFolder = resolverResult.Count > 1;
-
- foreach (var video in resolverResult)
- {
- var firstVideo = video.Files.First();
-
- var videoItem = new T
- {
- Path = video.Files[0].Path,
- IsInMixedFolder = isInMixedFolder,
- ProductionYear = video.Year,
- Name = video.Name,
- AdditionalParts = video.Files.Skip(1).Select(i => i.Path).ToList(),
- LocalAlternateVersions = video.AlternateVersions.Select(i => i.Path).ToList()
- };
-
- SetVideoType(videoItem, firstVideo);
- Set3DFormat(videoItem, firstVideo);
-
- result.Items.Add(videoItem);
- }
-
- result.ExtraFiles.AddRange(files.Where(i => !ContainsFile(resolverResult, i)));
-
- return result;
- }
-
- private bool ContainsFile(List<VideoInfo> result, FileSystemMetadata file)
- {
- return result.Any(i => ContainsFile(i, file));
- }
-
- private bool ContainsFile(VideoInfo result, FileSystemMetadata file)
- {
- return result.Files.Any(i => ContainsFile(i, file)) ||
- result.AlternateVersions.Any(i => ContainsFile(i, file)) ||
- result.Extras.Any(i => ContainsFile(i, file));
- }
-
- private bool ContainsFile(VideoFileInfo result, FileSystemMetadata file)
- {
- return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>Video.</returns>
- protected override Video Resolve(ItemResolveArgs args)
- {
- var collectionType = args.GetCollectionType();
-
- if (IsInvalid(args.Parent, collectionType))
- {
- return null;
- }
-
- // Find movies with their own folders
- if (args.IsDirectory)
- {
- var files = args.FileSystemChildren
- .Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
- .ToList();
-
- if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
- {
- return FindMovie<MusicVideo>(args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
- }
-
- if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
- {
- return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
- }
-
- if (string.IsNullOrEmpty(collectionType))
- {
- // Owned items will be caught by the plain video resolver
- if (args.Parent == null)
- {
- //return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
- return null;
- }
-
- if (args.HasParent<Series>())
- {
- return null;
- }
-
- {
- return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
- }
- }
-
- if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
- {
- return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
- }
-
- return null;
- }
-
- // Owned items will be caught by the plain video resolver
- if (args.Parent == null)
- {
- return null;
- }
-
- Video item = null;
-
- if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
- {
- item = ResolveVideo<MusicVideo>(args, false);
- }
-
- // To find a movie file, the collection type must be movies or boxsets
- else if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
- {
- item = ResolveVideo<Movie>(args, true);
- }
-
- else if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
- {
- item = ResolveVideo<Video>(args, false);
- }
- else if (string.IsNullOrEmpty(collectionType))
- {
- if (args.HasParent<Series>())
- {
- return null;
- }
-
- item = ResolveVideo<Video>(args, false);
- }
-
- if (item != null)
- {
- item.IsInMixedFolder = true;
- }
-
- return item;
- }
-
- private bool IsIgnored(string filename)
- {
- // Ignore samples
- var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase)
- .Replace("-", " ", StringComparison.OrdinalIgnoreCase)
- .Replace("_", " ", StringComparison.OrdinalIgnoreCase)
- .Replace("!", " ", StringComparison.OrdinalIgnoreCase);
-
- if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// Sets the initial item values.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- protected override void SetInitialItemValues(Video item, ItemResolveArgs args)
- {
- base.SetInitialItemValues(item, args);
-
- SetProviderIdsFromPath(item);
- }
-
- /// <summary>
- /// Sets the provider id from path.
- /// </summary>
- /// <param name="item">The item.</param>
- private void SetProviderIdsFromPath(Video item)
- {
- if (item is Movie || item is MusicVideo)
- {
- //we need to only look at the name of this actual item (not parents)
- var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(item.ContainingFolderPath);
-
- if (!string.IsNullOrWhiteSpace(justName))
- {
- // check for tmdb id
- var tmdbid = justName.GetAttributeValue("tmdbid");
-
- if (!string.IsNullOrWhiteSpace(tmdbid))
- {
- item.SetProviderId(MetadataProviders.Tmdb, tmdbid);
- }
- }
-
- if (!string.IsNullOrWhiteSpace(item.Path))
- {
- // check for imdb id - we use full media path, as we can assume, that this will match in any use case (wither id in parent dir or in file name)
- var imdbid = item.Path.GetAttributeValue("imdbid");
-
- if (!string.IsNullOrWhiteSpace(imdbid))
- {
- item.SetProviderId(MetadataProviders.Imdb, imdbid);
- }
- }
- }
- }
-
- /// <summary>
- /// Finds a movie based on a child file system entries
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>Movie.</returns>
- private T FindMovie<T>(string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool allowFilesAsFolders)
- where T : Video, new()
- {
- var multiDiscFolders = new List<FileSystemMetadata>();
-
- // Search for a folder rip
- foreach (var child in fileSystemEntries)
- {
- var filename = child.Name;
-
- if (child.IsDirectory)
- {
- if (IsDvdDirectory(filename))
- {
- var movie = new T
- {
- Path = path,
- VideoType = VideoType.Dvd
- };
- Set3DFormat(movie);
- return movie;
- }
- if (IsBluRayDirectory(filename))
- {
- var movie = new T
- {
- Path = path,
- VideoType = VideoType.BluRay
- };
- Set3DFormat(movie);
- return movie;
- }
-
- multiDiscFolders.Add(child);
- }
- else if (IsDvdFile(filename))
- {
- var movie = new T
- {
- Path = path,
- VideoType = VideoType.Dvd
- };
- Set3DFormat(movie);
- return movie;
- }
- }
-
- if (allowFilesAsFolders)
- {
- // TODO: Allow GetMultiDiscMovie in here
- var supportsMultiVersion = !string.Equals(collectionType, CollectionType.HomeVideos) &&
- !string.Equals(collectionType, CollectionType.Photos) &&
- !string.Equals(collectionType, CollectionType.MusicVideos);
-
- var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion);
-
- if (result.Items.Count == 1)
- {
- var movie = (T)result.Items[0];
- movie.IsInMixedFolder = false;
- movie.Name = Path.GetFileName(movie.ContainingFolderPath);
- return movie;
- }
-
- if (result.Items.Count == 0 && multiDiscFolders.Count > 0)
- {
- return GetMultiDiscMovie<T>(multiDiscFolders, directoryService);
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Gets the multi disc movie.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="multiDiscFolders">The folders.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <returns>``0.</returns>
- private T GetMultiDiscMovie<T>(List<FileSystemMetadata> multiDiscFolders, IDirectoryService directoryService)
- where T : Video, new()
- {
- var videoTypes = new List<VideoType>();
-
- var folderPaths = multiDiscFolders.Select(i => i.FullName).Where(i =>
- {
- var subFileEntries = directoryService.GetFileSystemEntries(i)
- .ToList();
-
- var subfolders = subFileEntries
- .Where(e => e.IsDirectory)
- .Select(d => d.Name)
- .ToList();
-
- if (subfolders.Any(IsDvdDirectory))
- {
- videoTypes.Add(VideoType.Dvd);
- return true;
- }
- if (subfolders.Any(IsBluRayDirectory))
- {
- videoTypes.Add(VideoType.BluRay);
- return true;
- }
-
- var subFiles = subFileEntries
- .Where(e => !e.IsDirectory)
- .Select(d => d.Name);
-
- if (subFiles.Any(IsDvdFile))
- {
- videoTypes.Add(VideoType.Dvd);
- return true;
- }
-
- return false;
-
- }).OrderBy(i => i).ToList();
-
- // If different video types were found, don't allow this
- if (videoTypes.Distinct().Count() > 1)
- {
- return null;
- }
-
- if (folderPaths.Count == 0)
- {
- return null;
- }
-
- var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
- var resolver = new StackResolver(namingOptions, new PatternsLogger());
-
- var result = resolver.ResolveDirectories(folderPaths);
-
- if (result.Stacks.Count != 1)
- {
- return null;
- }
-
- var returnVideo = new T
- {
- Path = folderPaths[0],
-
- AdditionalParts = folderPaths.Skip(1).ToList(),
-
- VideoType = videoTypes[0],
-
- Name = result.Stacks[0].Name
- };
-
- SetIsoType(returnVideo);
-
- return returnVideo;
- }
-
- private bool IsInvalid(Folder parent, string collectionType)
- {
- if (parent != null)
- {
- if (parent.IsRoot)
- {
- return true;
- }
- }
-
- var validCollectionTypes = new[]
- {
- CollectionType.Movies,
- CollectionType.HomeVideos,
- CollectionType.MusicVideos,
- CollectionType.Movies,
- CollectionType.Photos
- };
-
- if (string.IsNullOrWhiteSpace(collectionType))
- {
- return false;
- }
-
- return !validCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
deleted file mode 100644
index 957fafb92..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoAlbumResolver.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Entities;
-using System;
-using System.IO;
-using System.Linq;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
-{
- public class PhotoAlbumResolver : FolderResolver<PhotoAlbum>
- {
- private readonly IImageProcessor _imageProcessor;
- public PhotoAlbumResolver(IImageProcessor imageProcessor)
- {
- _imageProcessor = imageProcessor;
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>Trailer.</returns>
- protected override PhotoAlbum Resolve(ItemResolveArgs args)
- {
- // Must be an image file within a photo collection
- if (args.IsDirectory && string.Equals(args.GetCollectionType(), CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
- {
- if (HasPhotos(args))
- {
- return new PhotoAlbum
- {
- Path = args.Path
- };
- }
- }
-
- return null;
- }
-
- private bool HasPhotos(ItemResolveArgs args)
- {
- return args.FileSystemChildren.Any(i => (!i.IsDirectory) && PhotoResolver.IsImageFile(i.FullName, _imageProcessor));
- }
-
- public override ResolverPriority Priority
- {
- get
- {
- // Behind special folder resolver
- return ResolverPriority.Second;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
deleted file mode 100644
index 549ad522e..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
-{
- public class PhotoResolver : ItemResolver<Photo>
- {
- private readonly IImageProcessor _imageProcessor;
- private readonly ILibraryManager _libraryManager;
-
- public PhotoResolver(IImageProcessor imageProcessor, ILibraryManager libraryManager)
- {
- _imageProcessor = imageProcessor;
- _libraryManager = libraryManager;
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>Trailer.</returns>
- protected override Photo Resolve(ItemResolveArgs args)
- {
- if (!args.IsDirectory)
- {
- // Must be an image file within a photo collection
- var collectionType = args.GetCollectionType();
-
-
- if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase) ||
- (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos))
- {
- if (IsImageFile(args.Path, _imageProcessor))
- {
- var filename = Path.GetFileNameWithoutExtension(args.Path);
-
- // Make sure the image doesn't belong to a video file
- if (args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path)).Any(i => IsOwnedByMedia(args.GetLibraryOptions(), i, filename)))
- {
- return null;
- }
-
- return new Photo
- {
- Path = args.Path
- };
- }
- }
- }
-
- return null;
- }
-
- private bool IsOwnedByMedia(LibraryOptions libraryOptions, FileSystemMetadata file, string imageFilename)
- {
- if (_libraryManager.IsVideoFile(file.FullName, libraryOptions) && imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file.Name), StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
-
- return false;
- }
-
- private static readonly string[] IgnoreFiles =
- {
- "folder",
- "thumb",
- "landscape",
- "fanart",
- "backdrop",
- "poster",
- "cover"
- };
-
- internal static bool IsImageFile(string path, IImageProcessor imageProcessor)
- {
- var filename = Path.GetFileNameWithoutExtension(path) ?? string.Empty;
-
- if (IgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (IgnoreFiles.Any(i => filename.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1))
- {
- return false;
- }
-
- return imageProcessor.SupportedInputFormats.Contains((Path.GetExtension(path) ?? string.Empty).TrimStart('.'), StringComparer.OrdinalIgnoreCase);
- }
-
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
deleted file mode 100644
index a95739f22..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Playlists;
-using System;
-using System.IO;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
-{
- public class PlaylistResolver : FolderResolver<Playlist>
- {
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>BoxSet.</returns>
- protected override Playlist Resolve(ItemResolveArgs args)
- {
- // It's a boxset if all of the following conditions are met:
- // Is a Directory
- // Contains [playlist] in the path
- if (args.IsDirectory)
- {
- var filename = Path.GetFileName(args.Path);
-
- if (string.IsNullOrEmpty(filename))
- {
- return null;
- }
-
- if (filename.IndexOf("[playlist]", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return new Playlist
- {
- Path = args.Path,
- Name = ResolverHelper.StripBrackets(Path.GetFileName(args.Path))
- };
- }
- }
-
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
deleted file mode 100644
index 7a6198a00..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-using System;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
-{
- class SpecialFolderResolver : FolderResolver<Folder>
- {
- private readonly IFileSystem _fileSystem;
- private readonly IServerApplicationPaths _appPaths;
-
- public SpecialFolderResolver(IFileSystem fileSystem, IServerApplicationPaths appPaths)
- {
- _fileSystem = fileSystem;
- _appPaths = appPaths;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override ResolverPriority Priority
- {
- get { return ResolverPriority.First; }
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>Folder.</returns>
- protected override Folder Resolve(ItemResolveArgs args)
- {
- if (args.IsDirectory)
- {
- if (args.IsPhysicalRoot)
- {
- return new AggregateFolder();
- }
- if (string.Equals(args.Path, _appPaths.DefaultUserViewsPath, StringComparison.OrdinalIgnoreCase))
- {
- return new UserRootFolder(); //if we got here and still a root - must be user root
- }
- if (args.IsVf)
- {
- return new CollectionFolder
- {
- CollectionType = GetCollectionType(args),
- PhysicalLocationsList = args.PhysicalLocations.ToList()
- };
- }
- }
-
- return null;
- }
-
- private string GetCollectionType(ItemResolveArgs args)
- {
- return args.FileSystemChildren
- .Where(i =>
- {
-
- try
- {
- return !i.IsDirectory &&
- string.Equals(".collection", i.Extension, StringComparison.OrdinalIgnoreCase);
- }
- catch (IOException)
- {
- return false;
- }
-
- })
- .Select(i => _fileSystem.GetFileNameWithoutExtension(i))
- .FirstOrDefault();
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
deleted file mode 100644
index 6edc4a009..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using System.Linq;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
-{
- /// <summary>
- /// Class EpisodeResolver
- /// </summary>
- public class EpisodeResolver : BaseVideoResolver<Episode>
- {
- public EpisodeResolver(ILibraryManager libraryManager) : base(libraryManager)
- {
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>Episode.</returns>
- protected override Episode Resolve(ItemResolveArgs args)
- {
- var parent = args.Parent;
-
- if (parent == null)
- {
- return null;
- }
-
- var season = parent as Season;
- // Just in case the user decided to nest episodes.
- // Not officially supported but in some cases we can handle it.
- if (season == null)
- {
- season = parent.GetParents().OfType<Season>().FirstOrDefault();
- }
-
- // If the parent is a Season or Series, then this is an Episode if the VideoResolver returns something
- // Also handle flat tv folders
- if (season != null ||
- string.Equals(args.GetCollectionType(), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) ||
- args.HasParent<Series>())
- {
- var episode = ResolveVideo<Episode>(args, false);
-
- if (episode != null)
- {
- var series = parent as Series;
- if (series == null)
- {
- series = parent.GetParents().OfType<Series>().FirstOrDefault();
- }
-
- if (series != null)
- {
- episode.SeriesId = series.Id;
- episode.SeriesName = series.Name;
- episode.SeriesSortName = series.SortName;
- }
- if (season != null)
- {
- episode.SeasonId = season.Id;
- episode.SeasonName = season.Name;
- }
- }
-
- return episode;
- }
-
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
deleted file mode 100644
index fc4929748..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Naming.Common;
-using MediaBrowser.Naming.TV;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
-{
- /// <summary>
- /// Class SeasonResolver
- /// </summary>
- public class SeasonResolver : FolderResolver<Season>
- {
- /// <summary>
- /// The _config
- /// </summary>
- private readonly IServerConfigurationManager _config;
-
- private readonly ILibraryManager _libraryManager;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SeasonResolver"/> class.
- /// </summary>
- /// <param name="config">The config.</param>
- public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager)
- {
- _config = config;
- _libraryManager = libraryManager;
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>Season.</returns>
- protected override Season Resolve(ItemResolveArgs args)
- {
- if (args.Parent is Series && args.IsDirectory)
- {
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
- var series = ((Series)args.Parent);
-
- var season = new Season
- {
- IndexNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(args.Path, true, true).SeasonNumber,
- SeriesId = series.Id,
- SeriesSortName = series.SortName,
- SeriesName = series.Name
- };
-
- if (season.IndexNumber.HasValue && season.IndexNumber.Value == 0)
- {
- season.Name = _config.Configuration.SeasonZeroDisplayName;
- }
-
- return season;
- }
-
- return null;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
deleted file mode 100644
index 83566e2c1..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ /dev/null
@@ -1,251 +0,0 @@
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Naming.Common;
-using MediaBrowser.Naming.TV;
-using MediaBrowser.Server.Implementations.Logging;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
-{
- /// <summary>
- /// Class SeriesResolver
- /// </summary>
- public class SeriesResolver : FolderResolver<Series>
- {
- private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
- private readonly ILibraryManager _libraryManager;
-
- public SeriesResolver(IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager)
- {
- _fileSystem = fileSystem;
- _logger = logger;
- _libraryManager = libraryManager;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override ResolverPriority Priority
- {
- get
- {
- return ResolverPriority.Second;
- }
- }
-
- /// <summary>
- /// Resolves the specified args.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <returns>Series.</returns>
- protected override Series Resolve(ItemResolveArgs args)
- {
- if (args.IsDirectory)
- {
- if (args.HasParent<Series>() || args.HasParent<Season>())
- {
- return null;
- }
-
- var collectionType = args.GetCollectionType();
- if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
- {
- //if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
- //{
- // return new Series
- // {
- // Path = args.Path,
- // Name = Path.GetFileName(args.Path)
- // };
- //}
-
- var configuredContentType = _libraryManager.GetConfiguredContentType(args.Path);
- if (!string.Equals(configuredContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
- {
- return new Series
- {
- Path = args.Path,
- Name = Path.GetFileName(args.Path)
- };
- }
- }
- else if (string.IsNullOrWhiteSpace(collectionType))
- {
- if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
- {
- if (args.Parent.IsRoot)
- {
- // For now, return null, but if we want to allow this in the future then add some additional checks to guard against a misplaced tvshow.nfo
- return null;
- }
-
- return new Series
- {
- Path = args.Path,
- Name = Path.GetFileName(args.Path)
- };
- }
-
- if (args.Parent.IsRoot)
- {
- return null;
- }
-
- if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false))
- {
- return new Series
- {
- Path = args.Path,
- Name = Path.GetFileName(args.Path)
- };
- }
- }
- }
-
- return null;
- }
-
- public static bool IsSeriesFolder(string path,
- IEnumerable<FileSystemMetadata> fileSystemChildren,
- IDirectoryService directoryService,
- IFileSystem fileSystem,
- ILogger logger,
- ILibraryManager libraryManager,
- LibraryOptions libraryOptions,
- bool isTvContentType)
- {
- foreach (var child in fileSystemChildren)
- {
- //if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
- //{
- // //logger.Debug("Igoring series file or folder marked hidden: {0}", child.FullName);
- // continue;
- //}
-
- // Can't enforce this because files saved by Bitcasa are always marked System
- //if ((attributes & FileAttributes.System) == FileAttributes.System)
- //{
- // logger.Debug("Igoring series subfolder marked system: {0}", child.FullName);
- // continue;
- //}
-
- if (child.IsDirectory)
- {
- if (IsSeasonFolder(child.FullName, isTvContentType, libraryManager))
- {
- //logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName);
- return true;
- }
- }
- else
- {
- string fullName = child.FullName;
- if (libraryManager.IsVideoFile(fullName, libraryOptions))
- {
- if (isTvContentType)
- {
- return true;
- }
-
- var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
-
- // In mixed folders we need to be conservative and avoid expressions that may result in false positives (e.g. movies with numbers in the title)
- if (!isTvContentType)
- {
- namingOptions.EpisodeExpressions = namingOptions.EpisodeExpressions
- .Where(i => i.IsNamed && !i.IsOptimistic)
- .ToList();
- }
-
- var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions, new PatternsLogger());
- var episodeInfo = episodeResolver.Resolve(fullName, false, false);
- if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue)
- {
- return true;
- }
- }
- }
- }
-
- logger.Debug("{0} is not a series folder.", path);
- return false;
- }
-
- /// <summary>
- /// Determines whether [is place holder] [the specified path].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if [is place holder] [the specified path]; otherwise, <c>false</c>.</returns>
- /// <exception cref="System.ArgumentNullException">path</exception>
- private static bool IsVideoPlaceHolder(string path)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException("path");
- }
-
- var extension = Path.GetExtension(path);
-
- return string.Equals(extension, ".disc", StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Determines whether [is season folder] [the specified path].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="isTvContentType">if set to <c>true</c> [is tv content type].</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns>
- private static bool IsSeasonFolder(string path, bool isTvContentType, ILibraryManager libraryManager)
- {
- var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
-
- var seasonNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(path, isTvContentType, isTvContentType).SeasonNumber;
-
- return seasonNumber.HasValue;
- }
-
- /// <summary>
- /// Sets the initial item values.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- protected override void SetInitialItemValues(Series item, ItemResolveArgs args)
- {
- base.SetInitialItemValues(item, args);
-
- SetProviderIdFromPath(item, args.Path);
- }
-
- /// <summary>
- /// Sets the provider id from path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="path">The path.</param>
- private void SetProviderIdFromPath(Series item, string path)
- {
- var justName = Path.GetFileName(path);
-
- var id = justName.GetAttributeValue("tvdbid");
-
- if (!string.IsNullOrEmpty(id))
- {
- item.SetProviderId(MetadataProviders.Tvdb, id);
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs
deleted file mode 100644
index c7f21cef1..000000000
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-
-namespace MediaBrowser.Server.Implementations.Library.Resolvers
-{
- /// <summary>
- /// Resolves a Path into a Video
- /// </summary>
- public class VideoResolver : BaseVideoResolver<Video>
- {
- public VideoResolver(ILibraryManager libraryManager)
- : base(libraryManager)
- {
- }
-
- protected override Video Resolve(ItemResolveArgs args)
- {
- if (args.Parent != null)
- {
- // The movie resolver will handle this
- return null;
- }
-
- return base.Resolve(args);
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override ResolverPriority Priority
- {
- get { return ResolverPriority.Last; }
- }
- }
-
- public class GenericVideoResolver<T> : BaseVideoResolver<T>
- where T : Video, new ()
- {
- public GenericVideoResolver(ILibraryManager libraryManager) : base(libraryManager)
- {
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
deleted file mode 100644
index c266fb191..000000000
--- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
+++ /dev/null
@@ -1,280 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Search;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Extensions;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- /// <summary>
- /// Class LuceneSearchEngine
- /// http://www.codeproject.com/Articles/320219/Lucene-Net-ultra-fast-search-for-MVC-or-WebForms
- /// </summary>
- public class SearchEngine : ISearchEngine
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IUserManager _userManager;
- private readonly ILogger _logger;
-
- public SearchEngine(ILogManager logManager, ILibraryManager libraryManager, IUserManager userManager)
- {
- _libraryManager = libraryManager;
- _userManager = userManager;
-
- _logger = logManager.GetLogger("Lucene");
- }
-
- public async Task<QueryResult<SearchHintInfo>> GetSearchHints(SearchQuery query)
- {
- User user = null;
-
- if (string.IsNullOrWhiteSpace(query.UserId))
- {
- }
- else
- {
- user = _userManager.GetUserById(query.UserId);
- }
-
- var results = await GetSearchHints(query, user).ConfigureAwait(false);
-
- var searchResultArray = results.ToArray();
- results = searchResultArray;
-
- var count = searchResultArray.Length;
-
- if (query.StartIndex.HasValue)
- {
- results = results.Skip(query.StartIndex.Value);
- }
-
- if (query.Limit.HasValue)
- {
- results = results.Take(query.Limit.Value);
- }
-
- return new QueryResult<SearchHintInfo>
- {
- TotalRecordCount = count,
-
- Items = results.ToArray()
- };
- }
-
- private void AddIfMissing(List<string> list, string value)
- {
- if (!list.Contains(value, StringComparer.OrdinalIgnoreCase))
- {
- list.Add(value);
- }
- }
-
- /// <summary>
- /// Gets the search hints.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="user">The user.</param>
- /// <returns>IEnumerable{SearchHintResult}.</returns>
- /// <exception cref="System.ArgumentNullException">searchTerm</exception>
- private Task<IEnumerable<SearchHintInfo>> GetSearchHints(SearchQuery query, User user)
- {
- var searchTerm = query.SearchTerm;
-
- if (searchTerm != null)
- {
- searchTerm = searchTerm.Trim().RemoveDiacritics();
- }
-
- if (string.IsNullOrWhiteSpace(searchTerm))
- {
- throw new ArgumentNullException("searchTerm");
- }
-
- var terms = GetWords(searchTerm);
-
- var hints = new List<Tuple<BaseItem, string, int>>();
-
- var excludeItemTypes = new List<string>();
- var includeItemTypes = (query.IncludeItemTypes ?? new string[] { }).ToList();
-
- excludeItemTypes.Add(typeof(Year).Name);
- excludeItemTypes.Add(typeof(Folder).Name);
-
- if (query.IncludeGenres && (includeItemTypes.Count == 0 || includeItemTypes.Contains("Genre", StringComparer.OrdinalIgnoreCase)))
- {
- if (!query.IncludeMedia)
- {
- AddIfMissing(includeItemTypes, typeof(Genre).Name);
- AddIfMissing(includeItemTypes, typeof(GameGenre).Name);
- AddIfMissing(includeItemTypes, typeof(MusicGenre).Name);
- }
- }
- else
- {
- AddIfMissing(excludeItemTypes, typeof(Genre).Name);
- AddIfMissing(excludeItemTypes, typeof(GameGenre).Name);
- AddIfMissing(excludeItemTypes, typeof(MusicGenre).Name);
- }
-
- if (query.IncludePeople && (includeItemTypes.Count == 0 || includeItemTypes.Contains("People", StringComparer.OrdinalIgnoreCase) || includeItemTypes.Contains("Person", StringComparer.OrdinalIgnoreCase)))
- {
- if (!query.IncludeMedia)
- {
- AddIfMissing(includeItemTypes, typeof(Person).Name);
- }
- }
- else
- {
- AddIfMissing(excludeItemTypes, typeof(Person).Name);
- }
-
- if (query.IncludeStudios && (includeItemTypes.Count == 0 || includeItemTypes.Contains("Studio", StringComparer.OrdinalIgnoreCase)))
- {
- if (!query.IncludeMedia)
- {
- AddIfMissing(includeItemTypes, typeof(Studio).Name);
- }
- }
- else
- {
- AddIfMissing(excludeItemTypes, typeof(Studio).Name);
- }
-
- if (query.IncludeArtists && (includeItemTypes.Count == 0 || includeItemTypes.Contains("MusicArtist", StringComparer.OrdinalIgnoreCase)))
- {
- if (!query.IncludeMedia)
- {
- AddIfMissing(includeItemTypes, typeof(MusicArtist).Name);
- }
- }
- else
- {
- AddIfMissing(excludeItemTypes, typeof(MusicArtist).Name);
- }
-
- AddIfMissing(excludeItemTypes, typeof(CollectionFolder).Name);
-
- var mediaItems = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- NameContains = searchTerm,
- ExcludeItemTypes = excludeItemTypes.ToArray(),
- IncludeItemTypes = includeItemTypes.ToArray(),
- Limit = query.Limit,
- IncludeItemsByName = true,
- IsVirtualItem = false
- });
-
- // Add search hints based on item name
- hints.AddRange(mediaItems.Select(item =>
- {
- var index = GetIndex(item.Name, searchTerm, terms);
-
- return new Tuple<BaseItem, string, int>(item, index.Item1, index.Item2);
- }));
-
- var returnValue = hints.Where(i => i.Item3 >= 0).OrderBy(i => i.Item3).Select(i => new SearchHintInfo
- {
- Item = i.Item1,
- MatchedTerm = i.Item2
- });
-
- return Task.FromResult(returnValue);
- }
-
- /// <summary>
- /// Gets the index.
- /// </summary>
- /// <param name="input">The input.</param>
- /// <param name="searchInput">The search input.</param>
- /// <param name="searchWords">The search input.</param>
- /// <returns>System.Int32.</returns>
- private Tuple<string, int> GetIndex(string input, string searchInput, List<string> searchWords)
- {
- if (string.IsNullOrWhiteSpace(input))
- {
- throw new ArgumentNullException("input");
- }
-
- input = input.RemoveDiacritics();
-
- if (string.Equals(input, searchInput, StringComparison.OrdinalIgnoreCase))
- {
- return new Tuple<string, int>(searchInput, 0);
- }
-
- var index = input.IndexOf(searchInput, StringComparison.OrdinalIgnoreCase);
-
- if (index == 0)
- {
- return new Tuple<string, int>(searchInput, 1);
- }
- if (index > 0)
- {
- return new Tuple<string, int>(searchInput, 2);
- }
-
- var items = GetWords(input);
-
- for (var i = 0; i < searchWords.Count; i++)
- {
- var searchTerm = searchWords[i];
-
- for (var j = 0; j < items.Count; j++)
- {
- var item = items[j];
-
- if (string.Equals(item, searchTerm, StringComparison.OrdinalIgnoreCase))
- {
- return new Tuple<string, int>(searchTerm, 3 + (i + 1) * (j + 1));
- }
-
- index = item.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);
-
- if (index == 0)
- {
- return new Tuple<string, int>(searchTerm, 4 + (i + 1) * (j + 1));
- }
- if (index > 0)
- {
- return new Tuple<string, int>(searchTerm, 5 + (i + 1) * (j + 1));
- }
- }
- }
- return new Tuple<string, int>(null, -1);
- }
-
- /// <summary>
- /// Gets the words.
- /// </summary>
- /// <param name="term">The term.</param>
- /// <returns>System.String[][].</returns>
- private List<string> GetWords(string term)
- {
- var stoplist = GetStopList().ToList();
-
- return term.Split()
- .Where(i => !string.IsNullOrWhiteSpace(i) && !stoplist.Contains(i, StringComparer.OrdinalIgnoreCase))
- .ToList();
- }
-
- private IEnumerable<string> GetStopList()
- {
- return new[]
- {
- "the",
- "a",
- "of",
- "an"
- };
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs b/MediaBrowser.Server.Implementations/Library/UserDataManager.cs
deleted file mode 100644
index 9ee65a57c..000000000
--- a/MediaBrowser.Server.Implementations/Library/UserDataManager.cs
+++ /dev/null
@@ -1,287 +0,0 @@
-using MediaBrowser.Common.Events;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- /// <summary>
- /// Class UserDataManager
- /// </summary>
- public class UserDataManager : IUserDataManager
- {
- public event EventHandler<UserDataSaveEventArgs> UserDataSaved;
-
- private readonly ConcurrentDictionary<string, UserItemData> _userData =
- new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase);
-
- private readonly ILogger _logger;
- private readonly IServerConfigurationManager _config;
-
- public UserDataManager(ILogManager logManager, IServerConfigurationManager config)
- {
- _config = config;
- _logger = logManager.GetLogger(GetType().Name);
- }
-
- /// <summary>
- /// Gets or sets the repository.
- /// </summary>
- /// <value>The repository.</value>
- public IUserDataRepository Repository { get; set; }
-
- public async Task SaveUserData(Guid userId, IHasUserData item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
- {
- if (userData == null)
- {
- throw new ArgumentNullException("userData");
- }
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var keys = item.GetUserDataKeys();
-
- foreach (var key in keys)
- {
- await Repository.SaveUserData(userId, key, userData, cancellationToken).ConfigureAwait(false);
- }
-
- var cacheKey = GetCacheKey(userId, item.Id);
- _userData.AddOrUpdate(cacheKey, userData, (k, v) => userData);
-
- EventHelper.FireEventIfNotNull(UserDataSaved, this, new UserDataSaveEventArgs
- {
- Keys = keys,
- UserData = userData,
- SaveReason = reason,
- UserId = userId,
- Item = item
-
- }, _logger);
- }
-
- /// <summary>
- /// Save the provided user data for the given user. Batch operation. Does not fire any events or update the cache.
- /// </summary>
- /// <param name="userId"></param>
- /// <param name="userData"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
- public async Task SaveAllUserData(Guid userId, IEnumerable<UserItemData> userData, CancellationToken cancellationToken)
- {
- if (userData == null)
- {
- throw new ArgumentNullException("userData");
- }
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await Repository.SaveAllUserData(userId, userData, cancellationToken).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Retrieve all user data for the given user
- /// </summary>
- /// <param name="userId"></param>
- /// <returns></returns>
- public IEnumerable<UserItemData> GetAllUserData(Guid userId)
- {
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
-
- return Repository.GetAllUserData(userId);
- }
-
- public UserItemData GetUserData(Guid userId, Guid itemId, List<string> keys)
- {
- if (userId == Guid.Empty)
- {
- throw new ArgumentNullException("userId");
- }
- if (keys == null)
- {
- throw new ArgumentNullException("keys");
- }
- if (keys.Count == 0)
- {
- throw new ArgumentException("UserData keys cannot be empty.");
- }
-
- var cacheKey = GetCacheKey(userId, itemId);
-
- return _userData.GetOrAdd(cacheKey, k => GetUserDataInternal(userId, keys));
- }
-
- private UserItemData GetUserDataInternal(Guid userId, List<string> keys)
- {
- var userData = Repository.GetUserData(userId, keys);
-
- if (userData != null)
- {
- return userData;
- }
-
- if (keys.Count > 0)
- {
- return new UserItemData
- {
- UserId = userId,
- Key = keys[0]
- };
- }
-
- return null;
- }
-
- /// <summary>
- /// Gets the internal key.
- /// </summary>
- /// <returns>System.String.</returns>
- private string GetCacheKey(Guid userId, Guid itemId)
- {
- return userId.ToString("N") + itemId.ToString("N");
- }
-
- public UserItemData GetUserData(IHasUserData user, IHasUserData item)
- {
- return GetUserData(user.Id, item);
- }
-
- public UserItemData GetUserData(string userId, IHasUserData item)
- {
- return GetUserData(new Guid(userId), item);
- }
-
- public UserItemData GetUserData(Guid userId, IHasUserData item)
- {
- return GetUserData(userId, item.Id, item.GetUserDataKeys());
- }
-
- public async Task<UserItemDataDto> GetUserDataDto(IHasUserData item, User user)
- {
- var userData = GetUserData(user.Id, item);
- var dto = GetUserItemDataDto(userData);
-
- await item.FillUserDataDtoValues(dto, userData, null, user).ConfigureAwait(false);
- return dto;
- }
-
- public async Task<UserItemDataDto> GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user)
- {
- var userData = GetUserData(user.Id, item);
- var dto = GetUserItemDataDto(userData);
-
- await item.FillUserDataDtoValues(dto, userData, itemDto, user).ConfigureAwait(false);
- return dto;
- }
-
- /// <summary>
- /// Converts a UserItemData to a DTOUserItemData
- /// </summary>
- /// <param name="data">The data.</param>
- /// <returns>DtoUserItemData.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- private UserItemDataDto GetUserItemDataDto(UserItemData data)
- {
- if (data == null)
- {
- throw new ArgumentNullException("data");
- }
-
- return new UserItemDataDto
- {
- IsFavorite = data.IsFavorite,
- Likes = data.Likes,
- PlaybackPositionTicks = data.PlaybackPositionTicks,
- PlayCount = data.PlayCount,
- Rating = data.Rating,
- Played = data.Played,
- LastPlayedDate = data.LastPlayedDate,
- Key = data.Key
- };
- }
-
- public bool UpdatePlayState(BaseItem item, UserItemData data, long? reportedPositionTicks)
- {
- var playedToCompletion = false;
-
- var positionTicks = reportedPositionTicks ?? item.RunTimeTicks ?? 0;
- var hasRuntime = item.RunTimeTicks.HasValue && item.RunTimeTicks > 0;
-
- // If a position has been reported, and if we know the duration
- if (positionTicks > 0 && hasRuntime)
- {
- var pctIn = Decimal.Divide(positionTicks, item.RunTimeTicks.Value) * 100;
-
- // Don't track in very beginning
- if (pctIn < _config.Configuration.MinResumePct)
- {
- positionTicks = 0;
- }
-
- // If we're at the end, assume completed
- else if (pctIn > _config.Configuration.MaxResumePct || positionTicks >= item.RunTimeTicks.Value)
- {
- positionTicks = 0;
- data.Played = playedToCompletion = true;
- }
-
- else
- {
- // Enforce MinResumeDuration
- var durationSeconds = TimeSpan.FromTicks(item.RunTimeTicks.Value).TotalSeconds;
-
- if (durationSeconds < _config.Configuration.MinResumeDurationSeconds)
- {
- positionTicks = 0;
- data.Played = playedToCompletion = true;
- }
- }
- }
- else if (!hasRuntime)
- {
- // If we don't know the runtime we'll just have to assume it was fully played
- data.Played = playedToCompletion = true;
- positionTicks = 0;
- }
-
- if (!item.SupportsPlayedStatus)
- {
- positionTicks = 0;
- data.Played = false;
- }
- if (item is Audio)
- {
- positionTicks = 0;
- }
-
- data.PlaybackPositionTicks = positionTicks;
-
- return playedToCompletion;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
deleted file mode 100644
index 794c924eb..000000000
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ /dev/null
@@ -1,1021 +0,0 @@
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Connect;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Connect;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- /// <summary>
- /// Class UserManager
- /// </summary>
- public class UserManager : IUserManager
- {
- /// <summary>
- /// Gets the users.
- /// </summary>
- /// <value>The users.</value>
- public IEnumerable<User> Users { get; private set; }
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// Gets or sets the configuration manager.
- /// </summary>
- /// <value>The configuration manager.</value>
- private IServerConfigurationManager ConfigurationManager { get; set; }
-
- /// <summary>
- /// Gets the active user repository
- /// </summary>
- /// <value>The user repository.</value>
- private IUserRepository UserRepository { get; set; }
- public event EventHandler<GenericEventArgs<User>> UserPasswordChanged;
-
- private readonly IXmlSerializer _xmlSerializer;
- private readonly IJsonSerializer _jsonSerializer;
-
- private readonly INetworkManager _networkManager;
-
- private readonly Func<IImageProcessor> _imageProcessorFactory;
- private readonly Func<IDtoService> _dtoServiceFactory;
- private readonly Func<IConnectManager> _connectFactory;
- private readonly IServerApplicationHost _appHost;
- private readonly IFileSystem _fileSystem;
-
- public UserManager(ILogger logger, IServerConfigurationManager configurationManager, IUserRepository userRepository, IXmlSerializer xmlSerializer, INetworkManager networkManager, Func<IImageProcessor> imageProcessorFactory, Func<IDtoService> dtoServiceFactory, Func<IConnectManager> connectFactory, IServerApplicationHost appHost, IJsonSerializer jsonSerializer, IFileSystem fileSystem)
- {
- _logger = logger;
- UserRepository = userRepository;
- _xmlSerializer = xmlSerializer;
- _networkManager = networkManager;
- _imageProcessorFactory = imageProcessorFactory;
- _dtoServiceFactory = dtoServiceFactory;
- _connectFactory = connectFactory;
- _appHost = appHost;
- _jsonSerializer = jsonSerializer;
- _fileSystem = fileSystem;
- ConfigurationManager = configurationManager;
- Users = new List<User>();
-
- DeletePinFile();
- }
-
- #region UserUpdated Event
- /// <summary>
- /// Occurs when [user updated].
- /// </summary>
- public event EventHandler<GenericEventArgs<User>> UserUpdated;
- public event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated;
- public event EventHandler<GenericEventArgs<User>> UserLockedOut;
-
- /// <summary>
- /// Called when [user updated].
- /// </summary>
- /// <param name="user">The user.</param>
- private void OnUserUpdated(User user)
- {
- EventHelper.FireEventIfNotNull(UserUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
- }
- #endregion
-
- #region UserDeleted Event
- /// <summary>
- /// Occurs when [user deleted].
- /// </summary>
- public event EventHandler<GenericEventArgs<User>> UserDeleted;
- /// <summary>
- /// Called when [user deleted].
- /// </summary>
- /// <param name="user">The user.</param>
- private void OnUserDeleted(User user)
- {
- EventHelper.QueueEventIfNotNull(UserDeleted, this, new GenericEventArgs<User> { Argument = user }, _logger);
- }
- #endregion
-
- /// <summary>
- /// Gets a User by Id
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>User.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public User GetUserById(Guid id)
- {
- if (id == Guid.Empty)
- {
- throw new ArgumentNullException("id");
- }
-
- return Users.FirstOrDefault(u => u.Id == id);
- }
-
- /// <summary>
- /// Gets the user by identifier.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <returns>User.</returns>
- public User GetUserById(string id)
- {
- return GetUserById(new Guid(id));
- }
-
- public User GetUserByName(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase));
- }
-
- public async Task Initialize()
- {
- Users = await LoadUsers().ConfigureAwait(false);
-
- var users = Users.ToList();
-
- // If there are no local users with admin rights, make them all admins
- if (!users.Any(i => i.Policy.IsAdministrator))
- {
- foreach (var user in users)
- {
- if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value == UserLinkType.LinkedUser)
- {
- user.Policy.IsAdministrator = true;
- await UpdateUserPolicy(user, user.Policy, false).ConfigureAwait(false);
- }
- }
- }
- }
-
- public Task<bool> AuthenticateUser(string username, string passwordSha1, string remoteEndPoint)
- {
- return AuthenticateUser(username, passwordSha1, null, remoteEndPoint);
- }
-
- public bool IsValidUsername(string username)
- {
- // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
- return username.All(IsValidUsernameCharacter);
- }
-
- private bool IsValidUsernameCharacter(char i)
- {
- return char.IsLetterOrDigit(i) || char.Equals(i, '-') || char.Equals(i, '_') || char.Equals(i, '\'') ||
- char.Equals(i, '.');
- }
-
- public string MakeValidUsername(string username)
- {
- if (IsValidUsername(username))
- {
- return username;
- }
-
- // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
- var builder = new StringBuilder();
-
- foreach (var c in username)
- {
- if (IsValidUsernameCharacter(c))
- {
- builder.Append(c);
- }
- }
- return builder.ToString();
- }
-
- public async Task<bool> AuthenticateUser(string username, string passwordSha1, string passwordMd5, string remoteEndPoint)
- {
- if (string.IsNullOrWhiteSpace(username))
- {
- throw new ArgumentNullException("username");
- }
-
- var user = Users
- .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
-
- if (user == null)
- {
- throw new SecurityException("Invalid username or password entered.");
- }
-
- if (user.Policy.IsDisabled)
- {
- throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
- }
-
- var success = false;
-
- // Authenticate using local credentials if not a guest
- if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest)
- {
- success = string.Equals(GetPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
-
- if (!success && _networkManager.IsInLocalNetwork(remoteEndPoint) && user.Configuration.EnableLocalPassword)
- {
- success = string.Equals(GetLocalPasswordHash(user), passwordSha1.Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase);
- }
- }
-
- // Update LastActivityDate and LastLoginDate, then save
- if (success)
- {
- user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
- await UpdateUser(user).ConfigureAwait(false);
- await UpdateInvalidLoginAttemptCount(user, 0).ConfigureAwait(false);
- }
- else
- {
- await UpdateInvalidLoginAttemptCount(user, user.Policy.InvalidLoginAttemptCount + 1).ConfigureAwait(false);
- }
-
- _logger.Info("Authentication request for {0} {1}.", user.Name, success ? "has succeeded" : "has been denied");
-
- return success;
- }
-
- private async Task UpdateInvalidLoginAttemptCount(User user, int newValue)
- {
- if (user.Policy.InvalidLoginAttemptCount != newValue || newValue > 0)
- {
- user.Policy.InvalidLoginAttemptCount = newValue;
-
- var maxCount = user.Policy.IsAdministrator ?
- 3 :
- 5;
-
- var fireLockout = false;
-
- if (newValue >= maxCount)
- {
- //_logger.Debug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue.ToString(CultureInfo.InvariantCulture));
- //user.Policy.IsDisabled = true;
-
- //fireLockout = true;
- }
-
- await UpdateUserPolicy(user, user.Policy, false).ConfigureAwait(false);
-
- if (fireLockout)
- {
- if (UserLockedOut != null)
- {
- EventHelper.FireEventIfNotNull(UserLockedOut, this, new GenericEventArgs<User>(user), _logger);
- }
- }
- }
- }
-
- private string GetPasswordHash(User user)
- {
- return string.IsNullOrEmpty(user.Password)
- ? GetSha1String(string.Empty)
- : user.Password;
- }
-
- private string GetLocalPasswordHash(User user)
- {
- return string.IsNullOrEmpty(user.EasyPassword)
- ? GetSha1String(string.Empty)
- : user.EasyPassword;
- }
-
- private bool IsPasswordEmpty(string passwordHash)
- {
- return string.Equals(passwordHash, GetSha1String(string.Empty), StringComparison.OrdinalIgnoreCase);
- }
-
- /// <summary>
- /// Gets the sha1 string.
- /// </summary>
- /// <param name="str">The STR.</param>
- /// <returns>System.String.</returns>
- private static string GetSha1String(string str)
- {
- using (var provider = SHA1.Create())
- {
- var hash = provider.ComputeHash(Encoding.UTF8.GetBytes(str));
- return BitConverter.ToString(hash).Replace("-", string.Empty);
- }
- }
-
- /// <summary>
- /// Loads the users from the repository
- /// </summary>
- /// <returns>IEnumerable{User}.</returns>
- private async Task<IEnumerable<User>> LoadUsers()
- {
- var users = UserRepository.RetrieveAllUsers().ToList();
-
- // There always has to be at least one user.
- if (users.Count == 0)
- {
- var name = MakeValidUsername(Environment.UserName);
-
- var user = InstantiateNewUser(name);
-
- user.DateLastSaved = DateTime.UtcNow;
-
- await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
-
- users.Add(user);
-
- user.Policy.IsAdministrator = true;
- user.Policy.EnableContentDeletion = true;
- user.Policy.EnableRemoteControlOfOtherUsers = true;
- await UpdateUserPolicy(user, user.Policy, false).ConfigureAwait(false);
- }
-
- return users;
- }
-
- public UserDto GetUserDto(User user, string remoteEndPoint = null)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- var passwordHash = GetPasswordHash(user);
-
- var hasConfiguredPassword = !IsPasswordEmpty(passwordHash);
- var hasConfiguredEasyPassword = !IsPasswordEmpty(GetLocalPasswordHash(user));
-
- var hasPassword = user.Configuration.EnableLocalPassword && !string.IsNullOrEmpty(remoteEndPoint) && _networkManager.IsInLocalNetwork(remoteEndPoint) ?
- hasConfiguredEasyPassword :
- hasConfiguredPassword;
-
- var dto = new UserDto
- {
- Id = user.Id.ToString("N"),
- Name = user.Name,
- HasPassword = hasPassword,
- HasConfiguredPassword = hasConfiguredPassword,
- HasConfiguredEasyPassword = hasConfiguredEasyPassword,
- LastActivityDate = user.LastActivityDate,
- LastLoginDate = user.LastLoginDate,
- Configuration = user.Configuration,
- ConnectLinkType = user.ConnectLinkType,
- ConnectUserId = user.ConnectUserId,
- ConnectUserName = user.ConnectUserName,
- ServerId = _appHost.SystemId,
- Policy = user.Policy
- };
-
- var image = user.GetImageInfo(ImageType.Primary, 0);
-
- if (image != null)
- {
- dto.PrimaryImageTag = GetImageCacheTag(user, image);
-
- try
- {
- _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user);
- }
- catch (Exception ex)
- {
- // Have to use a catch-all unfortunately because some .net image methods throw plain Exceptions
- _logger.ErrorException("Error generating PrimaryImageAspectRatio for {0}", ex, user.Name);
- }
- }
-
- return dto;
- }
-
- public UserDto GetOfflineUserDto(User user)
- {
- var dto = GetUserDto(user);
-
- var offlinePasswordHash = GetLocalPasswordHash(user);
- dto.HasPassword = !IsPasswordEmpty(offlinePasswordHash);
-
- dto.OfflinePasswordSalt = Guid.NewGuid().ToString("N");
-
- // Hash the pin with the device Id to create a unique result for this device
- dto.OfflinePassword = GetSha1String((offlinePasswordHash + dto.OfflinePasswordSalt).ToLower());
-
- dto.ServerName = _appHost.FriendlyName;
-
- return dto;
- }
-
- private string GetImageCacheTag(BaseItem item, ItemImageInfo image)
- {
- try
- {
- return _imageProcessorFactory().GetImageCacheTag(item, image);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0} image info for {1}", ex, image.Type, image.Path);
- return null;
- }
- }
-
- /// <summary>
- /// Refreshes metadata for each user
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task RefreshUsersMetadata(CancellationToken cancellationToken)
- {
- var tasks = Users.Select(user => user.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), cancellationToken)).ToList();
-
- return Task.WhenAll(tasks);
- }
-
- /// <summary>
- /// Renames the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <param name="newName">The new name.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- /// <exception cref="System.ArgumentException"></exception>
- public async Task RenameUser(User user, string newName)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- if (string.IsNullOrEmpty(newName))
- {
- throw new ArgumentNullException("newName");
- }
-
- if (Users.Any(u => u.Id != user.Id && u.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", newName));
- }
-
- if (user.Name.Equals(newName, StringComparison.Ordinal))
- {
- throw new ArgumentException("The new and old names must be different.");
- }
-
- await user.Rename(newName);
-
- OnUserUpdated(user);
- }
-
- /// <summary>
- /// Updates the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <exception cref="System.ArgumentNullException">user</exception>
- /// <exception cref="System.ArgumentException"></exception>
- public async Task UpdateUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- if (user.Id == Guid.Empty || !Users.Any(u => u.Id.Equals(user.Id)))
- {
- throw new ArgumentException(string.Format("User with name '{0}' and Id {1} does not exist.", user.Name, user.Id));
- }
-
- user.DateModified = DateTime.UtcNow;
- user.DateLastSaved = DateTime.UtcNow;
-
- await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
-
- OnUserUpdated(user);
- }
-
- public event EventHandler<GenericEventArgs<User>> UserCreated;
-
- private readonly SemaphoreSlim _userListLock = new SemaphoreSlim(1, 1);
-
- /// <summary>
- /// Creates the user.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- /// <exception cref="System.ArgumentNullException">name</exception>
- /// <exception cref="System.ArgumentException"></exception>
- public async Task<User> CreateUser(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- if (!IsValidUsername(name))
- {
- throw new ArgumentException("Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)");
- }
-
- if (Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name));
- }
-
- await _userListLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
-
- try
- {
- var user = InstantiateNewUser(name);
-
- var list = Users.ToList();
- list.Add(user);
- Users = list;
-
- user.DateLastSaved = DateTime.UtcNow;
-
- await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
-
- EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs<User> { Argument = user }, _logger);
-
- return user;
- }
- finally
- {
- _userListLock.Release();
- }
- }
-
- /// <summary>
- /// Deletes the user.
- /// </summary>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- /// <exception cref="System.ArgumentException"></exception>
- public async Task DeleteUser(User user)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
-
- if (user.ConnectLinkType.HasValue)
- {
- await _connectFactory().RemoveConnect(user.Id.ToString("N")).ConfigureAwait(false);
- }
-
- var allUsers = Users.ToList();
-
- if (allUsers.FirstOrDefault(u => u.Id == user.Id) == null)
- {
- throw new ArgumentException(string.Format("The user cannot be deleted because there is no user with the Name {0} and Id {1}.", user.Name, user.Id));
- }
-
- if (allUsers.Count == 1)
- {
- throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one user in the system.", user.Name));
- }
-
- if (user.Policy.IsAdministrator && allUsers.Count(i => i.Policy.IsAdministrator) == 1)
- {
- throw new ArgumentException(string.Format("The user '{0}' cannot be deleted because there must be at least one admin user in the system.", user.Name));
- }
-
- await _userListLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
-
- try
- {
- var configPath = GetConfigurationFilePath(user);
-
- await UserRepository.DeleteUser(user, CancellationToken.None).ConfigureAwait(false);
-
- try
- {
- _fileSystem.DeleteFile(configPath);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error deleting file {0}", ex, configPath);
- }
-
- DeleteUserPolicy(user);
-
- // Force this to be lazy loaded again
- Users = await LoadUsers().ConfigureAwait(false);
-
- OnUserDeleted(user);
- }
- finally
- {
- _userListLock.Release();
- }
- }
-
- /// <summary>
- /// Resets the password by clearing it.
- /// </summary>
- /// <returns>Task.</returns>
- public Task ResetPassword(User user)
- {
- return ChangePassword(user, GetSha1String(string.Empty));
- }
-
- public Task ResetEasyPassword(User user)
- {
- return ChangeEasyPassword(user, GetSha1String(string.Empty));
- }
-
- public async Task ChangePassword(User user, string newPasswordSha1)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
- if (string.IsNullOrWhiteSpace(newPasswordSha1))
- {
- throw new ArgumentNullException("newPasswordSha1");
- }
-
- if (user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest)
- {
- throw new ArgumentException("Passwords for guests cannot be changed.");
- }
-
- user.Password = newPasswordSha1;
-
- await UpdateUser(user).ConfigureAwait(false);
-
- EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
- }
-
- public async Task ChangeEasyPassword(User user, string newPasswordSha1)
- {
- if (user == null)
- {
- throw new ArgumentNullException("user");
- }
- if (string.IsNullOrWhiteSpace(newPasswordSha1))
- {
- throw new ArgumentNullException("newPasswordSha1");
- }
-
- user.EasyPassword = newPasswordSha1;
-
- await UpdateUser(user).ConfigureAwait(false);
-
- EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
- }
-
- /// <summary>
- /// Instantiates the new user.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>User.</returns>
- private User InstantiateNewUser(string name)
- {
- return new User
- {
- Name = name,
- Id = Guid.NewGuid(),
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow,
- UsesIdForConfigurationPath = true
- };
- }
-
- private string PasswordResetFile
- {
- get { return Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "passwordreset.txt"); }
- }
-
- private string _lastPin;
- private PasswordPinCreationResult _lastPasswordPinCreationResult;
- private int _pinAttempts;
-
- private PasswordPinCreationResult CreatePasswordResetPin()
- {
- var num = new Random().Next(1, 9999);
-
- var path = PasswordResetFile;
-
- var pin = num.ToString("0000", CultureInfo.InvariantCulture);
- _lastPin = pin;
-
- var time = TimeSpan.FromMinutes(5);
- var expiration = DateTime.UtcNow.Add(time);
-
- var text = new StringBuilder();
-
- var localAddress = _appHost.GetLocalApiUrl().Result ?? string.Empty;
-
- text.AppendLine("Use your web browser to visit:");
- text.AppendLine(string.Empty);
- text.AppendLine(localAddress + "/web/forgotpasswordpin.html");
- text.AppendLine(string.Empty);
- text.AppendLine("Enter the following pin code:");
- text.AppendLine(string.Empty);
- text.AppendLine(pin);
- text.AppendLine(string.Empty);
- text.AppendLine("The pin code will expire at " + expiration.ToLocalTime().ToShortDateString() + " " + expiration.ToLocalTime().ToShortTimeString());
-
- _fileSystem.WriteAllText(path, text.ToString(), Encoding.UTF8);
-
- var result = new PasswordPinCreationResult
- {
- PinFile = path,
- ExpirationDate = expiration
- };
-
- _lastPasswordPinCreationResult = result;
- _pinAttempts = 0;
-
- return result;
- }
-
- public ForgotPasswordResult StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
- {
- DeletePinFile();
-
- var user = string.IsNullOrWhiteSpace(enteredUsername) ?
- null :
- GetUserByName(enteredUsername);
-
- if (user != null && user.ConnectLinkType.HasValue && user.ConnectLinkType.Value == UserLinkType.Guest)
- {
- throw new ArgumentException("Unable to process forgot password request for guests.");
- }
-
- var action = ForgotPasswordAction.InNetworkRequired;
- string pinFile = null;
- DateTime? expirationDate = null;
-
- if (user != null && !user.Policy.IsAdministrator)
- {
- action = ForgotPasswordAction.ContactAdmin;
- }
- else
- {
- if (isInNetwork)
- {
- action = ForgotPasswordAction.PinCode;
- }
-
- var result = CreatePasswordResetPin();
- pinFile = result.PinFile;
- expirationDate = result.ExpirationDate;
- }
-
- return new ForgotPasswordResult
- {
- Action = action,
- PinFile = pinFile,
- PinExpirationDate = expirationDate
- };
- }
-
- public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
- {
- DeletePinFile();
-
- var usersReset = new List<string>();
-
- var valid = !string.IsNullOrWhiteSpace(_lastPin) &&
- string.Equals(_lastPin, pin, StringComparison.OrdinalIgnoreCase) &&
- _lastPasswordPinCreationResult != null &&
- _lastPasswordPinCreationResult.ExpirationDate > DateTime.UtcNow;
-
- if (valid)
- {
- _lastPin = null;
- _lastPasswordPinCreationResult = null;
-
- var users = Users.Where(i => !i.ConnectLinkType.HasValue || i.ConnectLinkType.Value != UserLinkType.Guest)
- .ToList();
-
- foreach (var user in users)
- {
- await ResetPassword(user).ConfigureAwait(false);
-
- if (user.Policy.IsDisabled)
- {
- user.Policy.IsDisabled = false;
- await UpdateUserPolicy(user, user.Policy, true).ConfigureAwait(false);
- }
- usersReset.Add(user.Name);
- }
- }
- else
- {
- _pinAttempts++;
- if (_pinAttempts >= 3)
- {
- _lastPin = null;
- _lastPasswordPinCreationResult = null;
- }
- }
-
- return new PinRedeemResult
- {
- Success = valid,
- UsersReset = usersReset.ToArray()
- };
- }
-
- private void DeletePinFile()
- {
- try
- {
- _fileSystem.DeleteFile(PasswordResetFile);
- }
- catch
- {
-
- }
- }
-
- class PasswordPinCreationResult
- {
- public string PinFile { get; set; }
- public DateTime ExpirationDate { get; set; }
- }
-
- public UserPolicy GetUserPolicy(User user)
- {
- var path = GetPolifyFilePath(user);
-
- try
- {
- lock (_policySyncLock)
- {
- return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
- }
- }
- catch (DirectoryNotFoundException)
- {
- return GetDefaultPolicy(user);
- }
- catch (FileNotFoundException)
- {
- return GetDefaultPolicy(user);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reading policy file: {0}", ex, path);
-
- return GetDefaultPolicy(user);
- }
- }
-
- private UserPolicy GetDefaultPolicy(User user)
- {
- return new UserPolicy
- {
- EnableSync = true
- };
- }
-
- private readonly object _policySyncLock = new object();
- public Task UpdateUserPolicy(string userId, UserPolicy userPolicy)
- {
- var user = GetUserById(userId);
- return UpdateUserPolicy(user, userPolicy, true);
- }
-
- private async Task UpdateUserPolicy(User user, UserPolicy userPolicy, bool fireEvent)
- {
- // The xml serializer will output differently if the type is not exact
- if (userPolicy.GetType() != typeof(UserPolicy))
- {
- var json = _jsonSerializer.SerializeToString(userPolicy);
- userPolicy = _jsonSerializer.DeserializeFromString<UserPolicy>(json);
- }
-
- var path = GetPolifyFilePath(user);
-
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- lock (_policySyncLock)
- {
- _xmlSerializer.SerializeToFile(userPolicy, path);
- user.Policy = userPolicy;
- }
-
- await UpdateConfiguration(user, user.Configuration, true).ConfigureAwait(false);
- }
-
- private void DeleteUserPolicy(User user)
- {
- var path = GetPolifyFilePath(user);
-
- try
- {
- lock (_policySyncLock)
- {
- _fileSystem.DeleteFile(path);
- }
- }
- catch (IOException)
- {
-
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting policy file", ex);
- }
- }
-
- private string GetPolifyFilePath(User user)
- {
- return Path.Combine(user.ConfigurationDirectoryPath, "policy.xml");
- }
-
- private string GetConfigurationFilePath(User user)
- {
- return Path.Combine(user.ConfigurationDirectoryPath, "config.xml");
- }
-
- public UserConfiguration GetUserConfiguration(User user)
- {
- var path = GetConfigurationFilePath(user);
-
- try
- {
- lock (_configSyncLock)
- {
- return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
- }
- }
- catch (DirectoryNotFoundException)
- {
- return new UserConfiguration();
- }
- catch (FileNotFoundException)
- {
- return new UserConfiguration();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reading policy file: {0}", ex, path);
-
- return new UserConfiguration();
- }
- }
-
- private readonly object _configSyncLock = new object();
- public Task UpdateConfiguration(string userId, UserConfiguration config)
- {
- var user = GetUserById(userId);
- return UpdateConfiguration(user, config, true);
- }
-
- private async Task UpdateConfiguration(User user, UserConfiguration config, bool fireEvent)
- {
- var path = GetConfigurationFilePath(user);
-
- // The xml serializer will output differently if the type is not exact
- if (config.GetType() != typeof(UserConfiguration))
- {
- var json = _jsonSerializer.SerializeToString(config);
- config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json);
- }
-
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- lock (_configSyncLock)
- {
- _xmlSerializer.SerializeToFile(config, path);
- user.Configuration = config;
- }
-
- if (fireEvent)
- {
- EventHelper.FireEventIfNotNull(UserConfigurationUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
deleted file mode 100644
index 6a4e26ff9..000000000
--- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs
+++ /dev/null
@@ -1,292 +0,0 @@
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Channels;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Library;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Model.Globalization;
-
-namespace MediaBrowser.Server.Implementations.Library
-{
- public class UserViewManager : IUserViewManager
- {
- private readonly ILibraryManager _libraryManager;
- private readonly ILocalizationManager _localizationManager;
- private readonly IUserManager _userManager;
-
- private readonly IChannelManager _channelManager;
- private readonly ILiveTvManager _liveTvManager;
- private readonly IServerConfigurationManager _config;
-
- public UserViewManager(ILibraryManager libraryManager, ILocalizationManager localizationManager, IUserManager userManager, IChannelManager channelManager, ILiveTvManager liveTvManager, IServerConfigurationManager config)
- {
- _libraryManager = libraryManager;
- _localizationManager = localizationManager;
- _userManager = userManager;
- _channelManager = channelManager;
- _liveTvManager = liveTvManager;
- _config = config;
- }
-
- public async Task<IEnumerable<Folder>> GetUserViews(UserViewQuery query, CancellationToken cancellationToken)
- {
- var user = _userManager.GetUserById(query.UserId);
-
- var folders = user.RootFolder
- .GetChildren(user, true)
- .OfType<Folder>()
- .ToList();
-
- if (!query.IncludeHidden)
- {
- folders = folders.Where(i =>
- {
- var hidden = i as IHiddenFromDisplay;
- return hidden == null || !hidden.IsHiddenFromUser(user);
- }).ToList();
- }
-
- var plainFolderIds = user.Configuration.PlainFolderViews.Select(i => new Guid(i)).ToList();
-
- var groupedFolders = new List<ICollectionFolder>();
-
- var list = new List<Folder>();
-
- foreach (var folder in folders)
- {
- var collectionFolder = folder as ICollectionFolder;
- var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType;
-
- if (UserView.IsUserSpecific(folder))
- {
- list.Add(await _libraryManager.GetNamedView(user, folder.Name, folder.Id.ToString("N"), folderViewType, null, cancellationToken).ConfigureAwait(false));
- continue;
- }
-
- if (plainFolderIds.Contains(folder.Id) && UserView.IsEligibleForEnhancedView(folderViewType))
- {
- list.Add(folder);
- continue;
- }
-
- if (collectionFolder != null && UserView.IsEligibleForGrouping(folder) && user.IsFolderGrouped(folder.Id))
- {
- groupedFolders.Add(collectionFolder);
- continue;
- }
-
- if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
- {
- list.Add(await GetUserView(folder, folderViewType, string.Empty, cancellationToken).ConfigureAwait(false));
- }
- else
- {
- list.Add(folder);
- }
- }
-
- foreach (var viewType in new[] { CollectionType.Movies, CollectionType.TvShows })
- {
- var parents = groupedFolders.Where(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
- .ToList();
-
- if (parents.Count > 0)
- {
- list.Add(await GetUserView(parents, viewType, string.Empty, user, query.PresetViews, cancellationToken).ConfigureAwait(false));
- }
- }
-
- if (_config.Configuration.EnableFolderView)
- {
- var name = _localizationManager.GetLocalizedString("ViewType" + CollectionType.Folders);
- list.Add(await _libraryManager.GetNamedView(name, CollectionType.Folders, string.Empty, cancellationToken).ConfigureAwait(false));
- }
-
- if (query.IncludeExternalContent)
- {
- var channelResult = await _channelManager.GetChannelsInternal(new ChannelQuery
- {
- UserId = query.UserId
-
- }, cancellationToken).ConfigureAwait(false);
-
- var channels = channelResult.Items;
-
- if (_config.Configuration.EnableChannelView && channels.Length > 0)
- {
- list.Add(await _channelManager.GetInternalChannelFolder(cancellationToken).ConfigureAwait(false));
- }
- else
- {
- list.AddRange(channels);
- }
-
- if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
- {
- list.Add(await _liveTvManager.GetInternalLiveTvFolder(CancellationToken.None).ConfigureAwait(false));
- }
- }
-
- var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
-
- var orders = user.Configuration.OrderedViews.ToList();
-
- return list
- .OrderBy(i =>
- {
- var index = orders.IndexOf(i.Id.ToString("N"));
-
- if (index == -1)
- {
- var view = i as UserView;
- if (view != null)
- {
- if (view.DisplayParentId != Guid.Empty)
- {
- index = orders.IndexOf(view.DisplayParentId.ToString("N"));
- }
- }
- }
-
- return index == -1 ? int.MaxValue : index;
- })
- .ThenBy(sorted.IndexOf)
- .ThenBy(i => i.SortName);
- }
-
- public Task<UserView> GetUserSubView(string name, string parentId, string type, string sortName, CancellationToken cancellationToken)
- {
- var uniqueId = parentId + "subview" + type;
-
- return _libraryManager.GetNamedView(name, parentId, type, sortName, uniqueId, cancellationToken);
- }
-
- public Task<UserView> GetUserSubView(string parentId, string type, string sortName, CancellationToken cancellationToken)
- {
- var name = _localizationManager.GetLocalizedString("ViewType" + type);
-
- return GetUserSubView(name, parentId, type, sortName, cancellationToken);
- }
-
- private async Task<Folder> GetUserView(List<ICollectionFolder> parents, string viewType, string sortName, User user, string[] presetViews, CancellationToken cancellationToken)
- {
- if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
- {
- if (!presetViews.Contains(viewType, StringComparer.OrdinalIgnoreCase))
- {
- return (Folder)parents[0];
- }
-
- return await GetUserView((Folder)parents[0], viewType, string.Empty, cancellationToken).ConfigureAwait(false);
- }
-
- var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
- return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
- }
-
- public Task<UserView> GetUserView(Folder parent, string viewType, string sortName, CancellationToken cancellationToken)
- {
- return _libraryManager.GetShadowView(parent, viewType, sortName, cancellationToken);
- }
-
- public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request)
- {
- var user = _userManager.GetUserById(request.UserId);
-
- var libraryItems = GetItemsForLatestItems(user, request);
-
- var list = new List<Tuple<BaseItem, List<BaseItem>>>();
-
- foreach (var item in libraryItems)
- {
- // Only grab the index container for media
- var container = item.IsFolder || !request.GroupItems ? null : item.LatestItemsIndexContainer;
-
- if (container == null)
- {
- list.Add(new Tuple<BaseItem, List<BaseItem>>(null, new List<BaseItem> { item }));
- }
- else
- {
- var current = list.FirstOrDefault(i => i.Item1 != null && i.Item1.Id == container.Id);
-
- if (current != null)
- {
- current.Item2.Add(item);
- }
- else
- {
- list.Add(new Tuple<BaseItem, List<BaseItem>>(container, new List<BaseItem> { item }));
- }
- }
-
- if (list.Count >= request.Limit)
- {
- break;
- }
- }
-
- return list;
- }
-
- private IEnumerable<BaseItem> GetItemsForLatestItems(User user, LatestItemsQuery request)
- {
- var parentId = request.ParentId;
-
- var includeItemTypes = request.IncludeItemTypes;
- var limit = request.Limit ?? 10;
-
- var parentIds = string.IsNullOrEmpty(parentId)
- ? new string[] { }
- : new[] { parentId };
-
- if (parentIds.Length == 0)
- {
- parentIds = user.RootFolder.GetChildren(user, true)
- .OfType<Folder>()
- .Select(i => i.Id.ToString("N"))
- .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i))
- .ToArray();
- }
-
- if (parentIds.Length == 0)
- {
- return new List<BaseItem>();
- }
-
- var excludeItemTypes = includeItemTypes.Length == 0 ? new[]
- {
- typeof(Person).Name,
- typeof(Studio).Name,
- typeof(Year).Name,
- typeof(GameGenre).Name,
- typeof(MusicGenre).Name,
- typeof(Genre).Name
-
- } : new string[] { };
-
- return _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- IncludeItemTypes = includeItemTypes,
- SortOrder = SortOrder.Descending,
- SortBy = new[] { ItemSortBy.DateCreated },
- IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null,
- ExcludeItemTypes = excludeItemTypes,
- ExcludeLocationTypes = new[] { LocationType.Virtual },
- Limit = limit * 5,
- SourceTypes = parentIds.Length == 0 ? new[] { SourceType.Library } : new SourceType[] { },
- IsPlayed = request.IsPlayed
-
- }, parentIds);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
deleted file mode 100644
index 91b035a35..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Persistence;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- /// <summary>
- /// Class ArtistsPostScanTask
- /// </summary>
- public class ArtistsPostScanTask : ILibraryPostScanTask
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IItemRepository _itemRepo;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- public ArtistsPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return new ArtistsValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs
deleted file mode 100644
index 3dcdbeae9..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Persistence;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- /// <summary>
- /// Class ArtistsValidator
- /// </summary>
- public class ArtistsValidator
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
- private readonly IItemRepository _itemRepo;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="logger">The logger.</param>
- public ArtistsValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var names = _itemRepo.GetAllArtistNames();
-
- var numComplete = 0;
- var count = names.Count;
-
- foreach (var name in names)
- {
- try
- {
- var item = _libraryManager.GetArtist(name);
-
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- // Don't clutter the log
- break;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing {0}", ex, name);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
-
- progress.Report(percent);
- }
-
- progress.Report(100);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs
deleted file mode 100644
index f3891180e..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Persistence;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- /// <summary>
- /// Class GameGenresPostScanTask
- /// </summary>
- public class GameGenresPostScanTask : ILibraryPostScanTask
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IItemRepository _itemRepo;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GameGenresPostScanTask" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="logger">The logger.</param>
- public GameGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return new GameGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
deleted file mode 100644
index b06c0b3b9..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Persistence;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- class GameGenresValidator
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
- private readonly IItemRepository _itemRepo;
-
- public GameGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var names = _itemRepo.GetGameGenreNames();
-
- var numComplete = 0;
- var count = names.Count;
-
- foreach (var name in names)
- {
- try
- {
- var item = _libraryManager.GetGameGenre(name);
-
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- // Don't clutter the log
- break;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing {0}", ex, name);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
-
- progress.Report(percent);
- }
-
- progress.Report(100);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs
deleted file mode 100644
index ed2429769..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using MediaBrowser.Controller.Library;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- public class GenresPostScanTask : ILibraryPostScanTask
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IItemRepository _itemRepo;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="logger">The logger.</param>
- public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return new GenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
deleted file mode 100644
index f35bb5136..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Persistence;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- class GenresValidator
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- private readonly IItemRepository _itemRepo;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- public GenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var names = _itemRepo.GetGenreNames();
-
- var numComplete = 0;
- var count = names.Count;
-
- foreach (var name in names)
- {
- try
- {
- var item = _libraryManager.GetGenre(name);
-
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- // Don't clutter the log
- break;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing {0}", ex, name);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
-
- progress.Report(percent);
- }
-
- progress.Report(100);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
deleted file mode 100644
index 777532ff8..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Persistence;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- /// <summary>
- /// Class MusicGenresPostScanTask
- /// </summary>
- public class MusicGenresPostScanTask : ILibraryPostScanTask
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
- private readonly IItemRepository _itemRepo;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="logger">The logger.</param>
- public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return new MusicGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
deleted file mode 100644
index 2be99f106..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Persistence;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- class MusicGenresValidator
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
- private readonly IItemRepository _itemRepo;
-
- public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var names = _itemRepo.GetMusicGenreNames();
-
- var numComplete = 0;
- var count = names.Count;
-
- foreach (var name in names)
- {
- try
- {
- var item = _libraryManager.GetMusicGenre(name);
-
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- // Don't clutter the log
- break;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing {0}", ex, name);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
-
- progress.Report(percent);
- }
-
- progress.Report(100);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
deleted file mode 100644
index d9a7199be..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ /dev/null
@@ -1,172 +0,0 @@
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- /// <summary>
- /// Class PeopleValidator
- /// </summary>
- public class PeopleValidator
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="PeopleValidator" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="logger">The logger.</param>
- public PeopleValidator(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _config = config;
- _fileSystem = fileSystem;
- }
-
- private bool DownloadMetadata(PersonInfo i, PeopleMetadataOptions options)
- {
- if (i.IsType(PersonType.Actor))
- {
- return options.DownloadActorMetadata;
- }
- if (i.IsType(PersonType.Director))
- {
- return options.DownloadDirectorMetadata;
- }
- if (i.IsType(PersonType.Composer))
- {
- return options.DownloadComposerMetadata;
- }
- if (i.IsType(PersonType.Writer))
- {
- return options.DownloadWriterMetadata;
- }
- if (i.IsType(PersonType.Producer))
- {
- return options.DownloadProducerMetadata;
- }
- if (i.IsType(PersonType.GuestStar))
- {
- return options.DownloadGuestStarMetadata;
- }
-
- return options.DownloadOtherPeopleMetadata;
- }
-
- /// <summary>
- /// Validates the people.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
- {
- var innerProgress = new ActionableProgress<double>();
-
- innerProgress.RegisterAction(pct => progress.Report(pct * .15));
-
- var peopleOptions = _config.Configuration.PeopleMetadataOptions;
-
- var people = _libraryManager.GetPeople(new InternalPeopleQuery());
-
- var dict = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
-
- foreach (var person in people)
- {
- var isMetadataEnabled = DownloadMetadata(person, peopleOptions);
-
- bool currentValue;
- if (dict.TryGetValue(person.Name, out currentValue))
- {
- if (!currentValue && isMetadataEnabled)
- {
- dict[person.Name] = true;
- }
- }
- else
- {
- dict[person.Name] = isMetadataEnabled;
- }
- }
-
- var numComplete = 0;
-
- _logger.Debug("Will refresh {0} people", dict.Count);
-
- var numPeople = dict.Count;
-
- foreach (var person in dict)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- try
- {
- var item = _libraryManager.GetPerson(person.Key);
-
- var hasMetdata = !string.IsNullOrWhiteSpace(item.Overview);
- var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 30;
-
- var defaultMetadataRefreshMode = performFullRefresh
- ? MetadataRefreshMode.FullRefresh
- : MetadataRefreshMode.Default;
-
- var imageRefreshMode = performFullRefresh
- ? ImageRefreshMode.FullRefresh
- : ImageRefreshMode.Default;
-
- var options = new MetadataRefreshOptions(_fileSystem)
- {
- MetadataRefreshMode = person.Value ? defaultMetadataRefreshMode : MetadataRefreshMode.ValidationOnly,
- ImageRefreshMode = person.Value ? imageRefreshMode : ImageRefreshMode.ValidationOnly,
- ForceSave = performFullRefresh
- };
-
- await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error validating IBN entry {0}", ex, person);
- }
-
- // Update progress
- numComplete++;
- double percent = numComplete;
- percent /= numPeople;
-
- progress.Report(100 * percent);
- }
-
- progress.Report(100);
-
- _logger.Info("People validation complete");
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
deleted file mode 100644
index 77c6d5146..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Persistence;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- /// <summary>
- /// Class MusicGenresPostScanTask
- /// </summary>
- public class StudiosPostScanTask : ILibraryPostScanTask
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
-
- private readonly ILogger _logger;
- private readonly IItemRepository _itemRepo;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- public StudiosPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return new StudiosValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
deleted file mode 100644
index a19b8158a..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Persistence;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- class StudiosValidator
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
-
- private readonly IItemRepository _itemRepo;
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- public StudiosValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _itemRepo = itemRepo;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var names = _itemRepo.GetStudioNames();
-
- var numComplete = 0;
- var count = names.Count;
-
- foreach (var name in names)
- {
- try
- {
- var item = _libraryManager.GetStudio(name);
-
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- // Don't clutter the log
- break;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing {0}", ex, name);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
-
- progress.Report(percent);
- }
-
- progress.Report(100);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs
deleted file mode 100644
index 164b14223..000000000
--- a/MediaBrowser.Server.Implementations/Library/Validators/YearsPostScanTask.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Library.Validators
-{
- public class YearsPostScanTask : ILibraryPostScanTask
- {
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
-
- public YearsPostScanTask(ILibraryManager libraryManager, ILogger logger)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- }
-
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var yearNumber = 1900;
- var maxYear = DateTime.UtcNow.Year + 3;
- var count = maxYear - yearNumber + 1;
- var numComplete = 0;
-
- while (yearNumber < maxYear)
- {
- try
- {
- var year = _libraryManager.GetYear(yearNumber);
-
- await year.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- // Don't clutter the log
- break;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing year {0}", ex, yearNumber);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= count;
- percent *= 100;
-
- progress.Report(percent);
- yearNumber++;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs
deleted file mode 100644
index 23560b1aa..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.LiveTv
-{
- public class ChannelImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
- {
- private readonly ILiveTvManager _liveTvManager;
- private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
- private readonly IApplicationHost _appHost;
-
- public ChannelImageProvider(ILiveTvManager liveTvManager, IHttpClient httpClient, ILogger logger, IApplicationHost appHost)
- {
- _liveTvManager = liveTvManager;
- _httpClient = httpClient;
- _logger = logger;
- _appHost = appHost;
- }
-
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- return new[] { ImageType.Primary };
- }
-
- public async Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
- {
- var liveTvItem = (LiveTvChannel)item;
-
- var imageResponse = new DynamicImageResponse();
-
- var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, liveTvItem.ServiceName, StringComparison.OrdinalIgnoreCase));
-
- if (service != null && !item.HasImage(ImageType.Primary))
- {
- try
- {
- var response = await service.GetChannelImageAsync(liveTvItem.ExternalId, cancellationToken).ConfigureAwait(false);
-
- if (response != null)
- {
- imageResponse.HasImage = true;
- imageResponse.Stream = response.Stream;
- imageResponse.Format = response.Format;
- }
- }
- catch (NotImplementedException)
- {
- }
- }
-
- return imageResponse;
- }
-
- public string Name
- {
- get { return "Live TV Service Provider"; }
- }
-
- public bool Supports(IHasImages item)
- {
- return item is LiveTvChannel;
- }
-
- public int Order
- {
- get { return 0; }
- }
-
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
- {
- return GetSupportedImages(item).Any(i => !item.HasImage(i));
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
deleted file mode 100644
index 4137ab2cf..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/DirectRecorder.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- public class DirectRecorder : IRecorder
- {
- private readonly ILogger _logger;
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
-
- public DirectRecorder(ILogger logger, IHttpClient httpClient, IFileSystem fileSystem)
- {
- _logger = logger;
- _httpClient = httpClient;
- _fileSystem = fileSystem;
- }
-
- public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
- {
- return targetFile;
- }
-
- public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
- {
- var httpRequestOptions = new HttpRequestOptions()
- {
- Url = mediaSource.Path
- };
-
- httpRequestOptions.BufferContent = false;
-
- using (var response = await _httpClient.SendAsync(httpRequestOptions, "GET").ConfigureAwait(false))
- {
- _logger.Info("Opened recording stream from tuner provider");
-
- using (var output = _fileSystem.GetFileStream(targetFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
- {
- onStarted();
-
- _logger.Info("Copying recording stream to file {0}", targetFile);
-
- // The media source if infinite so we need to handle stopping ourselves
- var durationToken = new CancellationTokenSource(duration);
- cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
-
- await CopyUntilCancelled(response.Content, output, cancellationToken).ConfigureAwait(false);
- }
- }
-
- _logger.Info("Recording completed to file {0}", targetFile);
- }
-
- private const int BufferSize = 81920;
- public static Task CopyUntilCancelled(Stream source, Stream target, CancellationToken cancellationToken)
- {
- return CopyUntilCancelled(source, target, null, cancellationToken);
- }
- public static async Task CopyUntilCancelled(Stream source, Stream target, Action onStarted, CancellationToken cancellationToken)
- {
- while (!cancellationToken.IsCancellationRequested)
- {
- var bytesRead = await CopyToAsyncInternal(source, target, BufferSize, onStarted, cancellationToken).ConfigureAwait(false);
-
- onStarted = null;
-
- //var position = fs.Position;
- //_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
-
- if (bytesRead == 0)
- {
- await Task.Delay(100).ConfigureAwait(false);
- }
- }
- }
-
- private static async Task<int> CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, Action onStarted, CancellationToken cancellationToken)
- {
- byte[] buffer = new byte[bufferSize];
- int bytesRead;
- int totalBytesRead = 0;
-
- while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
- {
- await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
-
- totalBytesRead += bytesRead;
-
- if (onStarted != null)
- {
- onStarted();
- }
- onStarted = null;
- }
-
- return totalBytesRead;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
deleted file mode 100644
index 397e5fc9f..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ /dev/null
@@ -1,1976 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.FileOrganization;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Server.Implementations.FileOrganization;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Xml;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.FileOrganization;
-using Microsoft.Win32;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- public class EmbyTV : ILiveTvService, ISupportsDirectStreamProvider, ISupportsNewTimerIds, IDisposable
- {
- private readonly IServerApplicationHost _appHost;
- private readonly ILogger _logger;
- private readonly IHttpClient _httpClient;
- private readonly IServerConfigurationManager _config;
- private readonly IJsonSerializer _jsonSerializer;
-
- private readonly ItemDataProvider<SeriesTimerInfo> _seriesTimerProvider;
- private readonly TimerManager _timerProvider;
-
- private readonly LiveTvManager _liveTvManager;
- private readonly IFileSystem _fileSystem;
-
- private readonly ILibraryMonitor _libraryMonitor;
- private readonly ILibraryManager _libraryManager;
- private readonly IProviderManager _providerManager;
- private readonly IFileOrganizationService _organizationService;
- private readonly IMediaEncoder _mediaEncoder;
-
- public static EmbyTV Current;
-
- public event EventHandler DataSourceChanged;
- public event EventHandler<RecordingStatusChangedEventArgs> RecordingStatusChanged;
-
- private readonly ConcurrentDictionary<string, ActiveRecordingInfo> _activeRecordings =
- new ConcurrentDictionary<string, ActiveRecordingInfo>(StringComparer.OrdinalIgnoreCase);
-
- 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;
-
- _appHost = appHost;
- _logger = logger;
- _httpClient = httpClient;
- _config = config;
- _fileSystem = fileSystem;
- _libraryManager = libraryManager;
- _libraryMonitor = libraryMonitor;
- _providerManager = providerManager;
- _organizationService = organizationService;
- _mediaEncoder = mediaEncoder;
- _liveTvManager = (LiveTvManager)liveTvManager;
- _jsonSerializer = jsonSerializer;
-
- _seriesTimerProvider = new SeriesTimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "seriestimers"));
- _timerProvider = new TimerManager(fileSystem, jsonSerializer, _logger, Path.Combine(DataPath, "timers"), _logger);
- _timerProvider.TimerFired += _timerProvider_TimerFired;
-
- _config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated;
- }
-
- private void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
- {
- if (string.Equals(e.Key, "livetv", StringComparison.OrdinalIgnoreCase))
- {
- OnRecordingFoldersChanged();
- }
- }
-
- public void Start()
- {
- _timerProvider.RestartTimers();
-
- SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
- CreateRecordingFolders();
- }
-
- private void OnRecordingFoldersChanged()
- {
- CreateRecordingFolders();
- }
-
- internal void CreateRecordingFolders()
- {
- try
- {
- CreateRecordingFoldersInternal();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error creating recording folders", ex);
- }
- }
-
- internal void CreateRecordingFoldersInternal()
- {
- var recordingFolders = GetRecordingFolders();
-
- var virtualFolders = _libraryManager.GetVirtualFolders()
- .ToList();
-
- var allExistingPaths = virtualFolders.SelectMany(i => i.Locations).ToList();
-
- var pathsAdded = new List<string>();
-
- foreach (var recordingFolder in recordingFolders)
- {
- var pathsToCreate = recordingFolder.Locations
- .Where(i => !allExistingPaths.Contains(i, StringComparer.OrdinalIgnoreCase))
- .ToList();
-
- if (pathsToCreate.Count == 0)
- {
- continue;
- }
-
- var mediaPathInfos = pathsToCreate.Select(i => new MediaPathInfo { Path = i }).ToArray();
-
- var libraryOptions = new LibraryOptions
- {
- PathInfos = mediaPathInfos
- };
- try
- {
- _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, libraryOptions, true);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error creating virtual folder", ex);
- }
-
- pathsAdded.AddRange(pathsToCreate);
- }
-
- var config = GetConfiguration();
-
- var pathsToRemove = config.MediaLocationsCreated
- .Except(recordingFolders.SelectMany(i => i.Locations))
- .ToList();
-
- if (pathsAdded.Count > 0 || pathsToRemove.Count > 0)
- {
- pathsAdded.InsertRange(0, config.MediaLocationsCreated);
- config.MediaLocationsCreated = pathsAdded.Except(pathsToRemove).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
- _config.SaveConfiguration("livetv", config);
- }
-
- foreach (var path in pathsToRemove)
- {
- RemovePathFromLibrary(path);
- }
- }
-
- private void RemovePathFromLibrary(string path)
- {
- _logger.Debug("Removing path from library: {0}", path);
-
- var requiresRefresh = false;
- var virtualFolders = _libraryManager.GetVirtualFolders()
- .ToList();
-
- foreach (var virtualFolder in virtualFolders)
- {
- if (!virtualFolder.Locations.Contains(path, StringComparer.OrdinalIgnoreCase))
- {
- continue;
- }
-
- if (virtualFolder.Locations.Count == 1)
- {
- // remove entire virtual folder
- try
- {
- _libraryManager.RemoveVirtualFolder(virtualFolder.Name, true);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error removing virtual folder", ex);
- }
- }
- else
- {
- try
- {
- _libraryManager.RemoveMediaPath(virtualFolder.Name, path);
- requiresRefresh = true;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error removing media path", ex);
- }
- }
- }
-
- if (requiresRefresh)
- {
- _libraryManager.ValidateMediaLibrary(new Progress<Double>(), CancellationToken.None);
- }
- }
-
- void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
- {
- _logger.Info("Power mode changed to {0}", e.Mode);
-
- if (e.Mode == PowerModes.Resume)
- {
- _timerProvider.RestartTimers();
- }
- }
-
- public string Name
- {
- get { return "Emby"; }
- }
-
- public string DataPath
- {
- get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "livetv"); }
- }
-
- private string DefaultRecordingPath
- {
- get
- {
- return Path.Combine(DataPath, "recordings");
- }
- }
-
- private string RecordingPath
- {
- get
- {
- var path = GetConfiguration().RecordingPath;
-
- return string.IsNullOrWhiteSpace(path)
- ? DefaultRecordingPath
- : path;
- }
- }
-
- public string HomePageUrl
- {
- get { return "http://emby.media"; }
- }
-
- public async Task<LiveTvServiceStatusInfo> GetStatusInfoAsync(CancellationToken cancellationToken)
- {
- var status = new LiveTvServiceStatusInfo();
- var list = new List<LiveTvTunerInfo>();
-
- foreach (var hostInstance in _liveTvManager.TunerHosts)
- {
- try
- {
- var tuners = await hostInstance.GetTunerInfos(cancellationToken).ConfigureAwait(false);
-
- list.AddRange(tuners);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting tuners", ex);
- }
- }
-
- status.Tuners = list;
- status.Status = LiveTvServiceStatus.Ok;
- status.Version = _appHost.ApplicationVersion.ToString();
- status.IsVisible = false;
- return status;
- }
-
- public async Task RefreshSeriesTimers(CancellationToken cancellationToken, IProgress<double> progress)
- {
- var seriesTimers = await GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
-
- List<ChannelInfo> channels = null;
-
- foreach (var timer in seriesTimers)
- {
- List<ProgramInfo> epgData;
-
- if (timer.RecordAnyChannel)
- {
- if (channels == null)
- {
- channels = (await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false)).ToList();
- }
- var channelIds = channels.Select(i => i.Id).ToList();
- epgData = GetEpgDataForChannels(channelIds);
- }
- else
- {
- epgData = GetEpgDataForChannel(timer.ChannelId);
- }
- await UpdateTimersForSeriesTimer(epgData, timer, true).ConfigureAwait(false);
- }
-
- var timers = await GetTimersAsync(cancellationToken).ConfigureAwait(false);
-
- foreach (var timer in timers.ToList())
- {
- if (DateTime.UtcNow > timer.EndDate && !_activeRecordings.ContainsKey(timer.Id))
- {
- OnTimerOutOfDate(timer);
- }
- }
- }
-
- private void OnTimerOutOfDate(TimerInfo timer)
- {
- _timerProvider.Delete(timer);
- }
-
- private async Task<IEnumerable<ChannelInfo>> GetChannelsAsync(bool enableCache, CancellationToken cancellationToken)
- {
- var list = new List<ChannelInfo>();
-
- foreach (var hostInstance in _liveTvManager.TunerHosts)
- {
- try
- {
- var channels = await hostInstance.GetChannels(enableCache, cancellationToken).ConfigureAwait(false);
-
- list.AddRange(channels);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting channels", ex);
- }
- }
-
- foreach (var provider in GetListingProviders())
- {
- var enabledChannels = list
- .Where(i => IsListingProviderEnabledForTuner(provider.Item2, i.TunerHostId))
- .ToList();
-
- if (enabledChannels.Count > 0)
- {
- try
- {
- await provider.Item1.AddMetadata(provider.Item2, enabledChannels, cancellationToken).ConfigureAwait(false);
- }
- catch (NotSupportedException)
- {
-
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error adding metadata", ex);
- }
- }
- }
-
- return list;
- }
-
- public async Task<List<ChannelInfo>> GetChannelsForListingsProvider(ListingsProviderInfo listingsProvider, CancellationToken cancellationToken)
- {
- var list = new List<ChannelInfo>();
-
- foreach (var hostInstance in _liveTvManager.TunerHosts)
- {
- try
- {
- var channels = await hostInstance.GetChannels(false, cancellationToken).ConfigureAwait(false);
-
- list.AddRange(channels);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting channels", ex);
- }
- }
-
- return list
- .Where(i => IsListingProviderEnabledForTuner(listingsProvider, i.TunerHostId))
- .ToList();
- }
-
- public Task<IEnumerable<ChannelInfo>> GetChannelsAsync(CancellationToken cancellationToken)
- {
- return GetChannelsAsync(false, cancellationToken);
- }
-
- public Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken)
- {
- var timers = _timerProvider
- .GetAll()
- .Where(i => string.Equals(i.SeriesTimerId, timerId, StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- foreach (var timer in timers)
- {
- CancelTimerInternal(timer.Id, true);
- }
-
- var remove = _seriesTimerProvider.GetAll().FirstOrDefault(r => string.Equals(r.Id, timerId, StringComparison.OrdinalIgnoreCase));
- if (remove != null)
- {
- _seriesTimerProvider.Delete(remove);
- }
- return Task.FromResult(true);
- }
-
- private void CancelTimerInternal(string timerId, bool isSeriesCancelled)
- {
- var timer = _timerProvider.GetTimer(timerId);
- if (timer != null)
- {
- if (string.IsNullOrWhiteSpace(timer.SeriesTimerId) || isSeriesCancelled)
- {
- _timerProvider.Delete(timer);
- }
- else
- {
- timer.Status = RecordingStatus.Cancelled;
- _timerProvider.AddOrUpdate(timer, false);
- }
- }
- ActiveRecordingInfo activeRecordingInfo;
-
- if (_activeRecordings.TryGetValue(timerId, out activeRecordingInfo))
- {
- activeRecordingInfo.CancellationTokenSource.Cancel();
- }
- }
-
- public Task CancelTimerAsync(string timerId, CancellationToken cancellationToken)
- {
- CancelTimerInternal(timerId, false);
- return Task.FromResult(true);
- }
-
- public Task DeleteRecordingAsync(string recordingId, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task CreateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task CreateTimerAsync(TimerInfo info, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task<string> CreateTimer(TimerInfo timer, CancellationToken cancellationToken)
- {
- var existingTimer = _timerProvider.GetAll()
- .FirstOrDefault(i => string.Equals(timer.ProgramId, i.ProgramId, StringComparison.OrdinalIgnoreCase));
-
- if (existingTimer != null)
- {
- if (existingTimer.Status == RecordingStatus.Cancelled ||
- existingTimer.Status == RecordingStatus.Completed)
- {
- existingTimer.Status = RecordingStatus.New;
- _timerProvider.Update(existingTimer);
- return Task.FromResult(existingTimer.Id);
- }
- else
- {
- throw new ArgumentException("A scheduled recording already exists for this program.");
- }
- }
-
- timer.Id = Guid.NewGuid().ToString("N");
-
- ProgramInfo programInfo = null;
-
- if (!string.IsNullOrWhiteSpace(timer.ProgramId))
- {
- programInfo = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId);
- }
- if (programInfo == null)
- {
- _logger.Info("Unable to find program with Id {0}. Will search using start date", timer.ProgramId);
- programInfo = GetProgramInfoFromCache(timer.ChannelId, timer.StartDate);
- }
-
- if (programInfo != null)
- {
- RecordingHelper.CopyProgramInfoToTimerInfo(programInfo, timer);
- }
-
- _timerProvider.Add(timer);
- return Task.FromResult(timer.Id);
- }
-
- public async Task<string> CreateSeriesTimer(SeriesTimerInfo info, CancellationToken cancellationToken)
- {
- info.Id = Guid.NewGuid().ToString("N");
-
- List<ProgramInfo> epgData;
- if (info.RecordAnyChannel)
- {
- var channels = await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false);
- var channelIds = channels.Select(i => i.Id).ToList();
- epgData = GetEpgDataForChannels(channelIds);
- }
- else
- {
- epgData = GetEpgDataForChannel(info.ChannelId);
- }
-
- // populate info.seriesID
- var program = epgData.FirstOrDefault(i => string.Equals(i.Id, info.ProgramId, StringComparison.OrdinalIgnoreCase));
-
- if (program != null)
- {
- info.SeriesId = program.SeriesId;
- }
- else
- {
- throw new InvalidOperationException("SeriesId for program not found");
- }
-
- _seriesTimerProvider.Add(info);
- await UpdateTimersForSeriesTimer(epgData, info, false).ConfigureAwait(false);
-
- return info.Id;
- }
-
- public async Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken)
- {
- var instance = _seriesTimerProvider.GetAll().FirstOrDefault(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
-
- if (instance != null)
- {
- instance.ChannelId = info.ChannelId;
- instance.Days = info.Days;
- instance.EndDate = info.EndDate;
- instance.IsPostPaddingRequired = info.IsPostPaddingRequired;
- instance.IsPrePaddingRequired = info.IsPrePaddingRequired;
- instance.PostPaddingSeconds = info.PostPaddingSeconds;
- instance.PrePaddingSeconds = info.PrePaddingSeconds;
- instance.Priority = info.Priority;
- instance.RecordAnyChannel = info.RecordAnyChannel;
- instance.RecordAnyTime = info.RecordAnyTime;
- instance.RecordNewOnly = info.RecordNewOnly;
- instance.SkipEpisodesInLibrary = info.SkipEpisodesInLibrary;
- instance.KeepUpTo = info.KeepUpTo;
- instance.KeepUntil = info.KeepUntil;
- instance.StartDate = info.StartDate;
-
- _seriesTimerProvider.Update(instance);
-
- List<ProgramInfo> epgData;
- if (instance.RecordAnyChannel)
- {
- var channels = await GetChannelsAsync(true, CancellationToken.None).ConfigureAwait(false);
- var channelIds = channels.Select(i => i.Id).ToList();
- epgData = GetEpgDataForChannels(channelIds);
- }
- else
- {
- epgData = GetEpgDataForChannel(instance.ChannelId);
- }
-
- await UpdateTimersForSeriesTimer(epgData, instance, true).ConfigureAwait(false);
- }
- }
-
- public Task UpdateTimerAsync(TimerInfo updatedTimer, CancellationToken cancellationToken)
- {
- var existingTimer = _timerProvider.GetTimer(updatedTimer.Id);
-
- if (existingTimer == null)
- {
- throw new ResourceNotFoundException();
- }
-
- // Only update if not currently active
- ActiveRecordingInfo activeRecordingInfo;
- if (!_activeRecordings.TryGetValue(updatedTimer.Id, out activeRecordingInfo))
- {
- existingTimer.PrePaddingSeconds = updatedTimer.PrePaddingSeconds;
- existingTimer.PostPaddingSeconds = updatedTimer.PostPaddingSeconds;
- existingTimer.IsPostPaddingRequired = updatedTimer.IsPostPaddingRequired;
- existingTimer.IsPrePaddingRequired = updatedTimer.IsPrePaddingRequired;
- }
-
- return Task.FromResult(true);
- }
-
- private void UpdateExistingTimerWithNewMetadata(TimerInfo existingTimer, TimerInfo updatedTimer)
- {
- // Update the program info but retain the status
- existingTimer.ChannelId = updatedTimer.ChannelId;
- existingTimer.CommunityRating = updatedTimer.CommunityRating;
- existingTimer.EndDate = updatedTimer.EndDate;
- existingTimer.EpisodeNumber = updatedTimer.EpisodeNumber;
- existingTimer.EpisodeTitle = updatedTimer.EpisodeTitle;
- existingTimer.Genres = updatedTimer.Genres;
- existingTimer.HomePageUrl = updatedTimer.HomePageUrl;
- existingTimer.IsKids = updatedTimer.IsKids;
- existingTimer.IsNews = updatedTimer.IsNews;
- existingTimer.IsMovie = updatedTimer.IsMovie;
- existingTimer.IsProgramSeries = updatedTimer.IsProgramSeries;
- existingTimer.IsRepeat = updatedTimer.IsRepeat;
- existingTimer.IsSports = updatedTimer.IsSports;
- existingTimer.Name = updatedTimer.Name;
- existingTimer.OfficialRating = updatedTimer.OfficialRating;
- existingTimer.OriginalAirDate = updatedTimer.OriginalAirDate;
- existingTimer.Overview = updatedTimer.Overview;
- existingTimer.ProductionYear = updatedTimer.ProductionYear;
- existingTimer.ProgramId = updatedTimer.ProgramId;
- existingTimer.SeasonNumber = updatedTimer.SeasonNumber;
- existingTimer.ShortOverview = updatedTimer.ShortOverview;
- existingTimer.StartDate = updatedTimer.StartDate;
- }
-
- public Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task<ImageStream> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public Task<ImageStream> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- public async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken)
- {
- 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)
- {
- var excludeStatues = new List<RecordingStatus>
- {
- RecordingStatus.Completed
- };
-
- var timers = _timerProvider.GetAll()
- .Where(i => !excludeStatues.Contains(i.Status));
-
- return Task.FromResult(timers);
- }
-
- public Task<SeriesTimerInfo> GetNewTimerDefaultsAsync(CancellationToken cancellationToken, ProgramInfo program = null)
- {
- var config = GetConfiguration();
-
- var defaults = new SeriesTimerInfo()
- {
- PostPaddingSeconds = Math.Max(config.PostPaddingSeconds, 0),
- PrePaddingSeconds = Math.Max(config.PrePaddingSeconds, 0),
- RecordAnyChannel = false,
- RecordAnyTime = true,
- RecordNewOnly = true,
-
- Days = new List<DayOfWeek>
- {
- DayOfWeek.Sunday,
- DayOfWeek.Monday,
- DayOfWeek.Tuesday,
- DayOfWeek.Wednesday,
- DayOfWeek.Thursday,
- DayOfWeek.Friday,
- DayOfWeek.Saturday
- }
- };
-
- if (program != null)
- {
- defaults.SeriesId = program.SeriesId;
- defaults.ProgramId = program.Id;
- defaults.RecordNewOnly = !program.IsRepeat;
- }
-
- defaults.SkipEpisodesInLibrary = defaults.RecordNewOnly;
- defaults.KeepUntil = KeepUntil.UntilDeleted;
-
- return Task.FromResult(defaults);
- }
-
- public Task<IEnumerable<SeriesTimerInfo>> GetSeriesTimersAsync(CancellationToken cancellationToken)
- {
- return Task.FromResult((IEnumerable<SeriesTimerInfo>)_seriesTimerProvider.GetAll());
- }
-
- public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
- {
- try
- {
- return await GetProgramsAsyncInternal(channelId, startDateUtc, endDateUtc, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting programs", ex);
- return GetEpgDataForChannel(channelId).Where(i => i.StartDate <= endDateUtc && i.EndDate >= startDateUtc);
- }
- }
-
- private bool IsListingProviderEnabledForTuner(ListingsProviderInfo info, string tunerHostId)
- {
- if (info.EnableAllTuners)
- {
- return true;
- }
-
- if (string.IsNullOrWhiteSpace(tunerHostId))
- {
- throw new ArgumentNullException("tunerHostId");
- }
-
- return info.EnabledTuners.Contains(tunerHostId, StringComparer.OrdinalIgnoreCase);
- }
-
- private async Task<IEnumerable<ProgramInfo>> GetProgramsAsyncInternal(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
- {
- var channels = await GetChannelsAsync(true, cancellationToken).ConfigureAwait(false);
- var channel = channels.First(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase));
-
- foreach (var provider in GetListingProviders())
- {
- if (!IsListingProviderEnabledForTuner(provider.Item2, channel.TunerHostId))
- {
- _logger.Debug("Skipping getting programs for channel {0}-{1} from {2}-{3}, because it's not enabled for this tuner.", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty);
- continue;
- }
-
- _logger.Debug("Getting programs for channel {0}-{1} from {2}-{3}", channel.Number, channel.Name, provider.Item1.Name, provider.Item2.ListingsId ?? string.Empty);
-
- var channelMappings = GetChannelMappings(provider.Item2);
- var channelNumber = channel.Number;
- string mappedChannelNumber;
- if (channelMappings.TryGetValue(channelNumber, out mappedChannelNumber))
- {
- _logger.Debug("Found mapped channel on provider {0}. Tuner channel number: {1}, Mapped channel number: {2}", provider.Item1.Name, channelNumber, mappedChannelNumber);
- channelNumber = mappedChannelNumber;
- }
-
- var programs = await provider.Item1.GetProgramsAsync(provider.Item2, channelNumber, channel.Name, startDateUtc, endDateUtc, cancellationToken)
- .ConfigureAwait(false);
-
- var list = programs.ToList();
-
- // Replace the value that came from the provider with a normalized value
- foreach (var program in list)
- {
- program.ChannelId = channelId;
- }
-
- if (list.Count > 0)
- {
- SaveEpgDataForChannel(channelId, list);
-
- return list;
- }
- }
-
- return new List<ProgramInfo>();
- }
-
- private Dictionary<string, string> GetChannelMappings(ListingsProviderInfo info)
- {
- var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- foreach (var mapping in info.ChannelMappings)
- {
- dict[mapping.Name] = mapping.Value;
- }
-
- return dict;
- }
-
- private List<Tuple<IListingsProvider, ListingsProviderInfo>> GetListingProviders()
- {
- return GetConfiguration().ListingProviders
- .Select(i =>
- {
- var provider = _liveTvManager.ListingProviders.FirstOrDefault(l => string.Equals(l.Type, i.Type, StringComparison.OrdinalIgnoreCase));
-
- return provider == null ? null : new Tuple<IListingsProvider, ListingsProviderInfo>(provider, i);
- })
- .Where(i => i != null)
- .ToList();
- }
-
- public Task<MediaSourceInfo> GetRecordingStream(string recordingId, string streamId, CancellationToken cancellationToken)
- {
- throw new NotImplementedException();
- }
-
- private readonly SemaphoreSlim _liveStreamsSemaphore = new SemaphoreSlim(1, 1);
- private readonly List<LiveStream> _liveStreams = new List<LiveStream>();
-
- public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
- {
- var result = await GetChannelStreamWithDirectStreamProvider(channelId, streamId, cancellationToken).ConfigureAwait(false);
-
- return result.Item1;
- }
-
- public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStreamWithDirectStreamProvider(string channelId, string streamId, CancellationToken cancellationToken)
- {
- var result = await GetChannelStreamInternal(channelId, streamId, cancellationToken).ConfigureAwait(false);
-
- return new Tuple<MediaSourceInfo, IDirectStreamProvider>(result.Item2, result.Item1 as IDirectStreamProvider);
- }
-
- private MediaSourceInfo CloneMediaSource(MediaSourceInfo mediaSource, bool enableStreamSharing)
- {
- var json = _jsonSerializer.SerializeToString(mediaSource);
- mediaSource = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
-
- mediaSource.Id = Guid.NewGuid().ToString("N") + "_" + mediaSource.Id;
-
- //if (mediaSource.DateLiveStreamOpened.HasValue && enableStreamSharing)
- //{
- // var ticks = (DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value).Ticks - TimeSpan.FromSeconds(10).Ticks;
- // ticks = Math.Max(0, ticks);
- // mediaSource.Path += "?t=" + ticks.ToString(CultureInfo.InvariantCulture) + "&s=" + mediaSource.DateLiveStreamOpened.Value.Ticks.ToString(CultureInfo.InvariantCulture);
- //}
-
- return mediaSource;
- }
-
- public async Task<LiveStream> GetLiveStream(string uniqueId)
- {
- await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false);
-
- try
- {
- return _liveStreams
- .FirstOrDefault(i => string.Equals(i.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase));
- }
- finally
- {
- _liveStreamsSemaphore.Release();
- }
-
- }
-
- private async Task<Tuple<LiveStream, MediaSourceInfo, ITunerHost>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken)
- {
- _logger.Info("Streaming Channel " + channelId);
-
- await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- var result = _liveStreams.FirstOrDefault(i => string.Equals(i.OriginalStreamId, streamId, StringComparison.OrdinalIgnoreCase));
-
- if (result != null && result.EnableStreamSharing)
- {
- var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.EnableStreamSharing);
- result.SharedStreamIds.Add(openedMediaSource.Id);
- _liveStreamsSemaphore.Release();
-
- _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
-
- return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost);
- }
-
- try
- {
- foreach (var hostInstance in _liveTvManager.TunerHosts)
- {
- try
- {
- result = await hostInstance.GetChannelStream(channelId, streamId, cancellationToken).ConfigureAwait(false);
-
- var openedMediaSource = CloneMediaSource(result.OpenedMediaSource, result.EnableStreamSharing);
-
- result.SharedStreamIds.Add(openedMediaSource.Id);
- _liveStreams.Add(result);
-
- result.TunerHost = hostInstance;
- result.OriginalStreamId = streamId;
-
- _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}",
- streamId, openedMediaSource.Id, openedMediaSource.LiveStreamId);
-
- return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, hostInstance);
- }
- catch (FileNotFoundException)
- {
- }
- catch (OperationCanceledException)
- {
- }
- }
- }
- finally
- {
- _liveStreamsSemaphore.Release();
- }
-
- throw new ApplicationException("Tuner not found.");
- }
-
- public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
- {
- foreach (var hostInstance in _liveTvManager.TunerHosts)
- {
- try
- {
- var sources = await hostInstance.GetChannelStreamMediaSources(channelId, cancellationToken).ConfigureAwait(false);
-
- if (sources.Count > 0)
- {
- return sources;
- }
- }
- catch (NotImplementedException)
- {
-
- }
- }
-
- throw new NotImplementedException();
- }
-
- public async Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(string recordingId, CancellationToken cancellationToken)
- {
- ActiveRecordingInfo info;
-
- recordingId = recordingId.Replace("recording", string.Empty);
-
- if (_activeRecordings.TryGetValue(recordingId, out info))
- {
- var stream = new MediaSourceInfo
- {
- Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/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
- };
-
- var isAudio = false;
- await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
-
- return new List<MediaSourceInfo>
- {
- stream
- };
- }
-
- throw new FileNotFoundException();
- }
-
- public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
- {
- // Ignore the consumer id
- //id = id.Substring(id.IndexOf('_') + 1);
-
- await _liveStreamsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var stream = _liveStreams.FirstOrDefault(i => i.SharedStreamIds.Contains(id));
- if (stream != null)
- {
- stream.SharedStreamIds.Remove(id);
-
- _logger.Info("Live stream {0} consumer count is now {1}", id, stream.ConsumerCount);
-
- if (stream.ConsumerCount <= 0)
- {
- _liveStreams.Remove(stream);
-
- _logger.Info("Closing live stream {0}", id);
-
- await stream.Close().ConfigureAwait(false);
- _logger.Info("Live stream {0} closed successfully", id);
- }
- }
- else
- {
- _logger.Warn("Live stream not found: {0}, unable to close", id);
- }
- }
- catch (OperationCanceledException)
- {
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing live stream", ex);
- }
- finally
- {
- _liveStreamsSemaphore.Release();
- }
- }
-
- public Task RecordLiveStream(string id, CancellationToken cancellationToken)
- {
- return Task.FromResult(0);
- }
-
- public Task ResetTuner(string id, CancellationToken cancellationToken)
- {
- return Task.FromResult(0);
- }
-
- async void _timerProvider_TimerFired(object sender, GenericEventArgs<TimerInfo> e)
- {
- var timer = e.Argument;
-
- _logger.Info("Recording timer fired.");
-
- try
- {
- var recordingEndDate = timer.EndDate.AddSeconds(timer.PostPaddingSeconds);
-
- if (recordingEndDate <= DateTime.UtcNow)
- {
- _logger.Warn("Recording timer fired for updatedTimer {0}, Id: {1}, but the program has already ended.", timer.Name, timer.Id);
- OnTimerOutOfDate(timer);
- return;
- }
-
- var activeRecordingInfo = new ActiveRecordingInfo
- {
- CancellationTokenSource = new CancellationTokenSource(),
- Timer = timer
- };
-
- if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
- {
- await RecordStream(timer, recordingEndDate, activeRecordingInfo, activeRecordingInfo.CancellationTokenSource.Token).ConfigureAwait(false);
- }
- else
- {
- _logger.Info("Skipping RecordStream because it's already in progress.");
- }
- }
- catch (OperationCanceledException)
- {
-
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error recording stream", ex);
- }
- }
-
- private string GetRecordingPath(TimerInfo timer, out string seriesPath)
- {
- var recordPath = RecordingPath;
- var config = GetConfiguration();
- seriesPath = null;
-
- if (timer.IsProgramSeries)
- {
- var customRecordingPath = config.SeriesRecordingPath;
- var allowSubfolder = true;
- if (!string.IsNullOrWhiteSpace(customRecordingPath))
- {
- allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase);
- recordPath = customRecordingPath;
- }
-
- if (allowSubfolder && config.EnableRecordingSubfolders)
- {
- recordPath = Path.Combine(recordPath, "Series");
- }
-
- var folderName = _fileSystem.GetValidFilename(timer.Name).Trim();
-
- // Can't use the year here in the folder name because it is the year of the episode, not the series.
- recordPath = Path.Combine(recordPath, folderName);
-
- seriesPath = recordPath;
-
- if (timer.SeasonNumber.HasValue)
- {
- folderName = string.Format("Season {0}", timer.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture));
- recordPath = Path.Combine(recordPath, folderName);
- }
- }
- else if (timer.IsMovie)
- {
- var customRecordingPath = config.MovieRecordingPath;
- var allowSubfolder = true;
- if (!string.IsNullOrWhiteSpace(customRecordingPath))
- {
- allowSubfolder = string.Equals(customRecordingPath, recordPath, StringComparison.OrdinalIgnoreCase);
- recordPath = customRecordingPath;
- }
-
- if (allowSubfolder && config.EnableRecordingSubfolders)
- {
- recordPath = Path.Combine(recordPath, "Movies");
- }
-
- var folderName = _fileSystem.GetValidFilename(timer.Name).Trim();
- if (timer.ProductionYear.HasValue)
- {
- folderName += " (" + timer.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")";
- }
- recordPath = Path.Combine(recordPath, folderName);
- }
- else if (timer.IsKids)
- {
- if (config.EnableRecordingSubfolders)
- {
- recordPath = Path.Combine(recordPath, "Kids");
- }
-
- var folderName = _fileSystem.GetValidFilename(timer.Name).Trim();
- if (timer.ProductionYear.HasValue)
- {
- folderName += " (" + timer.ProductionYear.Value.ToString(CultureInfo.InvariantCulture) + ")";
- }
- recordPath = Path.Combine(recordPath, folderName);
- }
- else if (timer.IsSports)
- {
- if (config.EnableRecordingSubfolders)
- {
- recordPath = Path.Combine(recordPath, "Sports");
- }
- recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim());
- }
- else
- {
- if (config.EnableRecordingSubfolders)
- {
- recordPath = Path.Combine(recordPath, "Other");
- }
- recordPath = Path.Combine(recordPath, _fileSystem.GetValidFilename(timer.Name).Trim());
- }
-
- var recordingFileName = _fileSystem.GetValidFilename(RecordingHelper.GetRecordingName(timer)).Trim() + ".ts";
-
- return Path.Combine(recordPath, recordingFileName);
- }
-
- private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate,
- ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
- {
- if (timer == null)
- {
- throw new ArgumentNullException("timer");
- }
-
- ProgramInfo programInfo = null;
-
- if (!string.IsNullOrWhiteSpace(timer.ProgramId))
- {
- programInfo = GetProgramInfoFromCache(timer.ChannelId, timer.ProgramId);
- }
- if (programInfo == null)
- {
- _logger.Info("Unable to find program with Id {0}. Will search using start date", timer.ProgramId);
- programInfo = GetProgramInfoFromCache(timer.ChannelId, timer.StartDate);
- }
-
- if (programInfo != null)
- {
- RecordingHelper.CopyProgramInfoToTimerInfo(programInfo, timer);
- activeRecordingInfo.Program = programInfo;
- }
-
- string seriesPath = null;
- var recordPath = GetRecordingPath(timer, out seriesPath);
- var recordingStatus = RecordingStatus.New;
-
- string liveStreamId = null;
-
- OnRecordingStatusChanged();
-
- try
- {
- var allMediaSources = await GetChannelStreamMediaSources(timer.ChannelId, CancellationToken.None).ConfigureAwait(false);
-
- var liveStreamInfo = await GetChannelStreamInternal(timer.ChannelId, allMediaSources[0].Id, CancellationToken.None)
- .ConfigureAwait(false);
-
- var mediaStreamInfo = liveStreamInfo.Item2;
- liveStreamId = mediaStreamInfo.Id;
-
- // HDHR doesn't seem to release the tuner right away after first probing with ffmpeg
- //await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
-
- var recorder = await GetRecorder().ConfigureAwait(false);
-
- recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
- recordPath = EnsureFileUnique(recordPath, timer.Id);
-
- _libraryManager.RegisterIgnoredPath(recordPath);
- _libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
- _fileSystem.CreateDirectory(Path.GetDirectoryName(recordPath));
- activeRecordingInfo.Path = recordPath;
-
- var duration = recordingEndDate - DateTime.UtcNow;
-
- _logger.Info("Beginning recording. Will record for {0} minutes.",
- duration.TotalMinutes.ToString(CultureInfo.InvariantCulture));
-
- _logger.Info("Writing file to path: " + recordPath);
- _logger.Info("Opening recording stream from tuner provider");
-
- Action onStarted = () =>
- {
- timer.Status = RecordingStatus.InProgress;
- _timerProvider.AddOrUpdate(timer, false);
-
- SaveNfo(timer, recordPath, seriesPath);
- EnforceKeepUpTo(timer);
- };
-
- await recorder.Record(mediaStreamInfo, recordPath, duration, onStarted, cancellationToken)
- .ConfigureAwait(false);
-
- recordingStatus = RecordingStatus.Completed;
- _logger.Info("Recording completed: {0}", recordPath);
- }
- catch (OperationCanceledException)
- {
- _logger.Info("Recording stopped: {0}", recordPath);
- recordingStatus = RecordingStatus.Completed;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error recording to {0}", ex, recordPath);
- recordingStatus = RecordingStatus.Error;
- }
-
- if (!string.IsNullOrWhiteSpace(liveStreamId))
- {
- try
- {
- await CloseLiveStream(liveStreamId, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing live stream", ex);
- }
- }
-
- _libraryManager.UnRegisterIgnoredPath(recordPath);
- _libraryMonitor.ReportFileSystemChangeComplete(recordPath, true);
-
- ActiveRecordingInfo removed;
- _activeRecordings.TryRemove(timer.Id, out removed);
-
- if (recordingStatus != RecordingStatus.Completed && DateTime.UtcNow < timer.EndDate)
- {
- const int retryIntervalSeconds = 60;
- _logger.Info("Retrying recording in {0} seconds.", retryIntervalSeconds);
-
- timer.Status = RecordingStatus.New;
- timer.StartDate = DateTime.UtcNow.AddSeconds(retryIntervalSeconds);
- _timerProvider.AddOrUpdate(timer);
- }
- else if (File.Exists(recordPath))
- {
- timer.RecordingPath = recordPath;
- timer.Status = RecordingStatus.Completed;
- _timerProvider.AddOrUpdate(timer, false);
- OnSuccessfulRecording(timer, recordPath);
- }
- else
- {
- _timerProvider.Delete(timer);
- }
-
- OnRecordingStatusChanged();
- }
-
- private void OnRecordingStatusChanged()
- {
- EventHelper.FireEventIfNotNull(RecordingStatusChanged, this, new RecordingStatusChangedEventArgs
- {
-
- }, _logger);
- }
-
- private async void EnforceKeepUpTo(TimerInfo timer)
- {
- if (string.IsNullOrWhiteSpace(timer.SeriesTimerId))
- {
- return;
- }
-
- var seriesTimerId = timer.SeriesTimerId;
- var seriesTimer = _seriesTimerProvider.GetAll().FirstOrDefault(i => string.Equals(i.Id, seriesTimerId, StringComparison.OrdinalIgnoreCase));
-
- if (seriesTimer == null || seriesTimer.KeepUpTo <= 1)
- {
- return;
- }
-
- if (_disposed)
- {
- return;
- }
-
- await _recordingDeleteSemaphore.WaitAsync().ConfigureAwait(false);
-
- try
- {
- if (_disposed)
- {
- return;
- }
-
- var timersToDelete = _timerProvider.GetAll()
- .Where(i => i.Status == RecordingStatus.Completed && !string.IsNullOrWhiteSpace(i.RecordingPath))
- .Where(i => string.Equals(i.SeriesTimerId, seriesTimerId, StringComparison.OrdinalIgnoreCase))
- .OrderByDescending(i => i.EndDate)
- .Where(i => File.Exists(i.RecordingPath))
- .Skip(seriesTimer.KeepUpTo - 1)
- .ToList();
-
- await DeleteLibraryItemsForTimers(timersToDelete).ConfigureAwait(false);
- }
- finally
- {
- _recordingDeleteSemaphore.Release();
- }
- }
-
- private readonly SemaphoreSlim _recordingDeleteSemaphore = new SemaphoreSlim(1, 1);
- private async Task DeleteLibraryItemsForTimers(List<TimerInfo> timers)
- {
- foreach (var timer in timers)
- {
- if (_disposed)
- {
- return;
- }
-
- try
- {
- await DeleteLibraryItemForTimer(timer).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting recording", ex);
- }
- }
- }
-
- private async Task DeleteLibraryItemForTimer(TimerInfo timer)
- {
- var libraryItem = _libraryManager.FindByPath(timer.RecordingPath, false);
-
- if (libraryItem != null)
- {
- await _libraryManager.DeleteItem(libraryItem, new DeleteOptions
- {
- DeleteFileLocation = true
- });
- }
- else
- {
- try
- {
- File.Delete(timer.RecordingPath);
- }
- catch (DirectoryNotFoundException)
- {
-
- }
- catch (FileNotFoundException)
- {
-
- }
- }
-
- _timerProvider.Delete(timer);
- }
-
- private string EnsureFileUnique(string path, string timerId)
- {
- var originalPath = path;
- var index = 1;
-
- while (FileExists(path, timerId))
- {
- 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 bool FileExists(string path, string timerId)
- {
- if (_fileSystem.FileExists(path))
- {
- return true;
- }
-
- var hasRecordingAtPath = _activeRecordings
- .Values
- .ToList()
- .Any(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Timer.Id, timerId, StringComparison.OrdinalIgnoreCase));
-
- if (hasRecordingAtPath)
- {
- return true;
- }
- return false;
- }
-
- private async Task<IRecorder> GetRecorder()
- {
- var config = GetConfiguration();
-
- if (config.EnableRecordingEncoding)
- {
- var regInfo = await _liveTvManager.GetRegistrationInfo("embytvrecordingconversion").ConfigureAwait(false);
-
- if (regInfo.IsValid)
- {
- return new EncodedRecorder(_logger, _fileSystem, _mediaEncoder, _config.ApplicationPaths, _jsonSerializer, config, _httpClient);
- }
- }
-
- return new DirectRecorder(_logger, _httpClient, _fileSystem);
- }
-
- private async void OnSuccessfulRecording(TimerInfo timer, string path)
- {
- if (timer.IsProgramSeries && GetConfiguration().EnableAutoOrganize)
- {
- try
- {
- // this is to account for the library monitor holding a lock for additional time after the change is complete.
- // ideally this shouldn't be hard-coded
- await Task.Delay(30000).ConfigureAwait(false);
-
- var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager);
-
- var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false);
-
- if (result.Status == FileSortingStatus.Success)
- {
- return;
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error processing new recording", ex);
- }
- }
- }
-
- private void SaveNfo(TimerInfo timer, string recordingPath, string seriesPath)
- {
- try
- {
- if (timer.IsProgramSeries)
- {
- SaveSeriesNfo(timer, recordingPath, seriesPath);
- }
- else if (!timer.IsMovie || timer.IsSports || timer.IsNews)
- {
- SaveVideoNfo(timer, recordingPath);
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving nfo", ex);
- }
- }
-
- private void SaveSeriesNfo(TimerInfo timer, string recordingPath, string seriesPath)
- {
- var nfoPath = Path.Combine(seriesPath, "tvshow.nfo");
-
- if (File.Exists(nfoPath))
- {
- return;
- }
-
- using (var stream = _fileSystem.GetFileStream(nfoPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
- {
- var settings = new XmlWriterSettings
- {
- Indent = true,
- Encoding = Encoding.UTF8,
- CloseOutput = false
- };
-
- using (XmlWriter writer = XmlWriter.Create(stream, settings))
- {
- writer.WriteStartDocument(true);
- writer.WriteStartElement("tvshow");
-
- if (!string.IsNullOrWhiteSpace(timer.Name))
- {
- writer.WriteElementString("title", timer.Name);
- }
-
- writer.WriteEndElement();
- writer.WriteEndDocument();
- }
- }
- }
-
- public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
- private void SaveVideoNfo(TimerInfo timer, string recordingPath)
- {
- var nfoPath = Path.ChangeExtension(recordingPath, ".nfo");
-
- if (File.Exists(nfoPath))
- {
- return;
- }
-
- using (var stream = _fileSystem.GetFileStream(nfoPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
- {
- var settings = new XmlWriterSettings
- {
- Indent = true,
- Encoding = Encoding.UTF8,
- CloseOutput = false
- };
-
- using (XmlWriter writer = XmlWriter.Create(stream, settings))
- {
- writer.WriteStartDocument(true);
- writer.WriteStartElement("movie");
-
- if (!string.IsNullOrWhiteSpace(timer.Name))
- {
- writer.WriteElementString("title", timer.Name);
- }
-
- writer.WriteElementString("dateadded", DateTime.UtcNow.ToLocalTime().ToString(DateAddedFormat));
-
- if (timer.ProductionYear.HasValue)
- {
- writer.WriteElementString("year", timer.ProductionYear.Value.ToString(CultureInfo.InvariantCulture));
- }
- if (!string.IsNullOrEmpty(timer.OfficialRating))
- {
- writer.WriteElementString("mpaa", timer.OfficialRating);
- }
-
- var overview = (timer.Overview ?? string.Empty)
- .StripHtml()
- .Replace("&quot;", "'");
-
- writer.WriteElementString("plot", overview);
- writer.WriteElementString("lockdata", true.ToString().ToLower());
-
- if (timer.CommunityRating.HasValue)
- {
- writer.WriteElementString("rating", timer.CommunityRating.Value.ToString(CultureInfo.InvariantCulture));
- }
-
- if (timer.IsSports)
- {
- AddGenre(timer.Genres, "Sports");
- }
- if (timer.IsKids)
- {
- AddGenre(timer.Genres, "Kids");
- AddGenre(timer.Genres, "Children");
- }
- if (timer.IsNews)
- {
- AddGenre(timer.Genres, "News");
- }
-
- foreach (var genre in timer.Genres)
- {
- writer.WriteElementString("genre", genre);
- }
-
- if (!string.IsNullOrWhiteSpace(timer.ShortOverview))
- {
- writer.WriteElementString("outline", timer.ShortOverview);
- }
-
- if (!string.IsNullOrWhiteSpace(timer.HomePageUrl))
- {
- writer.WriteElementString("website", timer.HomePageUrl);
- }
-
- writer.WriteEndElement();
- writer.WriteEndDocument();
- }
- }
- }
-
- private void AddGenre(List<string> genres, string genre)
- {
- if (!genres.Contains(genre, StringComparer.OrdinalIgnoreCase))
- {
- genres.Add(genre);
- }
- }
-
- private ProgramInfo GetProgramInfoFromCache(string channelId, string programId)
- {
- var epgData = GetEpgDataForChannel(channelId);
- return epgData.FirstOrDefault(p => string.Equals(p.Id, programId, StringComparison.OrdinalIgnoreCase));
- }
-
- private ProgramInfo GetProgramInfoFromCache(string channelId, DateTime startDateUtc)
- {
- var epgData = GetEpgDataForChannel(channelId);
- var startDateTicks = startDateUtc.Ticks;
- // Find the first program that starts within 3 minutes
- return epgData.FirstOrDefault(p => Math.Abs(startDateTicks - p.StartDate.Ticks) <= TimeSpan.FromMinutes(3).Ticks);
- }
-
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration<LiveTvOptions>("livetv");
- }
-
- private bool ShouldCancelTimerForSeriesTimer(SeriesTimerInfo seriesTimer, TimerInfo timer)
- {
- if (!seriesTimer.RecordAnyTime)
- {
- if (Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(5).Ticks)
- {
- return true;
- }
-
- if (!seriesTimer.Days.Contains(timer.StartDate.ToLocalTime().DayOfWeek))
- {
- return true;
- }
- }
-
- if (seriesTimer.RecordNewOnly && timer.IsRepeat)
- {
- return true;
- }
-
- if (!seriesTimer.RecordAnyChannel && !string.Equals(timer.ChannelId, seriesTimer.ChannelId, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
-
- return seriesTimer.SkipEpisodesInLibrary && IsProgramAlreadyInLibrary(timer);
- }
-
- private async Task UpdateTimersForSeriesTimer(List<ProgramInfo> epgData, SeriesTimerInfo seriesTimer, bool deleteInvalidTimers)
- {
- var allTimers = GetTimersForSeries(seriesTimer, epgData)
- .ToList();
-
- var registration = await _liveTvManager.GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
-
- if (registration.IsValid)
- {
- foreach (var timer in allTimers)
- {
- var existingTimer = _timerProvider.GetTimer(timer.Id);
-
- if (existingTimer == null)
- {
- if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
- {
- timer.Status = RecordingStatus.Cancelled;
- }
- _timerProvider.Add(timer);
- }
- else
- {
- // Only update if not currently active
- ActiveRecordingInfo activeRecordingInfo;
- if (!_activeRecordings.TryGetValue(timer.Id, out activeRecordingInfo))
- {
- UpdateExistingTimerWithNewMetadata(existingTimer, timer);
-
- if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
- {
- existingTimer.Status = RecordingStatus.Cancelled;
- }
-
- existingTimer.SeriesTimerId = seriesTimer.Id;
- _timerProvider.Update(existingTimer);
- }
- }
- }
- }
-
- if (deleteInvalidTimers)
- {
- var allTimerIds = allTimers
- .Select(i => i.Id)
- .ToList();
-
- var deleteStatuses = new List<RecordingStatus>
- {
- RecordingStatus.New
- };
-
- var deletes = _timerProvider.GetAll()
- .Where(i => string.Equals(i.SeriesTimerId, seriesTimer.Id, StringComparison.OrdinalIgnoreCase))
- .Where(i => !allTimerIds.Contains(i.Id, StringComparer.OrdinalIgnoreCase) && i.StartDate > DateTime.UtcNow)
- .Where(i => deleteStatuses.Contains(i.Status))
- .ToList();
-
- foreach (var timer in deletes)
- {
- CancelTimerInternal(timer.Id, false);
- }
- }
- }
-
- private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer,
- IEnumerable<ProgramInfo> allPrograms)
- {
- if (seriesTimer == null)
- {
- throw new ArgumentNullException("seriesTimer");
- }
- if (allPrograms == null)
- {
- throw new ArgumentNullException("allPrograms");
- }
-
- // Exclude programs that have already ended
- allPrograms = allPrograms.Where(i => i.EndDate > DateTime.UtcNow);
-
- allPrograms = GetProgramsForSeries(seriesTimer, allPrograms);
-
- return allPrograms.Select(i => RecordingHelper.CreateTimer(i, seriesTimer));
- }
-
- private bool IsProgramAlreadyInLibrary(TimerInfo program)
- {
- if ((program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue) || !string.IsNullOrWhiteSpace(program.EpisodeTitle))
- {
- var seriesIds = _libraryManager.GetItemIds(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Name = program.Name
-
- }).Select(i => i.ToString("N")).ToArray();
-
- if (seriesIds.Length == 0)
- {
- return false;
- }
-
- if (program.EpisodeNumber.HasValue && program.SeasonNumber.HasValue)
- {
- var result = _libraryManager.GetItemsResult(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Episode).Name },
- ParentIndexNumber = program.SeasonNumber.Value,
- IndexNumber = program.EpisodeNumber.Value,
- AncestorIds = seriesIds,
- IsVirtualItem = false
- });
-
- if (result.TotalRecordCount > 0)
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- private IEnumerable<ProgramInfo> GetProgramsForSeries(SeriesTimerInfo seriesTimer, IEnumerable<ProgramInfo> allPrograms)
- {
- if (string.IsNullOrWhiteSpace(seriesTimer.SeriesId))
- {
- _logger.Error("seriesTimer.SeriesId is null. Cannot find programs for series");
- return new List<ProgramInfo>();
- }
-
- return allPrograms.Where(i => string.Equals(i.SeriesId, seriesTimer.SeriesId, StringComparison.OrdinalIgnoreCase));
- }
-
- private string GetChannelEpgCachePath(string channelId)
- {
- return Path.Combine(_config.CommonApplicationPaths.CachePath, "embytvepg", channelId + ".json");
- }
-
- private readonly object _epgLock = new object();
- private void SaveEpgDataForChannel(string channelId, List<ProgramInfo> epgData)
- {
- var path = GetChannelEpgCachePath(channelId);
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
- lock (_epgLock)
- {
- _jsonSerializer.SerializeToFile(epgData, path);
- }
- }
- private List<ProgramInfo> GetEpgDataForChannel(string channelId)
- {
- try
- {
- lock (_epgLock)
- {
- return _jsonSerializer.DeserializeFromFile<List<ProgramInfo>>(GetChannelEpgCachePath(channelId));
- }
- }
- catch
- {
- return new List<ProgramInfo>();
- }
- }
- private List<ProgramInfo> GetEpgDataForChannels(List<string> channelIds)
- {
- return channelIds.SelectMany(GetEpgDataForChannel).ToList();
- }
-
- private bool _disposed;
- public void Dispose()
- {
- _disposed = true;
- foreach (var pair in _activeRecordings.ToList())
- {
- pair.Value.CancellationTokenSource.Cancel();
- }
- }
-
- public List<VirtualFolderInfo> GetRecordingFolders()
- {
- var list = new List<VirtualFolderInfo>();
-
- var defaultFolder = RecordingPath;
- var defaultName = "Recordings";
-
- if (Directory.Exists(defaultFolder))
- {
- list.Add(new VirtualFolderInfo
- {
- Locations = new List<string> { defaultFolder },
- Name = defaultName
- });
- }
-
- var customPath = GetConfiguration().MovieRecordingPath;
- if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath))
- {
- list.Add(new VirtualFolderInfo
- {
- Locations = new List<string> { customPath },
- Name = "Recorded Movies",
- CollectionType = CollectionType.Movies
- });
- }
-
- customPath = GetConfiguration().SeriesRecordingPath;
- if ((!string.IsNullOrWhiteSpace(customPath) && !string.Equals(customPath, defaultFolder, StringComparison.OrdinalIgnoreCase)) && Directory.Exists(customPath))
- {
- list.Add(new VirtualFolderInfo
- {
- Locations = new List<string> { customPath },
- Name = "Recorded Series",
- CollectionType = CollectionType.TvShows
- });
- }
-
- return list;
- }
-
- class ActiveRecordingInfo
- {
- public string Path { get; set; }
- public TimerInfo Timer { get; set; }
- public ProgramInfo Program { get; set; }
- public CancellationTokenSource CancellationTokenSource { get; set; }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs
deleted file mode 100644
index 675fca325..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Threading.Tasks;
-using MediaBrowser.Common.Security;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- public class EmbyTVRegistration : IRequiresRegistration
- {
- private readonly ISecurityManager _securityManager;
-
- public static EmbyTVRegistration Instance;
-
- public EmbyTVRegistration(ISecurityManager securityManager)
- {
- _securityManager = securityManager;
- Instance = this;
- }
-
- private bool? _isXmlTvEnabled;
-
- public Task LoadRegistrationInfoAsync()
- {
- _isXmlTvEnabled = null;
- return Task.FromResult(true);
- }
-
- public async Task<bool> EnableXmlTv()
- {
- if (!_isXmlTvEnabled.HasValue)
- {
- var info = await _securityManager.GetRegistrationStatus("xmltv").ConfigureAwait(false);
- _isXmlTvEnabled = info.IsValid;
- }
- return _isXmlTvEnabled.Value;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
deleted file mode 100644
index 95c2b5ebb..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ /dev/null
@@ -1,326 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- public class EncodedRecorder : IRecorder
- {
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IHttpClient _httpClient;
- private readonly IMediaEncoder _mediaEncoder;
- private readonly IServerApplicationPaths _appPaths;
- private readonly LiveTvOptions _liveTvOptions;
- private bool _hasExited;
- private Stream _logFileStream;
- private string _targetPath;
- private Process _process;
- private readonly IJsonSerializer _json;
- private readonly TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
-
- public EncodedRecorder(ILogger logger, IFileSystem fileSystem, IMediaEncoder mediaEncoder, IServerApplicationPaths appPaths, IJsonSerializer json, LiveTvOptions liveTvOptions, IHttpClient httpClient)
- {
- _logger = logger;
- _fileSystem = fileSystem;
- _mediaEncoder = mediaEncoder;
- _appPaths = appPaths;
- _json = json;
- _liveTvOptions = liveTvOptions;
- _httpClient = httpClient;
- }
-
- private string OutputFormat
- {
- get
- {
- var format = _liveTvOptions.RecordingEncodingFormat;
-
- if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase) || string.Equals(_liveTvOptions.RecordedVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return "mkv";
- }
-
- return "mp4";
- }
- }
-
- public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
- {
- return Path.ChangeExtension(targetFile, "." + OutputFormat);
- }
-
- public async Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
- {
- var durationToken = new CancellationTokenSource(duration);
- cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, durationToken.Token).Token;
-
- await RecordFromFile(mediaSource, mediaSource.Path, targetFile, duration, onStarted, cancellationToken).ConfigureAwait(false);
-
- _logger.Info("Recording completed to file {0}", targetFile);
- }
-
- private Task RecordFromFile(MediaSourceInfo mediaSource, string inputFile, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken)
- {
- _targetPath = targetFile;
- _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
-
- var process = new Process
- {
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- UseShellExecute = false,
-
- // Must consume both stdout and stderr or deadlocks may occur
- //RedirectStandardOutput = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
-
- FileName = _mediaEncoder.EncoderPath,
- Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
-
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
- },
-
- EnableRaisingEvents = true
- };
-
- _process = process;
-
- var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
- _logger.Info(commandLineLogMessage);
-
- var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");
- _fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
-
- // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
- _logFileStream = _fileSystem.GetFileStream(logFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
-
- var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
- _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
-
- process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);
-
- process.Start();
-
- cancellationToken.Register(Stop);
-
- // MUST read both stdout and stderr asynchronously or a deadlock may occurr
- //process.BeginOutputReadLine();
-
- onStarted();
-
- // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
- StartStreamingLog(process.StandardError.BaseStream, _logFileStream);
-
- _logger.Info("ffmpeg recording process started for {0}", _targetPath);
-
- return _taskCompletionSource.Task;
- }
-
- private string GetCommandLineArgs(MediaSourceInfo mediaSource, string inputTempFile, string targetFile, TimeSpan duration)
- {
- string videoArgs;
- if (EncodeVideo(mediaSource))
- {
- var maxBitrate = 25000000;
- videoArgs = string.Format(
- "-codec:v:0 libx264 -force_key_frames \"expr:gte(t,n_forced*5)\" {0} -pix_fmt yuv420p -preset superfast -crf 23 -b:v {1} -maxrate {1} -bufsize ({1}*2) -vsync -1 -profile:v high -level 41",
- GetOutputSizeParam(),
- maxBitrate.ToString(CultureInfo.InvariantCulture));
- }
- else
- {
- videoArgs = "-codec:v:0 copy";
- }
-
- var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
- var inputModifiers = "-fflags +genpts -async 1 -vsync -1";
- var commandLineArgs = "-i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\"";
-
- long startTimeTicks = 0;
- //if (mediaSource.DateLiveStreamOpened.HasValue)
- //{
- // var elapsed = DateTime.UtcNow - mediaSource.DateLiveStreamOpened.Value;
- // elapsed -= TimeSpan.FromSeconds(10);
- // if (elapsed.TotalSeconds >= 0)
- // {
- // startTimeTicks = elapsed.Ticks + startTimeTicks;
- // }
- //}
-
- if (mediaSource.ReadAtNativeFramerate)
- {
- inputModifiers += " -re";
- }
-
- if (startTimeTicks > 0)
- {
- inputModifiers = "-ss " + _mediaEncoder.GetTimeParameter(startTimeTicks) + " " + inputModifiers;
- }
-
- commandLineArgs = string.Format(commandLineArgs, inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource), durationParam);
-
- return inputModifiers + " " + commandLineArgs;
- }
-
- private string GetAudioArgs(MediaSourceInfo mediaSource)
- {
- var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
- var inputAudioCodec = mediaStreams.Where(i => i.Type == MediaStreamType.Audio).Select(i => i.Codec).FirstOrDefault() ?? string.Empty;
-
- // do not copy aac because many players have difficulty with aac_latm
- if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings && !string.Equals(inputAudioCodec, "aac", StringComparison.OrdinalIgnoreCase))
- {
- return "-codec:a:0 copy";
- }
-
- var audioChannels = 2;
- var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
- if (audioStream != null)
- {
- audioChannels = audioStream.Channels ?? audioChannels;
- }
- return "-codec:a:0 aac -strict experimental -ab 320000";
- }
-
- private bool EncodeVideo(MediaSourceInfo mediaSource)
- {
- if (string.Equals(_liveTvOptions.RecordedVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
- return !mediaStreams.Any(i => i.Type == MediaStreamType.Video && string.Equals(i.Codec, "h264", StringComparison.OrdinalIgnoreCase) && !i.IsInterlaced);
- }
-
- protected string GetOutputSizeParam()
- {
- var filters = new List<string>();
-
- filters.Add("yadif=0:-1:0");
-
- var output = string.Empty;
-
- if (filters.Count > 0)
- {
- output += string.Format(" -vf \"{0}\"", string.Join(",", filters.ToArray()));
- }
-
- return output;
- }
-
- private void Stop()
- {
- if (!_hasExited)
- {
- try
- {
- _logger.Info("Killing ffmpeg recording process for {0}", _targetPath);
-
- //process.Kill();
- _process.StandardInput.WriteLine("q");
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error killing transcoding job for {0}", ex, _targetPath);
- }
- }
- }
-
- /// <summary>
- /// Processes the exited.
- /// </summary>
- private void OnFfMpegProcessExited(Process process, string inputFile)
- {
- _hasExited = true;
-
- DisposeLogStream();
-
- try
- {
- var exitCode = process.ExitCode;
-
- _logger.Info("FFMpeg recording exited with code {0} for {1}", exitCode, _targetPath);
-
- if (exitCode == 0)
- {
- _taskCompletionSource.TrySetResult(true);
- }
- else
- {
- _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed. Exit code {1}", _targetPath, exitCode)));
- }
- }
- catch
- {
- _logger.Error("FFMpeg recording exited with an error for {0}.", _targetPath);
- _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath)));
- }
- }
-
- private void DisposeLogStream()
- {
- if (_logFileStream != null)
- {
- try
- {
- _logFileStream.Dispose();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error disposing recording log stream", ex);
- }
-
- _logFileStream = null;
- }
- }
-
- private async void StartStreamingLog(Stream source, Stream target)
- {
- try
- {
- using (var reader = new StreamReader(source))
- {
- while (!reader.EndOfStream)
- {
- var line = await reader.ReadLineAsync().ConfigureAwait(false);
-
- var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
-
- await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
- await target.FlushAsync().ConfigureAwait(false);
- }
- }
- }
- catch (ObjectDisposedException)
- {
- // Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reading ffmpeg recording log", ex);
- }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
deleted file mode 100644
index 713cb9cd3..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EntryPoint.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using MediaBrowser.Controller.Plugins;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- public class EntryPoint : IServerEntryPoint
- {
- public void Run()
- {
- EmbyTV.Current.Start();
- }
-
- public void Dispose()
- {
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
deleted file mode 100644
index 5706b6ae9..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/IRecorder.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Dto;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- public interface IRecorder
- {
- /// <summary>
- /// Records the specified media source.
- /// </summary>
- /// <param name="mediaSource">The media source.</param>
- /// <param name="targetFile">The target file.</param>
- /// <param name="duration">The duration.</param>
- /// <param name="onStarted">The on started.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- Task Record(MediaSourceInfo mediaSource, string targetFile, TimeSpan duration, Action onStarted, CancellationToken cancellationToken);
-
- string GetOutputPath(MediaSourceInfo mediaSource, string targetFile);
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
deleted file mode 100644
index 9bda9c3f6..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- public class ItemDataProvider<T>
- where T : class
- {
- private readonly object _fileDataLock = new object();
- private List<T> _items;
- private readonly IJsonSerializer _jsonSerializer;
- protected readonly ILogger Logger;
- private readonly string _dataPath;
- protected readonly Func<T, T, bool> EqualityComparer;
- private readonly IFileSystem _fileSystem;
-
- public ItemDataProvider(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, Func<T, T, bool> equalityComparer)
- {
- Logger = logger;
- _dataPath = dataPath;
- EqualityComparer = equalityComparer;
- _jsonSerializer = jsonSerializer;
- _fileSystem = fileSystem;
- }
-
- public IReadOnlyList<T> GetAll()
- {
- lock (_fileDataLock)
- {
- if (_items == null)
- {
- Logger.Info("Loading live tv data from {0}", _dataPath);
- _items = GetItemsFromFile(_dataPath);
- }
- return _items.ToList();
- }
- }
-
- private List<T> GetItemsFromFile(string path)
- {
- var jsonFile = path + ".json";
-
- try
- {
- return _jsonSerializer.DeserializeFromFile<List<T>>(jsonFile) ?? new List<T>();
- }
- catch (FileNotFoundException)
- {
- }
- catch (DirectoryNotFoundException)
- {
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error deserializing {0}", ex, jsonFile);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error deserializing {0}", ex, jsonFile);
- }
- return new List<T>();
- }
-
- private void UpdateList(List<T> newList)
- {
- if (newList == null)
- {
- throw new ArgumentNullException("newList");
- }
-
- var file = _dataPath + ".json";
- _fileSystem.CreateDirectory(Path.GetDirectoryName(file));
-
- lock (_fileDataLock)
- {
- _jsonSerializer.SerializeToFile(newList, file);
- _items = newList;
- }
- }
-
- public virtual void Update(T item)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- var list = GetAll().ToList();
-
- var index = list.FindIndex(i => EqualityComparer(i, item));
-
- if (index == -1)
- {
- throw new ArgumentException("item not found");
- }
-
- list[index] = item;
-
- UpdateList(list);
- }
-
- public virtual void Add(T item)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- var list = GetAll().ToList();
-
- if (list.Any(i => EqualityComparer(i, item)))
- {
- throw new ArgumentException("item already exists");
- }
-
- list.Add(item);
-
- UpdateList(list);
- }
-
- public void AddOrUpdate(T item)
- {
- var list = GetAll().ToList();
-
- if (!list.Any(i => EqualityComparer(i, item)))
- {
- Add(item);
- }
- else
- {
- Update(item);
- }
- }
-
- public virtual void Delete(T item)
- {
- var list = GetAll().Where(i => !EqualityComparer(i, item)).ToList();
-
- UpdateList(list);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
deleted file mode 100644
index f7b4b3fde..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.LiveTv;
-using System;
-using System.Globalization;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- internal class RecordingHelper
- {
- public static DateTime GetStartTime(TimerInfo timer)
- {
- return timer.StartDate.AddSeconds(-timer.PrePaddingSeconds);
- }
-
- public static TimerInfo CreateTimer(ProgramInfo parent, SeriesTimerInfo seriesTimer)
- {
- var timer = new TimerInfo();
-
- timer.ChannelId = parent.ChannelId;
- timer.Id = (seriesTimer.Id + parent.Id).GetMD5().ToString("N");
- timer.StartDate = parent.StartDate;
- timer.EndDate = parent.EndDate;
- timer.ProgramId = parent.Id;
- timer.PrePaddingSeconds = seriesTimer.PrePaddingSeconds;
- timer.PostPaddingSeconds = seriesTimer.PostPaddingSeconds;
- timer.IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired;
- timer.IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired;
- timer.KeepUntil = seriesTimer.KeepUntil;
- timer.Priority = seriesTimer.Priority;
- timer.Name = parent.Name;
- timer.Overview = parent.Overview;
- timer.SeriesTimerId = seriesTimer.Id;
-
- CopyProgramInfoToTimerInfo(parent, timer);
-
- return timer;
- }
-
- public static void CopyProgramInfoToTimerInfo(ProgramInfo programInfo, TimerInfo timerInfo)
- {
- timerInfo.SeasonNumber = programInfo.SeasonNumber;
- timerInfo.EpisodeNumber = programInfo.EpisodeNumber;
- timerInfo.IsMovie = programInfo.IsMovie;
- timerInfo.IsKids = programInfo.IsKids;
- timerInfo.IsNews = programInfo.IsNews;
- timerInfo.IsSports = programInfo.IsSports;
- timerInfo.ProductionYear = programInfo.ProductionYear;
- timerInfo.EpisodeTitle = programInfo.EpisodeTitle;
- timerInfo.OriginalAirDate = programInfo.OriginalAirDate;
- timerInfo.IsProgramSeries = programInfo.IsSeries;
-
- timerInfo.HomePageUrl = programInfo.HomePageUrl;
- timerInfo.CommunityRating = programInfo.CommunityRating;
- timerInfo.ShortOverview = programInfo.ShortOverview;
- timerInfo.OfficialRating = programInfo.OfficialRating;
- timerInfo.IsRepeat = programInfo.IsRepeat;
- }
-
- public static string GetRecordingName(TimerInfo info)
- {
- var name = info.Name;
-
- if (info.IsProgramSeries)
- {
- var addHyphen = true;
-
- if (info.SeasonNumber.HasValue && info.EpisodeNumber.HasValue)
- {
- name += string.Format(" S{0}E{1}", info.SeasonNumber.Value.ToString("00", CultureInfo.InvariantCulture), info.EpisodeNumber.Value.ToString("00", CultureInfo.InvariantCulture));
- addHyphen = false;
- }
- else if (info.OriginalAirDate.HasValue)
- {
- name += " " + info.OriginalAirDate.Value.ToString("yyyy-MM-dd");
- }
- else
- {
- name += " " + DateTime.Now.ToString("yyyy-MM-dd");
- }
-
- if (!string.IsNullOrWhiteSpace(info.EpisodeTitle))
- {
- if (addHyphen)
- {
- name += " -";
- }
-
- name += " " + info.EpisodeTitle;
- }
- }
-
- else if (info.IsMovie && info.ProductionYear != null)
- {
- name += " (" + info.ProductionYear + ")";
- }
- else
- {
- name += " " + info.StartDate.ToString("yyyy-MM-dd") + " " + info.Id;
- }
-
- return name;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
deleted file mode 100644
index b2a347b77..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/SeriesTimerManager.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- public class SeriesTimerManager : ItemDataProvider<SeriesTimerInfo>
- {
- public SeriesTimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath)
- : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
- {
- }
-
- public override void Add(SeriesTimerInfo item)
- {
- if (string.IsNullOrWhiteSpace(item.Id))
- {
- throw new ArgumentException("SeriesTimerInfo.Id cannot be null or empty.");
- }
-
- base.Add(item);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
deleted file mode 100644
index 1e01df29d..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using MediaBrowser.Common.Events;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Concurrent;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.LiveTv;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
-{
- public class TimerManager : ItemDataProvider<TimerInfo>
- {
- private readonly ConcurrentDictionary<string, Timer> _timers = new ConcurrentDictionary<string, Timer>(StringComparer.OrdinalIgnoreCase);
- private readonly ILogger _logger;
-
- public event EventHandler<GenericEventArgs<TimerInfo>> TimerFired;
-
- public TimerManager(IFileSystem fileSystem, IJsonSerializer jsonSerializer, ILogger logger, string dataPath, ILogger logger1)
- : base(fileSystem, jsonSerializer, logger, dataPath, (r1, r2) => string.Equals(r1.Id, r2.Id, StringComparison.OrdinalIgnoreCase))
- {
- _logger = logger1;
- }
-
- public void RestartTimers()
- {
- StopTimers();
-
- foreach (var item in GetAll().ToList())
- {
- AddOrUpdateSystemTimer(item);
- }
- }
-
- public void StopTimers()
- {
- foreach (var pair in _timers.ToList())
- {
- pair.Value.Dispose();
- }
-
- _timers.Clear();
- }
-
- public override void Delete(TimerInfo item)
- {
- base.Delete(item);
- StopTimer(item);
- }
-
- public override void Update(TimerInfo item)
- {
- base.Update(item);
- AddOrUpdateSystemTimer(item);
- }
-
- public void AddOrUpdate(TimerInfo item, bool resetTimer)
- {
- if (resetTimer)
- {
- AddOrUpdate(item);
- return;
- }
-
- var list = GetAll().ToList();
-
- if (!list.Any(i => EqualityComparer(i, item)))
- {
- base.Add(item);
- }
- else
- {
- base.Update(item);
- }
- }
-
- public override void Add(TimerInfo item)
- {
- if (string.IsNullOrWhiteSpace(item.Id))
- {
- throw new ArgumentException("TimerInfo.Id cannot be null or empty.");
- }
-
- base.Add(item);
- AddOrUpdateSystemTimer(item);
- }
-
- private bool ShouldStartTimer(TimerInfo item)
- {
- if (item.Status == RecordingStatus.Completed ||
- item.Status == RecordingStatus.Cancelled)
- {
- return false;
- }
-
- return true;
- }
-
- private void AddOrUpdateSystemTimer(TimerInfo item)
- {
- StopTimer(item);
-
- if (!ShouldStartTimer(item))
- {
- return;
- }
-
- var startDate = RecordingHelper.GetStartTime(item);
- var now = DateTime.UtcNow;
-
- if (startDate < now)
- {
- EventHelper.FireEventIfNotNull(TimerFired, this, new GenericEventArgs<TimerInfo> { Argument = item }, Logger);
- return;
- }
-
- var dueTime = startDate - now;
- StartTimer(item, dueTime);
- }
-
- private void StartTimer(TimerInfo item, TimeSpan dueTime)
- {
- var timer = new Timer(TimerCallback, item.Id, dueTime, TimeSpan.Zero);
-
- if (_timers.TryAdd(item.Id, timer))
- {
- _logger.Info("Creating recording timer for {0}, {1}. Timer will fire in {2} minutes", item.Id, item.Name, dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
- }
- else
- {
- timer.Dispose();
- _logger.Warn("Timer already exists for item {0}", item.Id);
- }
- }
-
- private void StopTimer(TimerInfo item)
- {
- Timer timer;
- if (_timers.TryRemove(item.Id, out timer))
- {
- timer.Dispose();
- }
- }
-
- private void TimerCallback(object state)
- {
- var timerId = (string)state;
-
- var timer = GetAll().FirstOrDefault(i => string.Equals(i.Id, timerId, StringComparison.OrdinalIgnoreCase));
- if (timer != null)
- {
- EventHelper.FireEventIfNotNull(TimerFired, this, new GenericEventArgs<TimerInfo> { Argument = timer }, Logger);
- }
- }
-
- public TimerInfo GetTimer(string id)
- {
- return GetAll().FirstOrDefault(r => string.Equals(r.Id, id, StringComparison.OrdinalIgnoreCase));
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
deleted file mode 100644
index d5abcf98e..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ /dev/null
@@ -1,1310 +0,0 @@
-using System.Net;
-using MediaBrowser.Common;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.Listings
-{
- public class SchedulesDirect : IListingsProvider
- {
- private readonly ILogger _logger;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly IHttpClient _httpClient;
- private readonly SemaphoreSlim _tokenSemaphore = new SemaphoreSlim(1, 1);
- private readonly IApplicationHost _appHost;
-
- private const string ApiUrl = "https://json.schedulesdirect.org/20141201";
-
- private readonly Dictionary<string, Dictionary<string, ScheduleDirect.Station>> _channelPairingCache =
- new Dictionary<string, Dictionary<string, ScheduleDirect.Station>>(StringComparer.OrdinalIgnoreCase);
-
- public SchedulesDirect(ILogger logger, IJsonSerializer jsonSerializer, IHttpClient httpClient, IApplicationHost appHost)
- {
- _logger = logger;
- _jsonSerializer = jsonSerializer;
- _httpClient = httpClient;
- _appHost = appHost;
- }
-
- private string UserAgent
- {
- get { return "Emby/" + _appHost.ApplicationVersion; }
- }
-
- private List<string> GetScheduleRequestDates(DateTime startDateUtc, DateTime endDateUtc)
- {
- List<string> dates = new List<string>();
-
- var start = new List<DateTime> { startDateUtc, startDateUtc.ToLocalTime() }.Min().Date;
- var end = new List<DateTime> { endDateUtc, endDateUtc.ToLocalTime() }.Max().Date;
-
- while (start <= end)
- {
- dates.Add(start.ToString("yyyy-MM-dd"));
- start = start.AddDays(1);
- }
-
- return dates;
- }
-
- public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, string channelName, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
- {
- List<ProgramInfo> programsInfo = new List<ProgramInfo>();
-
- var token = await GetToken(info, cancellationToken).ConfigureAwait(false);
-
- if (string.IsNullOrWhiteSpace(token))
- {
- _logger.Warn("SchedulesDirect token is empty, returning empty program list");
- return programsInfo;
- }
-
- if (string.IsNullOrWhiteSpace(info.ListingsId))
- {
- _logger.Warn("ListingsId is null, returning empty program list");
- return programsInfo;
- }
-
- var dates = GetScheduleRequestDates(startDateUtc, endDateUtc);
-
- ScheduleDirect.Station station = GetStation(info.ListingsId, channelNumber, channelName);
-
- if (station == null)
- {
- _logger.Info("No Schedules Direct Station found for channel {0} with name {1}", channelNumber, channelName);
- return programsInfo;
- }
-
- string stationID = station.stationID;
-
- _logger.Info("Channel Station ID is: " + stationID);
- List<ScheduleDirect.RequestScheduleForChannel> requestList =
- new List<ScheduleDirect.RequestScheduleForChannel>()
- {
- new ScheduleDirect.RequestScheduleForChannel()
- {
- stationID = stationID,
- date = dates
- }
- };
-
- var requestString = _jsonSerializer.SerializeToString(requestList);
- _logger.Debug("Request string for schedules is: " + requestString);
-
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/schedules",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- // The data can be large so give it some extra time
- TimeoutMs = 60000,
- LogErrorResponseBody = true
- };
-
- httpOptions.RequestHeaders["token"] = token;
-
- httpOptions.RequestContent = requestString;
- using (var response = await Post(httpOptions, true, info).ConfigureAwait(false))
- {
- StreamReader reader = new StreamReader(response.Content);
- string responseString = reader.ReadToEnd();
- var dailySchedules = _jsonSerializer.DeserializeFromString<List<ScheduleDirect.Day>>(responseString);
- _logger.Debug("Found " + dailySchedules.Count + " programs on " + channelNumber + " ScheduleDirect");
-
- httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/programs",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
- };
-
- httpOptions.RequestHeaders["token"] = token;
-
- List<string> programsID = new List<string>();
- programsID = dailySchedules.SelectMany(d => d.programs.Select(s => s.programID)).Distinct().ToList();
- var requestBody = "[\"" + string.Join("\", \"", programsID) + "\"]";
- httpOptions.RequestContent = requestBody;
-
- using (var innerResponse = await Post(httpOptions, true, info).ConfigureAwait(false))
- {
- StreamReader innerReader = new StreamReader(innerResponse.Content);
- responseString = innerReader.ReadToEnd();
-
- var programDetails =
- _jsonSerializer.DeserializeFromString<List<ScheduleDirect.ProgramDetails>>(
- responseString);
- var programDict = programDetails.ToDictionary(p => p.programID, y => y);
-
- var images = await GetImageForPrograms(info, programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID).ToList(), cancellationToken);
-
- var schedules = dailySchedules.SelectMany(d => d.programs);
- foreach (ScheduleDirect.Program schedule in schedules)
- {
- //_logger.Debug("Proccesing Schedule for statio ID " + stationID +
- // " which corresponds to channel " + channelNumber + " and program id " +
- // schedule.programID + " which says it has images? " +
- // programDict[schedule.programID].hasImageArtwork);
-
- if (images != null)
- {
- var imageIndex = images.FindIndex(i => i.programID == schedule.programID.Substring(0, 10));
- if (imageIndex > -1)
- {
- var programEntry = programDict[schedule.programID];
-
- var data = images[imageIndex].data ?? new List<ScheduleDirect.ImageData>();
- data = data.OrderByDescending(GetSizeOrder).ToList();
-
- programEntry.primaryImage = GetProgramImage(ApiUrl, data, "Logo", true, 600);
- //programEntry.thumbImage = GetProgramImage(ApiUrl, data, "Iconic", false);
- //programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
- // GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
- // GetProgramImage(ApiUrl, data, "Banner-LO", false) ??
- // GetProgramImage(ApiUrl, data, "Banner-LOT", false);
- }
- }
-
- programsInfo.Add(GetProgram(channelNumber, schedule, programDict[schedule.programID]));
- }
- _logger.Info("Finished with EPGData");
- }
- }
-
- return programsInfo;
- }
-
- private int GetSizeOrder(ScheduleDirect.ImageData image)
- {
- if (!string.IsNullOrWhiteSpace(image.height))
- {
- int value;
- if (int.TryParse(image.height, out value))
- {
- return value;
- }
- }
-
- return 0;
- }
-
- private readonly object _channelCacheLock = new object();
- private ScheduleDirect.Station GetStation(string listingsId, string channelNumber, string channelName)
- {
- lock (_channelCacheLock)
- {
- Dictionary<string, ScheduleDirect.Station> channelPair;
- if (_channelPairingCache.TryGetValue(listingsId, out channelPair))
- {
- ScheduleDirect.Station station;
-
- if (channelPair.TryGetValue(channelNumber, out station))
- {
- return station;
- }
-
- if (!string.IsNullOrWhiteSpace(channelName))
- {
- channelName = NormalizeName(channelName);
-
- var result = channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.callsign ?? string.Empty), channelName, StringComparison.OrdinalIgnoreCase));
-
- if (result != null)
- {
- return result;
- }
- }
-
- if (!string.IsNullOrWhiteSpace(channelNumber))
- {
- return channelPair.Values.FirstOrDefault(i => string.Equals(NormalizeName(i.stationID ?? string.Empty), channelNumber, StringComparison.OrdinalIgnoreCase));
- }
- }
-
- return null;
- }
- }
-
- private void AddToChannelPairCache(string listingsId, string channelNumber, ScheduleDirect.Station schChannel)
- {
- lock (_channelCacheLock)
- {
- Dictionary<string, ScheduleDirect.Station> cache;
- if (_channelPairingCache.TryGetValue(listingsId, out cache))
- {
- cache[channelNumber] = schChannel;
- }
- else
- {
- cache = new Dictionary<string, ScheduleDirect.Station>();
- cache[channelNumber] = schChannel;
- _channelPairingCache[listingsId] = cache;
- }
- }
- }
-
- private void ClearPairCache(string listingsId)
- {
- lock (_channelCacheLock)
- {
- Dictionary<string, ScheduleDirect.Station> cache;
- if (_channelPairingCache.TryGetValue(listingsId, out cache))
- {
- cache.Clear();
- }
- }
- }
-
- private int GetChannelPairCacheCount(string listingsId)
- {
- lock (_channelCacheLock)
- {
- Dictionary<string, ScheduleDirect.Station> cache;
- if (_channelPairingCache.TryGetValue(listingsId, out cache))
- {
- return cache.Count;
- }
-
- return 0;
- }
- }
-
- private string NormalizeName(string value)
- {
- return value.Replace(" ", string.Empty).Replace("-", string.Empty);
- }
-
- public async Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels,
- CancellationToken cancellationToken)
- {
- var listingsId = info.ListingsId;
- if (string.IsNullOrWhiteSpace(listingsId))
- {
- throw new Exception("ListingsId required");
- }
-
- var token = await GetToken(info, cancellationToken);
-
- if (string.IsNullOrWhiteSpace(token))
- {
- throw new Exception("token required");
- }
-
- ClearPairCache(listingsId);
-
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/lineups/" + listingsId,
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
- };
-
- httpOptions.RequestHeaders["token"] = token;
-
- using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
- {
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
- _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
- _logger.Info("Mapping Stations to Channel");
- foreach (ScheduleDirect.Map map in root.map)
- {
- var channelNumber = map.logicalChannelNumber;
-
- if (string.IsNullOrWhiteSpace(channelNumber))
- {
- channelNumber = map.channel;
- }
- if (string.IsNullOrWhiteSpace(channelNumber))
- {
- channelNumber = map.atscMajor + "." + map.atscMinor;
- }
- channelNumber = channelNumber.TrimStart('0');
-
- _logger.Debug("Found channel: " + channelNumber + " in Schedules Direct");
-
- var schChannel = (root.stations ?? new List<ScheduleDirect.Station>()).FirstOrDefault(item => string.Equals(item.stationID, map.stationID, StringComparison.OrdinalIgnoreCase));
- if (schChannel != null)
- {
- AddToChannelPairCache(listingsId, channelNumber, schChannel);
- }
- else
- {
- AddToChannelPairCache(listingsId, channelNumber, new ScheduleDirect.Station
- {
- stationID = map.stationID
- });
- }
- }
- _logger.Info("Added " + GetChannelPairCacheCount(listingsId) + " channels to the dictionary");
-
- foreach (ChannelInfo channel in channels)
- {
- var station = GetStation(listingsId, channel.Number, channel.Name);
-
- if (station != null)
- {
- if (station.logo != null)
- {
- channel.ImageUrl = station.logo.URL;
- channel.HasImage = true;
- }
-
- if (!string.IsNullOrWhiteSpace(station.name))
- {
- channel.Name = station.name;
- }
- }
- else
- {
- _logger.Info("Schedules Direct doesnt have data for channel: " + channel.Number + " " + channel.Name);
- }
- }
- }
- }
-
- private ProgramInfo GetProgram(string channel, ScheduleDirect.Program programInfo,
- ScheduleDirect.ProgramDetails details)
- {
- //_logger.Debug("Show type is: " + (details.showType ?? "No ShowType"));
- DateTime startAt = GetDate(programInfo.airDateTime);
- DateTime endAt = startAt.AddSeconds(programInfo.duration);
- ProgramAudio audioType = ProgramAudio.Stereo;
-
- bool repeat = programInfo.@new == null;
- string newID = programInfo.programID + "T" + startAt.Ticks + "C" + channel;
-
- if (programInfo.audioProperties != null)
- {
- if (programInfo.audioProperties.Exists(item => string.Equals(item, "atmos", StringComparison.OrdinalIgnoreCase)))
- {
- audioType = ProgramAudio.Atmos;
- }
- else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd 5.1", StringComparison.OrdinalIgnoreCase)))
- {
- audioType = ProgramAudio.DolbyDigital;
- }
- else if (programInfo.audioProperties.Exists(item => string.Equals(item, "dd", StringComparison.OrdinalIgnoreCase)))
- {
- audioType = ProgramAudio.DolbyDigital;
- }
- else if (programInfo.audioProperties.Exists(item => string.Equals(item, "stereo", StringComparison.OrdinalIgnoreCase)))
- {
- audioType = ProgramAudio.Stereo;
- }
- else
- {
- audioType = ProgramAudio.Mono;
- }
- }
-
- string episodeTitle = null;
- if (details.episodeTitle150 != null)
- {
- episodeTitle = details.episodeTitle150;
- }
-
- var showType = details.showType ?? string.Empty;
-
- var info = new ProgramInfo
- {
- ChannelId = channel,
- Id = newID,
- StartDate = startAt,
- EndDate = endAt,
- Name = details.titles[0].title120 ?? "Unkown",
- OfficialRating = null,
- CommunityRating = null,
- EpisodeTitle = episodeTitle,
- Audio = audioType,
- IsRepeat = repeat,
- IsSeries = showType.IndexOf("series", StringComparison.OrdinalIgnoreCase) != -1,
- ImageUrl = details.primaryImage,
- IsKids = string.Equals(details.audience, "children", StringComparison.OrdinalIgnoreCase),
- IsSports = showType.IndexOf("sports", StringComparison.OrdinalIgnoreCase) != -1,
- IsMovie = showType.IndexOf("movie", StringComparison.OrdinalIgnoreCase) != -1 || showType.IndexOf("film", StringComparison.OrdinalIgnoreCase) != -1,
- ShowId = programInfo.programID,
- Etag = programInfo.md5
- };
-
- if (programInfo.videoProperties != null)
- {
- info.IsHD = programInfo.videoProperties.Contains("hdtv", StringComparer.OrdinalIgnoreCase);
- info.Is3D = programInfo.videoProperties.Contains("3d", StringComparer.OrdinalIgnoreCase);
- }
-
- if (details.contentRating != null && details.contentRating.Count > 0)
- {
- info.OfficialRating = details.contentRating[0].code.Replace("TV", "TV-").Replace("--", "-");
-
- var invalid = new[] { "N/A", "Approved", "Not Rated", "Passed" };
- if (invalid.Contains(info.OfficialRating, StringComparer.OrdinalIgnoreCase))
- {
- info.OfficialRating = null;
- }
- }
-
- if (details.descriptions != null)
- {
- if (details.descriptions.description1000 != null)
- {
- info.Overview = details.descriptions.description1000[0].description;
- }
- else if (details.descriptions.description100 != null)
- {
- info.ShortOverview = details.descriptions.description100[0].description;
- }
- }
-
- if (info.IsSeries)
- {
- info.SeriesId = programInfo.programID.Substring(0, 10);
-
- if (details.metadata != null)
- {
- var gracenote = details.metadata.Find(x => x.Gracenote != null).Gracenote;
- info.SeasonNumber = gracenote.season;
- info.EpisodeNumber = gracenote.episode;
- }
- }
-
- if (!string.IsNullOrWhiteSpace(details.originalAirDate) && (!info.IsSeries || info.IsRepeat))
- {
- info.OriginalAirDate = DateTime.Parse(details.originalAirDate);
- info.ProductionYear = info.OriginalAirDate.Value.Year;
- }
-
- if (details.genres != null)
- {
- info.Genres = details.genres.Where(g => !string.IsNullOrWhiteSpace(g)).ToList();
- info.IsNews = details.genres.Contains("news", StringComparer.OrdinalIgnoreCase);
-
- if (info.Genres.Contains("children", StringComparer.OrdinalIgnoreCase))
- {
- info.IsKids = true;
- }
- }
-
- return info;
- }
-
- private DateTime GetDate(string value)
- {
- var date = DateTime.ParseExact(value, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", CultureInfo.InvariantCulture);
-
- if (date.Kind != DateTimeKind.Utc)
- {
- date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
- }
- return date;
- }
-
- private string GetProgramImage(string apiUrl, List<ScheduleDirect.ImageData> images, string category, bool returnDefaultImage, int desiredWidth)
- {
- string url = null;
-
- var matches = images
- .Where(i => string.Equals(i.category, category, StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- if (matches.Count == 0)
- {
- if (!returnDefaultImage)
- {
- return null;
- }
- matches = images;
- }
-
- var match = matches.FirstOrDefault(i =>
- {
- if (!string.IsNullOrWhiteSpace(i.width))
- {
- int value;
- if (int.TryParse(i.width, out value))
- {
- return value <= desiredWidth;
- }
- }
-
- return false;
- });
-
- if (match == null)
- {
- // Get the second lowest quality image, when possible
- if (matches.Count > 1)
- {
- match = matches[matches.Count - 2];
- }
- else
- {
- match = matches.FirstOrDefault();
- }
- }
-
- if (match == null)
- {
- return null;
- }
-
- var uri = match.uri;
-
- if (!string.IsNullOrWhiteSpace(uri))
- {
- if (uri.IndexOf("http", StringComparison.OrdinalIgnoreCase) != -1)
- {
- url = uri;
- }
- else
- {
- url = apiUrl + "/image/" + uri;
- }
- }
- //_logger.Debug("URL for image is : " + url);
- return url;
- }
-
- private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
- ListingsProviderInfo info,
- List<string> programIds,
- CancellationToken cancellationToken)
- {
- var imageIdString = "[";
-
- programIds.ForEach(i =>
- {
- if (!imageIdString.Contains(i.Substring(0, 10)))
- {
- imageIdString += "\"" + i.Substring(0, 10) + "\",";
- }
- });
- imageIdString = imageIdString.TrimEnd(',') + "]";
-
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/metadata/programs",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- RequestContent = imageIdString,
- LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
- };
- List<ScheduleDirect.ShowImages> images;
- using (var innerResponse2 = await Post(httpOptions, true, info).ConfigureAwait(false))
- {
- images = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.ShowImages>>(
- innerResponse2.Content);
- }
-
- return images;
- }
-
- public async Task<List<NameIdPair>> GetHeadends(ListingsProviderInfo info, string country, string location, CancellationToken cancellationToken)
- {
- var token = await GetToken(info, cancellationToken);
-
- var lineups = new List<NameIdPair>();
-
- if (string.IsNullOrWhiteSpace(token))
- {
- return lineups;
- }
-
- var options = new HttpRequestOptions()
- {
- Url = ApiUrl + "/headends?country=" + country + "&postalcode=" + location,
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true
- };
-
- options.RequestHeaders["token"] = token;
-
- try
- {
- using (Stream responce = await Get(options, false, info).ConfigureAwait(false))
- {
- var root = _jsonSerializer.DeserializeFromStream<List<ScheduleDirect.Headends>>(responce);
-
- if (root != null)
- {
- foreach (ScheduleDirect.Headends headend in root)
- {
- foreach (ScheduleDirect.Lineup lineup in headend.lineups)
- {
- lineups.Add(new NameIdPair
- {
- Name = string.IsNullOrWhiteSpace(lineup.name) ? lineup.lineup : lineup.name,
- Id = lineup.uri.Substring(18)
- });
- }
- }
- }
- else
- {
- _logger.Info("No lineups available");
- }
- }
- }
- catch (Exception ex)
- {
- _logger.Error("Error getting headends", ex);
- }
-
- return lineups;
- }
-
- private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
- private DateTime _lastErrorResponse;
- private async Task<string> GetToken(ListingsProviderInfo info, CancellationToken cancellationToken)
- {
- var username = info.Username;
-
- // Reset the token if there's no username
- if (string.IsNullOrWhiteSpace(username))
- {
- return null;
- }
-
- var password = info.Password;
- if (string.IsNullOrWhiteSpace(password))
- {
- return null;
- }
-
- // Avoid hammering SD
- if ((DateTime.UtcNow - _lastErrorResponse).TotalMinutes < 1)
- {
- return null;
- }
-
- NameValuePair savedToken = null;
- if (!_tokens.TryGetValue(username, out savedToken))
- {
- savedToken = new NameValuePair();
- _tokens.TryAdd(username, savedToken);
- }
-
- if (!string.IsNullOrWhiteSpace(savedToken.Name) && !string.IsNullOrWhiteSpace(savedToken.Value))
- {
- long ticks;
- if (long.TryParse(savedToken.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out ticks))
- {
- // If it's under 24 hours old we can still use it
- if (DateTime.UtcNow.Ticks - ticks < TimeSpan.FromHours(20).Ticks)
- {
- return savedToken.Name;
- }
- }
- }
-
- await _tokenSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
- try
- {
- var result = await GetTokenInternal(username, password, cancellationToken).ConfigureAwait(false);
- savedToken.Name = result;
- savedToken.Value = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture);
- return result;
- }
- catch (HttpException ex)
- {
- if (ex.StatusCode.HasValue)
- {
- if ((int)ex.StatusCode.Value == 400)
- {
- _tokens.Clear();
- _lastErrorResponse = DateTime.UtcNow;
- }
- }
- throw;
- }
- finally
- {
- _tokenSemaphore.Release();
- }
- }
-
- private async Task<HttpResponseInfo> Post(HttpRequestOptions options,
- bool enableRetry,
- ListingsProviderInfo providerInfo)
- {
- try
- {
- return await _httpClient.Post(options).ConfigureAwait(false);
- }
- catch (HttpException ex)
- {
- _tokens.Clear();
-
- if (!ex.StatusCode.HasValue || (int)ex.StatusCode.Value >= 500)
- {
- enableRetry = false;
- }
-
- if (!enableRetry)
- {
- throw;
- }
- }
-
- var newToken = await GetToken(providerInfo, options.CancellationToken).ConfigureAwait(false);
- options.RequestHeaders["token"] = newToken;
- return await Post(options, false, providerInfo).ConfigureAwait(false);
- }
-
- private async Task<Stream> Get(HttpRequestOptions options,
- bool enableRetry,
- ListingsProviderInfo providerInfo)
- {
- try
- {
- return await _httpClient.Get(options).ConfigureAwait(false);
- }
- catch (HttpException ex)
- {
- _tokens.Clear();
-
- if (!ex.StatusCode.HasValue || (int)ex.StatusCode.Value >= 500)
- {
- enableRetry = false;
- }
-
- if (!enableRetry)
- {
- throw;
- }
- }
-
- var newToken = await GetToken(providerInfo, options.CancellationToken).ConfigureAwait(false);
- options.RequestHeaders["token"] = newToken;
- return await Get(options, false, providerInfo).ConfigureAwait(false);
- }
-
- private async Task<string> GetTokenInternal(string username, string password,
- CancellationToken cancellationToken)
- {
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/token",
- UserAgent = UserAgent,
- RequestContent = "{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}",
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true
- };
- //_logger.Info("Obtaining token from Schedules Direct from addres: " + httpOptions.Url + " with body " +
- // httpOptions.RequestContent);
-
- using (var responce = await Post(httpOptions, false, null).ConfigureAwait(false))
- {
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Token>(responce.Content);
- if (root.message == "OK")
- {
- _logger.Info("Authenticated with Schedules Direct token: " + root.token);
- return root.token;
- }
-
- throw new ApplicationException("Could not authenticate with Schedules Direct Error: " + root.message);
- }
- }
-
- private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken)
- {
- var token = await GetToken(info, cancellationToken);
-
- if (string.IsNullOrWhiteSpace(token))
- {
- throw new ArgumentException("Authentication required.");
- }
-
- if (string.IsNullOrWhiteSpace(info.ListingsId))
- {
- throw new ArgumentException("Listings Id required");
- }
-
- _logger.Info("Adding new LineUp ");
-
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/lineups/" + info.ListingsId,
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true,
- BufferContent = false
- };
-
- httpOptions.RequestHeaders["token"] = token;
-
- using (var response = await _httpClient.SendAsync(httpOptions, "PUT"))
- {
- }
- }
-
- public string Name
- {
- get { return "Schedules Direct"; }
- }
-
- public static string TypeName = "SchedulesDirect";
- public string Type
- {
- get { return TypeName; }
- }
-
- private async Task<bool> HasLineup(ListingsProviderInfo info, CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(info.ListingsId))
- {
- throw new ArgumentException("Listings Id required");
- }
-
- var token = await GetToken(info, cancellationToken);
-
- if (string.IsNullOrWhiteSpace(token))
- {
- throw new Exception("token required");
- }
-
- _logger.Info("Headends on account ");
-
- var options = new HttpRequestOptions()
- {
- Url = ApiUrl + "/lineups",
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true
- };
-
- options.RequestHeaders["token"] = token;
-
- try
- {
- using (var response = await Get(options, false, null).ConfigureAwait(false))
- {
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Lineups>(response);
-
- return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
- }
- }
- catch (HttpException ex)
- {
- // Apparently we're supposed to swallow this
- if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.BadRequest)
- {
- return false;
- }
-
- throw;
- }
- }
-
- public async Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings)
- {
- if (validateLogin)
- {
- if (string.IsNullOrWhiteSpace(info.Username))
- {
- throw new ArgumentException("Username is required");
- }
- if (string.IsNullOrWhiteSpace(info.Password))
- {
- throw new ArgumentException("Password is required");
- }
- }
- if (validateListings)
- {
- if (string.IsNullOrWhiteSpace(info.ListingsId))
- {
- throw new ArgumentException("Listings Id required");
- }
-
- var hasLineup = await HasLineup(info, CancellationToken.None).ConfigureAwait(false);
-
- if (!hasLineup)
- {
- await AddLineupToAccount(info, CancellationToken.None).ConfigureAwait(false);
- }
- }
- }
-
- public Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location)
- {
- return GetHeadends(info, country, location, CancellationToken.None);
- }
-
- public async Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken)
- {
- var listingsId = info.ListingsId;
- if (string.IsNullOrWhiteSpace(listingsId))
- {
- throw new Exception("ListingsId required");
- }
-
- await AddMetadata(info, new List<ChannelInfo>(), cancellationToken).ConfigureAwait(false);
-
- var token = await GetToken(info, cancellationToken);
-
- if (string.IsNullOrWhiteSpace(token))
- {
- throw new Exception("token required");
- }
-
- var httpOptions = new HttpRequestOptions()
- {
- Url = ApiUrl + "/lineups/" + listingsId,
- UserAgent = UserAgent,
- CancellationToken = cancellationToken,
- LogErrorResponseBody = true,
- // The data can be large so give it some extra time
- TimeoutMs = 60000
- };
-
- httpOptions.RequestHeaders["token"] = token;
-
- var list = new List<ChannelInfo>();
-
- using (var response = await Get(httpOptions, true, info).ConfigureAwait(false))
- {
- var root = _jsonSerializer.DeserializeFromStream<ScheduleDirect.Channel>(response);
- _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect");
- _logger.Info("Mapping Stations to Channel");
- foreach (ScheduleDirect.Map map in root.map)
- {
- var channelNumber = map.logicalChannelNumber;
-
- if (string.IsNullOrWhiteSpace(channelNumber))
- {
- channelNumber = map.channel;
- }
- if (string.IsNullOrWhiteSpace(channelNumber))
- {
- channelNumber = map.atscMajor + "." + map.atscMinor;
- }
- channelNumber = channelNumber.TrimStart('0');
-
- var name = channelNumber;
- var station = GetStation(listingsId, channelNumber, null);
-
- if (station != null && !string.IsNullOrWhiteSpace(station.name))
- {
- name = station.name;
- }
-
- list.Add(new ChannelInfo
- {
- Number = channelNumber,
- Name = name
- });
- }
- }
-
- return list;
- }
-
- public class ScheduleDirect
- {
- public class Token
- {
- public int code { get; set; }
- public string message { get; set; }
- public string serverID { get; set; }
- public string token { get; set; }
- }
- public class Lineup
- {
- public string lineup { get; set; }
- public string name { get; set; }
- public string transport { get; set; }
- public string location { get; set; }
- public string uri { get; set; }
- }
-
- public class Lineups
- {
- public int code { get; set; }
- public string serverID { get; set; }
- public string datetime { get; set; }
- public List<Lineup> lineups { get; set; }
- }
-
-
- public class Headends
- {
- public string headend { get; set; }
- public string transport { get; set; }
- public string location { get; set; }
- public List<Lineup> lineups { get; set; }
- }
-
-
-
- public class Map
- {
- public string stationID { get; set; }
- public string channel { get; set; }
- public string logicalChannelNumber { get; set; }
- public int uhfVhf { get; set; }
- public int atscMajor { get; set; }
- public int atscMinor { get; set; }
- }
-
- public class Broadcaster
- {
- public string city { get; set; }
- public string state { get; set; }
- public string postalcode { get; set; }
- public string country { get; set; }
- }
-
- public class Logo
- {
- public string URL { get; set; }
- public int height { get; set; }
- public int width { get; set; }
- public string md5 { get; set; }
- }
-
- public class Station
- {
- public string stationID { get; set; }
- public string name { get; set; }
- public string callsign { get; set; }
- public List<string> broadcastLanguage { get; set; }
- public List<string> descriptionLanguage { get; set; }
- public Broadcaster broadcaster { get; set; }
- public string affiliate { get; set; }
- public Logo logo { get; set; }
- public bool? isCommercialFree { get; set; }
- }
-
- public class Metadata
- {
- public string lineup { get; set; }
- public string modified { get; set; }
- public string transport { get; set; }
- }
-
- public class Channel
- {
- public List<Map> map { get; set; }
- public List<Station> stations { get; set; }
- public Metadata metadata { get; set; }
- }
-
- public class RequestScheduleForChannel
- {
- public string stationID { get; set; }
- public List<string> date { get; set; }
- }
-
-
-
-
- public class Rating
- {
- public string body { get; set; }
- public string code { get; set; }
- }
-
- public class Multipart
- {
- public int partNumber { get; set; }
- public int totalParts { get; set; }
- }
-
- public class Program
- {
- public string programID { get; set; }
- public string airDateTime { get; set; }
- public int duration { get; set; }
- public string md5 { get; set; }
- public List<string> audioProperties { get; set; }
- public List<string> videoProperties { get; set; }
- public List<Rating> ratings { get; set; }
- public bool? @new { get; set; }
- public Multipart multipart { get; set; }
- }
-
-
-
- public class MetadataSchedule
- {
- public string modified { get; set; }
- public string md5 { get; set; }
- public string startDate { get; set; }
- public string endDate { get; set; }
- public int days { get; set; }
- }
-
- public class Day
- {
- public string stationID { get; set; }
- public List<Program> programs { get; set; }
- public MetadataSchedule metadata { get; set; }
-
- public Day()
- {
- programs = new List<Program>();
- }
- }
-
- //
- public class Title
- {
- public string title120 { get; set; }
- }
-
- public class EventDetails
- {
- public string subType { get; set; }
- }
-
- public class Description100
- {
- public string descriptionLanguage { get; set; }
- public string description { get; set; }
- }
-
- public class Description1000
- {
- public string descriptionLanguage { get; set; }
- public string description { get; set; }
- }
-
- public class DescriptionsProgram
- {
- public List<Description100> description100 { get; set; }
- public List<Description1000> description1000 { get; set; }
- }
-
- public class Gracenote
- {
- public int season { get; set; }
- public int episode { get; set; }
- }
-
- public class MetadataPrograms
- {
- public Gracenote Gracenote { get; set; }
- }
-
- public class ContentRating
- {
- public string body { get; set; }
- public string code { get; set; }
- }
-
- public class Cast
- {
- public string billingOrder { get; set; }
- public string role { get; set; }
- public string nameId { get; set; }
- public string personId { get; set; }
- public string name { get; set; }
- public string characterName { get; set; }
- }
-
- public class Crew
- {
- public string billingOrder { get; set; }
- public string role { get; set; }
- public string nameId { get; set; }
- public string personId { get; set; }
- public string name { get; set; }
- }
-
- public class QualityRating
- {
- public string ratingsBody { get; set; }
- public string rating { get; set; }
- public string minRating { get; set; }
- public string maxRating { get; set; }
- public string increment { get; set; }
- }
-
- public class Movie
- {
- public string year { get; set; }
- public int duration { get; set; }
- public List<QualityRating> qualityRating { get; set; }
- }
-
- public class Recommendation
- {
- public string programID { get; set; }
- public string title120 { get; set; }
- }
-
- public class ProgramDetails
- {
- public string audience { get; set; }
- public string programID { get; set; }
- public List<Title> titles { get; set; }
- public EventDetails eventDetails { get; set; }
- public DescriptionsProgram descriptions { get; set; }
- public string originalAirDate { get; set; }
- public List<string> genres { get; set; }
- public string episodeTitle150 { get; set; }
- public List<MetadataPrograms> metadata { get; set; }
- public List<ContentRating> contentRating { get; set; }
- public List<Cast> cast { get; set; }
- public List<Crew> crew { get; set; }
- public string showType { get; set; }
- public bool hasImageArtwork { get; set; }
- public string primaryImage { get; set; }
- public string thumbImage { get; set; }
- public string bannerImage { get; set; }
- public string imageID { get; set; }
- public string md5 { get; set; }
- public List<string> contentAdvisory { get; set; }
- public Movie movie { get; set; }
- public List<Recommendation> recommendations { get; set; }
- }
-
- public class Caption
- {
- public string content { get; set; }
- public string lang { get; set; }
- }
-
- public class ImageData
- {
- public string width { get; set; }
- public string height { get; set; }
- public string uri { get; set; }
- public string size { get; set; }
- public string aspect { get; set; }
- public string category { get; set; }
- public string text { get; set; }
- public string primary { get; set; }
- public string tier { get; set; }
- public Caption caption { get; set; }
- }
-
- public class ShowImages
- {
- public string programID { get; set; }
- public List<ImageData> data { get; set; }
- }
-
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
deleted file mode 100644
index d3549aef5..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ /dev/null
@@ -1,233 +0,0 @@
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.XmlTv.Classes;
-using Emby.XmlTv.Entities;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.Listings
-{
- public class XmlTvListingsProvider : IListingsProvider
- {
- private readonly IServerConfigurationManager _config;
- private readonly IHttpClient _httpClient;
- private readonly ILogger _logger;
-
- public XmlTvListingsProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger)
- {
- _config = config;
- _httpClient = httpClient;
- _logger = logger;
- }
-
- public string Name
- {
- get { return "XmlTV"; }
- }
-
- public string Type
- {
- get { return "xmltv"; }
- }
-
- private string GetLanguage()
- {
- return _config.Configuration.PreferredMetadataLanguage;
- }
-
- private async Task<string> GetXml(string path, CancellationToken cancellationToken)
- {
- _logger.Info("xmltv path: {0}", path);
-
- if (!path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return path;
- }
-
- var cacheFilename = DateTime.UtcNow.DayOfYear.ToString(CultureInfo.InvariantCulture) + "-" + DateTime.UtcNow.Hour.ToString(CultureInfo.InvariantCulture) + ".xml";
- var cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename);
- if (File.Exists(cacheFile))
- {
- return cacheFile;
- }
-
- _logger.Info("Downloading xmltv listings from {0}", path);
-
- var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = path,
- Progress = new Progress<Double>(),
- DecompressionMethod = DecompressionMethods.GZip,
-
- // It's going to come back gzipped regardless of this value
- // So we need to make sure the decompression method is set to gzip
- EnableHttpCompression = true
-
- }).ConfigureAwait(false);
-
- Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
-
- using (var stream = File.OpenRead(tempFile))
- {
- using (var reader = new StreamReader(stream, Encoding.UTF8))
- {
- using (var fileStream = File.OpenWrite(cacheFile))
- {
- using (var writer = new StreamWriter(fileStream))
- {
- while (!reader.EndOfStream)
- {
- writer.WriteLine(reader.ReadLine());
- }
- }
- }
- }
- }
-
- _logger.Debug("Returning xmltv path {0}", cacheFile);
- return cacheFile;
- }
-
- public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, string channelName, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
- {
- if (!await EmbyTV.EmbyTVRegistration.Instance.EnableXmlTv().ConfigureAwait(false))
- {
- var length = endDateUtc - startDateUtc;
- if (length.TotalDays > 1)
- {
- endDateUtc = startDateUtc.AddDays(1);
- }
- }
-
- var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
- var reader = new XmlTvReader(path, GetLanguage(), null);
-
- var results = reader.GetProgrammes(channelNumber, startDateUtc, endDateUtc, cancellationToken);
- return results.Select(p => GetProgramInfo(p, info));
- }
-
- private ProgramInfo GetProgramInfo(XmlTvProgram p, ListingsProviderInfo info)
- {
- var programInfo = new ProgramInfo
- {
- ChannelId = p.ChannelId,
- EndDate = GetDate(p.EndDate),
- EpisodeNumber = p.Episode == null ? null : p.Episode.Episode,
- EpisodeTitle = p.Episode == null ? null : p.Episode.Title,
- Genres = p.Categories,
- Id = String.Format("{0}_{1:O}", p.ChannelId, p.StartDate), // Construct an id from the channel and start date,
- StartDate = GetDate(p.StartDate),
- Name = p.Title,
- Overview = p.Description,
- ShortOverview = p.Description,
- ProductionYear = !p.CopyrightDate.HasValue ? (int?)null : p.CopyrightDate.Value.Year,
- SeasonNumber = p.Episode == null ? null : p.Episode.Series,
- IsSeries = p.Episode != null,
- IsRepeat = p.IsRepeat,
- IsPremiere = p.Premiere != null,
- IsKids = p.Categories.Any(c => info.KidsCategories.Contains(c, StringComparer.InvariantCultureIgnoreCase)),
- IsMovie = p.Categories.Any(c => info.MovieCategories.Contains(c, StringComparer.InvariantCultureIgnoreCase)),
- IsNews = p.Categories.Any(c => info.NewsCategories.Contains(c, StringComparer.InvariantCultureIgnoreCase)),
- IsSports = p.Categories.Any(c => info.SportsCategories.Contains(c, StringComparer.InvariantCultureIgnoreCase)),
- ImageUrl = p.Icon != null && !String.IsNullOrEmpty(p.Icon.Source) ? p.Icon.Source : null,
- HasImage = p.Icon != null && !String.IsNullOrEmpty(p.Icon.Source),
- OfficialRating = p.Rating != null && !String.IsNullOrEmpty(p.Rating.Value) ? p.Rating.Value : null,
- CommunityRating = p.StarRating.HasValue ? p.StarRating.Value : (float?)null,
- SeriesId = p.Episode != null ? p.Title.GetMD5().ToString("N") : null
- };
-
- if (programInfo.IsMovie)
- {
- programInfo.IsSeries = false;
- programInfo.EpisodeNumber = null;
- programInfo.EpisodeTitle = null;
- }
-
- return programInfo;
- }
-
- private DateTime GetDate(DateTime date)
- {
- if (date.Kind != DateTimeKind.Utc)
- {
- date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
- }
- return date;
- }
-
- public async Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken)
- {
- // Add the channel image url
- var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
- var reader = new XmlTvReader(path, GetLanguage(), null);
- var results = reader.GetChannels().ToList();
-
- if (channels != null)
- {
- channels.ForEach(c =>
- {
- var channelNumber = info.GetMappedChannel(c.Number);
- var match = results.FirstOrDefault(r => string.Equals(r.Id, channelNumber, StringComparison.OrdinalIgnoreCase));
-
- if (match != null && match.Icon != null && !String.IsNullOrEmpty(match.Icon.Source))
- {
- c.ImageUrl = match.Icon.Source;
- }
- });
- }
- }
-
- public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings)
- {
- // Assume all urls are valid. check files for existence
- if (!info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase) && !File.Exists(info.Path))
- {
- throw new FileNotFoundException("Could not find the XmlTv file specified:", info.Path);
- }
-
- return Task.FromResult(true);
- }
-
- public async Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location)
- {
- // In theory this should never be called because there is always only one lineup
- var path = await GetXml(info.Path, CancellationToken.None).ConfigureAwait(false);
- var reader = new XmlTvReader(path, GetLanguage(), null);
- var results = reader.GetChannels();
-
- // Should this method be async?
- return results.Select(c => new NameIdPair() { Id = c.Id, Name = c.DisplayName }).ToList();
- }
-
- public async Task<List<ChannelInfo>> GetChannels(ListingsProviderInfo info, CancellationToken cancellationToken)
- {
- // In theory this should never be called because there is always only one lineup
- var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
- var reader = new XmlTvReader(path, GetLanguage(), null);
- var results = reader.GetChannels();
-
- // Should this method be async?
- return results.Select(c => new ChannelInfo()
- {
- Id = c.Id,
- Name = c.DisplayName,
- ImageUrl = c.Icon != null && !String.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null,
- Number = c.Id
-
- }).ToList();
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs
deleted file mode 100644
index 336c32bae..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveStreamHelper.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.LiveTv
-{
- public class LiveStreamHelper
- {
- private readonly IMediaEncoder _mediaEncoder;
- private readonly ILogger _logger;
-
- public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger)
- {
- _mediaEncoder = mediaEncoder;
- _logger = logger;
- }
-
- public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
- {
- var originalRuntime = mediaSource.RunTimeTicks;
-
- var now = DateTime.UtcNow;
-
- var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
- {
- InputPath = mediaSource.Path,
- Protocol = mediaSource.Protocol,
- MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
- ExtractChapters = false,
- AnalyzeDurationSections = 2
-
- }, cancellationToken).ConfigureAwait(false);
-
- _logger.Info("Live tv media info probe took {0} seconds", (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture));
-
- mediaSource.Bitrate = info.Bitrate;
- mediaSource.Container = info.Container;
- mediaSource.Formats = info.Formats;
- mediaSource.MediaStreams = info.MediaStreams;
- mediaSource.RunTimeTicks = info.RunTimeTicks;
- mediaSource.Size = info.Size;
- mediaSource.Timestamp = info.Timestamp;
- mediaSource.Video3DFormat = info.Video3DFormat;
- mediaSource.VideoType = info.VideoType;
-
- mediaSource.DefaultSubtitleStreamIndex = null;
-
- // Null this out so that it will be treated like a live stream
- if (!originalRuntime.HasValue)
- {
- mediaSource.RunTimeTicks = null;
- }
-
- var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio);
-
- if (audioStream == null || audioStream.Index == -1)
- {
- mediaSource.DefaultAudioStreamIndex = null;
- }
- else
- {
- mediaSource.DefaultAudioStreamIndex = audioStream.Index;
- }
-
- var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video);
- if (videoStream != null)
- {
- if (!videoStream.BitRate.HasValue)
- {
- var width = videoStream.Width ?? 1920;
-
- if (width >= 1900)
- {
- videoStream.BitRate = 8000000;
- }
-
- else if (width >= 1260)
- {
- videoStream.BitRate = 3000000;
- }
-
- else if (width >= 700)
- {
- videoStream.BitRate = 1000000;
- }
- }
-
- // This is coming up false and preventing stream copy
- videoStream.IsAVC = null;
- }
-
- // Try to estimate this
- if (!mediaSource.Bitrate.HasValue)
- {
- var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum();
-
- if (total > 0)
- {
- mediaSource.Bitrate = total;
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
deleted file mode 100644
index 57d1d79e1..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvConfigurationFactory.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.LiveTv;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Server.Implementations.LiveTv
-{
- public class LiveTvConfigurationFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- ConfigurationType = typeof(LiveTvOptions),
- Key = "livetv"
- }
- };
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
deleted file mode 100644
index c7a2d295d..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ /dev/null
@@ -1,390 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.LiveTv
-{
- public class LiveTvDtoService
- {
- private readonly ILogger _logger;
- private readonly IImageProcessor _imageProcessor;
-
- private readonly IUserDataManager _userDataManager;
- private readonly IDtoService _dtoService;
- private readonly IApplicationHost _appHost;
- private readonly ILibraryManager _libraryManager;
-
- public LiveTvDtoService(IDtoService dtoService, IUserDataManager userDataManager, IImageProcessor imageProcessor, ILogger logger, IApplicationHost appHost, ILibraryManager libraryManager)
- {
- _dtoService = dtoService;
- _userDataManager = userDataManager;
- _imageProcessor = imageProcessor;
- _logger = logger;
- _appHost = appHost;
- _libraryManager = libraryManager;
- }
-
- public TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service, LiveTvProgram program, LiveTvChannel channel)
- {
- var dto = new TimerInfoDto
- {
- Id = GetInternalTimerId(service.Name, info.Id).ToString("N"),
- Overview = info.Overview,
- EndDate = info.EndDate,
- Name = info.Name,
- StartDate = info.StartDate,
- ExternalId = info.Id,
- ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"),
- Status = info.Status,
- SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId) ? null : GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N"),
- PrePaddingSeconds = info.PrePaddingSeconds,
- PostPaddingSeconds = info.PostPaddingSeconds,
- IsPostPaddingRequired = info.IsPostPaddingRequired,
- IsPrePaddingRequired = info.IsPrePaddingRequired,
- KeepUntil = info.KeepUntil,
- ExternalChannelId = info.ChannelId,
- ExternalSeriesTimerId = info.SeriesTimerId,
- ServiceName = service.Name,
- ExternalProgramId = info.ProgramId,
- Priority = info.Priority,
- RunTimeTicks = (info.EndDate - info.StartDate).Ticks,
- ServerId = _appHost.SystemId
- };
-
- if (!string.IsNullOrEmpty(info.ProgramId))
- {
- dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N");
- }
-
- if (program != null)
- {
- dto.ProgramInfo = _dtoService.GetBaseItemDto(program, new DtoOptions());
-
- if (info.Status != RecordingStatus.Cancelled && info.Status != RecordingStatus.Error)
- {
- dto.ProgramInfo.TimerId = dto.Id;
- dto.ProgramInfo.Status = info.Status.ToString();
- }
-
- dto.ProgramInfo.SeriesTimerId = dto.SeriesTimerId;
- }
-
- if (channel != null)
- {
- dto.ChannelName = channel.Name;
- }
-
- return dto;
- }
-
- public SeriesTimerInfoDto GetSeriesTimerInfoDto(SeriesTimerInfo info, ILiveTvService service, string channelName)
- {
- var dto = new SeriesTimerInfoDto
- {
- Id = GetInternalSeriesTimerId(service.Name, info.Id).ToString("N"),
- Overview = info.Overview,
- EndDate = info.EndDate,
- Name = info.Name,
- StartDate = info.StartDate,
- ExternalId = info.Id,
- PrePaddingSeconds = info.PrePaddingSeconds,
- PostPaddingSeconds = info.PostPaddingSeconds,
- IsPostPaddingRequired = info.IsPostPaddingRequired,
- IsPrePaddingRequired = info.IsPrePaddingRequired,
- Days = info.Days,
- Priority = info.Priority,
- RecordAnyChannel = info.RecordAnyChannel,
- RecordAnyTime = info.RecordAnyTime,
- SkipEpisodesInLibrary = info.SkipEpisodesInLibrary,
- KeepUpTo = info.KeepUpTo,
- KeepUntil = info.KeepUntil,
- RecordNewOnly = info.RecordNewOnly,
- ExternalChannelId = info.ChannelId,
- ExternalProgramId = info.ProgramId,
- ServiceName = service.Name,
- ChannelName = channelName,
- ServerId = _appHost.SystemId
- };
-
- if (!string.IsNullOrEmpty(info.ChannelId))
- {
- dto.ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N");
- }
-
- if (!string.IsNullOrEmpty(info.ProgramId))
- {
- dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N");
- }
-
- 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;
- }
-
- public DayPattern? GetDayPattern(List<DayOfWeek> days)
- {
- DayPattern? pattern = null;
-
- if (days.Count > 0)
- {
- if (days.Count == 7)
- {
- pattern = DayPattern.Daily;
- }
- else if (days.Count == 2)
- {
- if (days.Contains(DayOfWeek.Saturday) && days.Contains(DayOfWeek.Sunday))
- {
- pattern = DayPattern.Weekends;
- }
- }
- else if (days.Count == 5)
- {
- if (days.Contains(DayOfWeek.Monday) && days.Contains(DayOfWeek.Tuesday) && days.Contains(DayOfWeek.Wednesday) && days.Contains(DayOfWeek.Thursday) && days.Contains(DayOfWeek.Friday))
- {
- pattern = DayPattern.Weekdays;
- }
- }
- }
-
- return pattern;
- }
-
- public LiveTvTunerInfoDto GetTunerInfoDto(string serviceName, LiveTvTunerInfo info, string channelName)
- {
- var dto = new LiveTvTunerInfoDto
- {
- Name = info.Name,
- Id = info.Id,
- Clients = info.Clients,
- ProgramName = info.ProgramName,
- SourceType = info.SourceType,
- Status = info.Status,
- ChannelName = channelName,
- Url = info.Url,
- CanReset = info.CanReset
- };
-
- if (!string.IsNullOrEmpty(info.ChannelId))
- {
- dto.ChannelId = GetInternalChannelId(serviceName, info.ChannelId).ToString("N");
- }
-
- if (!string.IsNullOrEmpty(info.RecordingId))
- {
- dto.RecordingId = GetInternalRecordingId(serviceName, info.RecordingId).ToString("N");
- }
-
- return dto;
- }
-
- internal string GetImageTag(IHasImages info)
- {
- try
- {
- return _imageProcessor.GetImageCacheTag(info, ImageType.Primary);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting image info for {0}", ex, info.Name);
- }
-
- return null;
- }
-
- private const string InternalVersionNumber = "4";
-
- public Guid GetInternalChannelId(string serviceName, string externalId)
- {
- var name = serviceName + externalId + InternalVersionNumber;
-
- return _libraryManager.GetNewItemId(name.ToLower(), typeof(LiveTvChannel));
- }
-
- public Guid GetInternalTimerId(string serviceName, string externalId)
- {
- var name = serviceName + externalId + InternalVersionNumber;
-
- return name.ToLower().GetMD5();
- }
-
- public Guid GetInternalSeriesTimerId(string serviceName, string externalId)
- {
- var name = serviceName + externalId + InternalVersionNumber;
-
- return name.ToLower().GetMD5();
- }
-
- public Guid GetInternalProgramId(string serviceName, string externalId)
- {
- var name = serviceName + externalId + InternalVersionNumber;
-
- return _libraryManager.GetNewItemId(name.ToLower(), typeof(LiveTvProgram));
- }
-
- public Guid GetInternalRecordingId(string serviceName, string externalId)
- {
- var name = serviceName + externalId + InternalVersionNumber + "0";
-
- return _libraryManager.GetNewItemId(name.ToLower(), typeof(ILiveTvRecording));
- }
-
- public async Task<TimerInfo> GetTimerInfo(TimerInfoDto dto, bool isNew, LiveTvManager liveTv, CancellationToken cancellationToken)
- {
- var info = new TimerInfo
- {
- Overview = dto.Overview,
- EndDate = dto.EndDate,
- Name = dto.Name,
- StartDate = dto.StartDate,
- Status = dto.Status,
- PrePaddingSeconds = dto.PrePaddingSeconds,
- PostPaddingSeconds = dto.PostPaddingSeconds,
- IsPostPaddingRequired = dto.IsPostPaddingRequired,
- IsPrePaddingRequired = dto.IsPrePaddingRequired,
- KeepUntil = dto.KeepUntil,
- Priority = dto.Priority,
- SeriesTimerId = dto.ExternalSeriesTimerId,
- ProgramId = dto.ExternalProgramId,
- ChannelId = dto.ExternalChannelId,
- Id = dto.ExternalId
- };
-
- // Convert internal server id's to external tv provider id's
- if (!isNew && !string.IsNullOrEmpty(dto.Id) && string.IsNullOrEmpty(info.Id))
- {
- var timer = await liveTv.GetSeriesTimer(dto.Id, cancellationToken).ConfigureAwait(false);
-
- info.Id = timer.ExternalId;
- }
-
- if (!string.IsNullOrEmpty(dto.ChannelId) && string.IsNullOrEmpty(info.ChannelId))
- {
- var channel = liveTv.GetInternalChannel(dto.ChannelId);
-
- if (channel != null)
- {
- info.ChannelId = channel.ExternalId;
- }
- }
-
- if (!string.IsNullOrEmpty(dto.ProgramId) && string.IsNullOrEmpty(info.ProgramId))
- {
- var program = liveTv.GetInternalProgram(dto.ProgramId);
-
- if (program != null)
- {
- info.ProgramId = program.ExternalId;
- }
- }
-
- if (!string.IsNullOrEmpty(dto.SeriesTimerId) && string.IsNullOrEmpty(info.SeriesTimerId))
- {
- var timer = await liveTv.GetSeriesTimer(dto.SeriesTimerId, cancellationToken).ConfigureAwait(false);
-
- if (timer != null)
- {
- info.SeriesTimerId = timer.ExternalId;
- }
- }
-
- return info;
- }
-
- public async Task<SeriesTimerInfo> GetSeriesTimerInfo(SeriesTimerInfoDto dto, bool isNew, LiveTvManager liveTv, CancellationToken cancellationToken)
- {
- var info = new SeriesTimerInfo
- {
- Overview = dto.Overview,
- EndDate = dto.EndDate,
- Name = dto.Name,
- StartDate = dto.StartDate,
- PrePaddingSeconds = dto.PrePaddingSeconds,
- PostPaddingSeconds = dto.PostPaddingSeconds,
- IsPostPaddingRequired = dto.IsPostPaddingRequired,
- IsPrePaddingRequired = dto.IsPrePaddingRequired,
- Days = dto.Days,
- Priority = dto.Priority,
- RecordAnyChannel = dto.RecordAnyChannel,
- RecordAnyTime = dto.RecordAnyTime,
- SkipEpisodesInLibrary = dto.SkipEpisodesInLibrary,
- KeepUpTo = dto.KeepUpTo,
- KeepUntil = dto.KeepUntil,
- RecordNewOnly = dto.RecordNewOnly,
- ProgramId = dto.ExternalProgramId,
- ChannelId = dto.ExternalChannelId,
- Id = dto.ExternalId
- };
-
- // Convert internal server id's to external tv provider id's
- if (!isNew && !string.IsNullOrEmpty(dto.Id) && string.IsNullOrEmpty(info.Id))
- {
- var timer = await liveTv.GetSeriesTimer(dto.Id, cancellationToken).ConfigureAwait(false);
-
- info.Id = timer.ExternalId;
- }
-
- if (!string.IsNullOrEmpty(dto.ChannelId) && string.IsNullOrEmpty(info.ChannelId))
- {
- var channel = liveTv.GetInternalChannel(dto.ChannelId);
-
- if (channel != null)
- {
- info.ChannelId = channel.ExternalId;
- }
- }
-
- if (!string.IsNullOrEmpty(dto.ProgramId) && string.IsNullOrEmpty(info.ProgramId))
- {
- var program = liveTv.GetInternalProgram(dto.ProgramId);
-
- if (program != null)
- {
- info.ProgramId = program.ExternalId;
- }
- }
-
- return info;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
deleted file mode 100644
index 93fc5459a..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ /dev/null
@@ -1,3022 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using IniParser;
-using IniParser.Model;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Server.Implementations.LiveTv.Listings;
-
-namespace MediaBrowser.Server.Implementations.LiveTv
-{
- /// <summary>
- /// Class LiveTvManager
- /// </summary>
- public class LiveTvManager : ILiveTvManager, IDisposable
- {
- private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly IItemRepository _itemRepo;
- private readonly IUserManager _userManager;
- private readonly IUserDataManager _userDataManager;
- private readonly ILibraryManager _libraryManager;
- private readonly ITaskManager _taskManager;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly IProviderManager _providerManager;
- private readonly ISecurityManager _security;
-
- private readonly IDtoService _dtoService;
- private readonly ILocalizationManager _localization;
-
- private readonly LiveTvDtoService _tvDtoService;
-
- private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
-
- private readonly SemaphoreSlim _refreshRecordingsLock = new SemaphoreSlim(1, 1);
-
- private readonly List<ITunerHost> _tunerHosts = new List<ITunerHost>();
- private readonly List<IListingsProvider> _listingProviders = new List<IListingsProvider>();
- private readonly IFileSystem _fileSystem;
-
- public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
- public event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
-
- public string GetEmbyTvActiveRecordingPath(string id)
- {
- return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
- }
-
- public Task<LiveStream> GetEmbyTvLiveStream(string id)
- {
- return EmbyTV.EmbyTV.Current.GetLiveStream(id);
- }
-
- public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager, IFileSystem fileSystem, ISecurityManager security)
- {
- _config = config;
- _logger = logger;
- _itemRepo = itemRepo;
- _userManager = userManager;
- _libraryManager = libraryManager;
- _taskManager = taskManager;
- _localization = localization;
- _jsonSerializer = jsonSerializer;
- _providerManager = providerManager;
- _fileSystem = fileSystem;
- _security = security;
- _dtoService = dtoService;
- _userDataManager = userDataManager;
-
- _tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger, appHost, _libraryManager);
- }
-
- /// <summary>
- /// Gets the services.
- /// </summary>
- /// <value>The services.</value>
- public IReadOnlyList<ILiveTvService> Services
- {
- get { return _services; }
- }
-
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration<LiveTvOptions>("livetv");
- }
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="services">The services.</param>
- /// <param name="tunerHosts">The tuner hosts.</param>
- /// <param name="listingProviders">The listing providers.</param>
- public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
- {
- _services.AddRange(services);
- _tunerHosts.AddRange(tunerHosts);
- _listingProviders.AddRange(listingProviders);
-
- foreach (var service in _services)
- {
- service.DataSourceChanged += service_DataSourceChanged;
- service.RecordingStatusChanged += Service_RecordingStatusChanged;
- }
- }
-
- private void Service_RecordingStatusChanged(object sender, RecordingStatusChangedEventArgs e)
- {
- _lastRecordingRefreshTime = DateTime.MinValue;
- }
-
- public List<ITunerHost> TunerHosts
- {
- get { return _tunerHosts; }
- }
-
- public List<IListingsProvider> ListingProviders
- {
- get { return _listingProviders; }
- }
-
- void service_DataSourceChanged(object sender, EventArgs e)
- {
- if (!_isDisposed)
- {
- _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
- }
- }
-
- public async Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
-
- var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
-
- var internalQuery = new InternalItemsQuery(user)
- {
- IsMovie = query.IsMovie,
- IsNews = query.IsNews,
- IsKids = query.IsKids,
- IsSports = query.IsSports,
- IsSeries = query.IsSeries,
- IncludeItemTypes = new[] { typeof(LiveTvChannel).Name },
- SortOrder = query.SortOrder ?? SortOrder.Ascending,
- TopParentIds = new[] { topFolder.Id.ToString("N") },
- IsFavorite = query.IsFavorite,
- IsLiked = query.IsLiked,
- StartIndex = query.StartIndex,
- Limit = query.Limit
- };
-
- internalQuery.OrderBy.AddRange(query.SortBy.Select(i => new Tuple<string, SortOrder>(i, query.SortOrder ?? SortOrder.Ascending)));
-
- if (query.EnableFavoriteSorting)
- {
- internalQuery.OrderBy.Insert(0, new Tuple<string, SortOrder>(ItemSortBy.IsFavoriteOrLiked, SortOrder.Descending));
- }
-
- if (!internalQuery.OrderBy.Any(i => string.Equals(i.Item1, ItemSortBy.SortName, StringComparison.OrdinalIgnoreCase)))
- {
- internalQuery.OrderBy.Add(new Tuple<string, SortOrder>(ItemSortBy.SortName, SortOrder.Ascending));
- }
-
- var channelResult = _libraryManager.GetItemsResult(internalQuery);
-
- var result = new QueryResult<LiveTvChannel>
- {
- Items = channelResult.Items.Cast<LiveTvChannel>().ToArray(),
- TotalRecordCount = channelResult.TotalRecordCount
- };
-
- return result;
- }
-
- public LiveTvChannel GetInternalChannel(string id)
- {
- return GetInternalChannel(new Guid(id));
- }
-
- private LiveTvChannel GetInternalChannel(Guid id)
- {
- return _libraryManager.GetItemById(id) as LiveTvChannel;
- }
-
- internal LiveTvProgram GetInternalProgram(string id)
- {
- return _libraryManager.GetItemById(id) as LiveTvProgram;
- }
-
- internal LiveTvProgram GetInternalProgram(Guid id)
- {
- return _libraryManager.GetItemById(id) as LiveTvProgram;
- }
-
- public async Task<BaseItem> GetInternalRecording(string id, CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- throw new ArgumentNullException("id");
- }
-
- var result = await GetInternalRecordings(new RecordingQuery
- {
- Id = id
-
- }, cancellationToken).ConfigureAwait(false);
-
- return result.Items.FirstOrDefault();
- }
-
- public async Task<MediaSourceInfo> GetRecordingStream(string id, CancellationToken cancellationToken)
- {
- var info = await GetLiveStream(id, null, false, cancellationToken).ConfigureAwait(false);
-
- return info.Item1;
- }
-
- public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken)
- {
- return await GetLiveStream(id, mediaSourceId, true, cancellationToken).ConfigureAwait(false);
- }
-
- public async Task<IEnumerable<MediaSourceInfo>> GetRecordingMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
- {
- var baseItem = (BaseItem)item;
- var service = GetService(baseItem);
-
- return await service.GetRecordingStreamMediaSources(baseItem.ExternalId, cancellationToken).ConfigureAwait(false);
- }
-
- public async Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
- {
- var baseItem = (LiveTvChannel)item;
- var service = GetService(baseItem);
-
- var sources = await service.GetChannelStreamMediaSources(baseItem.ExternalId, cancellationToken).ConfigureAwait(false);
-
- if (sources.Count == 0)
- {
- throw new NotImplementedException();
- }
-
- var list = sources.ToList();
-
- foreach (var source in list)
- {
- Normalize(source, service, baseItem.ChannelType == ChannelType.TV);
- }
-
- return list;
- }
-
- private ILiveTvService GetService(ILiveTvRecording item)
- {
- return GetService(item.ServiceName);
- }
-
- private ILiveTvService GetService(BaseItem item)
- {
- return GetService(item.ServiceName);
- }
-
- private ILiveTvService GetService(string name)
- {
- return _services.FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
- }
-
- private async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> GetLiveStream(string id, string mediaSourceId, bool isChannel, CancellationToken cancellationToken)
- {
- if (string.Equals(id, mediaSourceId, StringComparison.OrdinalIgnoreCase))
- {
- mediaSourceId = null;
- }
-
- MediaSourceInfo info;
- bool isVideo;
- ILiveTvService service;
- IDirectStreamProvider directStreamProvider = null;
-
- if (isChannel)
- {
- var channel = GetInternalChannel(id);
- isVideo = channel.ChannelType == ChannelType.TV;
- service = GetService(channel);
- _logger.Info("Opening channel stream from {0}, external channel Id: {1}", service.Name, channel.ExternalId);
-
- var supportsManagedStream = service as ISupportsDirectStreamProvider;
- if (supportsManagedStream != null)
- {
- var streamInfo = await supportsManagedStream.GetChannelStreamWithDirectStreamProvider(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
- info = streamInfo.Item1;
- directStreamProvider = streamInfo.Item2;
- }
- else
- {
- info = await service.GetChannelStream(channel.ExternalId, mediaSourceId, cancellationToken).ConfigureAwait(false);
- }
- info.RequiresClosing = true;
-
- if (info.RequiresClosing)
- {
- var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
-
- info.LiveStreamId = idPrefix + info.Id;
- }
- }
- else
- {
- var recording = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false);
- isVideo = !string.Equals(recording.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase);
- service = GetService(recording);
-
- _logger.Info("Opening recording stream from {0}, external recording Id: {1}", service.Name, recording.ExternalId);
- info = await service.GetRecordingStream(recording.ExternalId, null, cancellationToken).ConfigureAwait(false);
- info.RequiresClosing = true;
-
- if (info.RequiresClosing)
- {
- var idPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
-
- info.LiveStreamId = idPrefix + info.Id;
- }
- }
-
- _logger.Info("Live stream info: {0}", _jsonSerializer.SerializeToString(info));
- Normalize(info, service, isVideo);
-
- return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info, directStreamProvider);
- }
-
- private void Normalize(MediaSourceInfo mediaSource, ILiveTvService service, bool isVideo)
- {
- if (mediaSource.MediaStreams.Count == 0)
- {
- if (isVideo)
- {
- mediaSource.MediaStreams.AddRange(new List<MediaStream>
- {
- new MediaStream
- {
- Type = MediaStreamType.Video,
- // Set the index to -1 because we don't know the exact index of the video stream within the container
- Index = -1,
-
- // Set to true if unknown to enable deinterlacing
- IsInterlaced = true
- },
- new MediaStream
- {
- Type = MediaStreamType.Audio,
- // Set the index to -1 because we don't know the exact index of the audio stream within the container
- Index = -1
- }
- });
- }
- else
- {
- mediaSource.MediaStreams.AddRange(new List<MediaStream>
- {
- new MediaStream
- {
- Type = MediaStreamType.Audio,
- // Set the index to -1 because we don't know the exact index of the audio stream within the container
- Index = -1
- }
- });
- }
- }
-
- // Clean some bad data coming from providers
- foreach (var stream in mediaSource.MediaStreams)
- {
- if (stream.BitRate.HasValue && stream.BitRate <= 0)
- {
- stream.BitRate = null;
- }
- if (stream.Channels.HasValue && stream.Channels <= 0)
- {
- stream.Channels = null;
- }
- if (stream.AverageFrameRate.HasValue && stream.AverageFrameRate <= 0)
- {
- stream.AverageFrameRate = null;
- }
- if (stream.RealFrameRate.HasValue && stream.RealFrameRate <= 0)
- {
- stream.RealFrameRate = null;
- }
- if (stream.Width.HasValue && stream.Width <= 0)
- {
- stream.Width = null;
- }
- if (stream.Height.HasValue && stream.Height <= 0)
- {
- stream.Height = null;
- }
- if (stream.SampleRate.HasValue && stream.SampleRate <= 0)
- {
- stream.SampleRate = null;
- }
- if (stream.Level.HasValue && stream.Level <= 0)
- {
- stream.Level = null;
- }
- }
-
- var indexes = mediaSource.MediaStreams.Select(i => i.Index).Distinct().ToList();
-
- // If there are duplicate stream indexes, set them all to unknown
- if (indexes.Count != mediaSource.MediaStreams.Count)
- {
- foreach (var stream in mediaSource.MediaStreams)
- {
- stream.Index = -1;
- }
- }
-
- // Set the total bitrate if not already supplied
- if (!mediaSource.Bitrate.HasValue)
- {
- var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum();
-
- if (total > 0)
- {
- mediaSource.Bitrate = total;
- }
- }
-
- if (!(service is EmbyTV.EmbyTV))
- {
- // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
- mediaSource.SupportsDirectStream = false;
- mediaSource.SupportsTranscoding = true;
- foreach (var stream in mediaSource.MediaStreams)
- {
- if (stream.Type == MediaStreamType.Video && string.IsNullOrWhiteSpace(stream.NalLengthSize))
- {
- stream.NalLengthSize = "0";
- }
- }
- }
- }
-
- private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, Guid parentFolderId, CancellationToken cancellationToken)
- {
- var isNew = false;
- var forceUpdate = false;
-
- var id = _tvDtoService.GetInternalChannelId(serviceName, channelInfo.Id);
-
- var item = _itemRepo.RetrieveItem(id) as LiveTvChannel;
-
- if (item == null)
- {
- item = new LiveTvChannel
- {
- Name = channelInfo.Name,
- Id = id,
- DateCreated = DateTime.UtcNow,
- };
-
- isNew = true;
- }
-
- if (!string.Equals(channelInfo.Id, item.ExternalId))
- {
- isNew = true;
- }
- item.ExternalId = channelInfo.Id;
-
- if (!item.ParentId.Equals(parentFolderId))
- {
- isNew = true;
- }
- item.ParentId = parentFolderId;
-
- item.ChannelType = channelInfo.ChannelType;
- item.ServiceName = serviceName;
- item.Number = channelInfo.Number;
-
- //if (!string.Equals(item.ProviderImageUrl, channelInfo.ImageUrl, StringComparison.OrdinalIgnoreCase))
- //{
- // isNew = true;
- // replaceImages.Add(ImageType.Primary);
- //}
- //if (!string.Equals(item.ProviderImagePath, channelInfo.ImagePath, StringComparison.OrdinalIgnoreCase))
- //{
- // isNew = true;
- // replaceImages.Add(ImageType.Primary);
- //}
-
- if (!item.HasImage(ImageType.Primary))
- {
- if (!string.IsNullOrWhiteSpace(channelInfo.ImagePath))
- {
- item.SetImagePath(ImageType.Primary, channelInfo.ImagePath);
- forceUpdate = true;
- }
- else if (!string.IsNullOrWhiteSpace(channelInfo.ImageUrl))
- {
- item.SetImagePath(ImageType.Primary, channelInfo.ImageUrl);
- forceUpdate = true;
- }
- }
-
- if (string.IsNullOrEmpty(item.Name))
- {
- item.Name = channelInfo.Name;
- }
-
- if (isNew)
- {
- await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
- }
- else if (forceUpdate)
- {
- await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
- }
-
- await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
- {
- ForceSave = isNew || forceUpdate
-
- }, cancellationToken);
-
- return item;
- }
-
- private Tuple<LiveTvProgram, bool, bool> GetProgram(ProgramInfo info, Dictionary<Guid, LiveTvProgram> allExistingPrograms, LiveTvChannel channel, ChannelType channelType, string serviceName, CancellationToken cancellationToken)
- {
- var id = _tvDtoService.GetInternalProgramId(serviceName, info.Id);
-
- LiveTvProgram item = null;
- allExistingPrograms.TryGetValue(id, out item);
-
- var isNew = false;
- var forceUpdate = false;
-
- if (item == null)
- {
- isNew = true;
- item = new LiveTvProgram
- {
- Name = info.Name,
- Id = id,
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow,
- ExternalEtag = info.Etag
- };
- }
-
- var seriesId = info.SeriesId;
-
- if (!item.ParentId.Equals(channel.Id))
- {
- forceUpdate = true;
- }
- item.ParentId = channel.Id;
-
- //item.ChannelType = channelType;
- if (!string.Equals(item.ServiceName, serviceName, StringComparison.Ordinal))
- {
- forceUpdate = true;
- }
- item.ServiceName = serviceName;
-
- item.Audio = info.Audio;
- item.ChannelId = channel.Id.ToString("N");
- item.CommunityRating = item.CommunityRating ?? info.CommunityRating;
-
- item.EpisodeTitle = info.EpisodeTitle;
- item.ExternalId = info.Id;
- 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;
- item.IsLive = info.IsLive;
- item.IsMovie = info.IsMovie;
- item.IsNews = info.IsNews;
- item.IsPremiere = info.IsPremiere;
- item.IsRepeat = info.IsRepeat;
- item.IsSeries = info.IsSeries;
- item.IsSports = info.IsSports;
- item.Name = info.Name;
- item.OfficialRating = item.OfficialRating ?? info.OfficialRating;
- item.Overview = item.Overview ?? info.Overview;
- item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
-
- if (item.StartDate != info.StartDate)
- {
- forceUpdate = true;
- }
- item.StartDate = info.StartDate;
-
- if (item.EndDate != info.EndDate)
- {
- forceUpdate = true;
- }
- item.EndDate = info.EndDate;
-
- item.HomePageUrl = info.HomePageUrl;
-
- item.ProductionYear = info.ProductionYear;
-
- if (!info.IsSeries || info.IsRepeat)
- {
- item.PremiereDate = info.OriginalAirDate;
- }
-
- item.IndexNumber = info.EpisodeNumber;
- item.ParentIndexNumber = info.SeasonNumber;
-
- if (!item.HasImage(ImageType.Primary))
- {
- if (!string.IsNullOrWhiteSpace(info.ImagePath))
- {
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImagePath,
- Type = ImageType.Primary,
- IsPlaceholder = true
- }, 0);
- }
- else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
- {
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImageUrl,
- Type = ImageType.Primary,
- IsPlaceholder = true
- }, 0);
- }
- }
-
- var isUpdated = false;
- if (isNew)
- {
- }
- else if (forceUpdate || string.IsNullOrWhiteSpace(info.Etag))
- {
- isUpdated = true;
- }
- else
- {
- // Increment this whenver some internal change deems it necessary
- var etag = info.Etag + "4";
-
- if (!string.Equals(etag, item.ExternalEtag, StringComparison.OrdinalIgnoreCase))
- {
- item.ExternalEtag = etag;
- isUpdated = true;
- }
- }
-
- return new Tuple<LiveTvProgram, bool, bool>(item, isNew, isUpdated);
- }
-
- private async Task<Guid> CreateRecordingRecord(RecordingInfo info, string serviceName, Guid parentFolderId, CancellationToken cancellationToken)
- {
- var isNew = false;
-
- var id = _tvDtoService.GetInternalRecordingId(serviceName, info.Id);
-
- var item = _itemRepo.RetrieveItem(id);
-
- if (item == null)
- {
- if (info.ChannelType == ChannelType.TV)
- {
- item = new LiveTvVideoRecording
- {
- Name = info.Name,
- Id = id,
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow,
- VideoType = VideoType.VideoFile
- };
- }
- else
- {
- item = new LiveTvAudioRecording
- {
- Name = info.Name,
- Id = id,
- DateCreated = DateTime.UtcNow,
- DateModified = DateTime.UtcNow
- };
- }
-
- isNew = true;
- }
-
- item.ChannelId = _tvDtoService.GetInternalChannelId(serviceName, info.ChannelId).ToString("N");
- item.CommunityRating = info.CommunityRating;
- item.OfficialRating = info.OfficialRating;
- item.Overview = info.Overview;
- item.EndDate = info.EndDate;
- item.Genres = info.Genres;
- item.PremiereDate = info.OriginalAirDate;
-
- var recording = (ILiveTvRecording)item;
-
- recording.ExternalId = info.Id;
-
- var dataChanged = false;
-
- recording.Audio = info.Audio;
- recording.EndDate = info.EndDate;
- recording.EpisodeTitle = info.EpisodeTitle;
- recording.IsHD = info.IsHD;
- recording.IsKids = info.IsKids;
- recording.IsLive = info.IsLive;
- recording.IsMovie = info.IsMovie;
- recording.IsNews = info.IsNews;
- recording.IsPremiere = info.IsPremiere;
- recording.IsRepeat = info.IsRepeat;
- recording.IsSports = info.IsSports;
- recording.SeriesTimerId = info.SeriesTimerId;
- recording.TimerId = info.TimerId;
- recording.StartDate = info.StartDate;
-
- if (!dataChanged)
- {
- dataChanged = recording.IsSeries != info.IsSeries;
- }
- recording.IsSeries = info.IsSeries;
-
- if (!item.ParentId.Equals(parentFolderId))
- {
- dataChanged = true;
- }
- item.ParentId = parentFolderId;
-
- if (!item.HasImage(ImageType.Primary))
- {
- if (!string.IsNullOrWhiteSpace(info.ImagePath))
- {
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImagePath,
- Type = ImageType.Primary,
- IsPlaceholder = true
- }, 0);
- }
- else if (!string.IsNullOrWhiteSpace(info.ImageUrl))
- {
- item.SetImage(new ItemImageInfo
- {
- Path = info.ImageUrl,
- Type = ImageType.Primary,
- IsPlaceholder = true
- }, 0);
- }
- }
-
- var statusChanged = info.Status != recording.Status;
-
- recording.Status = info.Status;
-
- recording.ServiceName = serviceName;
-
- if (!string.IsNullOrEmpty(info.Path))
- {
- if (!dataChanged)
- {
- dataChanged = !string.Equals(item.Path, info.Path);
- }
- var fileInfo = _fileSystem.GetFileInfo(info.Path);
-
- recording.DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo);
- recording.DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo);
- item.Path = info.Path;
- }
- else if (!string.IsNullOrEmpty(info.Url))
- {
- if (!dataChanged)
- {
- dataChanged = !string.Equals(item.Path, info.Url);
- }
- item.Path = info.Url;
- }
-
- var metadataRefreshMode = MetadataRefreshMode.Default;
-
- if (isNew)
- {
- await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
- }
- else if (dataChanged || info.DateLastUpdated > recording.DateLastSaved || statusChanged)
- {
- metadataRefreshMode = MetadataRefreshMode.FullRefresh;
- await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
- }
-
- if (info.Status != RecordingStatus.InProgress)
- {
- _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(_fileSystem)
- {
- MetadataRefreshMode = metadataRefreshMode
- });
- }
-
- return item.Id;
- }
-
- public async Task<BaseItemDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
- {
- var program = GetInternalProgram(id);
-
- var dto = _dtoService.GetBaseItemDto(program, new DtoOptions(), user);
-
- var list = new List<Tuple<BaseItemDto, string, string, string>>();
- list.Add(new Tuple<BaseItemDto, string, string, string>(dto, program.ServiceName, program.ExternalId, program.ExternalSeriesIdLegacy));
-
- await AddRecordingInfo(list, cancellationToken).ConfigureAwait(false);
-
- return dto;
- }
-
- public async Task<QueryResult<BaseItemDto>> GetPrograms(ProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
-
- var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
-
- if (query.SortBy.Length == 0)
- {
- // Unless something else was specified, order by start date to take advantage of a specialized index
- query.SortBy = new[] { ItemSortBy.StartDate };
- }
-
- var internalQuery = new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
- MinEndDate = query.MinEndDate,
- MinStartDate = query.MinStartDate,
- MaxEndDate = query.MaxEndDate,
- MaxStartDate = query.MaxStartDate,
- ChannelIds = query.ChannelIds,
- IsMovie = query.IsMovie,
- IsSeries = query.IsSeries,
- IsSports = query.IsSports,
- IsKids = query.IsKids,
- IsNews = query.IsNews,
- Genres = query.Genres,
- StartIndex = query.StartIndex,
- Limit = query.Limit,
- SortBy = query.SortBy,
- SortOrder = query.SortOrder ?? SortOrder.Ascending,
- EnableTotalRecordCount = query.EnableTotalRecordCount,
- TopParentIds = new[] { topFolder.Id.ToString("N") },
- Name = query.Name,
- DtoOptions = options
- };
-
- 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<BaseItemDto>();
- }
- }
- else
- {
- // Better to return nothing than every program in the database
- return new QueryResult<BaseItemDto>();
- }
- }
-
- if (query.HasAired.HasValue)
- {
- if (query.HasAired.Value)
- {
- internalQuery.MaxEndDate = DateTime.UtcNow;
- }
- else
- {
- internalQuery.MinEndDate = DateTime.UtcNow;
- }
- }
-
- var queryResult = _libraryManager.QueryItems(internalQuery);
-
- RemoveFields(options);
-
- var returnArray = (await _dtoService.GetBaseItemDtos(queryResult.Items, options, user).ConfigureAwait(false)).ToArray();
-
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnArray,
- TotalRecordCount = queryResult.TotalRecordCount
- };
-
- return result;
- }
-
- public async Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
- {
- var user = _userManager.GetUserById(query.UserId);
-
- var topFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
-
- var internalQuery = new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
- IsAiring = query.IsAiring,
- IsNews = query.IsNews,
- IsMovie = query.IsMovie,
- IsSeries = query.IsSeries,
- IsSports = query.IsSports,
- IsKids = query.IsKids,
- EnableTotalRecordCount = query.EnableTotalRecordCount,
- SortBy = new[] { ItemSortBy.StartDate },
- TopParentIds = new[] { topFolder.Id.ToString("N") },
- DtoOptions = options
- };
-
- if (query.Limit.HasValue)
- {
- internalQuery.Limit = Math.Max(query.Limit.Value * 4, 200);
- }
-
- if (query.HasAired.HasValue)
- {
- if (query.HasAired.Value)
- {
- internalQuery.MaxEndDate = DateTime.UtcNow;
- }
- else
- {
- internalQuery.MinEndDate = DateTime.UtcNow;
- }
- }
-
- IEnumerable<LiveTvProgram> programs = _libraryManager.QueryItems(internalQuery).Items.Cast<LiveTvProgram>();
-
- var programList = programs.ToList();
-
- var factorChannelWatchCount = (query.IsAiring ?? false) || (query.IsKids ?? false) || (query.IsSports ?? false) || (query.IsMovie ?? false) || (query.IsNews ?? false) || (query.IsSeries ?? false);
-
- programs = programList.OrderBy(i => i.StartDate.Date)
- .ThenByDescending(i => GetRecommendationScore(i, user.Id, factorChannelWatchCount))
- .ThenBy(i => i.StartDate);
-
- if (query.Limit.HasValue)
- {
- programs = programs.Take(query.Limit.Value);
- }
-
- programList = programs.ToList();
-
- var returnArray = programList.ToArray();
-
- var result = new QueryResult<LiveTvProgram>
- {
- Items = returnArray,
- TotalRecordCount = returnArray.Length
- };
-
- return result;
- }
-
- public async Task<QueryResult<BaseItemDto>> GetRecommendedPrograms(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
- {
- var internalResult = await GetRecommendedProgramsInternal(query, options, cancellationToken).ConfigureAwait(false);
-
- var user = _userManager.GetUserById(query.UserId);
-
- RemoveFields(options);
-
- var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
-
- var result = new QueryResult<BaseItemDto>
- {
- Items = returnArray,
- TotalRecordCount = internalResult.TotalRecordCount
- };
-
- return result;
- }
-
- private int GetRecommendationScore(LiveTvProgram program, Guid userId, bool factorChannelWatchCount)
- {
- var score = 0;
-
- if (program.IsLive)
- {
- score++;
- }
-
- if (program.IsSeries && !program.IsRepeat)
- {
- score++;
- }
-
- var channel = GetInternalChannel(program.ChannelId);
-
- var channelUserdata = _userDataManager.GetUserData(userId, channel);
-
- if (channelUserdata.Likes ?? false)
- {
- score += 2;
- }
- else if (!(channelUserdata.Likes ?? true))
- {
- score -= 2;
- }
-
- if (channelUserdata.IsFavorite)
- {
- score += 3;
- }
-
- if (factorChannelWatchCount)
- {
- score += channelUserdata.PlayCount;
- }
-
- return score;
- }
-
- private async Task AddRecordingInfo(IEnumerable<Tuple<BaseItemDto, string, string, string>> programs, CancellationToken cancellationToken)
- {
- var timers = new Dictionary<string, List<TimerInfo>>();
- var seriesTimers = new Dictionary<string, List<SeriesTimerInfo>>();
-
- foreach (var programTuple in programs)
- {
- var program = programTuple.Item1;
- var serviceName = programTuple.Item2;
- var externalProgramId = programTuple.Item3;
- string externalSeriesId = programTuple.Item4;
-
- if (string.IsNullOrWhiteSpace(serviceName))
- {
- continue;
- }
-
- List<TimerInfo> timerList;
- if (!timers.TryGetValue(serviceName, out timerList))
- {
- try
- {
- var tempTimers = await GetService(serviceName).GetTimersAsync(cancellationToken).ConfigureAwait(false);
- timers[serviceName] = timerList = tempTimers.ToList();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting timer infos", ex);
- timers[serviceName] = timerList = new List<TimerInfo>();
- }
- }
-
- var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase));
- var foundSeriesTimer = false;
-
- if (timer != null)
- {
- if (timer.Status != RecordingStatus.Cancelled && timer.Status != RecordingStatus.Error)
- {
- program.TimerId = _tvDtoService.GetInternalTimerId(serviceName, timer.Id)
- .ToString("N");
-
- program.Status = timer.Status.ToString();
- }
-
- if (!string.IsNullOrEmpty(timer.SeriesTimerId))
- {
- program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(serviceName, timer.SeriesTimerId)
- .ToString("N");
-
- foundSeriesTimer = true;
- }
- }
-
- if (foundSeriesTimer || string.IsNullOrWhiteSpace(externalSeriesId))
- {
- continue;
- }
-
- List<SeriesTimerInfo> seriesTimerList;
- if (!seriesTimers.TryGetValue(serviceName, out seriesTimerList))
- {
- try
- {
- var tempTimers = await GetService(serviceName).GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
- seriesTimers[serviceName] = seriesTimerList = tempTimers.ToList();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting series timer infos", ex);
- seriesTimers[serviceName] = seriesTimerList = new List<SeriesTimerInfo>();
- }
- }
-
- var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase));
-
- if (seriesTimer != null)
- {
- program.SeriesTimerId = _tvDtoService.GetInternalSeriesTimerId(serviceName, seriesTimer.Id)
- .ToString("N");
- }
- }
- }
-
- internal Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return RefreshChannelsInternal(progress, cancellationToken);
- }
-
- private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)
- {
- EmbyTV.EmbyTV.Current.CreateRecordingFolders();
-
- var numComplete = 0;
- double progressPerService = _services.Count == 0
- ? 0
- : 1 / _services.Count;
-
- var newChannelIdList = new List<Guid>();
- var newProgramIdList = new List<Guid>();
-
- foreach (var service in _services)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- _logger.Debug("Refreshing guide from {0}", service.Name);
-
- try
- {
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(p * progressPerService));
-
- var idList = await RefreshChannelsInternal(service, innerProgress, cancellationToken).ConfigureAwait(false);
-
- newChannelIdList.AddRange(idList.Item1);
- newProgramIdList.AddRange(idList.Item2);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing channels for service", ex);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= _services.Count;
-
- progress.Report(100 * percent);
- }
-
- await CleanDatabaseInternal(newChannelIdList, new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false);
- await CleanDatabaseInternal(newProgramIdList, new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false);
-
- var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
-
- if (coreService != null)
- {
- await coreService.RefreshSeriesTimers(cancellationToken, new Progress<double>()).ConfigureAwait(false);
- }
-
- // Load these now which will prefetch metadata
- var dtoOptions = new DtoOptions();
- dtoOptions.Fields.Remove(ItemFields.SyncInfo);
- dtoOptions.Fields.Remove(ItemFields.BasicSyncInfo);
- await GetRecordings(new RecordingQuery(), dtoOptions, cancellationToken).ConfigureAwait(false);
-
- progress.Report(100);
- }
-
- private async Task<Tuple<List<Guid>, List<Guid>>> RefreshChannelsInternal(ILiveTvService service, IProgress<double> progress, CancellationToken cancellationToken)
- {
- progress.Report(10);
-
- var allChannels = await GetChannels(service, cancellationToken).ConfigureAwait(false);
- var allChannelsList = allChannels.ToList();
-
- var list = new List<LiveTvChannel>();
-
- var numComplete = 0;
- var parentFolder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
- var parentFolderId = parentFolder.Id;
-
- foreach (var channelInfo in allChannelsList)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- try
- {
- var item = await GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolderId, cancellationToken).ConfigureAwait(false);
-
- list.Add(item);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting channel information for {0}", ex, channelInfo.Item2.Name);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= allChannelsList.Count;
-
- progress.Report(5 * percent + 10);
- }
-
- progress.Report(15);
-
- numComplete = 0;
- var programs = new List<Guid>();
- var channels = new List<Guid>();
-
- var guideDays = GetGuideDays();
-
- _logger.Info("Refreshing guide with {0} days of guide data", guideDays);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- foreach (var currentChannel in list)
- {
- channels.Add(currentChannel.Id);
- cancellationToken.ThrowIfCancellationRequested();
-
- try
- {
- var start = DateTime.UtcNow.AddHours(-1);
- var end = start.AddDays(guideDays);
-
- var isMovie = false;
- var isSports = false;
- var isNews = false;
- var isKids = false;
- var iSSeries = false;
-
- var channelPrograms = await service.GetProgramsAsync(currentChannel.ExternalId, start, end, cancellationToken).ConfigureAwait(false);
-
- var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
- {
-
- IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
- ChannelIds = new string[] { currentChannel.Id.ToString("N") }
-
- }).Cast<LiveTvProgram>().ToDictionary(i => i.Id);
-
- var newPrograms = new List<LiveTvProgram>();
- var updatedPrograms = new List<LiveTvProgram>();
-
- foreach (var program in channelPrograms)
- {
- var programTuple = GetProgram(program, existingPrograms, currentChannel, currentChannel.ChannelType, service.Name, cancellationToken);
- var programItem = programTuple.Item1;
-
- if (programTuple.Item2)
- {
- newPrograms.Add(programItem);
- }
- else if (programTuple.Item3)
- {
- updatedPrograms.Add(programItem);
- }
-
- programs.Add(programItem.Id);
-
- if (program.IsMovie)
- {
- isMovie = true;
- }
-
- if (program.IsSeries)
- {
- iSSeries = true;
- }
-
- if (program.IsSports)
- {
- isSports = true;
- }
-
- if (program.IsNews)
- {
- isNews = true;
- }
-
- if (program.IsKids)
- {
- isKids = true;
- }
- }
-
- _logger.Debug("Channel {0} has {1} new programs and {2} updated programs", currentChannel.Name, newPrograms.Count, updatedPrograms.Count);
-
- if (newPrograms.Count > 0)
- {
- await _libraryManager.CreateItems(newPrograms, cancellationToken).ConfigureAwait(false);
- }
-
- // TODO: Do this in bulk
- foreach (var program in updatedPrograms)
- {
- await _libraryManager.UpdateItem(program, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
- }
-
- foreach (var program in newPrograms)
- {
- _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem));
- }
- foreach (var program in updatedPrograms)
- {
- _providerManager.QueueRefresh(program.Id, new MetadataRefreshOptions(_fileSystem));
- }
-
- currentChannel.IsMovie = isMovie;
- currentChannel.IsNews = isNews;
- currentChannel.IsSports = isSports;
- currentChannel.IsKids = isKids;
- currentChannel.IsSeries = iSSeries;
-
- await currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting programs for channel {0}", ex, currentChannel.Name);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= allChannelsList.Count;
-
- progress.Report(80 * percent + 10);
- }
- progress.Report(100);
-
- return new Tuple<List<Guid>, List<Guid>>(channels, programs);
- }
-
- private async Task CleanDatabaseInternal(List<Guid> currentIdList, string[] validTypes, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var list = _itemRepo.GetItemIdsList(new InternalItemsQuery
- {
- IncludeItemTypes = validTypes
-
- }).ToList();
-
- var numComplete = 0;
-
- foreach (var itemId in list)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (itemId == Guid.Empty)
- {
- // Somehow some invalid data got into the db. It probably predates the boundary checking
- continue;
- }
-
- if (!currentIdList.Contains(itemId))
- {
- var item = _libraryManager.GetItemById(itemId);
-
- if (item != null)
- {
- await _libraryManager.DeleteItem(item, new DeleteOptions
- {
- DeleteFileLocation = false
-
- }).ConfigureAwait(false);
- }
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= list.Count;
-
- progress.Report(100 * percent);
- }
- }
-
- private const int MaxGuideDays = 14;
- private double GetGuideDays()
- {
- var config = GetConfiguration();
-
- if (config.GuideDays.HasValue)
- {
- return Math.Max(1, Math.Min(config.GuideDays.Value, MaxGuideDays));
- }
-
- return 7;
- }
-
- private async Task<IEnumerable<Tuple<string, ChannelInfo>>> GetChannels(ILiveTvService service, CancellationToken cancellationToken)
- {
- var channels = await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false);
-
- return channels.Select(i => new Tuple<string, ChannelInfo>(service.Name, i));
- }
-
- private DateTime _lastRecordingRefreshTime;
- private async Task RefreshRecordings(CancellationToken cancellationToken)
- {
- const int cacheMinutes = 3;
-
- if ((DateTime.UtcNow - _lastRecordingRefreshTime).TotalMinutes < cacheMinutes)
- {
- return;
- }
-
- await _refreshRecordingsLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- if ((DateTime.UtcNow - _lastRecordingRefreshTime).TotalMinutes < cacheMinutes)
- {
- return;
- }
-
- var tasks = _services.Select(async i =>
- {
- try
- {
- var recs = await i.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
- return recs.Select(r => new Tuple<RecordingInfo, ILiveTvService>(r, i));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting recordings", ex);
- return new List<Tuple<RecordingInfo, ILiveTvService>>();
- }
- });
-
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
- var folder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
- var parentFolderId = folder.Id;
-
- var recordingTasks = results.SelectMany(i => i.ToList()).Select(i => CreateRecordingRecord(i.Item1, i.Item2.Name, parentFolderId, cancellationToken));
-
- var idList = await Task.WhenAll(recordingTasks).ConfigureAwait(false);
-
- await CleanDatabaseInternal(idList.ToList(), new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }, new Progress<double>(), cancellationToken).ConfigureAwait(false);
-
- _lastRecordingRefreshTime = DateTime.UtcNow;
- }
- finally
- {
- _refreshRecordingsLock.Release();
- }
- }
-
- private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user)
- {
- if (user == null)
- {
- return new QueryResult<BaseItem>();
- }
-
- if ((query.IsInProgress ?? false))
- {
- return new QueryResult<BaseItem>();
- }
-
- var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
- .SelectMany(i => i.Locations)
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .Select(i => _libraryManager.FindByPath(i, true))
- .Where(i => i != null)
- .Where(i => i.IsVisibleStandalone(user))
- .ToList();
-
- if (folders.Count == 0)
- {
- return new QueryResult<BaseItem>();
- }
-
- var includeItemTypes = new List<string>();
- var excludeItemTypes = new List<string>();
- var genres = new List<string>();
-
- if (query.IsMovie.HasValue)
- {
- if (query.IsMovie.Value)
- {
- includeItemTypes.Add(typeof(Movie).Name);
- }
- else
- {
- excludeItemTypes.Add(typeof(Movie).Name);
- }
- }
- if (query.IsSeries.HasValue)
- {
- if (query.IsSeries.Value)
- {
- includeItemTypes.Add(typeof(Episode).Name);
- }
- else
- {
- excludeItemTypes.Add(typeof(Episode).Name);
- }
- }
- if (query.IsSports.HasValue)
- {
- if (query.IsSports.Value)
- {
- genres.Add("Sports");
- }
- }
- if (query.IsKids.HasValue)
- {
- if (query.IsKids.Value)
- {
- genres.Add("Kids");
- genres.Add("Children");
- genres.Add("Family");
- }
- }
-
- return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
- {
- MediaTypes = new[] { MediaType.Video },
- Recursive = true,
- AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(),
- IsFolder = false,
- ExcludeLocationTypes = new[] { LocationType.Virtual },
- Limit = query.Limit,
- SortBy = new[] { ItemSortBy.DateCreated },
- SortOrder = SortOrder.Descending,
- EnableTotalRecordCount = query.EnableTotalRecordCount,
- IncludeItemTypes = includeItemTypes.ToArray(),
- ExcludeItemTypes = excludeItemTypes.ToArray(),
- Genres = genres.ToArray(),
- DtoOptions = dtoOptions
- });
- }
-
- public async Task<QueryResult<BaseItemDto>> GetRecordingSeries(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
- if (user != null && !IsLiveTvEnabled(user))
- {
- return new QueryResult<BaseItemDto>();
- }
-
- if (_services.Count > 1)
- {
- return new QueryResult<BaseItemDto>();
- }
-
- if (user == null || (query.IsInProgress ?? false))
- {
- return new QueryResult<BaseItemDto>();
- }
-
- var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders()
- .SelectMany(i => i.Locations)
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .Select(i => _libraryManager.FindByPath(i, true))
- .Where(i => i != null)
- .Where(i => i.IsVisibleStandalone(user))
- .ToList();
-
- if (folders.Count == 0)
- {
- return new QueryResult<BaseItemDto>();
- }
-
- var includeItemTypes = new List<string>();
- var excludeItemTypes = new List<string>();
-
- includeItemTypes.Add(typeof(Series).Name);
-
- var internalResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
- {
- Recursive = true,
- AncestorIds = folders.Select(i => i.Id.ToString("N")).ToArray(),
- Limit = query.Limit,
- SortBy = new[] { ItemSortBy.DateCreated },
- SortOrder = SortOrder.Descending,
- EnableTotalRecordCount = query.EnableTotalRecordCount,
- IncludeItemTypes = includeItemTypes.ToArray(),
- ExcludeItemTypes = excludeItemTypes.ToArray()
- });
-
- RemoveFields(options);
-
- var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
-
- return new QueryResult<BaseItemDto>
- {
- Items = returnArray,
- TotalRecordCount = internalResult.TotalRecordCount
- };
- }
-
- public async Task<QueryResult<BaseItem>> GetInternalRecordings(RecordingQuery query, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
- if (user != null && !IsLiveTvEnabled(user))
- {
- return new QueryResult<BaseItem>();
- }
-
- if (_services.Count == 1 && !(query.IsInProgress ?? false))
- {
- return GetEmbyRecordings(query, new DtoOptions(), user);
- }
-
- await RefreshRecordings(cancellationToken).ConfigureAwait(false);
-
- var internalQuery = new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }
- };
-
- if (!string.IsNullOrEmpty(query.ChannelId))
- {
- internalQuery.ChannelIds = new[] { query.ChannelId };
- }
-
- var queryResult = _libraryManager.GetItemList(internalQuery);
- IEnumerable<ILiveTvRecording> recordings = queryResult.Cast<ILiveTvRecording>();
-
- if (!string.IsNullOrWhiteSpace(query.Id))
- {
- var guid = new Guid(query.Id);
-
- recordings = recordings
- .Where(i => i.Id == guid);
- }
-
- if (!string.IsNullOrWhiteSpace(query.GroupId))
- {
- var guid = new Guid(query.GroupId);
-
- recordings = recordings.Where(i => GetRecordingGroupIds(i).Contains(guid));
- }
-
- if (query.IsInProgress.HasValue)
- {
- var val = query.IsInProgress.Value;
- recordings = recordings.Where(i => i.Status == RecordingStatus.InProgress == val);
- }
-
- if (query.Status.HasValue)
- {
- var val = query.Status.Value;
- recordings = recordings.Where(i => i.Status == val);
- }
-
- if (query.IsMovie.HasValue)
- {
- var val = query.IsMovie.Value;
- recordings = recordings.Where(i => i.IsMovie == val);
- }
-
- if (query.IsNews.HasValue)
- {
- var val = query.IsNews.Value;
- recordings = recordings.Where(i => i.IsNews == val);
- }
-
- if (query.IsSeries.HasValue)
- {
- var val = query.IsSeries.Value;
- recordings = recordings.Where(i => i.IsSeries == val);
- }
-
- if (query.IsKids.HasValue)
- {
- var val = query.IsKids.Value;
- recordings = recordings.Where(i => i.IsKids == val);
- }
-
- if (query.IsSports.HasValue)
- {
- var val = query.IsSports.Value;
- recordings = recordings.Where(i => i.IsSports == val);
- }
-
- if (!string.IsNullOrEmpty(query.SeriesTimerId))
- {
- var guid = new Guid(query.SeriesTimerId);
-
- recordings = recordings
- .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.ServiceName, i.SeriesTimerId) == guid);
- }
-
- recordings = recordings.OrderByDescending(i => i.StartDate);
-
- var entityList = recordings.ToList();
- IEnumerable<ILiveTvRecording> entities = entityList;
-
- if (query.StartIndex.HasValue)
- {
- entities = entities.Skip(query.StartIndex.Value);
- }
-
- if (query.Limit.HasValue)
- {
- entities = entities.Take(query.Limit.Value);
- }
-
- return new QueryResult<BaseItem>
- {
- Items = entities.Cast<BaseItem>().ToArray(),
- TotalRecordCount = entityList.Count
- };
- }
-
- public async Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, List<ItemFields> fields, User user = null)
- {
- var programTuples = new List<Tuple<BaseItemDto, string, string, string>>();
-
- foreach (var tuple in tuples)
- {
- var program = (LiveTvProgram)tuple.Item1;
- var dto = tuple.Item2;
-
- dto.StartDate = program.StartDate;
- dto.EpisodeTitle = program.EpisodeTitle;
-
- if (program.IsRepeat)
- {
- dto.IsRepeat = program.IsRepeat;
- }
- if (program.IsMovie)
- {
- dto.IsMovie = program.IsMovie;
- }
- if (program.IsSeries)
- {
- dto.IsSeries = program.IsSeries;
- }
- if (program.IsSports)
- {
- dto.IsSports = program.IsSports;
- }
- if (program.IsLive)
- {
- dto.IsLive = program.IsLive;
- }
- if (program.IsNews)
- {
- dto.IsNews = program.IsNews;
- }
- if (program.IsKids)
- {
- dto.IsKids = program.IsKids;
- }
- if (program.IsPremiere)
- {
- dto.IsPremiere = program.IsPremiere;
- }
-
- if (fields.Contains(ItemFields.ChannelInfo))
- {
- var channel = GetInternalChannel(program.ChannelId);
-
- if (channel != null)
- {
- dto.ChannelName = channel.Name;
- dto.MediaType = channel.MediaType;
- dto.ChannelNumber = channel.Number;
-
- if (channel.HasImage(ImageType.Primary))
- {
- dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
- }
- }
- }
-
- var serviceName = program.ServiceName;
-
- if (fields.Contains(ItemFields.ServiceName))
- {
- dto.ServiceName = serviceName;
- }
-
- programTuples.Add(new Tuple<BaseItemDto, string, string, string>(dto, serviceName, program.ExternalId, program.ExternalSeriesIdLegacy));
- }
-
- await AddRecordingInfo(programTuples, CancellationToken.None).ConfigureAwait(false);
- }
-
- public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, User user = null)
- {
- var recording = (ILiveTvRecording)item;
- var service = GetService(recording);
-
- var channel = string.IsNullOrWhiteSpace(recording.ChannelId) ? null : GetInternalChannel(recording.ChannelId);
-
- var info = recording;
-
- dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId)
- ? null
- : _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.RecordingStatus = info.Status;
- dto.IsRepeat = info.IsRepeat;
- dto.EpisodeTitle = info.EpisodeTitle;
- dto.IsMovie = info.IsMovie;
- dto.IsSeries = info.IsSeries;
- dto.IsSports = info.IsSports;
- dto.IsLive = info.IsLive;
- dto.IsNews = info.IsNews;
- dto.IsKids = info.IsKids;
- dto.IsPremiere = info.IsPremiere;
-
- dto.CanDelete = user == null
- ? recording.CanDelete()
- : recording.CanDelete(user);
-
- if (dto.MediaSources == null)
- {
- dto.MediaSources = recording.GetMediaSources(true).ToList();
- }
-
- if (dto.MediaStreams == null)
- {
- dto.MediaStreams = dto.MediaSources.SelectMany(i => i.MediaStreams).ToList();
- }
-
- if (info.Status == RecordingStatus.InProgress && info.EndDate.HasValue)
- {
- var now = DateTime.UtcNow.Ticks;
- var start = info.StartDate.Ticks;
- var end = info.EndDate.Value.Ticks;
-
- var pct = now - start;
- pct /= end;
- pct *= 100;
- dto.CompletionPercentage = pct;
- }
-
- if (channel != null)
- {
- dto.ChannelName = channel.Name;
-
- if (channel.HasImage(ImageType.Primary))
- {
- dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
- }
- }
- }
-
- public async Task<QueryResult<BaseItemDto>> GetRecordings(RecordingQuery query, DtoOptions options, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
-
- var internalResult = await GetInternalRecordings(query, cancellationToken).ConfigureAwait(false);
-
- RemoveFields(options);
-
- var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
-
- return new QueryResult<BaseItemDto>
- {
- Items = returnArray,
- TotalRecordCount = internalResult.TotalRecordCount
- };
- }
-
- public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
- {
- var tasks = _services.Select(async i =>
- {
- try
- {
- var recs = await i.GetTimersAsync(cancellationToken).ConfigureAwait(false);
- return recs.Select(r => new Tuple<TimerInfo, ILiveTvService>(r, i));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting recordings", ex);
- return new List<Tuple<TimerInfo, ILiveTvService>>();
- }
- });
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
- var timers = results.SelectMany(i => i.ToList());
-
- if (query.IsActive.HasValue)
- {
- if (query.IsActive.Value)
- {
- timers = timers.Where(i => i.Item1.Status == RecordingStatus.InProgress);
- }
- else
- {
- timers = timers.Where(i => i.Item1.Status != RecordingStatus.InProgress);
- }
- }
-
- if (query.IsScheduled.HasValue)
- {
- if (query.IsScheduled.Value)
- {
- timers = timers.Where(i => i.Item1.Status == RecordingStatus.New);
- }
- else
- {
- timers = timers.Where(i => i.Item1.Status != RecordingStatus.New);
- }
- }
-
- if (!string.IsNullOrEmpty(query.ChannelId))
- {
- var guid = new Guid(query.ChannelId);
- timers = timers.Where(i => guid == _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
- }
-
- if (!string.IsNullOrEmpty(query.SeriesTimerId))
- {
- var guid = new Guid(query.SeriesTimerId);
-
- timers = timers
- .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.Item2.Name, i.Item1.SeriesTimerId) == guid);
- }
-
- var returnList = new List<TimerInfoDto>();
-
- foreach (var i in timers)
- {
- var program = string.IsNullOrEmpty(i.Item1.ProgramId) ?
- null :
- GetInternalProgram(_tvDtoService.GetInternalProgramId(i.Item2.Name, i.Item1.ProgramId).ToString("N"));
-
- var channel = string.IsNullOrEmpty(i.Item1.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId));
-
- returnList.Add(_tvDtoService.GetTimerInfoDto(i.Item1, i.Item2, program, channel));
- }
-
- var returnArray = returnList
- .OrderBy(i => i.StartDate)
- .ToArray();
-
- return new QueryResult<TimerInfoDto>
- {
- Items = returnArray,
- TotalRecordCount = returnArray.Length
- };
- }
-
- public Task OnRecordingFileDeleted(BaseItem recording)
- {
- var service = GetService(recording);
-
- if (service is EmbyTV.EmbyTV)
- {
- // We can't trust that we'll be able to direct stream it through emby server, no matter what the provider says
- return service.DeleteRecordingAsync(recording.ExternalId, CancellationToken.None);
- }
-
- return Task.FromResult(true);
- }
-
- public async Task DeleteRecording(string recordingId)
- {
- var recording = await GetInternalRecording(recordingId, CancellationToken.None).ConfigureAwait(false);
-
- if (recording == null)
- {
- throw new ResourceNotFoundException(string.Format("Recording with Id {0} not found", recordingId));
- }
-
- await DeleteRecording((BaseItem)recording).ConfigureAwait(false);
- }
-
- public async Task DeleteRecording(BaseItem recording)
- {
- var service = GetService(recording.ServiceName);
-
- try
- {
- await service.DeleteRecordingAsync(recording.ExternalId, CancellationToken.None).ConfigureAwait(false);
- }
- catch (ResourceNotFoundException)
- {
-
- }
-
- _lastRecordingRefreshTime = DateTime.MinValue;
-
- // This is the responsibility of the live tv service
- await _libraryManager.DeleteItem((BaseItem)recording, new DeleteOptions
- {
- DeleteFileLocation = false
-
- }).ConfigureAwait(false);
-
- _lastRecordingRefreshTime = DateTime.MinValue;
- }
-
- public async Task CancelTimer(string id)
- {
- var timer = await GetTimer(id, CancellationToken.None).ConfigureAwait(false);
-
- if (timer == null)
- {
- throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
- }
-
- var service = GetService(timer.ServiceName);
-
- await service.CancelTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
- _lastRecordingRefreshTime = DateTime.MinValue;
-
- EventHelper.QueueEventIfNotNull(TimerCancelled, this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
- {
- Id = id
- }
- }, _logger);
- }
-
- public async Task CancelSeriesTimer(string id)
- {
- var timer = await GetSeriesTimer(id, CancellationToken.None).ConfigureAwait(false);
-
- if (timer == null)
- {
- throw new ResourceNotFoundException(string.Format("Timer with Id {0} not found", id));
- }
-
- var service = GetService(timer.ServiceName);
-
- await service.CancelSeriesTimerAsync(timer.ExternalId, CancellationToken.None).ConfigureAwait(false);
- _lastRecordingRefreshTime = DateTime.MinValue;
-
- EventHelper.QueueEventIfNotNull(SeriesTimerCancelled, this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
- {
- Id = id
- }
- }, _logger);
- }
-
- public async Task<BaseItemDto> GetRecording(string id, DtoOptions options, CancellationToken cancellationToken, User user = null)
- {
- var item = await GetInternalRecording(id, cancellationToken).ConfigureAwait(false);
-
- if (item == null)
- {
- return null;
- }
-
- return _dtoService.GetBaseItemDto((BaseItem)item, options, user);
- }
-
- public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)
- {
- var results = await GetTimers(new TimerQuery(), cancellationToken).ConfigureAwait(false);
-
- return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
- }
-
- public async Task<SeriesTimerInfoDto> GetSeriesTimer(string id, CancellationToken cancellationToken)
- {
- var results = await GetSeriesTimers(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false);
-
- return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
- }
-
- private async Task<QueryResult<SeriesTimerInfo>> 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<SeriesTimerInfo, ILiveTvService>(r, i);
- });
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting recordings", ex);
- return new List<Tuple<SeriesTimerInfo, ILiveTvService>>();
- }
- });
- 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<SeriesTimerInfo>
- {
- Items = returnArray,
- TotalRecordCount = returnArray.Length
- };
- }
-
- public async Task<QueryResult<SeriesTimerInfoDto>> GetSeriesTimers(SeriesTimerQuery query, CancellationToken cancellationToken)
- {
- var tasks = _services.Select(async i =>
- {
- try
- {
- var recs = await i.GetSeriesTimersAsync(cancellationToken).ConfigureAwait(false);
- return recs.Select(r => new Tuple<SeriesTimerInfo, ILiveTvService>(r, i));
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting recordings", ex);
- return new List<Tuple<SeriesTimerInfo, ILiveTvService>>();
- }
- });
- 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 =>
- {
- string channelName = null;
-
- if (!string.IsNullOrEmpty(i.Item1.ChannelId))
- {
- var internalChannelId = _tvDtoService.GetInternalChannelId(i.Item2.Name, i.Item1.ChannelId);
- var channel = GetInternalChannel(internalChannelId);
- channelName = channel == null ? null : channel.Name;
- }
-
- return _tvDtoService.GetSeriesTimerInfoDto(i.Item1, i.Item2, channelName);
-
- })
- .ToArray();
-
- return new QueryResult<SeriesTimerInfoDto>
- {
- Items = returnArray,
- TotalRecordCount = returnArray.Length
- };
- }
-
- public void AddChannelInfo(List<Tuple<BaseItemDto, LiveTvChannel>> tuples, DtoOptions options, User user)
- {
- var now = DateTime.UtcNow;
-
- var channelIds = tuples.Select(i => i.Item2.Id.ToString("N")).Distinct().ToArray();
-
- var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
- ChannelIds = channelIds,
- MaxStartDate = now,
- MinEndDate = now,
- Limit = channelIds.Length,
- SortBy = new[] { "StartDate" },
- TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Result.Id.ToString("N") }
-
- }).ToList() : new List<BaseItem>();
-
- RemoveFields(options);
-
- foreach (var tuple in tuples)
- {
- var dto = tuple.Item1;
- var channel = tuple.Item2;
-
- dto.Number = channel.Number;
- dto.ChannelNumber = channel.Number;
- dto.ChannelType = channel.ChannelType;
- dto.ServiceName = channel.ServiceName;
-
- if (options.Fields.Contains(ItemFields.MediaSources))
- {
- dto.MediaSources = channel.GetMediaSources(true).ToList();
- }
-
- if (options.AddCurrentProgram)
- {
- var channelIdString = channel.Id.ToString("N");
- var currentProgram = programs.FirstOrDefault(i => string.Equals(i.ChannelId, channelIdString));
-
- if (currentProgram != null)
- {
- dto.CurrentProgram = _dtoService.GetBaseItemDto(currentProgram, options, user);
- }
- }
- }
- }
-
- private async Task<Tuple<SeriesTimerInfo, ILiveTvService>> GetNewTimerDefaultsInternal(CancellationToken cancellationToken, LiveTvProgram program = null)
- {
- var service = program != null && !string.IsNullOrWhiteSpace(program.ServiceName) ?
- GetService(program) :
- _services.FirstOrDefault();
-
- ProgramInfo programInfo = null;
-
- if (program != null)
- {
- var channel = GetInternalChannel(program.ChannelId);
-
- programInfo = new ProgramInfo
- {
- Audio = program.Audio,
- ChannelId = channel.ExternalId,
- CommunityRating = program.CommunityRating,
- EndDate = program.EndDate ?? DateTime.MinValue,
- EpisodeTitle = program.EpisodeTitle,
- Genres = program.Genres,
- Id = program.ExternalId,
- IsHD = program.IsHD,
- IsKids = program.IsKids,
- IsLive = program.IsLive,
- IsMovie = program.IsMovie,
- IsNews = program.IsNews,
- IsPremiere = program.IsPremiere,
- IsRepeat = program.IsRepeat,
- IsSeries = program.IsSeries,
- IsSports = program.IsSports,
- OriginalAirDate = program.PremiereDate,
- Overview = program.Overview,
- StartDate = program.StartDate,
- //ImagePath = program.ExternalImagePath,
- Name = program.Name,
- OfficialRating = program.OfficialRating
- };
- }
-
- var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
-
- info.RecordAnyTime = true;
- info.Days = new List<DayOfWeek>
- {
- DayOfWeek.Sunday,
- DayOfWeek.Monday,
- DayOfWeek.Tuesday,
- DayOfWeek.Wednesday,
- DayOfWeek.Thursday,
- DayOfWeek.Friday,
- DayOfWeek.Saturday
- };
-
- info.Id = null;
-
- return new Tuple<SeriesTimerInfo, ILiveTvService>(info, service);
- }
-
- public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(CancellationToken cancellationToken)
- {
- var info = await GetNewTimerDefaultsInternal(cancellationToken).ConfigureAwait(false);
-
- var obj = _tvDtoService.GetSeriesTimerInfoDto(info.Item1, info.Item2, null);
-
- return obj;
- }
-
- public async Task<SeriesTimerInfoDto> GetNewTimerDefaults(string programId, CancellationToken cancellationToken)
- {
- var program = GetInternalProgram(programId);
- var programDto = await GetProgram(programId, cancellationToken).ConfigureAwait(false);
-
- var defaults = await GetNewTimerDefaultsInternal(cancellationToken, program).ConfigureAwait(false);
- var info = _tvDtoService.GetSeriesTimerInfoDto(defaults.Item1, defaults.Item2, null);
-
- info.Days = defaults.Item1.Days;
-
- info.DayPattern = _tvDtoService.GetDayPattern(info.Days);
-
- info.Name = program.Name;
- info.ChannelId = programDto.ChannelId;
- info.ChannelName = programDto.ChannelName;
- info.StartDate = program.StartDate;
- info.Name = program.Name;
- info.Overview = program.Overview;
- info.ProgramId = programDto.Id;
- info.ExternalProgramId = program.ExternalId;
-
- if (program.EndDate.HasValue)
- {
- info.EndDate = program.EndDate.Value;
- }
-
- return info;
- }
-
- public async Task CreateTimer(TimerInfoDto timer, CancellationToken cancellationToken)
- {
- var service = GetService(timer.ServiceName);
-
- var info = await _tvDtoService.GetTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
-
- // Set priority from default values
- var defaultValues = await service.GetNewTimerDefaultsAsync(cancellationToken).ConfigureAwait(false);
- info.Priority = defaultValues.Priority;
-
- string newTimerId = null;
- var supportsNewTimerIds = service as ISupportsNewTimerIds;
- if (supportsNewTimerIds != null)
- {
- newTimerId = await supportsNewTimerIds.CreateTimer(info, cancellationToken).ConfigureAwait(false);
- newTimerId = _tvDtoService.GetInternalTimerId(timer.ServiceName, newTimerId).ToString("N");
- }
- else
- {
- await service.CreateTimerAsync(info, cancellationToken).ConfigureAwait(false);
- }
-
- _lastRecordingRefreshTime = DateTime.MinValue;
- _logger.Info("New recording scheduled");
-
- EventHelper.QueueEventIfNotNull(TimerCreated, this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
- {
- ProgramId = _tvDtoService.GetInternalProgramId(timer.ServiceName, info.ProgramId).ToString("N"),
- Id = newTimerId
- }
- }, _logger);
- }
-
- public async Task CreateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
- {
- var registration = await GetRegistrationInfo("seriesrecordings").ConfigureAwait(false);
-
- if (!registration.IsValid)
- {
- _logger.Info("Creating series recordings requires an active Emby Premiere subscription.");
- return;
- }
-
- var service = GetService(timer.ServiceName);
-
- var info = await _tvDtoService.GetSeriesTimerInfo(timer, true, this, cancellationToken).ConfigureAwait(false);
-
- // Set priority from default values
- var defaultValues = await service.GetNewTimerDefaultsAsync(cancellationToken).ConfigureAwait(false);
- info.Priority = defaultValues.Priority;
-
- string newTimerId = null;
- var supportsNewTimerIds = service as ISupportsNewTimerIds;
- if (supportsNewTimerIds != null)
- {
- newTimerId = await supportsNewTimerIds.CreateSeriesTimer(info, cancellationToken).ConfigureAwait(false);
- newTimerId = _tvDtoService.GetInternalSeriesTimerId(timer.ServiceName, newTimerId).ToString("N");
- }
- else
- {
- await service.CreateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
- }
-
- _lastRecordingRefreshTime = DateTime.MinValue;
-
- EventHelper.QueueEventIfNotNull(SeriesTimerCreated, this, new GenericEventArgs<TimerEventInfo>
- {
- Argument = new TimerEventInfo
- {
- ProgramId = _tvDtoService.GetInternalProgramId(timer.ServiceName, info.ProgramId).ToString("N"),
- Id = newTimerId
- }
- }, _logger);
- }
-
- public async Task UpdateTimer(TimerInfoDto timer, CancellationToken cancellationToken)
- {
- var info = await _tvDtoService.GetTimerInfo(timer, false, this, cancellationToken).ConfigureAwait(false);
-
- var service = GetService(timer.ServiceName);
-
- await service.UpdateTimerAsync(info, cancellationToken).ConfigureAwait(false);
- _lastRecordingRefreshTime = DateTime.MinValue;
- }
-
- public async Task UpdateSeriesTimer(SeriesTimerInfoDto timer, CancellationToken cancellationToken)
- {
- var info = await _tvDtoService.GetSeriesTimerInfo(timer, false, this, cancellationToken).ConfigureAwait(false);
-
- var service = GetService(timer.ServiceName);
-
- await service.UpdateSeriesTimerAsync(info, cancellationToken).ConfigureAwait(false);
- _lastRecordingRefreshTime = DateTime.MinValue;
- }
-
- private IEnumerable<string> GetRecordingGroupNames(ILiveTvRecording recording)
- {
- var list = new List<string>();
-
- if (recording.IsSeries)
- {
- list.Add(recording.Name);
- }
-
- if (recording.IsKids)
- {
- list.Add("Kids");
- }
-
- if (recording.IsMovie)
- {
- list.Add("Movies");
- }
-
- if (recording.IsNews)
- {
- list.Add("News");
- }
-
- if (recording.IsSports)
- {
- list.Add("Sports");
- }
-
- if (!recording.IsSports && !recording.IsNews && !recording.IsMovie && !recording.IsKids && !recording.IsSeries)
- {
- list.Add("Others");
- }
-
- return list;
- }
-
- private List<Guid> GetRecordingGroupIds(ILiveTvRecording recording)
- {
- return GetRecordingGroupNames(recording).Select(i => i.ToLower()
- .GetMD5())
- .ToList();
- }
-
- public async Task<QueryResult<BaseItemDto>> GetRecordingGroups(RecordingGroupQuery query, CancellationToken cancellationToken)
- {
- var recordingResult = await GetInternalRecordings(new RecordingQuery
- {
- UserId = query.UserId
-
- }, cancellationToken).ConfigureAwait(false);
-
- var recordings = recordingResult.Items.OfType<ILiveTvRecording>().ToList();
-
- var groups = new List<BaseItemDto>();
-
- var series = recordings
- .Where(i => i.IsSeries)
- .ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase)
- .ToList();
-
- groups.AddRange(series.OrderByString(i => i.Key).Select(i => new BaseItemDto
- {
- Name = i.Key,
- RecordingCount = i.Count()
- }));
-
- groups.Add(new BaseItemDto
- {
- Name = "Kids",
- RecordingCount = recordings.Count(i => i.IsKids)
- });
-
- groups.Add(new BaseItemDto
- {
- Name = "Movies",
- RecordingCount = recordings.Count(i => i.IsMovie)
- });
-
- groups.Add(new BaseItemDto
- {
- Name = "News",
- RecordingCount = recordings.Count(i => i.IsNews)
- });
-
- groups.Add(new BaseItemDto
- {
- Name = "Sports",
- RecordingCount = recordings.Count(i => i.IsSports)
- });
-
- groups.Add(new BaseItemDto
- {
- Name = "Others",
- RecordingCount = recordings.Count(i => !i.IsSports && !i.IsNews && !i.IsMovie && !i.IsKids && !i.IsSeries)
- });
-
- groups = groups
- .Where(i => i.RecordingCount > 0)
- .ToList();
-
- foreach (var group in groups)
- {
- group.Id = group.Name.ToLower().GetMD5().ToString("N");
- }
-
- return new QueryResult<BaseItemDto>
- {
- Items = groups.ToArray(),
- TotalRecordCount = groups.Count
- };
- }
-
- public async Task CloseLiveStream(string id)
- {
- var parts = id.Split(new[] { '_' }, 2);
-
- var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), parts[0], StringComparison.OrdinalIgnoreCase));
-
- if (service == null)
- {
- throw new ArgumentException("Service not found.");
- }
-
- id = parts[1];
-
- _logger.Info("Closing live stream from {0}, stream Id: {1}", service.Name, id);
-
- await service.CloseLiveStream(id, CancellationToken.None).ConfigureAwait(false);
- }
-
- public GuideInfo GetGuideInfo()
- {
- var startDate = DateTime.UtcNow;
- var endDate = startDate.AddDays(14);
-
- return new GuideInfo
- {
- StartDate = startDate,
- EndDate = endDate
- };
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- }
-
- private bool _isDisposed = false;
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (dispose)
- {
- _isDisposed = true;
- }
- }
-
- private async Task<IEnumerable<LiveTvServiceInfo>> GetServiceInfos(CancellationToken cancellationToken)
- {
- var tasks = Services.Select(i => GetServiceInfo(i, cancellationToken));
-
- return await Task.WhenAll(tasks).ConfigureAwait(false);
- }
-
- private async Task<LiveTvServiceInfo> GetServiceInfo(ILiveTvService service, CancellationToken cancellationToken)
- {
- var info = new LiveTvServiceInfo
- {
- Name = service.Name
- };
-
- var tunerIdPrefix = service.GetType().FullName.GetMD5().ToString("N") + "_";
-
- try
- {
- var statusInfo = await service.GetStatusInfoAsync(cancellationToken).ConfigureAwait(false);
-
- info.Status = statusInfo.Status;
- info.StatusMessage = statusInfo.StatusMessage;
- info.Version = statusInfo.Version;
- info.HasUpdateAvailable = statusInfo.HasUpdateAvailable;
- info.HomePageUrl = service.HomePageUrl;
- info.IsVisible = statusInfo.IsVisible;
-
- info.Tuners = statusInfo.Tuners.Select(i =>
- {
- string channelName = null;
-
- if (!string.IsNullOrEmpty(i.ChannelId))
- {
- var internalChannelId = _tvDtoService.GetInternalChannelId(service.Name, i.ChannelId);
- var channel = GetInternalChannel(internalChannelId);
- channelName = channel == null ? null : channel.Name;
- }
-
- var dto = _tvDtoService.GetTunerInfoDto(service.Name, i, channelName);
-
- dto.Id = tunerIdPrefix + dto.Id;
-
- return dto;
-
- }).ToList();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting service status info from {0}", ex, service.Name ?? string.Empty);
-
- info.Status = LiveTvServiceStatus.Unavailable;
- info.StatusMessage = ex.Message;
- }
-
- return info;
- }
-
- public async Task<LiveTvInfo> GetLiveTvInfo(CancellationToken cancellationToken)
- {
- var services = await GetServiceInfos(CancellationToken.None).ConfigureAwait(false);
- var servicesList = services.ToList();
-
- var info = new LiveTvInfo
- {
- Services = servicesList.ToList(),
- IsEnabled = servicesList.Count > 0
- };
-
- info.EnabledUsers = _userManager.Users
- .Where(IsLiveTvEnabled)
- .Select(i => i.Id.ToString("N"))
- .ToList();
-
- return info;
- }
-
- private bool IsLiveTvEnabled(User user)
- {
- return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Count(i => i.IsEnabled) > 0);
- }
-
- public IEnumerable<User> GetEnabledUsers()
- {
- return _userManager.Users
- .Where(IsLiveTvEnabled);
- }
-
- /// <summary>
- /// Resets the tuner.
- /// </summary>
- /// <param name="id">The identifier.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task ResetTuner(string id, CancellationToken cancellationToken)
- {
- var parts = id.Split(new[] { '_' }, 2);
-
- var service = _services.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N"), parts[0], StringComparison.OrdinalIgnoreCase));
-
- if (service == null)
- {
- throw new ArgumentException("Service not found.");
- }
-
- return service.ResetTuner(parts[1], cancellationToken);
- }
-
- public async Task<BaseItemDto> GetLiveTvFolder(string userId, CancellationToken cancellationToken)
- {
- var user = string.IsNullOrEmpty(userId) ? null : _userManager.GetUserById(userId);
-
- var folder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
-
- return _dtoService.GetBaseItemDto(folder, new DtoOptions(), user);
- }
-
- private void RemoveFields(DtoOptions options)
- {
- options.Fields.Remove(ItemFields.CanDelete);
- options.Fields.Remove(ItemFields.CanDownload);
- options.Fields.Remove(ItemFields.DisplayPreferencesId);
- options.Fields.Remove(ItemFields.Etag);
- }
-
- public async Task<Folder> GetInternalLiveTvFolder(CancellationToken cancellationToken)
- {
- var name = _localization.GetLocalizedString("ViewTypeLiveTV");
- return await _libraryManager.GetNamedView(name, CollectionType.LiveTv, name, cancellationToken).ConfigureAwait(false);
- }
-
- public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
- {
- info = _jsonSerializer.DeserializeFromString<TunerHostInfo>(_jsonSerializer.SerializeToString(info));
-
- var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
-
- if (provider == null)
- {
- throw new ResourceNotFoundException();
- }
-
- var configurable = provider as IConfigurableTunerHost;
- if (configurable != null)
- {
- await configurable.Validate(info).ConfigureAwait(false);
- }
-
- var config = GetConfiguration();
-
- var index = config.TunerHosts.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
-
- if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
- {
- info.Id = Guid.NewGuid().ToString("N");
- config.TunerHosts.Add(info);
- }
- else
- {
- config.TunerHosts[index] = info;
- }
-
- _config.SaveConfiguration("livetv", config);
-
- if (dataSourceChanged)
- {
- _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
- }
-
- return info;
- }
-
- public async Task<ListingsProviderInfo> SaveListingProvider(ListingsProviderInfo info, bool validateLogin, bool validateListings)
- {
- info = _jsonSerializer.DeserializeFromString< ListingsProviderInfo>(_jsonSerializer.SerializeToString(info));
-
- var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
-
- if (provider == null)
- {
- throw new ResourceNotFoundException();
- }
-
- await provider.Validate(info, validateLogin, validateListings).ConfigureAwait(false);
-
- var config = GetConfiguration();
-
- var index = config.ListingProviders.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
-
- if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
- {
- info.Id = Guid.NewGuid().ToString("N");
- config.ListingProviders.Add(info);
- }
- else
- {
- config.ListingProviders[index] = info;
- }
-
- _config.SaveConfiguration("livetv", config);
-
- _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
-
- return info;
- }
-
- public void DeleteListingsProvider(string id)
- {
- var config = GetConfiguration();
-
- config.ListingProviders = config.ListingProviders.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToList();
-
- _config.SaveConfiguration("livetv", config);
- _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
- }
-
- public async Task<TunerChannelMapping> SetChannelMapping(string providerId, string tunerChannelNumber, string providerChannelNumber)
- {
- var config = GetConfiguration();
-
- var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase));
- listingsProviderInfo.ChannelMappings = listingsProviderInfo.ChannelMappings.Where(i => !string.Equals(i.Name, tunerChannelNumber, StringComparison.OrdinalIgnoreCase)).ToArray();
-
- if (!string.Equals(tunerChannelNumber, providerChannelNumber, StringComparison.OrdinalIgnoreCase))
- {
- var list = listingsProviderInfo.ChannelMappings.ToList();
- list.Add(new NameValuePair
- {
- Name = tunerChannelNumber,
- Value = providerChannelNumber
- });
- listingsProviderInfo.ChannelMappings = list.ToArray();
- }
-
- _config.SaveConfiguration("livetv", config);
-
- var tunerChannels = await GetChannelsForListingsProvider(providerId, CancellationToken.None)
- .ConfigureAwait(false);
-
- var providerChannels = await GetChannelsFromListingsProviderData(providerId, CancellationToken.None)
- .ConfigureAwait(false);
-
- var mappings = listingsProviderInfo.ChannelMappings.ToList();
-
- var tunerChannelMappings =
- tunerChannels.Select(i => GetTunerChannelMapping(i, mappings, providerChannels)).ToList();
-
- _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
-
- return tunerChannelMappings.First(i => string.Equals(i.Number, tunerChannelNumber, StringComparison.OrdinalIgnoreCase));
- }
-
- public TunerChannelMapping GetTunerChannelMapping(ChannelInfo channel, List<NameValuePair> mappings, List<ChannelInfo> providerChannels)
- {
- var result = new TunerChannelMapping
- {
- Name = channel.Number + " " + channel.Name,
- Number = channel.Number
- };
-
- var mapping = mappings.FirstOrDefault(i => string.Equals(i.Name, channel.Number, StringComparison.OrdinalIgnoreCase));
- var providerChannelNumber = channel.Number;
-
- if (mapping != null)
- {
- providerChannelNumber = mapping.Value;
- }
-
- var providerChannel = providerChannels.FirstOrDefault(i => string.Equals(i.Number, providerChannelNumber, StringComparison.OrdinalIgnoreCase));
-
- if (providerChannel != null)
- {
- result.ProviderChannelNumber = providerChannel.Number;
- result.ProviderChannelName = providerChannel.Name;
- }
-
- return result;
- }
-
- public Task<List<NameIdPair>> GetLineups(string providerType, string providerId, string country, string location)
- {
- var config = GetConfiguration();
-
- if (string.IsNullOrWhiteSpace(providerId))
- {
- var provider = _listingProviders.FirstOrDefault(i => string.Equals(providerType, i.Type, StringComparison.OrdinalIgnoreCase));
-
- if (provider == null)
- {
- throw new ResourceNotFoundException();
- }
-
- return provider.GetLineups(null, country, location);
- }
- else
- {
- var info = config.ListingProviders.FirstOrDefault(i => string.Equals(i.Id, providerId, StringComparison.OrdinalIgnoreCase));
-
- var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
-
- if (provider == null)
- {
- throw new ResourceNotFoundException();
- }
-
- return provider.GetLineups(info, country, location);
- }
- }
-
- public Task<MBRegistrationRecord> GetRegistrationInfo(string feature)
- {
- if (string.Equals(feature, "seriesrecordings", StringComparison.OrdinalIgnoreCase))
- {
- feature = "embytvseriesrecordings";
- }
-
- if (string.Equals(feature, "dvr-l", StringComparison.OrdinalIgnoreCase))
- {
- var config = GetConfiguration();
- if (config.TunerHosts.Count(i => i.IsEnabled) > 0 &&
- config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0)
- {
- return Task.FromResult(new MBRegistrationRecord
- {
- IsRegistered = true,
- IsValid = true
- });
- }
- }
-
- return _security.GetRegistrationStatus(feature);
- }
-
- public List<NameValuePair> GetSatIniMappings()
- {
- var names = GetType().Assembly.GetManifestResourceNames().Where(i => i.IndexOf("SatIp.ini", StringComparison.OrdinalIgnoreCase) != -1).ToList();
-
- return names.Select(GetSatIniMappings).Where(i => i != null).DistinctBy(i => i.Value.Split('|')[0]).ToList();
- }
-
- public NameValuePair GetSatIniMappings(string resource)
- {
- using (var stream = GetType().Assembly.GetManifestResourceStream(resource))
- {
- using (var reader = new StreamReader(stream))
- {
- var parser = new StreamIniDataParser();
- IniData data = parser.ReadData(reader);
-
- var satType1 = data["SATTYPE"]["1"];
- var satType2 = data["SATTYPE"]["2"];
-
- if (string.IsNullOrWhiteSpace(satType2))
- {
- return null;
- }
-
- var srch = "SatIp.ini.";
- var filename = Path.GetFileName(resource);
-
- return new NameValuePair
- {
- Name = satType1 + " " + satType2,
- Value = satType2 + "|" + filename.Substring(filename.IndexOf(srch) + srch.Length)
- };
- }
- }
- }
-
- public Task<List<ChannelInfo>> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken)
- {
- return new TunerHosts.SatIp.ChannelScan(_logger).Scan(info, cancellationToken);
- }
-
- public Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken)
- {
- var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
- return EmbyTV.EmbyTV.Current.GetChannelsForListingsProvider(info, cancellationToken);
- }
-
- public Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken)
- {
- var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
- var provider = _listingProviders.First(i => string.Equals(i.Type, info.Type, StringComparison.OrdinalIgnoreCase));
- return provider.GetChannels(info, cancellationToken);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
deleted file mode 100644
index 79d321e7e..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ /dev/null
@@ -1,219 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Server.Implementations.LiveTv
-{
- public class LiveTvMediaSourceProvider : IMediaSourceProvider
- {
- private readonly ILiveTvManager _liveTvManager;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly ILogger _logger;
- private readonly IMediaSourceManager _mediaSourceManager;
- private readonly IMediaEncoder _mediaEncoder;
- private readonly IServerApplicationHost _appHost;
-
- public LiveTvMediaSourceProvider(ILiveTvManager liveTvManager, IJsonSerializer jsonSerializer, ILogManager logManager, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IServerApplicationHost appHost)
- {
- _liveTvManager = liveTvManager;
- _jsonSerializer = jsonSerializer;
- _mediaSourceManager = mediaSourceManager;
- _mediaEncoder = mediaEncoder;
- _appHost = appHost;
- _logger = logManager.GetLogger(GetType().Name);
- }
-
- public Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
- {
- var baseItem = (BaseItem)item;
-
- if (baseItem.SourceType == SourceType.LiveTV)
- {
- if (string.IsNullOrWhiteSpace(baseItem.Path))
- {
- return GetMediaSourcesInternal(item, cancellationToken);
- }
- }
-
- return Task.FromResult<IEnumerable<MediaSourceInfo>>(new List<MediaSourceInfo>());
- }
-
- // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
- private const char StreamIdDelimeter = '_';
- private const string StreamIdDelimeterString = "_";
-
- private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(IHasMediaSources item, CancellationToken cancellationToken)
- {
- IEnumerable<MediaSourceInfo> sources;
-
- var forceRequireOpening = false;
-
- try
- {
- if (item is ILiveTvRecording)
- {
- sources = await _liveTvManager.GetRecordingMediaSources(item, cancellationToken)
- .ConfigureAwait(false);
- }
- else
- {
- sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken)
- .ConfigureAwait(false);
- }
- }
- catch (NotImplementedException)
- {
- var hasMediaSources = (IHasMediaSources)item;
-
- sources = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false)
- .ToList();
-
- forceRequireOpening = true;
- }
-
- var list = sources.ToList();
- var serverUrl = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
-
- foreach (var source in list)
- {
- source.Type = MediaSourceType.Default;
- source.BufferMs = source.BufferMs ?? 1500;
-
- if (source.RequiresOpening || forceRequireOpening)
- {
- source.RequiresOpening = true;
- }
-
- if (source.RequiresOpening)
- {
- var openKeys = new List<string>();
- openKeys.Add(item.GetType().Name);
- openKeys.Add(item.Id.ToString("N"));
- openKeys.Add(source.Id ?? string.Empty);
- source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray());
- }
-
- // Dummy this up so that direct play checks can still run
- if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http)
- {
- source.Path = serverUrl;
- }
- }
-
- _logger.Debug("MediaSources: {0}", _jsonSerializer.SerializeToString(list));
-
- return list;
- }
-
- public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken)
- {
- MediaSourceInfo stream = null;
- const bool isAudio = false;
-
- var keys = openToken.Split(new[] { StreamIdDelimeter }, 3);
- var mediaSourceId = keys.Length >= 3 ? keys[2] : null;
- IDirectStreamProvider directStreamProvider = null;
-
- if (string.Equals(keys[0], typeof(LiveTvChannel).Name, StringComparison.OrdinalIgnoreCase))
- {
- var info = await _liveTvManager.GetChannelStream(keys[1], mediaSourceId, cancellationToken).ConfigureAwait(false);
- stream = info.Item1;
- directStreamProvider = info.Item2;
- }
- else
- {
- stream = await _liveTvManager.GetRecordingStream(keys[1], cancellationToken).ConfigureAwait(false);
- }
-
- try
- {
- if (!stream.SupportsProbing || stream.MediaStreams.Any(i => i.Index != -1))
- {
- await AddMediaInfo(stream, isAudio, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error probing live tv stream", ex);
- }
-
- return new Tuple<MediaSourceInfo, IDirectStreamProvider>(stream, directStreamProvider);
- }
-
- private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
- {
- mediaSource.DefaultSubtitleStreamIndex = null;
-
- // Null this out so that it will be treated like a live stream
- mediaSource.RunTimeTicks = null;
-
- var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Audio);
-
- if (audioStream == null || audioStream.Index == -1)
- {
- mediaSource.DefaultAudioStreamIndex = null;
- }
- else
- {
- mediaSource.DefaultAudioStreamIndex = audioStream.Index;
- }
-
- var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == Model.Entities.MediaStreamType.Video);
- if (videoStream != null)
- {
- if (!videoStream.BitRate.HasValue)
- {
- var width = videoStream.Width ?? 1920;
-
- if (width >= 1900)
- {
- videoStream.BitRate = 8000000;
- }
-
- else if (width >= 1260)
- {
- videoStream.BitRate = 3000000;
- }
-
- else if (width >= 700)
- {
- videoStream.BitRate = 1000000;
- }
- }
- }
-
- // Try to estimate this
- if (!mediaSource.Bitrate.HasValue)
- {
- var total = mediaSource.MediaStreams.Select(i => i.BitRate ?? 0).Sum();
-
- if (total > 0)
- {
- mediaSource.Bitrate = total;
- }
- }
- }
-
- public Task CloseMediaSource(string liveStreamId)
- {
- return _liveTvManager.CloseLiveStream(liveStreamId);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs
deleted file mode 100644
index 3f0538bd0..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.LiveTv
-{
- public class ProgramImageProvider : IDynamicImageProvider, IHasItemChangeMonitor, IHasOrder
- {
- private readonly ILiveTvManager _liveTvManager;
-
- public ProgramImageProvider(ILiveTvManager liveTvManager)
- {
- _liveTvManager = liveTvManager;
- }
-
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- return new[] { ImageType.Primary };
- }
-
- public async Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
- {
- var liveTvItem = (LiveTvProgram)item;
-
- var imageResponse = new DynamicImageResponse();
-
- var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, liveTvItem.ServiceName, StringComparison.OrdinalIgnoreCase));
-
- if (service != null)
- {
- try
- {
- var channel = _liveTvManager.GetInternalChannel(liveTvItem.ChannelId);
-
- var response = await service.GetProgramImageAsync(liveTvItem.ExternalId, channel.ExternalId, cancellationToken).ConfigureAwait(false);
-
- if (response != null)
- {
- imageResponse.HasImage = true;
- imageResponse.Stream = response.Stream;
- imageResponse.Format = response.Format;
- }
- }
- catch (NotImplementedException)
- {
- }
- }
-
- return imageResponse;
- }
-
- public string Name
- {
- get { return "Live TV Service Provider"; }
- }
-
- public bool Supports(IHasImages item)
- {
- return item is LiveTvProgram;
- }
-
- public int Order
- {
- get
- {
- // Let the better providers run first
- return 100;
- }
- }
-
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
- {
- var liveTvItem = item as LiveTvProgram;
-
- if (liveTvItem != null)
- {
- return !liveTvItem.HasImage(ImageType.Primary);
- }
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs
deleted file mode 100644
index 25678c29d..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.LiveTv
-{
- public class RecordingImageProvider : IDynamicImageProvider, IHasItemChangeMonitor
- {
- private readonly ILiveTvManager _liveTvManager;
-
- public RecordingImageProvider(ILiveTvManager liveTvManager)
- {
- _liveTvManager = liveTvManager;
- }
-
- public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- return new[] { ImageType.Primary };
- }
-
- public async Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
- {
- var liveTvItem = (ILiveTvRecording)item;
-
- var imageResponse = new DynamicImageResponse();
-
- var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, liveTvItem.ServiceName, StringComparison.OrdinalIgnoreCase));
-
- if (service != null)
- {
- try
- {
- var response = await service.GetRecordingImageAsync(liveTvItem.ExternalId, cancellationToken).ConfigureAwait(false);
-
- if (response != null)
- {
- imageResponse.HasImage = true;
- imageResponse.Stream = response.Stream;
- imageResponse.Format = response.Format;
- }
- }
- catch (NotImplementedException)
- {
- }
- }
-
- return imageResponse;
- }
-
- public string Name
- {
- get { return "Live TV Service Provider"; }
- }
-
- public bool Supports(IHasImages item)
- {
- return item is ILiveTvRecording;
- }
-
- public int Order
- {
- get { return 0; }
- }
-
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
- {
- var liveTvItem = item as ILiveTvRecording;
-
- if (liveTvItem != null)
- {
- return !liveTvItem.HasImage(ImageType.Primary);
- }
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
deleted file mode 100644
index bf0ed90ea..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.LiveTv;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.LiveTv
-{
- public class RefreshChannelsScheduledTask : IScheduledTask, IConfigurableScheduledTask
- {
- private readonly ILiveTvManager _liveTvManager;
- private readonly IConfigurationManager _config;
-
- public RefreshChannelsScheduledTask(ILiveTvManager liveTvManager, IConfigurationManager config)
- {
- _liveTvManager = liveTvManager;
- _config = config;
- }
-
- public string Name
- {
- get { return "Refresh Guide"; }
- }
-
- public string Description
- {
- get { return "Downloads channel information from live tv services."; }
- }
-
- public string Category
- {
- get { return "Live TV"; }
- }
-
- public Task Execute(System.Threading.CancellationToken cancellationToken, IProgress<double> progress)
- {
- var manager = (LiveTvManager)_liveTvManager;
-
- return manager.RefreshChannels(progress, cancellationToken);
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(12).Ticks}
- };
- }
-
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration<LiveTvOptions>("livetv");
- }
-
- public bool IsHidden
- {
- get { return _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Count(i => i.IsEnabled) == 0; }
- }
-
- public bool IsEnabled
- {
- get { return true; }
- }
-
- public bool IsLogged
- {
- get { return true; }
- }
-
- public string Key
- {
- get { return "RefreshGuide"; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
deleted file mode 100644
index 0fe74798f..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ /dev/null
@@ -1,249 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
-{
- public abstract class BaseTunerHost
- {
- protected readonly IServerConfigurationManager Config;
- protected readonly ILogger Logger;
- protected IJsonSerializer JsonSerializer;
- protected readonly IMediaEncoder MediaEncoder;
-
- private readonly ConcurrentDictionary<string, ChannelCache> _channelCache =
- new ConcurrentDictionary<string, ChannelCache>(StringComparer.OrdinalIgnoreCase);
-
- protected BaseTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder)
- {
- Config = config;
- Logger = logger;
- JsonSerializer = jsonSerializer;
- MediaEncoder = mediaEncoder;
- }
-
- protected abstract Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
- public abstract string Type { get; }
-
- public async Task<IEnumerable<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
- {
- ChannelCache cache = null;
- var key = tuner.Id;
-
- if (enableCache && !string.IsNullOrWhiteSpace(key) && _channelCache.TryGetValue(key, out cache))
- {
- if (DateTime.UtcNow - cache.Date < TimeSpan.FromMinutes(60))
- {
- return cache.Channels.ToList();
- }
- }
-
- var result = await GetChannelsInternal(tuner, cancellationToken).ConfigureAwait(false);
- var list = result.ToList();
- Logger.Debug("Channels from {0}: {1}", tuner.Url, JsonSerializer.SerializeToString(list));
-
- if (!string.IsNullOrWhiteSpace(key) && list.Count > 0)
- {
- cache = cache ?? new ChannelCache();
- cache.Date = DateTime.UtcNow;
- cache.Channels = list;
- _channelCache.AddOrUpdate(key, cache, (k, v) => cache);
- }
-
- return list;
- }
-
- protected virtual List<TunerHostInfo> GetTunerHosts()
- {
- return GetConfiguration().TunerHosts
- .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))
- .ToList();
- }
-
- public async Task<IEnumerable<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken)
- {
- var list = new List<ChannelInfo>();
-
- var hosts = GetTunerHosts();
-
- foreach (var host in hosts)
- {
- try
- {
- var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false);
- var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList();
-
- list.AddRange(newChannels);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error getting channel list", ex);
- }
- }
-
- return list;
- }
-
- protected abstract Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
-
- public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
- {
- if (IsValidChannelId(channelId))
- {
- var hosts = GetTunerHosts();
-
- var hostsWithChannel = new List<TunerHostInfo>();
-
- foreach (var host in hosts)
- {
- try
- {
- var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
-
- if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
- {
- hostsWithChannel.Add(host);
- }
- }
- catch (Exception ex)
- {
- Logger.Error("Error getting channels", ex);
- }
- }
-
- foreach (var host in hostsWithChannel)
- {
- try
- {
- // Check to make sure the tuner is available
- // If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error
- if (hostsWithChannel.Count > 1 &&
- !await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false))
- {
- Logger.Error("Tuner is not currently available");
- continue;
- }
-
- var mediaSources = await GetChannelStreamMediaSources(host, channelId, cancellationToken).ConfigureAwait(false);
-
- // Prefix the id with the host Id so that we can easily find it
- foreach (var mediaSource in mediaSources)
- {
- mediaSource.Id = host.Id + mediaSource.Id;
- }
-
- return mediaSources;
- }
- catch (Exception ex)
- {
- Logger.Error("Error opening tuner", ex);
- }
- }
- }
-
- return new List<MediaSourceInfo>();
- }
-
- protected abstract Task<LiveStream> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
-
- public async Task<LiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
- {
- if (!IsValidChannelId(channelId))
- {
- throw new FileNotFoundException();
- }
-
- var hosts = GetTunerHosts();
-
- var hostsWithChannel = new List<TunerHostInfo>();
-
- foreach (var host in hosts)
- {
- if (string.IsNullOrWhiteSpace(streamId))
- {
- try
- {
- var channels = await GetChannels(host, true, cancellationToken).ConfigureAwait(false);
-
- if (channels.Any(i => string.Equals(i.Id, channelId, StringComparison.OrdinalIgnoreCase)))
- {
- hostsWithChannel.Add(host);
- }
- }
- catch (Exception ex)
- {
- Logger.Error("Error getting channels", ex);
- }
- }
- else if (streamId.StartsWith(host.Id, StringComparison.OrdinalIgnoreCase))
- {
- hostsWithChannel = new List<TunerHostInfo> { host };
- streamId = streamId.Substring(host.Id.Length);
- break;
- }
- }
-
- foreach (var host in hostsWithChannel)
- {
- try
- {
- var liveStream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
- await liveStream.Open(cancellationToken).ConfigureAwait(false);
- return liveStream;
- }
- catch (Exception ex)
- {
- Logger.Error("Error opening tuner", ex);
- }
- }
-
- throw new LiveTvConflictException();
- }
-
- protected virtual bool EnableMediaProbing
- {
- get { return false; }
- }
-
- protected async Task<bool> IsAvailable(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
- {
- try
- {
- return await IsAvailableInternal(tuner, channelId, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error checking tuner availability", ex);
- return false;
- }
- }
-
- protected abstract Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
-
- protected abstract bool IsValidChannelId(string channelId);
-
- protected LiveTvOptions GetConfiguration()
- {
- return Config.GetConfiguration<LiveTvOptions>("livetv");
- }
-
- private class ChannelCache
- {
- public DateTime Date;
- public List<ChannelInfo> Channels;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs
deleted file mode 100644
index f039da927..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs
+++ /dev/null
@@ -1,159 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using System.Threading;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
-{
- public class HdHomerunDiscovery : IServerEntryPoint
- {
- private readonly IDeviceDiscovery _deviceDiscovery;
- private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly ILiveTvManager _liveTvManager;
- private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
- private readonly IHttpClient _httpClient;
- private readonly IJsonSerializer _json;
-
- public HdHomerunDiscovery(IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config, ILogger logger, ILiveTvManager liveTvManager, IHttpClient httpClient, IJsonSerializer json)
- {
- _deviceDiscovery = deviceDiscovery;
- _config = config;
- _logger = logger;
- _liveTvManager = liveTvManager;
- _httpClient = httpClient;
- _json = json;
- }
-
- public void Run()
- {
- _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
- }
-
- void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
- {
- string server = null;
- var info = e.Argument;
-
- if (info.Headers.TryGetValue("SERVER", out server) && server.IndexOf("HDHomeRun", StringComparison.OrdinalIgnoreCase) != -1)
- {
- string location;
- if (info.Headers.TryGetValue("Location", out location))
- {
- //_logger.Debug("HdHomerun found at {0}", location);
-
- // Just get the beginning of the url
- Uri uri;
- if (Uri.TryCreate(location, UriKind.Absolute, out uri))
- {
- var apiUrl = location.Replace(uri.LocalPath, String.Empty, StringComparison.OrdinalIgnoreCase)
- .TrimEnd('/');
-
- //_logger.Debug("HdHomerun api url: {0}", apiUrl);
- AddDevice(apiUrl);
- }
- }
- }
- }
-
- private async void AddDevice(string url)
- {
- await _semaphore.WaitAsync().ConfigureAwait(false);
-
- try
- {
- var options = GetConfiguration();
-
- if (options.TunerHosts.Any(i =>
- string.Equals(i.Type, HdHomerunHost.DeviceType, StringComparison.OrdinalIgnoreCase) &&
- UriEquals(i.Url, url)))
- {
- return;
- }
-
- // Strip off the port
- url = new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped).TrimEnd('/');
-
- // Test it by pulling down the lineup
- using (var stream = await _httpClient.Get(new HttpRequestOptions
- {
- Url = string.Format("{0}/discover.json", url),
- CancellationToken = CancellationToken.None,
- BufferContent = false
- }))
- {
- var response = _json.DeserializeFromStream<HdHomerunHost.DiscoverResponse>(stream);
-
- var existing = GetConfiguration().TunerHosts
- .FirstOrDefault(i => string.Equals(i.Type, HdHomerunHost.DeviceType, StringComparison.OrdinalIgnoreCase) && string.Equals(i.DeviceId, response.DeviceID, StringComparison.OrdinalIgnoreCase));
-
- if (existing == null)
- {
- await _liveTvManager.SaveTunerHost(new TunerHostInfo
- {
- Type = HdHomerunHost.DeviceType,
- Url = url,
- DataVersion = 1,
- DeviceId = response.DeviceID
-
- }).ConfigureAwait(false);
- }
- else
- {
- if (!string.Equals(existing.Url, url, StringComparison.OrdinalIgnoreCase))
- {
- existing.Url = url;
- await _liveTvManager.SaveTunerHost(existing).ConfigureAwait(false);
- }
- }
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving device", ex);
- }
- finally
- {
- _semaphore.Release();
- }
- }
-
- private bool UriEquals(string savedUri, string location)
- {
- return string.Equals(NormalizeUrl(location), NormalizeUrl(savedUri), StringComparison.OrdinalIgnoreCase);
- }
-
- private string NormalizeUrl(string url)
- {
- if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- url = "http://" + url;
- }
-
- url = url.TrimEnd('/');
-
- // Strip off the port
- return new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped);
- }
-
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration<LiveTvOptions>("livetv");
- }
-
- public void Dispose()
- {
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
deleted file mode 100644
index a32f4cca2..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ /dev/null
@@ -1,570 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Net;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
-{
- public class HdHomerunHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
- {
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
- private readonly IServerApplicationHost _appHost;
-
- public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient, IFileSystem fileSystem, IServerApplicationHost appHost)
- : base(config, logger, jsonSerializer, mediaEncoder)
- {
- _httpClient = httpClient;
- _fileSystem = fileSystem;
- _appHost = appHost;
- }
-
- public string Name
- {
- get { return "HD Homerun"; }
- }
-
- public override string Type
- {
- get { return DeviceType; }
- }
-
- public static string DeviceType
- {
- get { return "hdhomerun"; }
- }
-
- private const string ChannelIdPrefix = "hdhr_";
-
- private string GetChannelId(TunerHostInfo info, Channels i)
- {
- var id = ChannelIdPrefix + i.GuideNumber.ToString(CultureInfo.InvariantCulture);
-
- if (info.DataVersion >= 1)
- {
- id += '_' + (i.GuideName ?? string.Empty).GetMD5().ToString("N");
- }
-
- return id;
- }
-
- private async Task<IEnumerable<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
- {
- var options = new HttpRequestOptions
- {
- Url = string.Format("{0}/lineup.json", GetApiUrl(info, false)),
- CancellationToken = cancellationToken,
- BufferContent = false
- };
- using (var stream = await _httpClient.Get(options))
- {
- var lineup = JsonSerializer.DeserializeFromStream<List<Channels>>(stream) ?? new List<Channels>();
-
- if (info.ImportFavoritesOnly)
- {
- lineup = lineup.Where(i => i.Favorite).ToList();
- }
-
- return lineup.Where(i => !i.DRM).ToList();
- }
- }
-
- protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
- {
- var lineup = await GetLineup(info, cancellationToken).ConfigureAwait(false);
-
- return lineup.Select(i => new ChannelInfo
- {
- Name = i.GuideName,
- Number = i.GuideNumber.ToString(CultureInfo.InvariantCulture),
- Id = GetChannelId(info, i),
- IsFavorite = i.Favorite,
- TunerHostId = info.Id,
- IsHD = i.HD == 1,
- AudioCodec = i.AudioCodec,
- VideoCodec = i.VideoCodec
- });
- }
-
- private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
- private async Task<string> GetModelInfo(TunerHostInfo info, CancellationToken cancellationToken)
- {
- lock (_modelCache)
- {
- DiscoverResponse response;
- if (_modelCache.TryGetValue(info.Url, out response))
- {
- return response.ModelNumber;
- }
- }
-
- try
- {
- using (var stream = await _httpClient.Get(new HttpRequestOptions()
- {
- Url = string.Format("{0}/discover.json", GetApiUrl(info, false)),
- CancellationToken = cancellationToken,
- CacheLength = TimeSpan.FromDays(1),
- CacheMode = CacheMode.Unconditional,
- TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
- BufferContent = false
- }))
- {
- var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
-
- lock (_modelCache)
- {
- _modelCache[info.Id] = response;
- }
-
- return response.ModelNumber;
- }
- }
- catch (HttpException ex)
- {
- if (ex.StatusCode.HasValue && ex.StatusCode.Value == System.Net.HttpStatusCode.NotFound)
- {
- var defaultValue = "HDHR";
- // HDHR4 doesn't have this api
- lock (_modelCache)
- {
- _modelCache[info.Id] = new DiscoverResponse
- {
- ModelNumber = defaultValue
- };
- }
- return defaultValue;
- }
-
- throw;
- }
- }
-
- public async Task<List<LiveTvTunerInfo>> GetTunerInfos(TunerHostInfo info, CancellationToken cancellationToken)
- {
- var model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
-
- using (var stream = await _httpClient.Get(new HttpRequestOptions()
- {
- Url = string.Format("{0}/tuners.html", GetApiUrl(info, false)),
- CancellationToken = cancellationToken,
- TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
- BufferContent = false
- }))
- {
- var tuners = new List<LiveTvTunerInfo>();
- using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
- {
- while (!sr.EndOfStream)
- {
- string line = StripXML(sr.ReadLine());
- if (line.Contains("Channel"))
- {
- LiveTvTunerStatus status;
- var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
- var name = line.Substring(0, index - 1);
- var currentChannel = line.Substring(index + 7);
- if (currentChannel != "none") { status = LiveTvTunerStatus.LiveTv; } else { status = LiveTvTunerStatus.Available; }
- tuners.Add(new LiveTvTunerInfo
- {
- Name = name,
- SourceType = string.IsNullOrWhiteSpace(model) ? Name : model,
- ProgramName = currentChannel,
- Status = status
- });
- }
- }
- }
- return tuners;
- }
- }
-
- public async Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
- {
- var list = new List<LiveTvTunerInfo>();
-
- foreach (var host in GetConfiguration().TunerHosts
- .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)))
- {
- try
- {
- list.AddRange(await GetTunerInfos(host, cancellationToken).ConfigureAwait(false));
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error getting tuner info", ex);
- }
- }
-
- return list;
- }
-
- private string GetApiUrl(TunerHostInfo info, bool isPlayback)
- {
- var url = info.Url;
-
- if (string.IsNullOrWhiteSpace(url))
- {
- throw new ArgumentException("Invalid tuner info");
- }
-
- if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- url = "http://" + url;
- }
-
- var uri = new Uri(url);
-
- if (isPlayback)
- {
- var builder = new UriBuilder(uri);
- builder.Port = 5004;
- uri = builder.Uri;
- }
-
- return uri.AbsoluteUri.TrimEnd('/');
- }
-
- private static string StripXML(string source)
- {
- char[] buffer = new char[source.Length];
- int bufferIndex = 0;
- bool inside = false;
-
- for (int i = 0; i < source.Length; i++)
- {
- char let = source[i];
- if (let == '<')
- {
- inside = true;
- continue;
- }
- if (let == '>')
- {
- inside = false;
- continue;
- }
- if (!inside)
- {
- buffer[bufferIndex] = let;
- bufferIndex++;
- }
- }
- return new string(buffer, 0, bufferIndex);
- }
-
- private class Channels
- {
- public string GuideNumber { get; set; }
- public string GuideName { get; set; }
- public string VideoCodec { get; set; }
- public string AudioCodec { get; set; }
- public string URL { get; set; }
- public bool Favorite { get; set; }
- public bool DRM { get; set; }
- public int HD { get; set; }
- }
-
- private async Task<MediaSourceInfo> GetMediaSource(TunerHostInfo info, string channelId, string profile)
- {
- int? width = null;
- int? height = null;
- bool isInterlaced = true;
- string videoCodec = null;
- string audioCodec = "ac3";
-
- int? videoBitrate = null;
- int? audioBitrate = null;
-
- if (string.Equals(profile, "mobile", StringComparison.OrdinalIgnoreCase))
- {
- width = 1280;
- height = 720;
- isInterlaced = false;
- videoCodec = "h264";
- videoBitrate = 2000000;
- }
- else if (string.Equals(profile, "heavy", StringComparison.OrdinalIgnoreCase))
- {
- width = 1920;
- height = 1080;
- isInterlaced = false;
- videoCodec = "h264";
- videoBitrate = 15000000;
- }
- else if (string.Equals(profile, "internet540", StringComparison.OrdinalIgnoreCase))
- {
- width = 960;
- height = 546;
- isInterlaced = false;
- videoCodec = "h264";
- videoBitrate = 2500000;
- }
- else if (string.Equals(profile, "internet480", StringComparison.OrdinalIgnoreCase))
- {
- width = 848;
- height = 480;
- isInterlaced = false;
- videoCodec = "h264";
- videoBitrate = 2000000;
- }
- else if (string.Equals(profile, "internet360", StringComparison.OrdinalIgnoreCase))
- {
- width = 640;
- height = 360;
- isInterlaced = false;
- videoCodec = "h264";
- videoBitrate = 1500000;
- }
- else if (string.Equals(profile, "internet240", StringComparison.OrdinalIgnoreCase))
- {
- width = 432;
- height = 240;
- isInterlaced = false;
- videoCodec = "h264";
- videoBitrate = 1000000;
- }
-
- var channels = await GetChannels(info, true, CancellationToken.None).ConfigureAwait(false);
- var channel = channels.FirstOrDefault(i => string.Equals(i.Number, channelId, StringComparison.OrdinalIgnoreCase));
- if (channel != null)
- {
- if (string.IsNullOrWhiteSpace(videoCodec))
- {
- videoCodec = channel.VideoCodec;
- }
- audioCodec = channel.AudioCodec;
-
- if (!videoBitrate.HasValue)
- {
- videoBitrate = (channel.IsHD ?? true) ? 15000000 : 2000000;
- }
- audioBitrate = (channel.IsHD ?? true) ? 448000 : 192000;
- }
-
- // normalize
- if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase))
- {
- videoCodec = "mpeg2video";
- }
-
- string nal = null;
- if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
- {
- nal = "0";
- }
-
- var url = GetApiUrl(info, true) + "/auto/v" + channelId;
-
- if (!string.IsNullOrWhiteSpace(profile) && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase))
- {
- url += "?transcode=" + profile;
- }
-
- var id = profile;
- if (string.IsNullOrWhiteSpace(id))
- {
- id = "native";
- }
- id += "_" + url.GetMD5().ToString("N");
-
- var mediaSource = new MediaSourceInfo
- {
- Path = url,
- Protocol = MediaProtocol.Http,
- MediaStreams = new List<MediaStream>
- {
- new MediaStream
- {
- Type = MediaStreamType.Video,
- // Set the index to -1 because we don't know the exact index of the video stream within the container
- Index = -1,
- IsInterlaced = isInterlaced,
- Codec = videoCodec,
- Width = width,
- Height = height,
- BitRate = videoBitrate,
- NalLengthSize = nal
-
- },
- new MediaStream
- {
- Type = MediaStreamType.Audio,
- // Set the index to -1 because we don't know the exact index of the audio stream within the container
- Index = -1,
- Codec = audioCodec,
- BitRate = audioBitrate
- }
- },
- RequiresOpening = true,
- RequiresClosing = false,
- BufferMs = 0,
- Container = "ts",
- Id = id,
- SupportsDirectPlay = false,
- SupportsDirectStream = true,
- SupportsTranscoding = true,
- IsInfiniteStream = true
- };
-
- return mediaSource;
- }
-
- protected EncodingOptions GetEncodingOptions()
- {
- return Config.GetConfiguration<EncodingOptions>("encoding");
- }
-
- private string GetHdHrIdFromChannelId(string channelId)
- {
- return channelId.Split('_')[1];
- }
-
- protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
- {
- var list = new List<MediaSourceInfo>();
-
- if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
- {
- return list;
- }
- var hdhrId = GetHdHrIdFromChannelId(channelId);
-
- list.Add(await GetMediaSource(info, hdhrId, "native").ConfigureAwait(false));
-
- try
- {
- if (info.AllowHWTranscoding)
- {
- string model = await GetModelInfo(info, cancellationToken).ConfigureAwait(false);
- model = model ?? string.Empty;
-
- if ((model.IndexOf("hdtc", StringComparison.OrdinalIgnoreCase) != -1))
- {
- list.Add(await GetMediaSource(info, hdhrId, "heavy").ConfigureAwait(false));
-
- list.Add(await GetMediaSource(info, hdhrId, "internet540").ConfigureAwait(false));
- list.Add(await GetMediaSource(info, hdhrId, "internet480").ConfigureAwait(false));
- list.Add(await GetMediaSource(info, hdhrId, "internet360").ConfigureAwait(false));
- list.Add(await GetMediaSource(info, hdhrId, "internet240").ConfigureAwait(false));
- list.Add(await GetMediaSource(info, hdhrId, "mobile").ConfigureAwait(false));
- }
- }
- }
- catch
- {
-
- }
-
- return list;
- }
-
- protected override bool IsValidChannelId(string channelId)
- {
- if (string.IsNullOrWhiteSpace(channelId))
- {
- throw new ArgumentNullException("channelId");
- }
-
- return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
- }
-
- protected override async Task<LiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
- {
- var profile = streamId.Split('_')[0];
-
- Logger.Info("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelId, streamId, profile);
-
- if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
- {
- throw new ArgumentException("Channel not found");
- }
- var hdhrId = GetHdHrIdFromChannelId(channelId);
-
- var mediaSource = await GetMediaSource(info, hdhrId, profile).ConfigureAwait(false);
-
- var liveStream = new HdHomerunLiveStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
- liveStream.EnableStreamSharing = true;
- return liveStream;
- }
-
- public async Task Validate(TunerHostInfo info)
- {
- if (!info.IsEnabled)
- {
- return;
- }
-
- lock (_modelCache)
- {
- _modelCache.Clear();
- }
-
- try
- {
- // Test it by pulling down the lineup
- using (var stream = await _httpClient.Get(new HttpRequestOptions
- {
- Url = string.Format("{0}/discover.json", GetApiUrl(info, false)),
- CancellationToken = CancellationToken.None,
- BufferContent = false
- }))
- {
- var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
-
- info.DeviceId = response.DeviceID;
- }
- }
- catch (HttpException ex)
- {
- if (ex.StatusCode.HasValue && ex.StatusCode.Value == System.Net.HttpStatusCode.NotFound)
- {
- // HDHR4 doesn't have this api
- return;
- }
-
- throw;
- }
- }
-
- protected override async Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
- {
- var info = await GetTunerInfos(tuner, cancellationToken).ConfigureAwait(false);
-
- return info.Any(i => i.Status == LiveTvTunerStatus.Available);
- }
-
- public class DiscoverResponse
- {
- public string FriendlyName { get; set; }
- public string ModelNumber { get; set; }
- public string FirmwareName { get; set; }
- public string FirmwareVersion { get; set; }
- public string DeviceID { get; set; }
- public string DeviceAuth { get; set; }
- public string BaseURL { get; set; }
- public string LineupURL { get; set; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
deleted file mode 100644
index 7bb4dc92e..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunLiveStream.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
-{
- public class HdHomerunLiveStream : LiveStream, IDirectStreamProvider
- {
- private readonly ILogger _logger;
- private readonly IHttpClient _httpClient;
- private readonly IFileSystem _fileSystem;
- private readonly IServerApplicationPaths _appPaths;
- private readonly IServerApplicationHost _appHost;
-
- private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
- private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
- private readonly MulticastStream _multicastStream;
-
-
- public HdHomerunLiveStream(MediaSourceInfo mediaSource, string originalStreamId, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost)
- : base(mediaSource)
- {
- _fileSystem = fileSystem;
- _httpClient = httpClient;
- _logger = logger;
- _appPaths = appPaths;
- _appHost = appHost;
- OriginalStreamId = originalStreamId;
- _multicastStream = new MulticastStream(_logger);
- }
-
- protected override async Task OpenInternal(CancellationToken openCancellationToken)
- {
- _liveStreamCancellationTokenSource.Token.ThrowIfCancellationRequested();
-
- var mediaSource = OriginalMediaSource;
-
- var url = mediaSource.Path;
-
- _logger.Info("Opening HDHR Live stream from {0}", url);
-
- var taskCompletionSource = new TaskCompletionSource<bool>();
-
- StartStreaming(url, taskCompletionSource, _liveStreamCancellationTokenSource.Token);
-
- //OpenedMediaSource.Protocol = MediaProtocol.File;
- //OpenedMediaSource.Path = tempFile;
- //OpenedMediaSource.ReadAtNativeFramerate = true;
-
- OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
- OpenedMediaSource.Protocol = MediaProtocol.Http;
- OpenedMediaSource.SupportsDirectPlay = false;
- OpenedMediaSource.SupportsDirectStream = true;
- OpenedMediaSource.SupportsTranscoding = true;
-
- await taskCompletionSource.Task.ConfigureAwait(false);
-
- //await Task.Delay(5000).ConfigureAwait(false);
- }
-
- public override Task Close()
- {
- _logger.Info("Closing HDHR live stream");
- _liveStreamCancellationTokenSource.Cancel();
-
- return _liveStreamTaskCompletionSource.Task;
- }
-
- private async Task StartStreaming(string url, TaskCompletionSource<bool> openTaskCompletionSource, CancellationToken cancellationToken)
- {
- await Task.Run(async () =>
- {
- var isFirstAttempt = true;
-
- while (!cancellationToken.IsCancellationRequested)
- {
- try
- {
- using (var response = await _httpClient.SendAsync(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- BufferContent = false
-
- }, "GET").ConfigureAwait(false))
- {
- _logger.Info("Opened HDHR stream from {0}", url);
-
- if (!cancellationToken.IsCancellationRequested)
- {
- _logger.Info("Beginning multicastStream.CopyUntilCancelled");
-
- Action onStarted = null;
- if (isFirstAttempt)
- {
- onStarted = () => openTaskCompletionSource.TrySetResult(true);
- }
-
- await _multicastStream.CopyUntilCancelled(response.Content, onStarted, cancellationToken).ConfigureAwait(false);
- }
- }
- }
- catch (OperationCanceledException)
- {
- break;
- }
- catch (Exception ex)
- {
- if (isFirstAttempt)
- {
- _logger.ErrorException("Error opening live stream:", ex);
- openTaskCompletionSource.TrySetException(ex);
- break;
- }
-
- _logger.ErrorException("Error copying live stream, will reopen", ex);
- }
-
- isFirstAttempt = false;
- }
-
- _liveStreamTaskCompletionSource.TrySetResult(true);
-
- }).ConfigureAwait(false);
- }
-
- public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
- {
- return _multicastStream.CopyToAsync(stream);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
deleted file mode 100644
index b85b3810a..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
-{
- public class M3UTunerHost : BaseTunerHost, ITunerHost, IConfigurableTunerHost
- {
- private readonly IFileSystem _fileSystem;
- private readonly IHttpClient _httpClient;
- private readonly IServerApplicationHost _appHost;
-
- public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
- : base(config, logger, jsonSerializer, mediaEncoder)
- {
- _fileSystem = fileSystem;
- _httpClient = httpClient;
- _appHost = appHost;
- }
-
- public override string Type
- {
- get { return "m3u"; }
- }
-
- public string Name
- {
- get { return "M3U Tuner"; }
- }
-
- private const string ChannelIdPrefix = "m3u_";
-
- protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
- {
- return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
- }
-
- public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
- {
- var list = GetTunerHosts()
- .Select(i => new LiveTvTunerInfo()
- {
- Name = Name,
- SourceType = Type,
- Status = LiveTvTunerStatus.Available,
- Id = i.Url.GetMD5().ToString("N"),
- Url = i.Url
- })
- .ToList();
-
- return Task.FromResult(list);
- }
-
- protected override async Task<LiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
- {
- var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
-
- var liveStream = new LiveStream(sources.First());
- return liveStream;
- }
-
- public async Task Validate(TunerHostInfo info)
- {
- using (var stream = await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
- {
-
- }
- }
-
- protected override bool IsValidChannelId(string channelId)
- {
- return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
- }
-
- protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
- {
- var urlHash = info.Url.GetMD5().ToString("N");
- var prefix = ChannelIdPrefix + urlHash;
- if (!channelId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
- {
- return null;
- }
-
- var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
- var m3uchannels = channels.Cast<M3UChannel>();
- var channel = m3uchannels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase));
- if (channel != null)
- {
- var path = channel.Path;
- MediaProtocol protocol = MediaProtocol.File;
- if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- protocol = MediaProtocol.Http;
- }
- else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase))
- {
- protocol = MediaProtocol.Rtmp;
- }
- else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase))
- {
- protocol = MediaProtocol.Rtsp;
- }
- else if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
- {
- protocol = MediaProtocol.Udp;
- }
-
- var mediaSource = new MediaSourceInfo
- {
- Path = channel.Path,
- Protocol = protocol,
- MediaStreams = new List<MediaStream>
- {
- new MediaStream
- {
- Type = MediaStreamType.Video,
- // Set the index to -1 because we don't know the exact index of the video stream within the container
- Index = -1,
- IsInterlaced = true
- },
- new MediaStream
- {
- Type = MediaStreamType.Audio,
- // Set the index to -1 because we don't know the exact index of the audio stream within the container
- Index = -1
-
- }
- },
- RequiresOpening = false,
- RequiresClosing = false,
-
- ReadAtNativeFramerate = false,
-
- Id = channel.Path.GetMD5().ToString("N"),
- IsInfiniteStream = true
- };
-
- return new List<MediaSourceInfo> { mediaSource };
- }
- return new List<MediaSourceInfo>();
- }
-
- protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
deleted file mode 100644
index 3bfe902df..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ /dev/null
@@ -1,169 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
-{
- public class M3uParser
- {
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IHttpClient _httpClient;
- private readonly IServerApplicationHost _appHost;
-
- public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
- {
- _logger = logger;
- _fileSystem = fileSystem;
- _httpClient = httpClient;
- _appHost = appHost;
- }
-
- public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
- {
- var urlHash = url.GetMD5().ToString("N");
-
- // Read the file and display it line by line.
- using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
- {
- return GetChannels(reader, urlHash, channelIdPrefix, tunerHostId);
- }
- }
-
- public Task<Stream> GetListingsStream(string url, CancellationToken cancellationToken)
- {
- if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return _httpClient.Get(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- // Some data providers will require a user agent
- UserAgent = _appHost.FriendlyName + "/" + _appHost.ApplicationVersion
- });
- }
- return Task.FromResult(_fileSystem.OpenRead(url));
- }
-
- private List<M3UChannel> GetChannels(StreamReader reader, string urlHash, string channelIdPrefix, string tunerHostId)
- {
- var channels = new List<M3UChannel>();
- string line;
- string extInf = "";
- while ((line = reader.ReadLine()) != null)
- {
- line = line.Trim();
- if (string.IsNullOrWhiteSpace(line))
- {
- continue;
- }
-
- if (line.StartsWith("#EXTM3U", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- if (line.StartsWith("#EXTINF:", StringComparison.OrdinalIgnoreCase))
- {
- extInf = line.Substring(8).Trim();
- _logger.Info("Found m3u channel: {0}", extInf);
- }
- else if (!string.IsNullOrWhiteSpace(extInf) && !line.StartsWith("#", StringComparison.OrdinalIgnoreCase))
- {
- var channel = GetChannelnfo(extInf, tunerHostId, line);
- channel.Id = channelIdPrefix + urlHash + line.GetMD5().ToString("N");
- channel.Path = line;
- channels.Add(channel);
- extInf = "";
- }
- }
- return channels;
- }
- private M3UChannel GetChannelnfo(string extInf, string tunerHostId, string mediaUrl)
- {
- var titleIndex = extInf.LastIndexOf(',');
- var channel = new M3UChannel();
- channel.TunerHostId = tunerHostId;
-
- channel.Number = extInf.Trim().Split(' ')[0] ?? "0";
- channel.Name = extInf.Substring(titleIndex + 1);
-
- //Check for channel number with the format from SatIp
- int number;
- var numberIndex = channel.Name.IndexOf('.');
- if (numberIndex > 0)
- {
- if (int.TryParse(channel.Name.Substring(0, numberIndex), out number))
- {
- channel.Number = number.ToString();
- channel.Name = channel.Name.Substring(numberIndex + 1);
- }
- }
-
- if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(mediaUrl))
- {
- channel.Number = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last());
- }
-
- if (string.Equals(channel.Number, "-1", StringComparison.OrdinalIgnoreCase))
- {
- channel.Number = "0";
- }
-
- channel.ImageUrl = FindProperty("tvg-logo", extInf);
-
- var name = FindProperty("tvg-name", extInf);
- if (string.IsNullOrWhiteSpace(name))
- {
- name = FindProperty("tvg-id", extInf);
- }
-
- channel.Name = name;
-
- var numberString = FindProperty("tvg-id", extInf);
- if (string.IsNullOrWhiteSpace(numberString))
- {
- numberString = FindProperty("channel-id", extInf);
- }
-
- if (!string.IsNullOrWhiteSpace(numberString))
- {
- channel.Number = numberString;
- }
-
- return channel;
-
- }
- private string FindProperty(string property, string properties)
- {
- var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
- var matches = reg.Matches(properties);
- foreach (Match match in matches)
- {
- if (match.Groups[1].Value == property)
- {
- return match.Groups[2].Value;
- }
- }
- return null;
- }
- }
-
-
- public class M3UChannel : ChannelInfo
- {
- public string Path { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
deleted file mode 100644
index 8ff3fd6c1..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
-{
- public class MulticastStream
- {
- private readonly List<QueueStream> _outputStreams = new List<QueueStream>();
- private const int BufferSize = 81920;
- private CancellationToken _cancellationToken;
- private readonly ILogger _logger;
-
- public MulticastStream(ILogger logger)
- {
- _logger = logger;
- }
-
- public async Task CopyUntilCancelled(Stream source, Action onStarted, CancellationToken cancellationToken)
- {
- _cancellationToken = cancellationToken;
-
- while (!cancellationToken.IsCancellationRequested)
- {
- byte[] buffer = new byte[BufferSize];
-
- var bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
-
- if (bytesRead > 0)
- {
- byte[] copy = new byte[bytesRead];
- Buffer.BlockCopy(buffer, 0, copy, 0, bytesRead);
-
- List<QueueStream> streams = null;
-
- lock (_outputStreams)
- {
- streams = _outputStreams.ToList();
- }
-
- foreach (var stream in streams)
- {
- stream.Queue(copy);
- }
-
- if (onStarted != null)
- {
- var onStartedCopy = onStarted;
- onStarted = null;
- Task.Run(onStartedCopy);
- }
- }
-
- else
- {
- await Task.Delay(100).ConfigureAwait(false);
- }
- }
- }
-
- public Task CopyToAsync(Stream stream)
- {
- var result = new QueueStream(stream, _logger)
- {
- OnFinished = OnFinished
- };
-
- lock (_outputStreams)
- {
- _outputStreams.Add(result);
- }
-
- result.Start(_cancellationToken);
-
- return result.TaskCompletion.Task;
- }
-
- public void RemoveOutputStream(QueueStream stream)
- {
- lock (_outputStreams)
- {
- _outputStreams.Remove(stream);
- }
- }
-
- private void OnFinished(QueueStream queueStream)
- {
- RemoveOutputStream(queueStream);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs
deleted file mode 100644
index c1566b900..000000000
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
-{
- public class QueueStream
- {
- private readonly Stream _outputStream;
- private readonly ConcurrentQueue<byte[]> _queue = new ConcurrentQueue<byte[]>();
- private CancellationToken _cancellationToken;
- public TaskCompletionSource<bool> TaskCompletion { get; private set; }
-
- public Action<QueueStream> OnFinished { get; set; }
- private readonly ILogger _logger;
-
- public QueueStream(Stream outputStream, ILogger logger)
- {
- _outputStream = outputStream;
- _logger = logger;
- TaskCompletion = new TaskCompletionSource<bool>();
- }
-
- public void Queue(byte[] bytes)
- {
- _queue.Enqueue(bytes);
- }
-
- public void Start(CancellationToken cancellationToken)
- {
- _cancellationToken = cancellationToken;
- Task.Run(() => StartInternal());
- }
-
- private byte[] Dequeue()
- {
- byte[] bytes;
- if (_queue.TryDequeue(out bytes))
- {
- return bytes;
- }
-
- return null;
- }
-
- private async Task StartInternal()
- {
- var cancellationToken = _cancellationToken;
-
- try
- {
- while (!cancellationToken.IsCancellationRequested)
- {
- var bytes = Dequeue();
- if (bytes != null)
- {
- await _outputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- await Task.Delay(50, cancellationToken).ConfigureAwait(false);
- }
- }
-
- TaskCompletion.TrySetResult(true);
- _logger.Debug("QueueStream complete");
- }
- catch (OperationCanceledException)
- {
- _logger.Debug("QueueStream cancelled");
- TaskCompletion.TrySetCanceled();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in QueueStream", ex);
- TaskCompletion.TrySetException(ex);
- }
- finally
- {
- if (OnFinished != null)
- {
- OnFinished(this);
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs
index 8dca261a3..55101ce10f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/SatIp/SatIpHost.cs
@@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Emby.Server.Implementations.LiveTv.TunerHosts;
using MediaBrowser.Model.IO;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
@@ -20,7 +21,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
{
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 15c2368e1..6118e1501 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -46,26 +46,23 @@
<HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
</Reference>
<Reference Include="Emby.XmlTv, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\Emby.XmlTv.1.0.0.58\lib\portable-net46+win10\Emby.XmlTv.dll</HintPath>
+ <HintPath>..\packages\Emby.XmlTv.1.0.0.63\lib\portable-net45+win8\Emby.XmlTv.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="INIFileParser, Version=2.3.0.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL">
<HintPath>..\packages\ini-parser.2.3.0\lib\net20\INIFileParser.dll</HintPath>
<Private>True</Private>
</Reference>
- <Reference Include="MediaBrowser.Naming, Version=1.0.6146.28476, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\MediaBrowser.Naming.1.0.0.57\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
- <Private>True</Private>
- </Reference>
<Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.1.0.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
<Private>True</Private>
</Reference>
- <Reference Include="Mono.Nat">
+ <Reference Include="Mono.Nat, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
<HintPath>..\ThirdParty\emby\Mono.Nat.dll</HintPath>
</Reference>
- <Reference Include="Patterns.Logging, Version=1.0.6149.1756, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\Patterns.Logging.1.0.0.4\lib\portable-net45+dnxcore50+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
+ <Reference Include="Patterns.Logging, Version=1.0.6151.30227, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\Patterns.Logging.1.0.0.6\lib\portable-net45+win8\Patterns.Logging.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Api.Swagger">
@@ -108,50 +105,19 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
- <Compile Include="Activity\ActivityManager.cs" />
<Compile Include="Activity\ActivityRepository.cs" />
<Compile Include="Archiving\ZipClient.cs" />
- <Compile Include="Branding\BrandingConfigurationFactory.cs" />
- <Compile Include="Channels\ChannelConfigurations.cs" />
- <Compile Include="Channels\ChannelDynamicMediaSourceProvider.cs" />
- <Compile Include="Channels\ChannelImageProvider.cs" />
- <Compile Include="Channels\ChannelManager.cs" />
- <Compile Include="Channels\ChannelPostScanTask.cs" />
- <Compile Include="Channels\RefreshChannelsScheduledTask.cs" />
- <Compile Include="Collections\CollectionManager.cs" />
<Compile Include="Collections\CollectionsDynamicFolder.cs" />
- <Compile Include="Collections\CollectionImageProvider.cs" />
<Compile Include="Configuration\ServerConfigurationManager.cs" />
<Compile Include="Connect\ConnectData.cs" />
+ <Compile Include="Connect\ConnectEntryPoint.cs" />
<Compile Include="Connect\ConnectManager.cs" />
<Compile Include="Connect\Responses.cs" />
<Compile Include="Connect\Validator.cs" />
- <Compile Include="Devices\DeviceManager.cs" />
<Compile Include="Devices\DeviceRepository.cs" />
<Compile Include="Devices\CameraUploadsFolder.cs" />
- <Compile Include="Dto\DtoService.cs" />
- <Compile Include="EntryPoints\ActivityLogEntryPoint.cs" />
- <Compile Include="EntryPoints\AutomaticRestartEntryPoint.cs" />
<Compile Include="EntryPoints\ExternalPortForwarding.cs" />
- <Compile Include="EntryPoints\LibraryChangedNotifier.cs" />
- <Compile Include="EntryPoints\LoadRegistrations.cs" />
- <Compile Include="EntryPoints\Notifications\Notifications.cs" />
- <Compile Include="EntryPoints\Notifications\WebSocketNotifier.cs" />
- <Compile Include="EntryPoints\RecordingNotifier.cs" />
- <Compile Include="EntryPoints\RefreshUsersMetadata.cs" />
- <Compile Include="EntryPoints\UsageEntryPoint.cs" />
- <Compile Include="Connect\ConnectEntryPoint.cs" />
- <Compile Include="EntryPoints\UsageReporter.cs" />
- <Compile Include="FileOrganization\EpisodeFileOrganizer.cs" />
- <Compile Include="FileOrganization\Extensions.cs" />
- <Compile Include="FileOrganization\FileOrganizationNotifier.cs" />
- <Compile Include="FileOrganization\FileOrganizationService.cs" />
- <Compile Include="FileOrganization\NameUtils.cs" />
- <Compile Include="FileOrganization\TvFolderOrganizer.cs" />
<Compile Include="EntryPoints\UdpServerEntryPoint.cs" />
- <Compile Include="EntryPoints\ServerEventNotifier.cs" />
- <Compile Include="EntryPoints\UserDataChangeNotifier.cs" />
- <Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
<Compile Include="HttpServer\IHttpListener.cs" />
<Compile Include="HttpServer\Security\AuthorizationContext.cs" />
<Compile Include="HttpServer\ContainerAdapter.cs" />
@@ -175,79 +141,9 @@
<Compile Include="HttpServer\SocketSharp\WebSocketSharpListener.cs" />
<Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
<Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
- <Compile Include="Intros\DefaultIntroProvider.cs" />
<Compile Include="IO\FileRefresher.cs" />
<Compile Include="IO\LibraryMonitor.cs" />
<Compile Include="IO\MemoryStreamProvider.cs" />
- <Compile Include="IO\ThrottledStream.cs" />
- <Compile Include="Library\CoreResolutionIgnoreRule.cs" />
- <Compile Include="Library\LibraryManager.cs" />
- <Compile Include="Library\LocalTrailerPostScanTask.cs" />
- <Compile Include="Library\MediaSourceManager.cs" />
- <Compile Include="Library\MusicManager.cs" />
- <Compile Include="Library\PathExtensions.cs" />
- <Compile Include="Library\Resolvers\SpecialFolderResolver.cs" />
- <Compile Include="Library\Resolvers\BaseVideoResolver.cs" />
- <Compile Include="Library\Resolvers\PhotoAlbumResolver.cs" />
- <Compile Include="Library\Resolvers\PhotoResolver.cs" />
- <Compile Include="Library\Resolvers\PlaylistResolver.cs" />
- <Compile Include="Library\SearchEngine.cs" />
- <Compile Include="Library\ResolverHelper.cs" />
- <Compile Include="Library\Resolvers\Audio\AudioResolver.cs" />
- <Compile Include="Library\Resolvers\Audio\MusicAlbumResolver.cs" />
- <Compile Include="Library\Resolvers\Audio\MusicArtistResolver.cs" />
- <Compile Include="Library\Resolvers\ItemResolver.cs" />
- <Compile Include="Library\Resolvers\FolderResolver.cs" />
- <Compile Include="Library\Resolvers\Movies\BoxSetResolver.cs" />
- <Compile Include="Library\Resolvers\Movies\MovieResolver.cs" />
- <Compile Include="Library\Resolvers\TV\EpisodeResolver.cs" />
- <Compile Include="Library\Resolvers\TV\SeasonResolver.cs" />
- <Compile Include="Library\Resolvers\TV\SeriesResolver.cs" />
- <Compile Include="Library\Resolvers\VideoResolver.cs" />
- <Compile Include="Library\UserDataManager.cs" />
- <Compile Include="Library\UserManager.cs" />
- <Compile Include="Library\UserViewManager.cs" />
- <Compile Include="Library\Validators\ArtistsPostScanTask.cs" />
- <Compile Include="Library\Validators\ArtistsValidator.cs" />
- <Compile Include="Library\Validators\GameGenresPostScanTask.cs" />
- <Compile Include="Library\Validators\GameGenresValidator.cs" />
- <Compile Include="Library\Validators\GenresPostScanTask.cs" />
- <Compile Include="Library\Validators\GenresValidator.cs" />
- <Compile Include="Library\Validators\MusicGenresPostScanTask.cs" />
- <Compile Include="Library\Validators\MusicGenresValidator.cs" />
- <Compile Include="Library\Validators\PeopleValidator.cs" />
- <Compile Include="Library\Validators\StudiosPostScanTask.cs" />
- <Compile Include="Library\Validators\StudiosValidator.cs" />
- <Compile Include="Library\Validators\YearsPostScanTask.cs" />
- <Compile Include="LiveTv\ChannelImageProvider.cs" />
- <Compile Include="LiveTv\EmbyTV\DirectRecorder.cs" />
- <Compile Include="LiveTv\EmbyTV\EmbyTV.cs" />
- <Compile Include="LiveTv\EmbyTV\EmbyTVRegistration.cs" />
- <Compile Include="LiveTv\EmbyTV\EncodedRecorder.cs" />
- <Compile Include="LiveTv\EmbyTV\EntryPoint.cs" />
- <Compile Include="LiveTv\EmbyTV\IRecorder.cs" />
- <Compile Include="LiveTv\EmbyTV\ItemDataProvider.cs" />
- <Compile Include="LiveTv\EmbyTV\RecordingHelper.cs" />
- <Compile Include="LiveTv\EmbyTV\SeriesTimerManager.cs" />
- <Compile Include="LiveTv\EmbyTV\TimerManager.cs" />
- <Compile Include="LiveTv\Listings\SchedulesDirect.cs" />
- <Compile Include="LiveTv\Listings\XmlTvListingsProvider.cs" />
- <Compile Include="LiveTv\LiveStreamHelper.cs" />
- <Compile Include="LiveTv\LiveTvConfigurationFactory.cs" />
- <Compile Include="LiveTv\LiveTvDtoService.cs" />
- <Compile Include="LiveTv\LiveTvManager.cs" />
- <Compile Include="LiveTv\LiveTvMediaSourceProvider.cs" />
- <Compile Include="LiveTv\TunerHosts\BaseTunerHost.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunDiscovery.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunLiveStream.cs" />
- <Compile Include="LiveTv\TunerHosts\M3uParser.cs" />
- <Compile Include="LiveTv\TunerHosts\M3UTunerHost.cs" />
- <Compile Include="LiveTv\ProgramImageProvider.cs" />
- <Compile Include="LiveTv\RecordingImageProvider.cs" />
- <Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
- <Compile Include="LiveTv\TunerHosts\MulticastStream.cs" />
- <Compile Include="LiveTv\TunerHosts\QueueStream.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\ChannelScan.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\ReportBlock.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpAppPacket.cs" />
@@ -266,122 +162,43 @@
<Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspResponse.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspSession.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspStatusCode.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\SatIpHost.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\SatIpDiscovery.cs" />
+ <Compile Include="LiveTv\TunerHosts\SatIp\SatIpHost.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\TransmissionMode.cs" />
<Compile Include="LiveTv\TunerHosts\SatIp\Utils.cs" />
<Compile Include="Localization\LocalizationManager.cs" />
<Compile Include="Logging\PatternsLogger.cs" />
- <Compile Include="MediaEncoder\EncodingManager.cs" />
- <Compile Include="Notifications\IConfigurableNotificationService.cs" />
<Compile Include="Persistence\BaseSqliteRepository.cs" />
- <Compile Include="Persistence\CleanDatabaseScheduledTask.cs" />
<Compile Include="Persistence\DataExtensions.cs" />
<Compile Include="Persistence\IDbConnector.cs" />
<Compile Include="Persistence\MediaStreamColumns.cs" />
- <Compile Include="Reflection\AssemblyInfo.cs" />
<Compile Include="Security\MBLicenseFile.cs" />
<Compile Include="Security\PluginSecurityManager.cs" />
<Compile Include="Security\RegRecord.cs" />
<Compile Include="Serialization\JsonSerializer.cs" />
<Compile Include="Social\SharingManager.cs" />
<Compile Include="Social\SharingRepository.cs" />
- <Compile Include="Sorting\StartDateComparer.cs" />
- <Compile Include="Sync\SyncHelper.cs" />
- <Compile Include="Sync\SyncJobOptions.cs" />
- <Compile Include="Sync\SyncNotificationEntryPoint.cs" />
- <Compile Include="TextEncoding\TextEncoding.cs" />
- <Compile Include="Threading\PeriodicTimer.cs" />
- <Compile Include="TV\SeriesPostScanTask.cs" />
- <Compile Include="Updates\InstallationManager.cs" />
- <Compile Include="UserViews\CollectionFolderImageProvider.cs" />
- <Compile Include="UserViews\DynamicImageProvider.cs" />
- <Compile Include="News\NewsEntryPoint.cs" />
- <Compile Include="News\NewsService.cs" />
- <Compile Include="Notifications\CoreNotificationTypes.cs" />
- <Compile Include="Notifications\InternalNotificationService.cs" />
- <Compile Include="Notifications\NotificationConfigurationFactory.cs" />
- <Compile Include="Notifications\NotificationManager.cs" />
<Compile Include="Persistence\SqliteFileOrganizationRepository.cs" />
<Compile Include="Notifications\SqliteNotificationsRepository.cs" />
<Compile Include="Persistence\TypeMapper.cs" />
- <Compile Include="Photos\BaseDynamicImageProvider.cs" />
<Compile Include="Playlists\ManualPlaylistsFolder.cs" />
- <Compile Include="Photos\PhotoAlbumImageProvider.cs" />
- <Compile Include="Playlists\PlaylistImageProvider.cs" />
- <Compile Include="Playlists\PlaylistManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="ScheduledTasks\PeopleValidationTask.cs" />
- <Compile Include="ScheduledTasks\ChapterImagesTask.cs" />
- <Compile Include="ScheduledTasks\PluginUpdateTask.cs" />
- <Compile Include="ScheduledTasks\RefreshIntrosTask.cs" />
- <Compile Include="ScheduledTasks\RefreshMediaLibraryTask.cs" />
- <Compile Include="ScheduledTasks\SystemUpdateTask.cs" />
<Compile Include="Security\AuthenticationRepository.cs" />
<Compile Include="Security\EncryptionManager.cs" />
<Compile Include="ServerApplicationPaths.cs" />
- <Compile Include="ServerManager\ServerManager.cs" />
- <Compile Include="ServerManager\WebSocketConnection.cs" />
- <Compile Include="Session\HttpSessionController.cs" />
- <Compile Include="Session\SessionManager.cs">
- <SubType>Code</SubType>
- </Compile>
- <Compile Include="Session\SessionWebSocketListener.cs" />
- <Compile Include="Session\WebSocketController.cs" />
- <Compile Include="Sorting\AiredEpisodeOrderComparer.cs" />
- <Compile Include="Sorting\AirTimeComparer.cs" />
- <Compile Include="Sorting\AlbumArtistComparer.cs" />
- <Compile Include="Sorting\AlbumComparer.cs" />
- <Compile Include="Sorting\AlphanumComparator.cs" />
- <Compile Include="Sorting\ArtistComparer.cs" />
- <Compile Include="Sorting\BudgetComparer.cs" />
- <Compile Include="Sorting\CommunityRatingComparer.cs" />
- <Compile Include="Sorting\CriticRatingComparer.cs" />
- <Compile Include="Sorting\DateCreatedComparer.cs" />
- <Compile Include="Sorting\DateLastMediaAddedComparer.cs" />
- <Compile Include="Sorting\DatePlayedComparer.cs" />
- <Compile Include="Sorting\GameSystemComparer.cs" />
- <Compile Include="Sorting\IsFavoriteOrLikeComparer.cs" />
- <Compile Include="Sorting\IsFolderComparer.cs" />
- <Compile Include="Sorting\IsPlayedComparer.cs" />
- <Compile Include="Sorting\IsUnplayedComparer.cs" />
- <Compile Include="Sorting\MetascoreComparer.cs" />
- <Compile Include="Sorting\NameComparer.cs" />
- <Compile Include="Sorting\OfficialRatingComparer.cs" />
- <Compile Include="Sorting\PlayCountComparer.cs" />
- <Compile Include="Sorting\PlayersComparer.cs" />
- <Compile Include="Sorting\PremiereDateComparer.cs" />
- <Compile Include="Sorting\ProductionYearComparer.cs" />
- <Compile Include="Sorting\RandomComparer.cs" />
- <Compile Include="Sorting\RevenueComparer.cs" />
- <Compile Include="Sorting\RuntimeComparer.cs" />
- <Compile Include="Sorting\SeriesSortNameComparer.cs" />
- <Compile Include="Sorting\SortNameComparer.cs" />
<Compile Include="Persistence\SqliteDisplayPreferencesRepository.cs" />
<Compile Include="Persistence\SqliteItemRepository.cs" />
<Compile Include="Persistence\SqliteUserDataRepository.cs" />
<Compile Include="Persistence\SqliteUserRepository.cs" />
- <Compile Include="Sorting\StudioComparer.cs" />
- <Compile Include="Sync\AppSyncProvider.cs" />
- <Compile Include="Sync\CloudSyncProfile.cs" />
- <Compile Include="Sync\IHasSyncQuality.cs" />
- <Compile Include="Sync\MediaSync.cs" />
- <Compile Include="Sync\MultiProviderSync.cs" />
- <Compile Include="Sync\ServerSyncScheduledTask.cs" />
- <Compile Include="Sync\SyncedMediaSourceProvider.cs" />
- <Compile Include="Sync\SyncRegistrationInfo.cs" />
- <Compile Include="Sync\SyncConfig.cs" />
- <Compile Include="Sync\SyncJobProcessor.cs" />
- <Compile Include="Sync\SyncManager.cs" />
<Compile Include="Sync\SyncRepository.cs" />
- <Compile Include="Sync\SyncConvertScheduledTask.cs" />
- <Compile Include="Sync\TargetDataProvider.cs" />
- <Compile Include="TV\TVSeriesManager.cs" />
<Compile Include="Udp\UdpMessageReceivedEventArgs.cs" />
<Compile Include="Udp\UdpServer.cs" />
- <Compile Include="Xml\XmlReaderSettingsFactory.cs" />
</ItemGroup>
<ItemGroup>
+ <ProjectReference Include="..\Emby.Server.Implementations\Emby.Server.Implementations.csproj">
+ <Project>{e383961b-9356-4d5d-8233-9a1079d03055}</Project>
+ <Name>Emby.Server.Implementations</Name>
+ </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
@@ -400,23 +217,6 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\us.txt" />
- <EmbeddedResource Include="Localization\Ratings\au.txt" />
- <EmbeddedResource Include="Localization\Ratings\gb.txt" />
- <EmbeddedResource Include="Localization\Ratings\nl.txt" />
- <EmbeddedResource Include="Localization\Ratings\br.txt" />
- <EmbeddedResource Include="Localization\Ratings\dk.txt" />
- <EmbeddedResource Include="Localization\Ratings\de.txt" />
- <EmbeddedResource Include="Localization\Ratings\mx.txt" />
- <EmbeddedResource Include="Localization\Ratings\co.txt" />
- <EmbeddedResource Include="Localization\Ratings\fr.txt" />
- <EmbeddedResource Include="Localization\Ratings\ie.txt" />
- <EmbeddedResource Include="Localization\Ratings\jp.txt" />
- <EmbeddedResource Include="Localization\Ratings\kz.txt" />
- <EmbeddedResource Include="Localization\Ratings\nz.txt" />
- <EmbeddedResource Include="Localization\Ratings\ru.txt" />
- </ItemGroup>
- <ItemGroup>
<Content Include="..\ThirdParty\ServiceStack\swagger-ui\lib\backbone-min.js">
<Link>swagger-ui\lib\backbone-min.js</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -481,7 +281,6 @@
<Link>swagger-ui\swagger-ui.min.js</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <EmbeddedResource Include="Localization\countries.json" />
<Content Include="..\ThirdParty\ServiceStack\swagger-ui\fonts\droid-sans-v6-latin-700.eot">
<Link>swagger-ui\fonts\droid-sans-v6-latin-700.eot</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -514,11 +313,29 @@
<Link>swagger-ui\fonts\droid-sans-v6-latin-regular.woff2</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <EmbeddedResource Include="Localization\Ratings\us.txt" />
+ <EmbeddedResource Include="Localization\Ratings\ru.txt" />
+ <EmbeddedResource Include="Localization\Ratings\nz.txt" />
+ <EmbeddedResource Include="Localization\Ratings\nl.txt" />
+ <EmbeddedResource Include="Localization\Ratings\mx.txt" />
+ <EmbeddedResource Include="Localization\Ratings\kz.txt" />
+ <EmbeddedResource Include="Localization\Ratings\jp.txt" />
+ <EmbeddedResource Include="Localization\Ratings\ie.txt" />
+ <EmbeddedResource Include="Localization\Ratings\gb.txt" />
+ <EmbeddedResource Include="Localization\Ratings\fr.txt" />
+ <EmbeddedResource Include="Localization\Ratings\dk.txt" />
+ <EmbeddedResource Include="Localization\Ratings\de.txt" />
+ <EmbeddedResource Include="Localization\Ratings\co.txt" />
+ <EmbeddedResource Include="Localization\Ratings\ca.txt" />
+ <EmbeddedResource Include="Localization\Ratings\br.txt" />
+ <EmbeddedResource Include="Localization\Ratings\be.txt" />
+ <EmbeddedResource Include="Localization\Ratings\au.txt" />
+ <EmbeddedResource Include="Localization\iso6392.txt" />
<None Include="app.config" />
- <EmbeddedResource Include="Localization\Core\core.json" />
<EmbeddedResource Include="Localization\Core\ar.json" />
<EmbeddedResource Include="Localization\Core\bg-BG.json" />
<EmbeddedResource Include="Localization\Core\ca.json" />
+ <EmbeddedResource Include="Localization\Core\core.json" />
<EmbeddedResource Include="Localization\Core\cs.json" />
<EmbeddedResource Include="Localization\Core\da.json" />
<EmbeddedResource Include="Localization\Core\de.json" />
@@ -529,10 +346,13 @@
<EmbeddedResource Include="Localization\Core\es-MX.json" />
<EmbeddedResource Include="Localization\Core\es.json" />
<EmbeddedResource Include="Localization\Core\fi.json" />
+ <EmbeddedResource Include="Localization\Core\fr-CA.json" />
<EmbeddedResource Include="Localization\Core\fr.json" />
<EmbeddedResource Include="Localization\Core\gsw.json" />
<EmbeddedResource Include="Localization\Core\he.json" />
<EmbeddedResource Include="Localization\Core\hr.json" />
+ <EmbeddedResource Include="Localization\Core\hu.json" />
+ <EmbeddedResource Include="Localization\Core\id.json" />
<EmbeddedResource Include="Localization\Core\it.json" />
<EmbeddedResource Include="Localization\Core\kk.json" />
<EmbeddedResource Include="Localization\Core\ko.json" />
@@ -550,186 +370,181 @@
<EmbeddedResource Include="Localization\Core\uk.json" />
<EmbeddedResource Include="Localization\Core\vi.json" />
<EmbeddedResource Include="Localization\Core\zh-CN.json" />
- <EmbeddedResource Include="Localization\Core\zh-TW.json" />
<EmbeddedResource Include="Localization\Core\zh-HK.json" />
- <EmbeddedResource Include="Localization\Core\hu.json" />
- <EmbeddedResource Include="Localization\Core\id.json" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0030.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0049.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0070.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0090.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0100.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0130.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0160.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0170.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0192.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0200.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0215.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0235.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0255.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0260.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0282.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0305.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0308.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0310.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0315.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0330.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0360.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0380.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0390.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0400.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0420.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0435.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0450.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0460.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0475.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0480.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0490.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0505.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0510.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0520.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0525.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0530.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0549.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0560.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0570.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0600.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0620.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0642.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0650.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0660.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0685.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0705.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0721.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0740.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0750.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0765.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0785.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0830.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0851.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0865.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0875.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0880.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0900.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0915.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0922.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0935.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0950.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\0965.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1005.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1030.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1055.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1082.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1100.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1105.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1130.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1155.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1160.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1180.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1195.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1222.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1240.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1250.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1280.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1320.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1340.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1380.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1400.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1440.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1500.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1520.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1540.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1560.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1590.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1600 OPTUS D1 FTA %28160.0E%29.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1600.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1620.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1640.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1660.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1690.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1720.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1800.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\1830.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2210.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2230.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2250.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2270.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2290.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2310.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2330.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2350.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2370.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2390.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2410.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2432.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2451.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2470.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2489.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2500.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2527.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2550.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2570.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2590.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2608.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2630.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2650.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2669.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2690.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2710.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2728.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2730.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2750.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2760.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2770.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2780.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2812.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2820.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2830.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2850.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2873.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2880.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2881.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2882.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2900.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2930.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2950.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2970.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2985.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\2990.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3020.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3045.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3070.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3100.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3125.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3150.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3169.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3195.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3225.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3255.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3285.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3300.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3325.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3355.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3380.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3400.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3420.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3450.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3460.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3475.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3490.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3520.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3527.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3550.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3560.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3592.ini" />
- <EmbeddedResource Include="LiveTv\TunerHosts\SatIp\ini\satellite\3594.ini" />
- <EmbeddedResource Include="Localization\Core\fr-CA.json" />
+ <EmbeddedResource Include="Localization\Core\zh-TW.json" />
+ <EmbeddedResource Include="Localization\countries.json" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0030.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0049.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0070.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0090.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0100.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0130.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0160.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0170.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0192.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0200.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0215.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0235.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0255.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0260.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0282.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0305.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0308.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0310.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0315.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0330.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0360.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0380.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0390.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0400.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0420.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0435.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0450.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0460.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0475.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0480.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0490.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0505.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0510.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0520.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0525.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0530.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0549.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0560.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0570.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0600.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0620.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0642.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0650.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0660.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0685.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0705.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0721.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0740.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0750.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0765.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0785.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0830.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0851.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0865.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0875.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0880.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0900.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0915.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0922.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0935.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0950.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\0965.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1005.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1030.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1055.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1082.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1100.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1105.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1130.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1155.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1160.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1180.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1195.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1222.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1240.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1250.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1280.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1320.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1340.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1380.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1400.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1440.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1500.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1520.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1540.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1560.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1590.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1600 OPTUS D1 FTA %28160.0E%29.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1600.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1620.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1640.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1660.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1690.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1720.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1800.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\1830.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2210.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2230.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2250.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2270.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2290.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2310.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2330.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2350.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2370.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2390.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2410.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2432.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2451.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2470.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2489.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2500.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2527.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2550.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2570.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2590.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2608.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2630.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2650.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2669.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2690.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2710.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2728.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2730.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2750.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2760.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2770.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2780.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2812.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2820.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2830.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2850.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2873.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2880.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2881.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2882.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2900.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2930.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2950.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2970.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2985.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\2990.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3020.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3045.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3070.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3100.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3125.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3150.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3169.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3195.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3225.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3255.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3285.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3300.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3325.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3355.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3380.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3400.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3420.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3450.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3460.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3475.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3490.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3520.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3527.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3550.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3560.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3592.ini" />
+ <None Include="LiveTv\TunerHosts\SatIp\ini\satellite\3594.ini" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
- <EmbeddedResource Include="Localization\Ratings\ca.txt" />
- </ItemGroup>
- <ItemGroup>
<Content Include="..\ThirdParty\ServiceStack\swagger-ui\css\reset.css">
<Link>swagger-ui\css\reset.css</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -778,8 +593,6 @@
<Link>swagger-ui\lib\shred\content.js</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <EmbeddedResource Include="Localization\iso6392.txt" />
- <EmbeddedResource Include="Localization\Ratings\be.txt" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
deleted file mode 100644
index 9f1744cc9..000000000
--- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ /dev/null
@@ -1,236 +0,0 @@
-using MediaBrowser.Controller.Chapters;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Library;
-
-namespace MediaBrowser.Server.Implementations.MediaEncoder
-{
- public class EncodingManager : IEncodingManager
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
- private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
- private readonly IMediaEncoder _encoder;
- private readonly IChapterManager _chapterManager;
- private readonly ILibraryManager _libraryManager;
-
- public EncodingManager(IFileSystem fileSystem,
- ILogger logger,
- IMediaEncoder encoder,
- IChapterManager chapterManager, ILibraryManager libraryManager)
- {
- _fileSystem = fileSystem;
- _logger = logger;
- _encoder = encoder;
- _chapterManager = chapterManager;
- _libraryManager = libraryManager;
- }
-
- /// <summary>
- /// Gets the chapter images data path.
- /// </summary>
- /// <value>The chapter images data path.</value>
- private string GetChapterImagesPath(IHasImages item)
- {
- return Path.Combine(item.GetInternalMetadataPath(), "chapters");
- }
-
- /// <summary>
- /// Determines whether [is eligible for chapter image extraction] [the specified video].
- /// </summary>
- /// <param name="video">The video.</param>
- /// <returns><c>true</c> if [is eligible for chapter image extraction] [the specified video]; otherwise, <c>false</c>.</returns>
- private bool IsEligibleForChapterImageExtraction(Video video)
- {
- if (video.IsPlaceHolder)
- {
- return false;
- }
-
- var libraryOptions = _libraryManager.GetLibraryOptions(video);
- if (libraryOptions != null)
- {
- if (!libraryOptions.EnableChapterImageExtraction)
- {
- return false;
- }
- }
- else
- {
- return false;
- }
-
- // Can't extract images if there are no video streams
- return video.DefaultVideoStreamIndex.HasValue;
- }
-
- /// <summary>
- /// The first chapter ticks
- /// </summary>
- private static readonly long FirstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
-
- public async Task<bool> RefreshChapterImages(ChapterImageRefreshOptions options, CancellationToken cancellationToken)
- {
- var extractImages = options.ExtractImages;
- var video = options.Video;
- var chapters = options.Chapters;
- var saveChapters = options.SaveChapters;
-
- if (!IsEligibleForChapterImageExtraction(video))
- {
- extractImages = false;
- }
-
- var success = true;
- var changesMade = false;
-
- var runtimeTicks = video.RunTimeTicks ?? 0;
-
- var currentImages = GetSavedChapterImages(video);
-
- foreach (var chapter in chapters)
- {
- if (chapter.StartPositionTicks >= runtimeTicks)
- {
- _logger.Info("Stopping chapter extraction for {0} because a chapter was found with a position greater than the runtime.", video.Name);
- break;
- }
-
- var path = GetChapterImagePath(video, chapter.StartPositionTicks);
-
- if (!currentImages.Contains(path, StringComparer.OrdinalIgnoreCase))
- {
- if (extractImages)
- {
- if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso)
- {
- continue;
- }
- if (video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd)
- {
- if (video.PlayableStreamFileNames.Count != 1)
- {
- continue;
- }
- }
-
- try
- {
- // Add some time for the first chapter to make sure we don't end up with a black image
- var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(FirstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
-
- var protocol = MediaProtocol.File;
-
- var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, video.PlayableStreamFileNames);
-
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- var container = video.Container;
-
- var tempFile = await _encoder.ExtractVideoImage(inputPath, container, protocol, video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
- File.Copy(tempFile, path, true);
-
- try
- {
- File.Delete(tempFile);
- }
- catch
- {
-
- }
-
- chapter.ImagePath = path;
- chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
- changesMade = true;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error extracting chapter images for {0}", ex, string.Join(",", video.Path));
- success = false;
- break;
- }
- }
- else if (!string.IsNullOrEmpty(chapter.ImagePath))
- {
- chapter.ImagePath = null;
- changesMade = true;
- }
- }
- else if (!string.Equals(path, chapter.ImagePath, StringComparison.OrdinalIgnoreCase))
- {
- chapter.ImagePath = path;
- chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
- changesMade = true;
- }
- }
-
- if (saveChapters && changesMade)
- {
- await _chapterManager.SaveChapters(video.Id.ToString(), chapters, cancellationToken).ConfigureAwait(false);
- }
-
- DeleteDeadImages(currentImages, chapters);
-
- return success;
- }
-
- private string GetChapterImagePath(Video video, long chapterPositionTicks)
- {
- var filename = video.DateModified.Ticks.ToString(_usCulture) + "_" + chapterPositionTicks.ToString(_usCulture) + ".jpg";
-
- return Path.Combine(GetChapterImagesPath(video), filename);
- }
-
- private List<string> GetSavedChapterImages(Video video)
- {
- var path = GetChapterImagesPath(video);
-
- try
- {
- return _fileSystem.GetFilePaths(path)
- .ToList();
- }
- catch (DirectoryNotFoundException)
- {
- return new List<string>();
- }
- }
-
- private void DeleteDeadImages(IEnumerable<string> images, IEnumerable<ChapterInfo> chapters)
- {
- var deadImages = images
- .Except(chapters.Select(i => i.ImagePath).Where(i => !string.IsNullOrEmpty(i)), StringComparer.OrdinalIgnoreCase)
- .Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i), StringComparer.OrdinalIgnoreCase))
- .ToList();
-
- foreach (var image in deadImages)
- {
- _logger.Debug("Deleting dead chapter image {0}", image);
-
- try
- {
- _fileSystem.DeleteFile(image);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error deleting {0}.", ex, image);
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs b/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs
deleted file mode 100644
index 22cb4e86a..000000000
--- a/MediaBrowser.Server.Implementations/News/NewsEntryPoint.cs
+++ /dev/null
@@ -1,170 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.News;
-using MediaBrowser.Model.Notifications;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Xml;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Server.Implementations.Threading;
-
-namespace MediaBrowser.Server.Implementations.News
-{
- public class NewsEntryPoint : IServerEntryPoint
- {
- private PeriodicTimer _timer;
- private readonly IHttpClient _httpClient;
- private readonly IApplicationPaths _appPaths;
- private readonly IFileSystem _fileSystem;
- private readonly ILogger _logger;
- private readonly IJsonSerializer _json;
-
- private readonly INotificationManager _notifications;
- private readonly IUserManager _userManager;
-
- private readonly TimeSpan _frequency = TimeSpan.FromHours(24);
-
- public NewsEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IJsonSerializer json, INotificationManager notifications, IUserManager userManager)
- {
- _httpClient = httpClient;
- _appPaths = appPaths;
- _fileSystem = fileSystem;
- _logger = logger;
- _json = json;
- _notifications = notifications;
- _userManager = userManager;
- }
-
- public void Run()
- {
- _timer = new PeriodicTimer(OnTimerFired, null, TimeSpan.FromMilliseconds(500), _frequency);
- }
-
- /// <summary>
- /// Called when [timer fired].
- /// </summary>
- /// <param name="state">The state.</param>
- private async void OnTimerFired(object state)
- {
- var path = Path.Combine(_appPaths.CachePath, "news.json");
-
- try
- {
- await DownloadNews(path).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error downloading news", ex);
- }
- }
-
- private async Task DownloadNews(string path)
- {
- DateTime? lastUpdate = null;
-
- if (_fileSystem.FileExists(path))
- {
- lastUpdate = _fileSystem.GetLastWriteTimeUtc(path);
- }
-
- var requestOptions = new HttpRequestOptions
- {
- Url = "http://emby.media/community/index.php?/blog/rss/1-media-browser-developers-blog",
- Progress = new Progress<double>(),
- UserAgent = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.42 Safari/537.36",
- BufferContent = false
- };
-
- using (var stream = await _httpClient.Get(requestOptions).ConfigureAwait(false))
- {
- var doc = new XmlDocument();
- doc.Load(stream);
-
- var news = ParseRssItems(doc).ToList();
-
- _json.SerializeToFile(news, path);
-
- await CreateNotifications(news, lastUpdate, CancellationToken.None).ConfigureAwait(false);
- }
- }
-
- private Task CreateNotifications(List<NewsItem> items, DateTime? lastUpdate, CancellationToken cancellationToken)
- {
- if (lastUpdate.HasValue)
- {
- items = items.Where(i => i.Date.ToUniversalTime() >= lastUpdate.Value)
- .ToList();
- }
-
- var tasks = items.Select(i => _notifications.SendNotification(new NotificationRequest
- {
- Date = i.Date,
- Name = i.Title,
- Description = i.Description,
- Url = i.Link,
- UserIds = _userManager.Users.Select(u => u.Id.ToString("N")).ToList()
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- private IEnumerable<NewsItem> ParseRssItems(XmlDocument xmlDoc)
- {
- var nodes = xmlDoc.SelectNodes("rss/channel/item");
-
- if (nodes != null)
- {
- foreach (XmlNode node in nodes)
- {
- var newsItem = new NewsItem();
-
- newsItem.Title = ParseDocElements(node, "title");
-
- newsItem.DescriptionHtml = ParseDocElements(node, "description");
- newsItem.Description = newsItem.DescriptionHtml.StripHtml();
-
- newsItem.Link = ParseDocElements(node, "link");
-
- var date = ParseDocElements(node, "pubDate");
- DateTime parsedDate;
-
- if (DateTime.TryParse(date, out parsedDate))
- {
- newsItem.Date = parsedDate;
- }
-
- yield return newsItem;
- }
- }
- }
-
- private string ParseDocElements(XmlNode parent, string xPath)
- {
- var node = parent.SelectSingleNode(xPath);
-
- return node != null ? node.InnerText : string.Empty;
- }
-
- public void Dispose()
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/News/NewsService.cs b/MediaBrowser.Server.Implementations/News/NewsService.cs
deleted file mode 100644
index 0059ba421..000000000
--- a/MediaBrowser.Server.Implementations/News/NewsService.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.News;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace MediaBrowser.Server.Implementations.News
-{
- public class NewsService : INewsService
- {
- private readonly IApplicationPaths _appPaths;
- private readonly IJsonSerializer _json;
-
- public NewsService(IApplicationPaths appPaths, IJsonSerializer json)
- {
- _appPaths = appPaths;
- _json = json;
- }
-
- public QueryResult<NewsItem> GetProductNews(NewsQuery query)
- {
- try
- {
- return GetProductNewsInternal(query);
- }
- catch (DirectoryNotFoundException)
- {
- // No biggie
- return new QueryResult<NewsItem>
- {
- Items = new NewsItem[] { }
- };
- }
- catch (FileNotFoundException)
- {
- // No biggie
- return new QueryResult<NewsItem>
- {
- Items = new NewsItem[] { }
- };
- }
- }
-
- private QueryResult<NewsItem> GetProductNewsInternal(NewsQuery query)
- {
- var path = Path.Combine(_appPaths.CachePath, "news.json");
-
- var items = GetNewsItems(path).OrderByDescending(i => i.Date);
-
- var itemsArray = items.ToArray();
- var count = itemsArray.Length;
-
- if (query.StartIndex.HasValue)
- {
- itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray();
- }
-
- if (query.Limit.HasValue)
- {
- itemsArray = itemsArray.Take(query.Limit.Value).ToArray();
- }
-
- return new QueryResult<NewsItem>
- {
- Items = itemsArray,
- TotalRecordCount = count
- };
- }
-
- private IEnumerable<NewsItem> GetNewsItems(string path)
- {
- return _json.DeserializeFromFile<List<NewsItem>>(path);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs
deleted file mode 100644
index 0f2629f58..000000000
--- a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs
+++ /dev/null
@@ -1,198 +0,0 @@
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Model.Notifications;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Globalization;
-
-namespace MediaBrowser.Server.Implementations.Notifications
-{
- public class CoreNotificationTypes : INotificationTypeFactory
- {
- private readonly ILocalizationManager _localization;
- private readonly IServerApplicationHost _appHost;
-
- public CoreNotificationTypes(ILocalizationManager localization, IServerApplicationHost appHost)
- {
- _localization = localization;
- _appHost = appHost;
- }
-
- public IEnumerable<NotificationTypeInfo> GetNotificationTypes()
- {
- var knownTypes = new List<NotificationTypeInfo>
- {
- new NotificationTypeInfo
- {
- Type = NotificationType.ApplicationUpdateInstalled.ToString(),
- DefaultDescription = "{ReleaseNotes}",
- DefaultTitle = "A new version of Emby Server has been installed.",
- Variables = new List<string>{"Version"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.InstallationFailed.ToString(),
- DefaultTitle = "{Name} installation failed.",
- Variables = new List<string>{"Name", "Version"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.PluginInstalled.ToString(),
- DefaultTitle = "{Name} was installed.",
- Variables = new List<string>{"Name", "Version"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.PluginError.ToString(),
- DefaultTitle = "{Name} has encountered an error.",
- DefaultDescription = "{ErrorMessage}",
- Variables = new List<string>{"Name", "ErrorMessage"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.PluginUninstalled.ToString(),
- DefaultTitle = "{Name} was uninstalled.",
- Variables = new List<string>{"Name", "Version"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.PluginUpdateInstalled.ToString(),
- DefaultTitle = "{Name} was updated.",
- DefaultDescription = "{ReleaseNotes}",
- Variables = new List<string>{"Name", "ReleaseNotes", "Version"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.ServerRestartRequired.ToString(),
- DefaultTitle = "Please restart Emby Server to finish updating."
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.TaskFailed.ToString(),
- DefaultTitle = "{Name} failed.",
- DefaultDescription = "{ErrorMessage}",
- Variables = new List<string>{"Name", "ErrorMessage"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.NewLibraryContent.ToString(),
- DefaultTitle = "{Name} has been added to your media library.",
- Variables = new List<string>{"Name"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.AudioPlayback.ToString(),
- DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
- Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.GamePlayback.ToString(),
- DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
- Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.VideoPlayback.ToString(),
- DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
- Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.AudioPlaybackStopped.ToString(),
- DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
- Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.GamePlaybackStopped.ToString(),
- DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
- Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.VideoPlaybackStopped.ToString(),
- DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
- Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.CameraImageUploaded.ToString(),
- DefaultTitle = "A new camera image has been uploaded from {DeviceName}.",
- Variables = new List<string>{"DeviceName"}
- },
-
- new NotificationTypeInfo
- {
- Type = NotificationType.UserLockedOut.ToString(),
- DefaultTitle = "{UserName} has been locked out.",
- Variables = new List<string>{"UserName"}
- }
- };
-
- if (!_appHost.CanSelfUpdate)
- {
- knownTypes.Add(new NotificationTypeInfo
- {
- Type = NotificationType.ApplicationUpdateAvailable.ToString(),
- DefaultTitle = "A new version of Emby Server is available for download."
- });
- }
-
- foreach (var type in knownTypes)
- {
- Update(type);
- }
-
- var systemName = _localization.GetLocalizedString("CategorySystem");
-
- return knownTypes.OrderByDescending(i => string.Equals(i.Category, systemName, StringComparison.OrdinalIgnoreCase))
- .ThenBy(i => i.Category)
- .ThenBy(i => i.Name);
- }
-
- private void Update(NotificationTypeInfo note)
- {
- note.Name = _localization.GetLocalizedString("NotificationOption" + note.Type) ?? note.Type;
-
- note.IsBasedOnUserEvent = note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1;
-
- if (note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1)
- {
- note.Category = _localization.GetLocalizedString("CategoryUser");
- }
- else if (note.Type.IndexOf("Plugin", StringComparison.OrdinalIgnoreCase) != -1)
- {
- note.Category = _localization.GetLocalizedString("CategoryPlugin");
- }
- else if (note.Type.IndexOf("CameraImageUploaded", StringComparison.OrdinalIgnoreCase) != -1)
- {
- note.Category = _localization.GetLocalizedString("CategorySync");
- }
- else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1)
- {
- note.Category = _localization.GetLocalizedString("CategoryUser");
- }
- else
- {
- note.Category = _localization.GetLocalizedString("CategorySystem");
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs b/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs
deleted file mode 100644
index cdfd0f640..000000000
--- a/MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace MediaBrowser.Server.Implementations.Notifications
-{
- public interface IConfigurableNotificationService
- {
- bool IsHidden { get; }
- bool IsEnabled(string notificationType);
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs b/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs
deleted file mode 100644
index 4a625f0fb..000000000
--- a/MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Model.Notifications;
-using System.Threading;
-using System.Threading.Tasks;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Notifications
-{
- public class InternalNotificationService : INotificationService, IConfigurableNotificationService
- {
- private readonly INotificationsRepository _repo;
-
- public InternalNotificationService(INotificationsRepository repo)
- {
- _repo = repo;
- }
-
- public string Name
- {
- get { return "Dashboard Notifications"; }
- }
-
- public Task SendNotification(UserNotification request, CancellationToken cancellationToken)
- {
- return _repo.AddNotification(new Notification
- {
- Date = request.Date,
- Description = request.Description,
- Level = request.Level,
- Name = request.Name,
- Url = request.Url,
- UserId = request.User.Id.ToString("N")
-
- }, cancellationToken);
- }
-
- public bool IsEnabledForUser(User user)
- {
- return user.Policy.IsAdministrator;
- }
-
- public bool IsHidden
- {
- get { return true; }
- }
-
- public bool IsEnabled(string notificationType)
- {
- if (notificationType.IndexOf("playback", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return false;
- }
- if (notificationType.IndexOf("newlibrarycontent", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return false;
- }
- return true;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs b/MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs
deleted file mode 100644
index a336eba0e..000000000
--- a/MediaBrowser.Server.Implementations/Notifications/NotificationConfigurationFactory.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Notifications;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Server.Implementations.Notifications
-{
- public class NotificationConfigurationFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- Key = "notifications",
- ConfigurationType = typeof (NotificationOptions)
- }
- };
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs b/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs
deleted file mode 100644
index f19ff8a5f..000000000
--- a/MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs
+++ /dev/null
@@ -1,296 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Notifications;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Notifications;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Server.Implementations.Notifications
-{
- public class NotificationManager : INotificationManager
- {
- private readonly ILogger _logger;
- private readonly IUserManager _userManager;
- private readonly IServerConfigurationManager _config;
-
- private INotificationService[] _services;
- private INotificationTypeFactory[] _typeFactories;
-
- public NotificationManager(ILogManager logManager, IUserManager userManager, IServerConfigurationManager config)
- {
- _userManager = userManager;
- _config = config;
- _logger = logManager.GetLogger(GetType().Name);
- }
-
- private NotificationOptions GetConfiguration()
- {
- return _config.GetConfiguration<NotificationOptions>("notifications");
- }
-
- public Task SendNotification(NotificationRequest request, CancellationToken cancellationToken)
- {
- var notificationType = request.NotificationType;
-
- var options = string.IsNullOrWhiteSpace(notificationType) ?
- null :
- GetConfiguration().GetOptions(notificationType);
-
- var users = GetUserIds(request, options)
- .Select(i => _userManager.GetUserById(i));
-
- var title = GetTitle(request, options);
- var description = GetDescription(request, options);
-
- var tasks = _services.Where(i => IsEnabled(i, notificationType))
- .Select(i => SendNotification(request, i, users, title, description, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- private Task SendNotification(NotificationRequest request,
- INotificationService service,
- IEnumerable<User> users,
- string title,
- string description,
- CancellationToken cancellationToken)
- {
- users = users.Where(i => IsEnabledForUser(service, i))
- .ToList();
-
- var tasks = users.Select(i => SendNotification(request, service, title, description, i, cancellationToken));
-
- return Task.WhenAll(tasks);
-
- }
-
- private IEnumerable<string> GetUserIds(NotificationRequest request, NotificationOption options)
- {
- if (request.SendToUserMode.HasValue)
- {
- switch (request.SendToUserMode.Value)
- {
- case SendToUserType.Admins:
- return _userManager.Users.Where(i => i.Policy.IsAdministrator)
- .Select(i => i.Id.ToString("N"));
- case SendToUserType.All:
- return _userManager.Users.Select(i => i.Id.ToString("N"));
- case SendToUserType.Custom:
- return request.UserIds;
- default:
- throw new ArgumentException("Unrecognized SendToUserMode: " + request.SendToUserMode.Value);
- }
- }
-
- if (options != null && !string.IsNullOrWhiteSpace(request.NotificationType))
- {
- var config = GetConfiguration();
-
- return _userManager.Users
- .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N"), i.Policy))
- .Select(i => i.Id.ToString("N"));
- }
-
- return request.UserIds;
- }
-
- private async Task SendNotification(NotificationRequest request,
- INotificationService service,
- string title,
- string description,
- User user,
- CancellationToken cancellationToken)
- {
- var notification = new UserNotification
- {
- Date = request.Date,
- Description = description,
- Level = request.Level,
- Name = title,
- Url = request.Url,
- User = user
- };
-
- _logger.Debug("Sending notification via {0} to user {1}", service.Name, user.Name);
-
- try
- {
- await service.SendNotification(notification, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending notification to {0}", ex, service.Name);
- }
- }
-
- private string GetTitle(NotificationRequest request, NotificationOption options)
- {
- var title = request.Name;
-
- // If empty, grab from options
- if (string.IsNullOrEmpty(title))
- {
- if (!string.IsNullOrEmpty(request.NotificationType))
- {
- if (options != null)
- {
- title = options.Title;
- }
- }
- }
-
- // If still empty, grab default
- if (string.IsNullOrEmpty(title))
- {
- if (!string.IsNullOrEmpty(request.NotificationType))
- {
- var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase));
-
- if (info != null)
- {
- title = info.DefaultTitle;
- }
- }
- }
-
- title = title ?? string.Empty;
-
- foreach (var pair in request.Variables)
- {
- var token = "{" + pair.Key + "}";
-
- title = title.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase);
- }
-
- return title;
- }
-
- private string GetDescription(NotificationRequest request, NotificationOption options)
- {
- var text = request.Description;
-
- // If empty, grab from options
- if (string.IsNullOrEmpty(text))
- {
- if (!string.IsNullOrEmpty(request.NotificationType))
- {
- if (options != null)
- {
- text = options.Description;
- }
- }
- }
-
- // If still empty, grab default
- if (string.IsNullOrEmpty(text))
- {
- if (!string.IsNullOrEmpty(request.NotificationType))
- {
- var info = GetNotificationTypes().FirstOrDefault(i => string.Equals(i.Type, request.NotificationType, StringComparison.OrdinalIgnoreCase));
-
- if (info != null)
- {
- text = info.DefaultDescription;
- }
- }
- }
-
- text = text ?? string.Empty;
-
- foreach (var pair in request.Variables)
- {
- var token = "{" + pair.Key + "}";
-
- text = text.Replace(token, pair.Value, StringComparison.OrdinalIgnoreCase);
- }
-
- return text;
- }
-
- private bool IsEnabledForUser(INotificationService service, User user)
- {
- try
- {
- return service.IsEnabledForUser(user);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in IsEnabledForUser", ex);
- return false;
- }
- }
-
- private bool IsEnabled(INotificationService service, string notificationType)
- {
- if (string.IsNullOrEmpty(notificationType))
- {
- return true;
- }
-
- var configurable = service as IConfigurableNotificationService;
-
- if (configurable != null)
- {
- return configurable.IsEnabled(notificationType);
- }
-
- return GetConfiguration().IsServiceEnabled(service.Name, notificationType);
- }
-
- public void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories)
- {
- _services = services.ToArray();
- _typeFactories = notificationTypeFactories.ToArray();
- }
-
- public IEnumerable<NotificationTypeInfo> GetNotificationTypes()
- {
- var list = _typeFactories.Select(i =>
- {
- try
- {
- return i.GetNotificationTypes().ToList();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in GetNotificationTypes", ex);
- return new List<NotificationTypeInfo>();
- }
-
- }).SelectMany(i => i).ToList();
-
- var config = GetConfiguration();
-
- foreach (var i in list)
- {
- i.Enabled = config.IsEnabled(i.Type);
- }
-
- return list;
- }
-
- public IEnumerable<NotificationServiceInfo> GetNotificationServices()
- {
- return _services.Where(i =>
- {
- var configurable = i as IConfigurableNotificationService;
-
- return configurable == null || !configurable.IsHidden;
-
- }).Select(i => new NotificationServiceInfo
- {
- Name = i.Name,
- Id = i.Name.GetMD5().ToString("N")
-
- }).OrderBy(i => i.Name);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
deleted file mode 100644
index ed1d21d9a..000000000
--- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
+++ /dev/null
@@ -1,360 +0,0 @@
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Server.Implementations.ScheduledTasks;
-
-namespace MediaBrowser.Server.Implementations.Persistence
-{
- public class CleanDatabaseScheduledTask : IScheduledTask
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IItemRepository _itemRepo;
- private readonly ILogger _logger;
- private readonly IServerConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
- private readonly IHttpServer _httpServer;
- private readonly ILocalizationManager _localization;
- private readonly ITaskManager _taskManager;
-
- public const int MigrationVersion = 23;
- public static bool EnableUnavailableMessage = false;
-
- public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IHttpServer httpServer, ILocalizationManager localization, ITaskManager taskManager)
- {
- _libraryManager = libraryManager;
- _itemRepo = itemRepo;
- _logger = logger;
- _config = config;
- _fileSystem = fileSystem;
- _httpServer = httpServer;
- _localization = localization;
- _taskManager = taskManager;
- }
-
- public string Name
- {
- get { return "Clean Database"; }
- }
-
- public string Description
- {
- get { return "Deletes obsolete content from the database."; }
- }
-
- public string Category
- {
- get { return "Library"; }
- }
-
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- OnProgress(0);
-
- // Ensure these objects are lazy loaded.
- // Without this there is a deadlock that will need to be investigated
- var rootChildren = _libraryManager.RootFolder.Children.ToList();
- rootChildren = _libraryManager.GetUserRootFolder().Children.ToList();
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p =>
- {
- double newPercentCommplete = .4 * p;
- OnProgress(newPercentCommplete);
-
- progress.Report(newPercentCommplete);
- });
-
- await UpdateToLatestSchema(cancellationToken, innerProgress).ConfigureAwait(false);
-
- innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p =>
- {
- double newPercentCommplete = 40 + .05 * p;
- OnProgress(newPercentCommplete);
- progress.Report(newPercentCommplete);
- });
- await CleanDeadItems(cancellationToken, innerProgress).ConfigureAwait(false);
- progress.Report(45);
-
- innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p =>
- {
- double newPercentCommplete = 45 + .55 * p;
- OnProgress(newPercentCommplete);
- progress.Report(newPercentCommplete);
- });
- await CleanDeletedItems(cancellationToken, innerProgress).ConfigureAwait(false);
- progress.Report(100);
-
- await _itemRepo.UpdateInheritedValues(cancellationToken).ConfigureAwait(false);
-
- if (_config.Configuration.MigrationVersion < MigrationVersion)
- {
- _config.Configuration.MigrationVersion = MigrationVersion;
- _config.SaveConfiguration();
- }
-
- if (_config.Configuration.SchemaVersion < SqliteItemRepository.LatestSchemaVersion)
- {
- _config.Configuration.SchemaVersion = SqliteItemRepository.LatestSchemaVersion;
- _config.SaveConfiguration();
- }
-
- if (EnableUnavailableMessage)
- {
- EnableUnavailableMessage = false;
- _httpServer.GlobalResponse = null;
- _taskManager.QueueScheduledTask<RefreshMediaLibraryTask>();
- }
-
- _taskManager.SuspendTriggers = false;
- }
-
- private void OnProgress(double newPercentCommplete)
- {
- if (EnableUnavailableMessage)
- {
- var html = "<!doctype html><html><head><title>Emby</title></head><body>";
- var text = _localization.GetLocalizedString("DbUpgradeMessage");
- html += string.Format(text, newPercentCommplete.ToString("N2", CultureInfo.InvariantCulture));
-
- html += "<script>setTimeout(function(){window.location.reload(true);}, 5000);</script>";
- html += "</body></html>";
-
- _httpServer.GlobalResponse = html;
- }
- }
-
- private async Task UpdateToLatestSchema(CancellationToken cancellationToken, IProgress<double> progress)
- {
- var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
- {
- IsCurrentSchema = false,
- ExcludeItemTypes = new[] { typeof(LiveTvProgram).Name }
- });
-
- var numComplete = 0;
- var numItems = itemIds.Count;
-
- _logger.Debug("Upgrading schema for {0} items", numItems);
-
- var list = new List<BaseItem>();
-
- foreach (var itemId in itemIds)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (itemId != Guid.Empty)
- {
- // Somehow some invalid data got into the db. It probably predates the boundary checking
- var item = _libraryManager.GetItemById(itemId);
-
- if (item != null)
- {
- list.Add(item);
- }
- }
-
- if (list.Count >= 1000)
- {
- try
- {
- await _itemRepo.SaveItems(list, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving item", ex);
- }
-
- list.Clear();
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= numItems;
- progress.Report(percent * 100);
- }
-
- if (list.Count > 0)
- {
- try
- {
- await _itemRepo.SaveItems(list, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving item", ex);
- }
- }
-
- progress.Report(100);
- }
-
- private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
- {
- var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
- {
- HasDeadParentId = true
- });
-
- var numComplete = 0;
- var numItems = itemIds.Count;
-
- _logger.Debug("Cleaning {0} items with dead parent links", numItems);
-
- foreach (var itemId in itemIds)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var item = _libraryManager.GetItemById(itemId);
-
- if (item != null)
- {
- _logger.Info("Cleaning item {0} type: {1} path: {2}", item.Name, item.GetType().Name, item.Path ?? string.Empty);
-
- await item.Delete(new DeleteOptions
- {
- DeleteFileLocation = false
-
- }).ConfigureAwait(false);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= numItems;
- progress.Report(percent * 100);
- }
-
- progress.Report(100);
- }
-
- private async Task CleanDeletedItems(CancellationToken cancellationToken, IProgress<double> progress)
- {
- var result = _itemRepo.GetItemIdsWithPath(new InternalItemsQuery
- {
- LocationTypes = new[] { LocationType.FileSystem },
- //Limit = limit,
-
- // These have their own cleanup routines
- ExcludeItemTypes = new[]
- {
- typeof(Person).Name,
- typeof(Genre).Name,
- typeof(MusicGenre).Name,
- typeof(GameGenre).Name,
- typeof(Studio).Name,
- typeof(Year).Name,
- typeof(Channel).Name,
- typeof(AggregateFolder).Name,
- typeof(CollectionFolder).Name
- }
- });
-
- var numComplete = 0;
- var numItems = result.Items.Length;
-
- foreach (var item in result.Items)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var path = item.Item2;
-
- try
- {
- if (_fileSystem.FileExists(path) || _fileSystem.DirectoryExists(path))
- {
- continue;
- }
-
- var libraryItem = _libraryManager.GetItemById(item.Item1);
-
- if (libraryItem.IsTopParent)
- {
- continue;
- }
-
- var hasDualAccess = libraryItem as IHasDualAccess;
- if (hasDualAccess != null && hasDualAccess.IsAccessedByName)
- {
- continue;
- }
-
- var libraryItemPath = libraryItem.Path;
- if (!string.Equals(libraryItemPath, path, StringComparison.OrdinalIgnoreCase))
- {
- _logger.Error("CleanDeletedItems aborting delete for item {0}-{1} because paths don't match. {2}---{3}", libraryItem.Id, libraryItem.Name, libraryItem.Path ?? string.Empty, path ?? string.Empty);
- continue;
- }
-
- if (Folder.IsPathOffline(path))
- {
- await libraryItem.UpdateIsOffline(true).ConfigureAwait(false);
- continue;
- }
-
- _logger.Info("Deleting item from database {0} because path no longer exists. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty);
-
- await libraryItem.OnFileDeleted().ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in CleanDeletedItems. File {0}", ex, path);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= numItems;
- progress.Report(percent * 100);
- }
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
- };
- }
-
- public string Key
- {
- get { return "CleanDatabase"; }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs
deleted file mode 100644
index 0dd7672bc..000000000
--- a/MediaBrowser.Server.Implementations/Photos/BaseDynamicImageProvider.cs
+++ /dev/null
@@ -1,361 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.Configuration;
-
-namespace MediaBrowser.Server.Implementations.Photos
-{
- public abstract class BaseDynamicImageProvider<T> : IHasItemChangeMonitor, IForcedProvider, ICustomMetadataProvider<T>, IHasOrder
- where T : IHasMetadata
- {
- protected IFileSystem FileSystem { get; private set; }
- protected IProviderManager ProviderManager { get; private set; }
- protected IApplicationPaths ApplicationPaths { get; private set; }
- protected IImageProcessor ImageProcessor { get; set; }
-
- protected BaseDynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor)
- {
- ApplicationPaths = applicationPaths;
- ProviderManager = providerManager;
- FileSystem = fileSystem;
- ImageProcessor = imageProcessor;
- }
-
- protected virtual bool Supports(IHasImages item)
- {
- return true;
- }
-
- public virtual IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- return new List<ImageType>
- {
- ImageType.Primary,
- ImageType.Thumb
- };
- }
-
- private IEnumerable<ImageType> GetEnabledImages(IHasImages item)
- {
- //var options = ProviderManager.GetMetadataOptions(item);
-
- return GetSupportedImages(item);
- //return GetSupportedImages(item).Where(i => IsEnabled(options, i, item)).ToList();
- }
-
- private bool IsEnabled(MetadataOptions options, ImageType type, IHasImages item)
- {
- if (type == ImageType.Backdrop)
- {
- if (item.LockedFields.Contains(MetadataFields.Backdrops))
- {
- return false;
- }
- }
- else if (type == ImageType.Screenshot)
- {
- if (item.LockedFields.Contains(MetadataFields.Screenshots))
- {
- return false;
- }
- }
- else
- {
- if (item.LockedFields.Contains(MetadataFields.Images))
- {
- return false;
- }
- }
-
- return options.IsEnabled(type);
- }
-
- public async Task<ItemUpdateType> FetchAsync(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
- {
- if (!Supports(item))
- {
- return ItemUpdateType.None;
- }
-
- var updateType = ItemUpdateType.None;
- var supportedImages = GetEnabledImages(item).ToList();
-
- if (supportedImages.Contains(ImageType.Primary))
- {
- var primaryResult = await FetchAsync(item, ImageType.Primary, options, cancellationToken).ConfigureAwait(false);
- updateType = updateType | primaryResult;
- }
-
- if (supportedImages.Contains(ImageType.Thumb))
- {
- var thumbResult = await FetchAsync(item, ImageType.Thumb, options, cancellationToken).ConfigureAwait(false);
- updateType = updateType | thumbResult;
- }
-
- return updateType;
- }
-
- protected async Task<ItemUpdateType> FetchAsync(IHasImages item, ImageType imageType, MetadataRefreshOptions options, CancellationToken cancellationToken)
- {
- var image = item.GetImageInfo(imageType, 0);
-
- if (image != null)
- {
- if (!image.IsLocalFile)
- {
- return ItemUpdateType.None;
- }
-
- if (!FileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path))
- {
- return ItemUpdateType.None;
- }
- }
-
- var items = await GetItemsWithImages(item).ConfigureAwait(false);
-
- return await FetchToFileInternal(item, items, imageType, cancellationToken).ConfigureAwait(false);
- }
-
- protected async Task<ItemUpdateType> FetchToFileInternal(IHasImages item,
- List<BaseItem> itemsWithImages,
- ImageType imageType,
- CancellationToken cancellationToken)
- {
- var outputPathWithoutExtension = Path.Combine(ApplicationPaths.TempDirectory, Guid.NewGuid().ToString("N"));
- FileSystem.CreateDirectory(Path.GetDirectoryName(outputPathWithoutExtension));
- string outputPath = await CreateImage(item, itemsWithImages, outputPathWithoutExtension, imageType, 0).ConfigureAwait(false);
-
- if (string.IsNullOrWhiteSpace(outputPath))
- {
- return ItemUpdateType.None;
- }
-
- await ProviderManager.SaveImage(item, outputPath, "image/png", imageType, null, false, cancellationToken).ConfigureAwait(false);
-
- return ItemUpdateType.ImageUpdate;
- }
-
- protected abstract Task<List<BaseItem>> GetItemsWithImages(IHasImages item);
-
- protected Task<string> CreateThumbCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
- {
- return CreateCollage(primaryItem, items, outputPath, 640, 360);
- }
-
- protected virtual IEnumerable<string> GetStripCollageImagePaths(IHasImages primaryItem, IEnumerable<BaseItem> items)
- {
- return items
- .Select(i =>
- {
- var image = i.GetImageInfo(ImageType.Primary, 0);
-
- if (image != null && image.IsLocalFile)
- {
- return image.Path;
- }
- image = i.GetImageInfo(ImageType.Thumb, 0);
-
- if (image != null && image.IsLocalFile)
- {
- return image.Path;
- }
- return null;
- })
- .Where(i => !string.IsNullOrWhiteSpace(i));
- }
-
- protected Task<string> CreatePosterCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
- {
- return CreateCollage(primaryItem, items, outputPath, 400, 600);
- }
-
- protected Task<string> CreateSquareCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath)
- {
- return CreateCollage(primaryItem, items, outputPath, 600, 600);
- }
-
- protected Task<string> CreateThumbCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height)
- {
- return CreateCollage(primaryItem, items, outputPath, width, height);
- }
-
- private async Task<string> CreateCollage(IHasImages primaryItem, List<BaseItem> items, string outputPath, int width, int height)
- {
- FileSystem.CreateDirectory(Path.GetDirectoryName(outputPath));
-
- var options = new ImageCollageOptions
- {
- Height = height,
- Width = width,
- OutputPath = outputPath,
- InputPaths = GetStripCollageImagePaths(primaryItem, items).ToArray()
- };
-
- if (options.InputPaths.Length == 0)
- {
- return null;
- }
-
- if (!ImageProcessor.SupportsImageCollageCreation)
- {
- return null;
- }
-
- await ImageProcessor.CreateImageCollage(options).ConfigureAwait(false);
- return outputPath;
- }
-
- public string Name
- {
- get { return "Dynamic Image Provider"; }
- }
-
- protected virtual async Task<string> CreateImage(IHasImages item,
- List<BaseItem> itemsWithImages,
- string outputPathWithoutExtension,
- ImageType imageType,
- int imageIndex)
- {
- if (itemsWithImages.Count == 0)
- {
- return null;
- }
-
- string outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
-
- if (imageType == ImageType.Thumb)
- {
- return await CreateThumbCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
- }
-
- if (imageType == ImageType.Primary)
- {
- if (item is UserView)
- {
- return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
- }
- if (item is Playlist || item is MusicGenre)
- {
- return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
- }
- return await CreatePosterCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
- }
-
- throw new ArgumentException("Unexpected image type");
- }
-
- protected virtual int MaxImageAgeDays
- {
- get { return 7; }
- }
-
- public bool HasChanged(IHasMetadata item, IDirectoryService directoryServicee)
- {
- if (!Supports(item))
- {
- return false;
- }
-
- var supportedImages = GetEnabledImages(item).ToList();
-
- if (supportedImages.Contains(ImageType.Primary) && HasChanged(item, ImageType.Primary))
- {
- return true;
- }
- if (supportedImages.Contains(ImageType.Thumb) && HasChanged(item, ImageType.Thumb))
- {
- return true;
- }
-
- return false;
- }
-
- protected bool HasChanged(IHasImages item, ImageType type)
- {
- var image = item.GetImageInfo(type, 0);
-
- if (image != null)
- {
- if (!image.IsLocalFile)
- {
- return false;
- }
-
- if (!FileSystem.ContainsSubPath(item.GetInternalMetadataPath(), image.Path))
- {
- return false;
- }
-
- var age = DateTime.UtcNow - image.DateModified;
- if (age.TotalDays <= MaxImageAgeDays)
- {
- return false;
- }
- }
-
- return true;
- }
-
- protected List<BaseItem> GetFinalItems(List<BaseItem> items)
- {
- return GetFinalItems(items, 4);
- }
-
- protected virtual List<BaseItem> GetFinalItems(List<BaseItem> items, int limit)
- {
- // Rotate the images once every x days
- var random = DateTime.Now.DayOfYear % MaxImageAgeDays;
-
- return items
- .OrderBy(i => (random + string.Empty + items.IndexOf(i)).GetMD5())
- .Take(limit)
- .OrderBy(i => i.Name)
- .ToList();
- }
-
- public int Order
- {
- get
- {
- // Run before the default image provider which will download placeholders
- return 0;
- }
- }
-
- protected async Task<string> CreateSingleImage(List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType)
- {
- var image = itemsWithImages
- .Where(i => i.HasImage(imageType) && i.GetImageInfo(imageType, 0).IsLocalFile && Path.HasExtension(i.GetImagePath(imageType)))
- .Select(i => i.GetImagePath(imageType))
- .FirstOrDefault();
-
- if (string.IsNullOrWhiteSpace(image))
- {
- return null;
- }
-
- var ext = Path.GetExtension(image);
-
- var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ext);
- File.Copy(image, outputPath);
-
- return outputPath;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs b/MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
deleted file mode 100644
index ab03f5aaa..000000000
--- a/MediaBrowser.Server.Implementations/Photos/PhotoAlbumImageProvider.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Server.Implementations.Photos
-{
- public class PhotoAlbumImageProvider : BaseDynamicImageProvider<PhotoAlbum>
- {
- public PhotoAlbumImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor)
- : base(fileSystem, providerManager, applicationPaths, imageProcessor)
- {
- }
-
- protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
- {
- var photoAlbum = (PhotoAlbum)item;
- var items = GetFinalItems(photoAlbum.Children.ToList());
-
- return Task.FromResult(items);
- }
-
- protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
- {
- return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs b/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs
deleted file mode 100644
index 0249b85ee..000000000
--- a/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Server.Implementations.Photos;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Playlists
-{
- public class PlaylistImageProvider : BaseDynamicImageProvider<Playlist>
- {
- public PlaylistImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
- {
- }
-
- protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
- {
- var playlist = (Playlist)item;
-
- var items = playlist.GetManageableItems()
- .Select(i =>
- {
- var subItem = i.Item2;
-
- var episode = subItem as Episode;
-
- if (episode != null)
- {
- var series = episode.Series;
- if (series != null && series.HasImage(ImageType.Primary))
- {
- return series;
- }
- }
-
- if (subItem.HasImage(ImageType.Primary))
- {
- return subItem;
- }
-
- var parent = subItem.GetParent();
-
- if (parent != null && parent.HasImage(ImageType.Primary))
- {
- if (parent is MusicAlbum)
- {
- return parent;
- }
- }
-
- return null;
- })
- .Where(i => i != null)
- .DistinctBy(i => i.Id)
- .ToList();
-
- return Task.FromResult(GetFinalItems(items));
- }
- }
-
- public class MusicGenreImageProvider : BaseDynamicImageProvider<MusicGenre>
- {
- private readonly ILibraryManager _libraryManager;
-
- public MusicGenreImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
- {
- _libraryManager = libraryManager;
- }
-
- protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
- {
- var items = _libraryManager.GetItemList(new InternalItemsQuery
- {
- Genres = new[] { item.Name },
- IncludeItemTypes = new[] { typeof(MusicAlbum).Name, typeof(MusicVideo).Name, typeof(Audio).Name },
- SortBy = new[] { ItemSortBy.Random },
- Limit = 4,
- Recursive = true,
- ImageTypes = new[] { ImageType.Primary }
-
- }).ToList();
-
- return Task.FromResult(GetFinalItems(items));
- }
-
- //protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
- //{
- // return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
- //}
- }
-
-}
diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs b/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
deleted file mode 100644
index 13202d94c..000000000
--- a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
+++ /dev/null
@@ -1,275 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Playlists;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Playlists
-{
- public class PlaylistManager : IPlaylistManager
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
- private readonly ILibraryMonitor _iLibraryMonitor;
- private readonly ILogger _logger;
- private readonly IUserManager _userManager;
- private readonly IProviderManager _providerManager;
-
- public PlaylistManager(ILibraryManager libraryManager, IFileSystem fileSystem, ILibraryMonitor iLibraryMonitor, ILogger logger, IUserManager userManager, IProviderManager providerManager)
- {
- _libraryManager = libraryManager;
- _fileSystem = fileSystem;
- _iLibraryMonitor = iLibraryMonitor;
- _logger = logger;
- _userManager = userManager;
- _providerManager = providerManager;
- }
-
- public IEnumerable<Playlist> GetPlaylists(string userId)
- {
- var user = _userManager.GetUserById(userId);
-
- return GetPlaylistsFolder(userId).GetChildren(user, true).OfType<Playlist>();
- }
-
- public async Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest options)
- {
- var name = options.Name;
-
- var folderName = _fileSystem.GetValidFilename(name) + " [playlist]";
-
- var parentFolder = GetPlaylistsFolder(null);
-
- if (parentFolder == null)
- {
- throw new ArgumentException();
- }
-
- if (string.IsNullOrWhiteSpace(options.MediaType))
- {
- foreach (var itemId in options.ItemIdList)
- {
- var item = _libraryManager.GetItemById(itemId);
-
- if (item == null)
- {
- throw new ArgumentException("No item exists with the supplied Id");
- }
-
- if (!string.IsNullOrWhiteSpace(item.MediaType))
- {
- options.MediaType = item.MediaType;
- }
- else if (item is MusicArtist || item is MusicAlbum || item is MusicGenre)
- {
- options.MediaType = MediaType.Audio;
- }
- else if (item is Genre)
- {
- options.MediaType = MediaType.Video;
- }
- else
- {
- var folder = item as Folder;
- if (folder != null)
- {
- options.MediaType = folder.GetRecursiveChildren(i => !i.IsFolder && i.SupportsAddingToPlaylist)
- .Select(i => i.MediaType)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
- }
- }
-
- if (!string.IsNullOrWhiteSpace(options.MediaType))
- {
- break;
- }
- }
- }
-
- if (string.IsNullOrWhiteSpace(options.MediaType))
- {
- throw new ArgumentException("A playlist media type is required.");
- }
-
- var user = _userManager.GetUserById(options.UserId);
-
- var path = Path.Combine(parentFolder.Path, folderName);
- path = GetTargetPath(path);
-
- _iLibraryMonitor.ReportFileSystemChangeBeginning(path);
-
- try
- {
- _fileSystem.CreateDirectory(path);
-
- var playlist = new Playlist
- {
- Name = name,
- Path = path
- };
-
- playlist.Shares.Add(new Share
- {
- UserId = options.UserId,
- CanEdit = true
- });
-
- playlist.SetMediaType(options.MediaType);
-
- await parentFolder.AddChild(playlist, CancellationToken.None).ConfigureAwait(false);
-
- await playlist.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { ForceSave = true }, CancellationToken.None)
- .ConfigureAwait(false);
-
- if (options.ItemIdList.Count > 0)
- {
- await AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user);
- }
-
- return new PlaylistCreationResult
- {
- Id = playlist.Id.ToString("N")
- };
- }
- finally
- {
- // Refresh handled internally
- _iLibraryMonitor.ReportFileSystemChangeComplete(path, false);
- }
- }
-
- private string GetTargetPath(string path)
- {
- while (_fileSystem.DirectoryExists(path))
- {
- path += "1";
- }
-
- return path;
- }
-
- private Task<IEnumerable<BaseItem>> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType, User user)
- {
- var items = itemIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null);
-
- return Playlist.GetPlaylistItems(playlistMediaType, items, user);
- }
-
- public Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds, string userId)
- {
- var user = string.IsNullOrWhiteSpace(userId) ? null : _userManager.GetUserById(userId);
-
- return AddToPlaylistInternal(playlistId, itemIds, user);
- }
-
- private async Task AddToPlaylistInternal(string playlistId, IEnumerable<string> itemIds, User user)
- {
- var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
-
- if (playlist == null)
- {
- throw new ArgumentException("No Playlist exists with the supplied Id");
- }
-
- var list = new List<LinkedChild>();
-
- var items = (await GetPlaylistItems(itemIds, playlist.MediaType, user).ConfigureAwait(false))
- .Where(i => i.SupportsAddingToPlaylist)
- .ToList();
-
- foreach (var item in items)
- {
- list.Add(LinkedChild.Create(item));
- }
-
- playlist.LinkedChildren.AddRange(list);
-
- await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
-
- _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(_fileSystem)
- {
- ForceSave = true
- });
- }
-
- public async Task RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds)
- {
- var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
-
- if (playlist == null)
- {
- throw new ArgumentException("No Playlist exists with the supplied Id");
- }
-
- var children = playlist.GetManageableItems().ToList();
-
- var idList = entryIds.ToList();
-
- var removals = children.Where(i => idList.Contains(i.Item1.Id));
-
- playlist.LinkedChildren = children.Except(removals)
- .Select(i => i.Item1)
- .ToList();
-
- await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
-
- _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(_fileSystem)
- {
- ForceSave = true
- });
- }
-
- public async Task MoveItem(string playlistId, string entryId, int newIndex)
- {
- var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
-
- if (playlist == null)
- {
- throw new ArgumentException("No Playlist exists with the supplied Id");
- }
-
- var children = playlist.GetManageableItems().ToList();
-
- var oldIndex = children.FindIndex(i => string.Equals(entryId, i.Item1.Id, StringComparison.OrdinalIgnoreCase));
-
- if (oldIndex == newIndex)
- {
- return;
- }
-
- var item = playlist.LinkedChildren[oldIndex];
-
- playlist.LinkedChildren.Remove(item);
-
- if (newIndex >= playlist.LinkedChildren.Count)
- {
- playlist.LinkedChildren.Add(item);
- }
- else
- {
- playlist.LinkedChildren.Insert(newIndex, item);
- }
-
- await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
- }
-
- public Folder GetPlaylistsFolder(string userId)
- {
- return _libraryManager.RootFolder.Children.OfType<PlaylistsFolder>()
- .FirstOrDefault() ?? _libraryManager.GetUserRootFolder().Children.OfType<PlaylistsFolder>()
- .FirstOrDefault();
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Reflection/AssemblyInfo.cs b/MediaBrowser.Server.Implementations/Reflection/AssemblyInfo.cs
deleted file mode 100644
index ec25e7951..000000000
--- a/MediaBrowser.Server.Implementations/Reflection/AssemblyInfo.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-using System.IO;
-using MediaBrowser.Model.Reflection;
-
-namespace MediaBrowser.Server.Implementations.Reflection
-{
- public class AssemblyInfo : IAssemblyInfo
- {
- public Stream GetManifestResourceStream(Type type, string resource)
- {
- return type.Assembly.GetManifestResourceStream(resource);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
deleted file mode 100644
index 63941f3b6..000000000
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/ChapterImagesTask.cs
+++ /dev/null
@@ -1,201 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
-{
- /// <summary>
- /// Class ChapterImagesTask
- /// </summary>
- class ChapterImagesTask : IScheduledTask
- {
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
-
- /// <summary>
- /// The current new item timer
- /// </summary>
- /// <value>The new item timer.</value>
- private Timer NewItemTimer { get; set; }
-
- private readonly IItemRepository _itemRepo;
-
- private readonly IApplicationPaths _appPaths;
-
- private readonly IEncodingManager _encodingManager;
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
- /// </summary>
- /// <param name="logManager">The log manager.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="itemRepo">The item repo.</param>
- public ChapterImagesTask(ILogManager logManager, ILibraryManager libraryManager, IItemRepository itemRepo, IApplicationPaths appPaths, IEncodingManager encodingManager, IFileSystem fileSystem)
- {
- _logger = logManager.GetLogger(GetType().Name);
- _libraryManager = libraryManager;
- _itemRepo = itemRepo;
- _appPaths = appPaths;
- _encodingManager = encodingManager;
- _fileSystem = fileSystem;
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- new TaskTriggerInfo
- {
- Type = TaskTriggerInfo.TriggerDaily,
- TimeOfDayTicks = TimeSpan.FromHours(1).Ticks,
- MaxRuntimeMs = Convert.ToInt32(TimeSpan.FromHours(4).TotalMilliseconds)
- }
- };
- }
-
- public string Key
- {
- get { return "RefreshChapterImages"; }
- }
-
- /// <summary>
- /// Returns the task to be executed
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- var videos = _libraryManager.GetItemList(new InternalItemsQuery
- {
- MediaTypes = new[] { MediaType.Video },
- IsFolder = false,
- Recursive = true
- })
- .OfType<Video>()
- .ToList();
-
- var numComplete = 0;
-
- var failHistoryPath = Path.Combine(_appPaths.CachePath, "chapter-failures.txt");
-
- List<string> previouslyFailedImages;
-
- try
- {
- previouslyFailedImages = _fileSystem.ReadAllText(failHistoryPath)
- .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
- .ToList();
- }
- catch (FileNotFoundException)
- {
- previouslyFailedImages = new List<string>();
- }
- catch (DirectoryNotFoundException)
- {
- previouslyFailedImages = new List<string>();
- }
-
- foreach (var video in videos)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var key = video.Path + video.DateModified.Ticks;
-
- var extract = !previouslyFailedImages.Contains(key, StringComparer.OrdinalIgnoreCase);
-
- try
- {
- var chapters = _itemRepo.GetChapters(video.Id).ToList();
-
- var success = await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions
- {
- SaveChapters = true,
- ExtractImages = extract,
- Video = video,
- Chapters = chapters
-
- }, CancellationToken.None);
-
- if (!success)
- {
- previouslyFailedImages.Add(key);
-
- var parentPath = Path.GetDirectoryName(failHistoryPath);
-
- _fileSystem.CreateDirectory(parentPath);
-
- _fileSystem.WriteAllText(failHistoryPath, string.Join("|", previouslyFailedImages.ToArray()));
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= videos.Count;
-
- progress.Report(100 * percent);
- }
- catch (ObjectDisposedException)
- {
- break;
- }
- }
- }
-
- /// <summary>
- /// Gets the name of the task
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get
- {
- return "Chapter image extraction";
- }
- }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description
- {
- get { return "Creates thumbnails for videos that have chapters."; }
- }
-
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
- public string Category
- {
- get
- {
- return "Library";
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs
deleted file mode 100644
index f90e61902..000000000
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using MediaBrowser.Controller.Library;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
-{
- /// <summary>
- /// Class PeopleValidationTask
- /// </summary>
- public class PeopleValidationTask : IScheduledTask
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
-
- private readonly IServerApplicationHost _appHost;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- public PeopleValidationTask(ILibraryManager libraryManager, IServerApplicationHost appHost)
- {
- _libraryManager = libraryManager;
- _appHost = appHost;
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- // Randomize the default start hour because this operation can really hammer internet metadata providers
- var startHour = new Random(_appHost.SystemId.GetHashCode()).Next(0, 8);
-
- return new[] {
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerDaily, TimeOfDayTicks = TimeSpan.FromHours(startHour).Ticks}
- };
- }
-
- public string Key
- {
- get { return "RefreshPeople"; }
- }
-
- /// <summary>
- /// Returns the task to be executed
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- return _libraryManager.ValidatePeople(cancellationToken, progress);
- }
-
- /// <summary>
- /// Gets the name of the task
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return "Refresh people"; }
- }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description
- {
- get { return "Updates metadata for actors and directors in your media library."; }
- }
-
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
- public string Category
- {
- get
- {
- return "Library";
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs
deleted file mode 100644
index 1d81ec043..000000000
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/PluginUpdateTask.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
-{
- /// <summary>
- /// Plugin Update Task
- /// </summary>
- public class PluginUpdateTask : IScheduledTask
- {
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- private readonly IInstallationManager _installationManager;
-
- private readonly IApplicationHost _appHost;
-
- public PluginUpdateTask(ILogger logger, IInstallationManager installationManager, IApplicationHost appHost)
- {
- _logger = logger;
- _installationManager = installationManager;
- _appHost = appHost;
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- // At startup
- new TaskTriggerInfo {Type = TaskTriggerInfo.TriggerStartup},
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
- };
- }
-
- public string Key
- {
- get { return "PluginUpdates"; }
- }
-
- /// <summary>
- /// Update installed plugins
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- progress.Report(0);
-
- var packagesToInstall = (await _installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, true, cancellationToken).ConfigureAwait(false)).ToList();
-
- progress.Report(10);
-
- var numComplete = 0;
-
- // Create tasks for each one
- var tasks = packagesToInstall.Select(i => Task.Run(async () =>
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- try
- {
- await _installationManager.InstallPackage(i, true, new Progress<double>(), cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- // InstallPackage has it's own inner cancellation token, so only throw this if it's ours
- if (cancellationToken.IsCancellationRequested)
- {
- throw;
- }
- }
- catch (HttpException ex)
- {
- _logger.ErrorException("Error downloading {0}", ex, i.name);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error updating {0}", ex, i.name);
- }
-
- // Update progress
- lock (progress)
- {
- numComplete++;
- double percent = numComplete;
- percent /= packagesToInstall.Count;
-
- progress.Report(90 * percent + 10);
- }
- }));
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
-
- progress.Report(100);
- }
-
- /// <summary>
- /// Gets the name of the task
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return "Check for plugin updates"; }
- }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description
- {
- get { return "Downloads and installs updates for plugins that are configured to update automatically."; }
- }
-
- public string Category
- {
- get { return "Application"; }
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs
deleted file mode 100644
index f10dbcfe7..000000000
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshIntrosTask.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
-{
- /// <summary>
- /// Class RefreshIntrosTask
- /// </summary>
- public class RefreshIntrosTask : ILibraryPostScanTask
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RefreshIntrosTask" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- public RefreshIntrosTask(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _fileSystem = fileSystem;
- }
-
- /// <summary>
- /// Runs the specified progress.
- /// </summary>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var files = _libraryManager.GetAllIntroFiles().ToList();
-
- var numComplete = 0;
-
- foreach (var file in files)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- try
- {
- await RefreshIntro(file, cancellationToken).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing intro {0}", ex, file);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= files.Count;
- progress.Report(percent * 100);
- }
- }
-
- /// <summary>
- /// Refreshes the intro.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task RefreshIntro(string path, CancellationToken cancellationToken)
- {
- var item = _libraryManager.ResolvePath(_fileSystem.GetFileSystemInfo(path));
-
- if (item == null)
- {
- _logger.Error("Intro resolver returned null for {0}", path);
- return;
- }
-
- var dbItem = _libraryManager.GetItemById(item.Id);
-
- if (dbItem != null)
- {
- item = dbItem;
- }
-
- // Force the save if it's a new item
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
deleted file mode 100644
index e695adb54..000000000
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Server.Implementations.Library;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
-{
- /// <summary>
- /// Class RefreshMediaLibraryTask
- /// </summary>
- public class RefreshMediaLibraryTask : IScheduledTask
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RefreshMediaLibraryTask" /> class.
- /// </summary>
- /// <param name="libraryManager">The library manager.</param>
- public RefreshMediaLibraryTask(ILibraryManager libraryManager, IServerConfigurationManager config)
- {
- _libraryManager = libraryManager;
- _config = config;
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(12).Ticks}
- };
- }
-
- /// <summary>
- /// Executes the internal.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- progress.Report(0);
-
- return ((LibraryManager)_libraryManager).ValidateMediaLibraryInternal(progress, cancellationToken);
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return "Scan media library"; }
- }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description
- {
- get { return "Scans your media library and refreshes metatata based on configuration."; }
- }
-
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
- public string Category
- {
- get
- {
- return "Library";
- }
- }
-
- public string Key
- {
- get { return "RefreshLibrary"; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs
deleted file mode 100644
index e44eacf3d..000000000
--- a/MediaBrowser.Server.Implementations/ScheduledTasks/SystemUpdateTask.cs
+++ /dev/null
@@ -1,148 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.ScheduledTasks
-{
- /// <summary>
- /// Plugin Update Task
- /// </summary>
- public class SystemUpdateTask : IScheduledTask
- {
- /// <summary>
- /// The _app host
- /// </summary>
- private readonly IApplicationHost _appHost;
-
- /// <summary>
- /// Gets or sets the configuration manager.
- /// </summary>
- /// <value>The configuration manager.</value>
- private IConfigurationManager ConfigurationManager { get; set; }
- /// <summary>
- /// Gets or sets the logger.
- /// </summary>
- /// <value>The logger.</value>
- private ILogger Logger { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SystemUpdateTask" /> class.
- /// </summary>
- /// <param name="appHost">The app host.</param>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <param name="logger">The logger.</param>
- public SystemUpdateTask(IApplicationHost appHost, IConfigurationManager configurationManager, ILogger logger)
- {
- _appHost = appHost;
- ConfigurationManager = configurationManager;
- Logger = logger;
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- // At startup
- new TaskTriggerInfo {Type = TaskTriggerInfo.TriggerStartup},
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
- };
- }
-
- /// <summary>
- /// Returns the task to be executed
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="progress">The progress.</param>
- /// <returns>Task.</returns>
- public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- EventHandler<double> innerProgressHandler = (sender, e) => progress.Report(e * .1);
-
- // Create a progress object for the update check
- var innerProgress = new Progress<double>();
- innerProgress.ProgressChanged += innerProgressHandler;
-
- var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, innerProgress).ConfigureAwait(false);
-
- // Release the event handler
- innerProgress.ProgressChanged -= innerProgressHandler;
-
- progress.Report(10);
-
- if (!updateInfo.IsUpdateAvailable)
- {
- Logger.Debug("No application update available.");
- progress.Report(100);
- return;
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (!_appHost.CanSelfUpdate) return;
-
- if (ConfigurationManager.CommonConfiguration.EnableAutoUpdate)
- {
- Logger.Info("Update Revision {0} available. Updating...", updateInfo.AvailableVersion);
-
- innerProgressHandler = (sender, e) => progress.Report(e * .9 + .1);
-
- innerProgress = new Progress<double>();
- innerProgress.ProgressChanged += innerProgressHandler;
-
- await _appHost.UpdateApplication(updateInfo.Package, cancellationToken, innerProgress).ConfigureAwait(false);
-
- // Release the event handler
- innerProgress.ProgressChanged -= innerProgressHandler;
- }
- else
- {
- Logger.Info("A new version of " + _appHost.Name + " is available.");
- }
-
- progress.Report(100);
- }
-
- /// <summary>
- /// Gets the name of the task
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return "Check for application updates"; }
- }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- /// <value>The description.</value>
- public string Description
- {
- get { return "Downloads and installs application updates."; }
- }
-
- /// <summary>
- /// Gets the category.
- /// </summary>
- /// <value>The category.</value>
- public string Category
- {
- get { return "Application"; }
- }
-
- public string Key
- {
- get { return "SystemUpdateTask"; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs b/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs
deleted file mode 100644
index 4e706324f..000000000
--- a/MediaBrowser.Server.Implementations/ServerManager/ServerManager.cs
+++ /dev/null
@@ -1,353 +0,0 @@
-using MediaBrowser.Common.Events;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Linq;
-using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Server.Implementations.ServerManager
-{
- /// <summary>
- /// Manages the Http Server, Udp Server and WebSocket connections
- /// </summary>
- public class ServerManager : IServerManager
- {
- /// <summary>
- /// Both the Ui and server will have a built-in HttpServer.
- /// People will inevitably want remote control apps so it's needed in the Ui too.
- /// </summary>
- /// <value>The HTTP server.</value>
- private IHttpServer HttpServer { get; set; }
-
- /// <summary>
- /// Gets or sets the json serializer.
- /// </summary>
- /// <value>The json serializer.</value>
- private readonly IJsonSerializer _jsonSerializer;
-
- /// <summary>
- /// The web socket connections
- /// </summary>
- private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
- /// <summary>
- /// Gets the web socket connections.
- /// </summary>
- /// <value>The web socket connections.</value>
- public IEnumerable<IWebSocketConnection> WebSocketConnections
- {
- get { return _webSocketConnections; }
- }
-
- public event EventHandler<GenericEventArgs<IWebSocketConnection>> WebSocketConnected;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// The _application host
- /// </summary>
- private readonly IServerApplicationHost _applicationHost;
-
- /// <summary>
- /// Gets or sets the configuration manager.
- /// </summary>
- /// <value>The configuration manager.</value>
- private IServerConfigurationManager ConfigurationManager { get; set; }
-
- /// <summary>
- /// Gets the web socket listeners.
- /// </summary>
- /// <value>The web socket listeners.</value>
- private readonly List<IWebSocketListener> _webSocketListeners = new List<IWebSocketListener>();
-
- private bool _disposed;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ServerManager" /> class.
- /// </summary>
- /// <param name="applicationHost">The application host.</param>
- /// <param name="jsonSerializer">The json serializer.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <exception cref="System.ArgumentNullException">applicationHost</exception>
- public ServerManager(IServerApplicationHost applicationHost, IJsonSerializer jsonSerializer, ILogger logger, IServerConfigurationManager configurationManager, IMemoryStreamProvider memoryStreamProvider)
- {
- if (applicationHost == null)
- {
- throw new ArgumentNullException("applicationHost");
- }
- if (jsonSerializer == null)
- {
- throw new ArgumentNullException("jsonSerializer");
- }
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
- _logger = logger;
- _jsonSerializer = jsonSerializer;
- _applicationHost = applicationHost;
- ConfigurationManager = configurationManager;
- _memoryStreamProvider = memoryStreamProvider;
- }
-
- /// <summary>
- /// Starts this instance.
- /// </summary>
- public void Start(IEnumerable<string> urlPrefixes, string certificatePath)
- {
- ReloadHttpServer(urlPrefixes, certificatePath);
- }
-
- /// <summary>
- /// Restarts the Http Server, or starts it if not currently running
- /// </summary>
- private void ReloadHttpServer(IEnumerable<string> urlPrefixes, string certificatePath)
- {
- _logger.Info("Loading Http Server");
-
- try
- {
- HttpServer = _applicationHost.Resolve<IHttpServer>();
- HttpServer.StartServer(urlPrefixes, certificatePath);
- }
- catch (SocketException ex)
- {
- _logger.ErrorException("The http server is unable to start due to a Socket error. This can occasionally happen when the operating system takes longer than usual to release the IP bindings from the previous session. This can take up to five minutes. Please try waiting or rebooting the system.", ex);
-
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error starting Http Server", ex);
-
- throw;
- }
-
- HttpServer.WebSocketConnected += HttpServer_WebSocketConnected;
- }
-
- /// <summary>
- /// Handles the WebSocketConnected event of the HttpServer control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="WebSocketConnectEventArgs" /> instance containing the event data.</param>
- void HttpServer_WebSocketConnected(object sender, WebSocketConnectEventArgs e)
- {
- if (_disposed)
- {
- return;
- }
-
- var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger, _memoryStreamProvider)
- {
- OnReceive = ProcessWebSocketMessageReceived,
- Url = e.Url,
- QueryString = e.QueryString ?? new QueryParamCollection()
- };
-
- _webSocketConnections.Add(connection);
-
- if (WebSocketConnected != null)
- {
- EventHelper.FireEventIfNotNull(WebSocketConnected, this, new GenericEventArgs<IWebSocketConnection> (connection), _logger);
- }
- }
-
- /// <summary>
- /// Processes the web socket message received.
- /// </summary>
- /// <param name="result">The result.</param>
- private async void ProcessWebSocketMessageReceived(WebSocketMessageInfo result)
- {
- if (_disposed)
- {
- return;
- }
-
- //_logger.Debug("Websocket message received: {0}", result.MessageType);
-
- var tasks = _webSocketListeners.Select(i => Task.Run(async () =>
- {
- try
- {
- await i.ProcessMessage(result).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("{0} failed processing WebSocket message {1}", ex, i.GetType().Name, result.MessageType ?? string.Empty);
- }
- }));
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="data">The data.</param>
- /// <returns>Task.</returns>
- public void SendWebSocketMessage<T>(string messageType, T data)
- {
- SendWebSocketMessage(messageType, () => data);
- }
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
- public void SendWebSocketMessage<T>(string messageType, Func<T> dataFunction)
- {
- SendWebSocketMessageAsync(messageType, dataFunction, CancellationToken.None);
- }
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">messageType</exception>
- public Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken)
- {
- return SendWebSocketMessageAsync(messageType, dataFunction, _webSocketConnections, cancellationToken);
- }
-
- /// <summary>
- /// Sends the web socket message async.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The data function.</param>
- /// <param name="connections">The connections.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">messageType
- /// or
- /// dataFunction
- /// or
- /// cancellationToken</exception>
- private async Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, IEnumerable<IWebSocketConnection> connections, CancellationToken cancellationToken)
- {
- if (string.IsNullOrEmpty(messageType))
- {
- throw new ArgumentNullException("messageType");
- }
-
- if (dataFunction == null)
- {
- throw new ArgumentNullException("dataFunction");
- }
-
- if (_disposed)
- {
- throw new ObjectDisposedException(GetType().Name);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var connectionsList = connections.Where(s => s.State == WebSocketState.Open).ToList();
-
- if (connectionsList.Count > 0)
- {
- _logger.Info("Sending web socket message {0}", messageType);
-
- var message = new WebSocketMessage<T> { MessageType = messageType, Data = dataFunction() };
- var json = _jsonSerializer.SerializeToString(message);
-
- var tasks = connectionsList.Select(s => Task.Run(() =>
- {
- try
- {
- s.SendAsync(json, cancellationToken);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending web socket message {0} to {1}", ex, messageType, s.RemoteEndPoint);
- }
-
- }, cancellationToken));
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Disposes the current HttpServer
- /// </summary>
- private void DisposeHttpServer()
- {
- foreach (var socket in _webSocketConnections)
- {
- // Dispose the connection
- socket.Dispose();
- }
-
- _webSocketConnections.Clear();
-
- if (HttpServer != null)
- {
- HttpServer.WebSocketConnected -= HttpServer_WebSocketConnected;
- HttpServer.Dispose();
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- _disposed = true;
-
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (dispose)
- {
- DisposeHttpServer();
- }
- }
-
- /// <summary>
- /// Adds the web socket listeners.
- /// </summary>
- /// <param name="listeners">The listeners.</param>
- public void AddWebSocketListeners(IEnumerable<IWebSocketListener> listeners)
- {
- _webSocketListeners.AddRange(listeners);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs b/MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs
deleted file mode 100644
index c1bd8ed6b..000000000
--- a/MediaBrowser.Server.Implementations/ServerManager/WebSocketConnection.cs
+++ /dev/null
@@ -1,292 +0,0 @@
-using System.Text;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Specialized;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Services;
-using UniversalDetector;
-
-namespace MediaBrowser.Server.Implementations.ServerManager
-{
- /// <summary>
- /// Class WebSocketConnection
- /// </summary>
- public class WebSocketConnection : IWebSocketConnection
- {
- public event EventHandler<EventArgs> Closed;
-
- /// <summary>
- /// The _socket
- /// </summary>
- private readonly IWebSocket _socket;
-
- /// <summary>
- /// The _remote end point
- /// </summary>
- public string RemoteEndPoint { get; private set; }
-
- /// <summary>
- /// The _cancellation token source
- /// </summary>
- private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
-
- /// <summary>
- /// The logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// The _json serializer
- /// </summary>
- private readonly IJsonSerializer _jsonSerializer;
-
- /// <summary>
- /// Gets or sets the receive action.
- /// </summary>
- /// <value>The receive action.</value>
- public Action<WebSocketMessageInfo> OnReceive { get; set; }
-
- /// <summary>
- /// Gets the last activity date.
- /// </summary>
- /// <value>The last activity date.</value>
- public DateTime LastActivityDate { get; private set; }
-
- /// <summary>
- /// Gets the id.
- /// </summary>
- /// <value>The id.</value>
- public Guid Id { get; private set; }
-
- /// <summary>
- /// Gets or sets the URL.
- /// </summary>
- /// <value>The URL.</value>
- public string Url { get; set; }
- /// <summary>
- /// Gets or sets the query string.
- /// </summary>
- /// <value>The query string.</value>
- public QueryParamCollection QueryString { get; set; }
- private readonly IMemoryStreamProvider _memoryStreamProvider;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="WebSocketConnection" /> class.
- /// </summary>
- /// <param name="socket">The socket.</param>
- /// <param name="remoteEndPoint">The remote end point.</param>
- /// <param name="jsonSerializer">The json serializer.</param>
- /// <param name="logger">The logger.</param>
- /// <exception cref="System.ArgumentNullException">socket</exception>
- public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger, IMemoryStreamProvider memoryStreamProvider)
- {
- if (socket == null)
- {
- throw new ArgumentNullException("socket");
- }
- if (string.IsNullOrEmpty(remoteEndPoint))
- {
- throw new ArgumentNullException("remoteEndPoint");
- }
- if (jsonSerializer == null)
- {
- throw new ArgumentNullException("jsonSerializer");
- }
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
- Id = Guid.NewGuid();
- _jsonSerializer = jsonSerializer;
- _socket = socket;
- _socket.OnReceiveBytes = OnReceiveInternal;
- _socket.OnReceive = OnReceiveInternal;
- RemoteEndPoint = remoteEndPoint;
- _logger = logger;
- _memoryStreamProvider = memoryStreamProvider;
-
- socket.Closed += socket_Closed;
- }
-
- void socket_Closed(object sender, EventArgs e)
- {
- EventHelper.FireEventIfNotNull(Closed, this, EventArgs.Empty, _logger);
- }
-
- /// <summary>
- /// Called when [receive].
- /// </summary>
- /// <param name="bytes">The bytes.</param>
- private void OnReceiveInternal(byte[] bytes)
- {
- LastActivityDate = DateTime.UtcNow;
-
- if (OnReceive == null)
- {
- return;
- }
- var charset = DetectCharset(bytes);
-
- if (string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase))
- {
- OnReceiveInternal(Encoding.UTF8.GetString(bytes));
- }
- else
- {
- OnReceiveInternal(Encoding.ASCII.GetString(bytes));
- }
- }
- private string DetectCharset(byte[] bytes)
- {
- try
- {
- using (var ms = _memoryStreamProvider.CreateNew(bytes))
- {
- var detector = new CharsetDetector();
- detector.Feed(ms);
- detector.DataEnd();
-
- var charset = detector.Charset;
-
- if (!string.IsNullOrWhiteSpace(charset))
- {
- //_logger.Debug("UniversalDetector detected charset {0}", charset);
- }
-
- return charset;
- }
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error attempting to determine web socket message charset", ex);
- }
-
- return null;
- }
-
- private void OnReceiveInternal(string message)
- {
- LastActivityDate = DateTime.UtcNow;
-
- if (!message.StartsWith("{", StringComparison.OrdinalIgnoreCase))
- {
- // This info is useful sometimes but also clogs up the log
- //_logger.Error("Received web socket message that is not a json structure: " + message);
- return;
- }
-
- if (OnReceive == null)
- {
- return;
- }
-
- try
- {
- var stub = (WebSocketMessage<object>)_jsonSerializer.DeserializeFromString(message, typeof(WebSocketMessage<object>));
-
- var info = new WebSocketMessageInfo
- {
- MessageType = stub.MessageType,
- Data = stub.Data == null ? null : stub.Data.ToString(),
- Connection = this
- };
-
- OnReceive(info);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error processing web socket message", ex);
- }
- }
-
- /// <summary>
- /// Sends a message asynchronously.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="message">The message.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">message</exception>
- public Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
- {
- if (message == null)
- {
- throw new ArgumentNullException("message");
- }
-
- var json = _jsonSerializer.SerializeToString(message);
-
- return SendAsync(json, cancellationToken);
- }
-
- /// <summary>
- /// Sends a message asynchronously.
- /// </summary>
- /// <param name="buffer">The buffer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendAsync(byte[] buffer, CancellationToken cancellationToken)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException("buffer");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- return _socket.SendAsync(buffer, true, cancellationToken);
- }
-
- public Task SendAsync(string text, CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(text))
- {
- throw new ArgumentNullException("text");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- return _socket.SendAsync(text, true, cancellationToken);
- }
-
- /// <summary>
- /// Gets the state.
- /// </summary>
- /// <value>The state.</value>
- public WebSocketState State
- {
- get { return _socket.State; }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (dispose)
- {
- _cancellationTokenSource.Dispose();
- _socket.Dispose();
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs b/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs
deleted file mode 100644
index f54c452cc..000000000
--- a/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs
+++ /dev/null
@@ -1,186 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Model.System;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Session
-{
- public class HttpSessionController : ISessionController, IDisposable
- {
- private readonly IHttpClient _httpClient;
- private readonly IJsonSerializer _json;
- private readonly ISessionManager _sessionManager;
-
- public SessionInfo Session { get; private set; }
-
- private readonly string _postUrl;
-
- public HttpSessionController(IHttpClient httpClient,
- IJsonSerializer json,
- SessionInfo session,
- string postUrl, ISessionManager sessionManager)
- {
- _httpClient = httpClient;
- _json = json;
- Session = session;
- _postUrl = postUrl;
- _sessionManager = sessionManager;
- }
-
- public void OnActivity()
- {
- }
-
- private string PostUrl
- {
- get
- {
- return string.Format("http://{0}{1}", Session.RemoteEndPoint, _postUrl);
- }
- }
-
- public bool IsSessionActive
- {
- get
- {
- return (DateTime.UtcNow - Session.LastActivityDate).TotalMinutes <= 10;
- }
- }
-
- public bool SupportsMediaControl
- {
- get { return true; }
- }
-
- private Task SendMessage(string name, CancellationToken cancellationToken)
- {
- return SendMessage(name, new Dictionary<string, string>(), cancellationToken);
- }
-
- private async Task SendMessage(string name,
- Dictionary<string, string> args,
- CancellationToken cancellationToken)
- {
- var url = PostUrl + "/" + name + ToQueryString(args);
-
- await _httpClient.Post(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- BufferContent = false
-
- }).ConfigureAwait(false);
- }
-
- public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
- {
- var dict = new Dictionary<string, string>();
-
- dict["ItemIds"] = string.Join(",", command.ItemIds);
-
- if (command.StartPositionTicks.HasValue)
- {
- dict["StartPositionTicks"] = command.StartPositionTicks.Value.ToString(CultureInfo.InvariantCulture);
- }
-
- return SendMessage(command.PlayCommand.ToString(), dict, cancellationToken);
- }
-
- public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
- {
- var args = new Dictionary<string, string>();
-
- if (command.Command == PlaystateCommand.Seek)
- {
- if (!command.SeekPositionTicks.HasValue)
- {
- throw new ArgumentException("SeekPositionTicks cannot be null");
- }
-
- args["SeekPositionTicks"] = command.SeekPositionTicks.Value.ToString(CultureInfo.InvariantCulture);
- }
-
- return SendMessage(command.Command.ToString(), args, cancellationToken);
- }
-
- public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken)
- {
- return SendMessage("RestartRequired", cancellationToken);
- }
-
- public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
- {
- return Task.FromResult(true);
- }
-
- public Task SendServerShutdownNotification(CancellationToken cancellationToken)
- {
- return SendMessage("ServerShuttingDown", cancellationToken);
- }
-
- public Task SendServerRestartNotification(CancellationToken cancellationToken)
- {
- return SendMessage("ServerRestarting", cancellationToken);
- }
-
- public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
- {
- return SendMessage(command.Name, command.Arguments, cancellationToken);
- }
-
- public Task SendMessage<T>(string name, T data, CancellationToken cancellationToken)
- {
- // Not supported or needed right now
- return Task.FromResult(true);
- }
-
- private string ToQueryString(Dictionary<string, string> nvc)
- {
- var array = (from item in nvc
- select string.Format("{0}={1}", WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value)))
- .ToArray();
-
- var args = string.Join("&", array);
-
- if (string.IsNullOrEmpty(args))
- {
- return args;
- }
-
- return "?" + args;
- }
-
- public void Dispose()
- {
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
deleted file mode 100644
index 6d86ff091..000000000
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ /dev/null
@@ -1,1930 +0,0 @@
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Library;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Net;
-
-namespace MediaBrowser.Server.Implementations.Session
-{
- /// <summary>
- /// Class SessionManager
- /// </summary>
- public class SessionManager : ISessionManager
- {
- /// <summary>
- /// The _user data repository
- /// </summary>
- private readonly IUserDataManager _userDataManager;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- private readonly ILibraryManager _libraryManager;
- private readonly IUserManager _userManager;
- private readonly IMusicManager _musicManager;
- private readonly IDtoService _dtoService;
- private readonly IImageProcessor _imageProcessor;
- private readonly IMediaSourceManager _mediaSourceManager;
-
- private readonly IHttpClient _httpClient;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly IServerApplicationHost _appHost;
-
- private readonly IAuthenticationRepository _authRepo;
- private readonly IDeviceManager _deviceManager;
-
- /// <summary>
- /// The _active connections
- /// </summary>
- private readonly ConcurrentDictionary<string, SessionInfo> _activeConnections =
- new ConcurrentDictionary<string, SessionInfo>(StringComparer.OrdinalIgnoreCase);
-
- public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
-
- public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationSucceeded;
-
- /// <summary>
- /// Occurs when [playback start].
- /// </summary>
- public event EventHandler<PlaybackProgressEventArgs> PlaybackStart;
- /// <summary>
- /// Occurs when [playback progress].
- /// </summary>
- public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
- /// <summary>
- /// Occurs when [playback stopped].
- /// </summary>
- public event EventHandler<PlaybackStopEventArgs> PlaybackStopped;
-
- public event EventHandler<SessionEventArgs> SessionStarted;
- public event EventHandler<SessionEventArgs> CapabilitiesChanged;
- public event EventHandler<SessionEventArgs> SessionEnded;
- public event EventHandler<SessionEventArgs> SessionActivity;
-
- private IEnumerable<ISessionControllerFactory> _sessionFactories = new List<ISessionControllerFactory>();
-
- private readonly SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
-
- public SessionManager(IUserDataManager userDataManager, ILogger logger, ILibraryManager libraryManager, IUserManager userManager, IMusicManager musicManager, IDtoService dtoService, IImageProcessor imageProcessor, IJsonSerializer jsonSerializer, IServerApplicationHost appHost, IHttpClient httpClient, IAuthenticationRepository authRepo, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager)
- {
- _userDataManager = userDataManager;
- _logger = logger;
- _libraryManager = libraryManager;
- _userManager = userManager;
- _musicManager = musicManager;
- _dtoService = dtoService;
- _imageProcessor = imageProcessor;
- _jsonSerializer = jsonSerializer;
- _appHost = appHost;
- _httpClient = httpClient;
- _authRepo = authRepo;
- _deviceManager = deviceManager;
- _mediaSourceManager = mediaSourceManager;
-
- _deviceManager.DeviceOptionsUpdated += _deviceManager_DeviceOptionsUpdated;
- }
-
- void _deviceManager_DeviceOptionsUpdated(object sender, GenericEventArgs<DeviceInfo> e)
- {
- foreach (var session in Sessions)
- {
- if (string.Equals(session.DeviceId, e.Argument.Id))
- {
- session.DeviceName = e.Argument.Name;
- }
- }
- }
-
- /// <summary>
- /// Adds the parts.
- /// </summary>
- /// <param name="sessionFactories">The session factories.</param>
- public void AddParts(IEnumerable<ISessionControllerFactory> sessionFactories)
- {
- _sessionFactories = sessionFactories.ToList();
- }
-
- /// <summary>
- /// Gets all connections.
- /// </summary>
- /// <value>All connections.</value>
- public IEnumerable<SessionInfo> Sessions
- {
- get { return _activeConnections.Values.OrderByDescending(c => c.LastActivityDate).ToList(); }
- }
-
- private void OnSessionStarted(SessionInfo info)
- {
- EventHelper.QueueEventIfNotNull(SessionStarted, this, new SessionEventArgs
- {
- SessionInfo = info
-
- }, _logger);
-
- if (!string.IsNullOrWhiteSpace(info.DeviceId))
- {
- var capabilities = GetSavedCapabilities(info.DeviceId);
-
- if (capabilities != null)
- {
- info.AppIconUrl = capabilities.IconUrl;
- ReportCapabilities(info, capabilities, false);
- }
- }
- }
-
- private async void OnSessionEnded(SessionInfo info)
- {
- try
- {
- await SendSessionEndedNotification(info, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendSessionEndedNotification", ex);
- }
-
- EventHelper.QueueEventIfNotNull(SessionEnded, this, new SessionEventArgs
- {
- SessionInfo = info
-
- }, _logger);
-
- var disposable = info.SessionController as IDisposable;
-
- if (disposable != null)
- {
- _logger.Debug("Disposing session controller {0}", disposable.GetType().Name);
-
- try
- {
- disposable.Dispose();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error disposing session controller", ex);
- }
- }
- }
-
- /// <summary>
- /// Logs the user activity.
- /// </summary>
- /// <param name="appName">Type of the client.</param>
- /// <param name="appVersion">The app version.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <param name="remoteEndPoint">The remote end point.</param>
- /// <param name="user">The user.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">user</exception>
- /// <exception cref="System.UnauthorizedAccessException"></exception>
- public async Task<SessionInfo> LogSessionActivity(string appName,
- string appVersion,
- string deviceId,
- string deviceName,
- string remoteEndPoint,
- User user)
- {
- if (string.IsNullOrEmpty(appName))
- {
- throw new ArgumentNullException("appName");
- }
- if (string.IsNullOrEmpty(appVersion))
- {
- throw new ArgumentNullException("appVersion");
- }
- if (string.IsNullOrEmpty(deviceId))
- {
- throw new ArgumentNullException("deviceId");
- }
- if (string.IsNullOrEmpty(deviceName))
- {
- throw new ArgumentNullException("deviceName");
- }
-
- var activityDate = DateTime.UtcNow;
- var session = await GetSessionInfo(appName, appVersion, deviceId, deviceName, remoteEndPoint, user).ConfigureAwait(false);
- var lastActivityDate = session.LastActivityDate;
- session.LastActivityDate = activityDate;
-
- if (user != null)
- {
- var userLastActivityDate = user.LastActivityDate ?? DateTime.MinValue;
- user.LastActivityDate = activityDate;
-
- if ((activityDate - userLastActivityDate).TotalSeconds > 60)
- {
- try
- {
- await _userManager.UpdateUser(user).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error updating user", ex);
- }
- }
- }
-
- if ((activityDate - lastActivityDate).TotalSeconds > 10)
- {
- EventHelper.FireEventIfNotNull(SessionActivity, this, new SessionEventArgs
- {
- SessionInfo = session
-
- }, _logger);
- }
-
- var controller = session.SessionController;
- if (controller != null)
- {
- controller.OnActivity();
- }
-
- return session;
- }
-
- public async void ReportSessionEnded(string sessionId)
- {
- await _sessionLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
-
- try
- {
- var session = GetSession(sessionId, false);
-
- if (session != null)
- {
- var key = GetSessionKey(session.Client, session.DeviceId);
-
- SessionInfo removed;
- _activeConnections.TryRemove(key, out removed);
-
- OnSessionEnded(session);
- }
- }
- finally
- {
- _sessionLock.Release();
- }
- }
-
- private Task<MediaSourceInfo> GetMediaSource(IHasMediaSources item, string mediaSourceId, string liveStreamId)
- {
- return _mediaSourceManager.GetMediaSource(item, mediaSourceId, liveStreamId, false, CancellationToken.None);
- }
-
- /// <summary>
- /// Updates the now playing item id.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <param name="info">The information.</param>
- /// <param name="libraryItem">The library item.</param>
- private async Task UpdateNowPlayingItem(SessionInfo session, PlaybackProgressInfo info, BaseItem libraryItem)
- {
- if (string.IsNullOrWhiteSpace(info.MediaSourceId))
- {
- info.MediaSourceId = info.ItemId;
- }
-
- if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null)
- {
- var current = session.NowPlayingItem;
-
- if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase))
- {
- var runtimeTicks = libraryItem.RunTimeTicks;
-
- MediaSourceInfo mediaSource = null;
- var hasMediaSources = libraryItem as IHasMediaSources;
- if (hasMediaSources != null)
- {
- mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
-
- if (mediaSource != null)
- {
- runtimeTicks = mediaSource.RunTimeTicks;
- }
- }
-
- info.Item = GetItemInfo(libraryItem, libraryItem, mediaSource);
-
- info.Item.RunTimeTicks = runtimeTicks;
- }
- else
- {
- info.Item = current;
- }
- }
-
- session.NowPlayingItem = info.Item;
- session.LastActivityDate = DateTime.UtcNow;
- session.LastPlaybackCheckIn = DateTime.UtcNow;
-
- session.PlayState.IsPaused = info.IsPaused;
- session.PlayState.PositionTicks = info.PositionTicks;
- session.PlayState.MediaSourceId = info.MediaSourceId;
- session.PlayState.CanSeek = info.CanSeek;
- session.PlayState.IsMuted = info.IsMuted;
- session.PlayState.VolumeLevel = info.VolumeLevel;
- session.PlayState.AudioStreamIndex = info.AudioStreamIndex;
- session.PlayState.SubtitleStreamIndex = info.SubtitleStreamIndex;
- session.PlayState.PlayMethod = info.PlayMethod;
- session.PlayState.RepeatMode = info.RepeatMode;
- }
-
- /// <summary>
- /// Removes the now playing item id.
- /// </summary>
- /// <param name="session">The session.</param>
- /// <exception cref="System.ArgumentNullException">item</exception>
- private void RemoveNowPlayingItem(SessionInfo session)
- {
- session.NowPlayingItem = null;
- session.PlayState = new PlayerStateInfo();
-
- if (!string.IsNullOrEmpty(session.DeviceId))
- {
- ClearTranscodingInfo(session.DeviceId);
- }
- }
-
- private string GetSessionKey(string appName, string deviceId)
- {
- return appName + deviceId;
- }
-
- /// <summary>
- /// Gets the connection.
- /// </summary>
- /// <param name="appName">Type of the client.</param>
- /// <param name="appVersion">The app version.</param>
- /// <param name="deviceId">The device id.</param>
- /// <param name="deviceName">Name of the device.</param>
- /// <param name="remoteEndPoint">The remote end point.</param>
- /// <param name="user">The user.</param>
- /// <returns>SessionInfo.</returns>
- private async Task<SessionInfo> GetSessionInfo(string appName, string appVersion, string deviceId, string deviceName, string remoteEndPoint, User user)
- {
- if (string.IsNullOrWhiteSpace(deviceId))
- {
- throw new ArgumentNullException("deviceId");
- }
- var key = GetSessionKey(appName, deviceId);
-
- await _sessionLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
-
- var userId = user == null ? (Guid?)null : user.Id;
- var username = user == null ? null : user.Name;
-
- try
- {
- SessionInfo sessionInfo;
- DeviceInfo device = null;
-
- if (!_activeConnections.TryGetValue(key, out sessionInfo))
- {
- sessionInfo = new SessionInfo
- {
- Client = appName,
- DeviceId = deviceId,
- ApplicationVersion = appVersion,
- Id = key.GetMD5().ToString("N")
- };
-
- sessionInfo.DeviceName = deviceName;
- sessionInfo.UserId = userId;
- sessionInfo.UserName = username;
- sessionInfo.RemoteEndPoint = remoteEndPoint;
-
- OnSessionStarted(sessionInfo);
-
- _activeConnections.TryAdd(key, sessionInfo);
-
- if (!string.IsNullOrEmpty(deviceId))
- {
- var userIdString = userId.HasValue ? userId.Value.ToString("N") : null;
- device = await _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString).ConfigureAwait(false);
- }
- }
-
- device = device ?? _deviceManager.GetDevice(deviceId);
-
- if (device == null)
- {
- var userIdString = userId.HasValue ? userId.Value.ToString("N") : null;
- device = await _deviceManager.RegisterDevice(deviceId, deviceName, appName, appVersion, userIdString).ConfigureAwait(false);
- }
-
- if (device != null)
- {
- if (!string.IsNullOrEmpty(device.CustomName))
- {
- deviceName = device.CustomName;
- }
- }
-
- sessionInfo.DeviceName = deviceName;
- sessionInfo.UserId = userId;
- sessionInfo.UserName = username;
- sessionInfo.RemoteEndPoint = remoteEndPoint;
- sessionInfo.ApplicationVersion = appVersion;
-
- if (!userId.HasValue)
- {
- sessionInfo.AdditionalUsers.Clear();
- }
-
- if (sessionInfo.SessionController == null)
- {
- sessionInfo.SessionController = _sessionFactories
- .Select(i => i.GetSessionController(sessionInfo))
- .FirstOrDefault(i => i != null);
- }
-
- return sessionInfo;
- }
- finally
- {
- _sessionLock.Release();
- }
- }
-
- private List<User> GetUsers(SessionInfo session)
- {
- var users = new List<User>();
-
- if (session.UserId.HasValue)
- {
- var user = _userManager.GetUserById(session.UserId.Value);
-
- if (user == null)
- {
- throw new InvalidOperationException("User not found");
- }
-
- users.Add(user);
-
- var additionalUsers = session.AdditionalUsers
- .Select(i => _userManager.GetUserById(i.UserId))
- .Where(i => i != null);
-
- users.AddRange(additionalUsers);
- }
-
- return users;
- }
-
- private Timer _idleTimer;
-
- private void StartIdleCheckTimer()
- {
- if (_idleTimer == null)
- {
- _idleTimer = new Timer(CheckForIdlePlayback, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5));
- }
- }
- private void StopIdleCheckTimer()
- {
- if (_idleTimer != null)
- {
- _idleTimer.Dispose();
- _idleTimer = null;
- }
- }
-
- private async void CheckForIdlePlayback(object state)
- {
- var playingSessions = Sessions.Where(i => i.NowPlayingItem != null)
- .ToList();
-
- if (playingSessions.Count > 0)
- {
- var idle = playingSessions
- .Where(i => (DateTime.UtcNow - i.LastPlaybackCheckIn).TotalMinutes > 5)
- .ToList();
-
- foreach (var session in idle)
- {
- _logger.Debug("Session {0} has gone idle while playing", session.Id);
-
- try
- {
- await OnPlaybackStopped(new PlaybackStopInfo
- {
- Item = session.NowPlayingItem,
- ItemId = session.NowPlayingItem == null ? null : session.NowPlayingItem.Id,
- SessionId = session.Id,
- MediaSourceId = session.PlayState == null ? null : session.PlayState.MediaSourceId,
- PositionTicks = session.PlayState == null ? null : session.PlayState.PositionTicks
- });
- }
- catch (Exception ex)
- {
- _logger.Debug("Error calling OnPlaybackStopped", ex);
- }
- }
-
- playingSessions = Sessions.Where(i => i.NowPlayingItem != null)
- .ToList();
- }
-
- if (playingSessions.Count == 0)
- {
- StopIdleCheckTimer();
- }
- }
-
- /// <summary>
- /// Used to report that playback has started for an item
- /// </summary>
- /// <param name="info">The info.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">info</exception>
- public async Task OnPlaybackStart(PlaybackStartInfo info)
- {
- if (info == null)
- {
- throw new ArgumentNullException("info");
- }
-
- var session = GetSession(info.SessionId);
-
- var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
- ? null
- : _libraryManager.GetItemById(new Guid(info.ItemId));
-
- await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false);
-
- if (!string.IsNullOrEmpty(session.DeviceId) && info.PlayMethod != PlayMethod.Transcode)
- {
- ClearTranscodingInfo(session.DeviceId);
- }
-
- session.QueueableMediaTypes = info.QueueableMediaTypes;
-
- var users = GetUsers(session);
-
- if (libraryItem != null)
- {
- foreach (var user in users)
- {
- await OnPlaybackStart(user.Id, libraryItem).ConfigureAwait(false);
- }
- }
-
- // Nothing to save here
- // Fire events to inform plugins
- EventHelper.QueueEventIfNotNull(PlaybackStart, this, new PlaybackProgressEventArgs
- {
- Item = libraryItem,
- Users = users,
- MediaSourceId = info.MediaSourceId,
- MediaInfo = info.Item,
- DeviceName = session.DeviceName,
- ClientName = session.Client,
- DeviceId = session.DeviceId
-
- }, _logger);
-
- await SendPlaybackStartNotification(session, CancellationToken.None).ConfigureAwait(false);
-
- StartIdleCheckTimer();
- }
-
- /// <summary>
- /// Called when [playback start].
- /// </summary>
- /// <param name="userId">The user identifier.</param>
- /// <param name="item">The item.</param>
- /// <returns>Task.</returns>
- private async Task OnPlaybackStart(Guid userId, IHasUserData item)
- {
- var data = _userDataManager.GetUserData(userId, item);
-
- data.PlayCount++;
- data.LastPlayedDate = DateTime.UtcNow;
-
- if (item.SupportsPlayedStatus)
- {
- if (!(item is Video))
- {
- data.Played = true;
- }
- }
- else
- {
- data.Played = false;
- }
-
- await _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Used to report playback progress for an item
- /// </summary>
- /// <param name="info">The info.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
- public async Task OnPlaybackProgress(PlaybackProgressInfo info)
- {
- if (info == null)
- {
- throw new ArgumentNullException("info");
- }
-
- var session = GetSession(info.SessionId);
-
- var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
- ? null
- : _libraryManager.GetItemById(new Guid(info.ItemId));
-
- await UpdateNowPlayingItem(session, info, libraryItem).ConfigureAwait(false);
-
- var users = GetUsers(session);
-
- if (libraryItem != null)
- {
- foreach (var user in users)
- {
- await OnPlaybackProgress(user, libraryItem, info).ConfigureAwait(false);
- }
- }
-
- if (!string.IsNullOrWhiteSpace(info.LiveStreamId))
- {
- try
- {
- await _mediaSourceManager.PingLiveStream(info.LiveStreamId, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing live stream", ex);
- }
- }
-
- EventHelper.FireEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs
- {
- Item = libraryItem,
- Users = users,
- PlaybackPositionTicks = session.PlayState.PositionTicks,
- MediaSourceId = session.PlayState.MediaSourceId,
- MediaInfo = info.Item,
- DeviceName = session.DeviceName,
- ClientName = session.Client,
- DeviceId = session.DeviceId,
- IsPaused = info.IsPaused,
- PlaySessionId = info.PlaySessionId
-
- }, _logger);
-
- StartIdleCheckTimer();
- }
-
- private async Task OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
- {
- var data = _userDataManager.GetUserData(user.Id, item);
-
- var positionTicks = info.PositionTicks;
-
- if (positionTicks.HasValue)
- {
- _userDataManager.UpdatePlayState(item, data, positionTicks.Value);
-
- UpdatePlaybackSettings(user, info, data);
-
- await _userDataManager.SaveUserData(user.Id, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None).ConfigureAwait(false);
- }
- }
-
- private void UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data)
- {
- if (user.Configuration.RememberAudioSelections)
- {
- data.AudioStreamIndex = info.AudioStreamIndex;
- }
- else
- {
- data.AudioStreamIndex = null;
- }
-
- if (user.Configuration.RememberSubtitleSelections)
- {
- data.SubtitleStreamIndex = info.SubtitleStreamIndex;
- }
- else
- {
- data.SubtitleStreamIndex = null;
- }
- }
-
- /// <summary>
- /// Used to report that playback has ended for an item
- /// </summary>
- /// <param name="info">The info.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">info</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">positionTicks</exception>
- public async Task OnPlaybackStopped(PlaybackStopInfo info)
- {
- if (info == null)
- {
- throw new ArgumentNullException("info");
- }
-
- if (info.PositionTicks.HasValue && info.PositionTicks.Value < 0)
- {
- throw new ArgumentOutOfRangeException("positionTicks");
- }
-
- var session = GetSession(info.SessionId);
-
- var libraryItem = string.IsNullOrWhiteSpace(info.ItemId)
- ? null
- : _libraryManager.GetItemById(new Guid(info.ItemId));
-
- // Normalize
- if (string.IsNullOrWhiteSpace(info.MediaSourceId))
- {
- info.MediaSourceId = info.ItemId;
- }
-
- if (!string.IsNullOrWhiteSpace(info.ItemId) && info.Item == null && libraryItem != null)
- {
- var current = session.NowPlayingItem;
-
- if (current == null || !string.Equals(current.Id, info.ItemId, StringComparison.OrdinalIgnoreCase))
- {
- MediaSourceInfo mediaSource = null;
-
- var hasMediaSources = libraryItem as IHasMediaSources;
- if (hasMediaSources != null)
- {
- mediaSource = await GetMediaSource(hasMediaSources, info.MediaSourceId, info.LiveStreamId).ConfigureAwait(false);
- }
-
- info.Item = GetItemInfo(libraryItem, libraryItem, mediaSource);
- }
- else
- {
- info.Item = current;
- }
- }
-
- RemoveNowPlayingItem(session);
-
- var users = GetUsers(session);
- var playedToCompletion = false;
-
- if (libraryItem != null)
- {
- foreach (var user in users)
- {
- playedToCompletion = await OnPlaybackStopped(user.Id, libraryItem, info.PositionTicks, info.Failed).ConfigureAwait(false);
- }
- }
-
- if (!string.IsNullOrWhiteSpace(info.LiveStreamId))
- {
- try
- {
- await _mediaSourceManager.CloseLiveStream(info.LiveStreamId).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error closing live stream", ex);
- }
- }
-
- EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackStopEventArgs
- {
- Item = libraryItem,
- Users = users,
- PlaybackPositionTicks = info.PositionTicks,
- PlayedToCompletion = playedToCompletion,
- MediaSourceId = info.MediaSourceId,
- MediaInfo = info.Item,
- DeviceName = session.DeviceName,
- ClientName = session.Client,
- DeviceId = session.DeviceId
-
- }, _logger);
-
- await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false);
- }
-
- private async Task<bool> OnPlaybackStopped(Guid userId, BaseItem item, long? positionTicks, bool playbackFailed)
- {
- bool playedToCompletion = false;
-
- if (!playbackFailed)
- {
- var data = _userDataManager.GetUserData(userId, item);
-
- if (positionTicks.HasValue)
- {
- playedToCompletion = _userDataManager.UpdatePlayState(item, data, positionTicks.Value);
- }
- else
- {
- // If the client isn't able to report this, then we'll just have to make an assumption
- data.PlayCount++;
- data.Played = item.SupportsPlayedStatus;
- data.PlaybackPositionTicks = 0;
- playedToCompletion = true;
- }
-
- await _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None).ConfigureAwait(false);
- }
-
- return playedToCompletion;
- }
-
- /// <summary>
- /// Gets the session.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="throwOnMissing">if set to <c>true</c> [throw on missing].</param>
- /// <returns>SessionInfo.</returns>
- /// <exception cref="ResourceNotFoundException"></exception>
- private SessionInfo GetSession(string sessionId, bool throwOnMissing = true)
- {
- var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId));
-
- if (session == null && throwOnMissing)
- {
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
- }
-
- return session;
- }
-
- private SessionInfo GetSessionToRemoteControl(string sessionId)
- {
- // Accept either device id or session id
- var session = Sessions.FirstOrDefault(i => string.Equals(i.Id, sessionId));
-
- if (session == null)
- {
- throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
- }
-
- return session;
- }
-
- public Task SendMessageCommand(string controllingSessionId, string sessionId, MessageCommand command, CancellationToken cancellationToken)
- {
- var generalCommand = new GeneralCommand
- {
- Name = GeneralCommandType.DisplayMessage.ToString()
- };
-
- generalCommand.Arguments["Header"] = command.Header;
- generalCommand.Arguments["Text"] = command.Text;
-
- if (command.TimeoutMs.HasValue)
- {
- generalCommand.Arguments["TimeoutMs"] = command.TimeoutMs.Value.ToString(CultureInfo.InvariantCulture);
- }
-
- return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken);
- }
-
- public Task SendGeneralCommand(string controllingSessionId, string sessionId, GeneralCommand command, CancellationToken cancellationToken)
- {
- var session = GetSessionToRemoteControl(sessionId);
-
- var controllingSession = GetSession(controllingSessionId);
- AssertCanControl(session, controllingSession);
-
- return session.SessionController.SendGeneralCommand(command, cancellationToken);
- }
-
- public async Task SendPlayCommand(string controllingSessionId, string sessionId, PlayRequest command, CancellationToken cancellationToken)
- {
- var session = GetSessionToRemoteControl(sessionId);
-
- var user = session.UserId.HasValue ? _userManager.GetUserById(session.UserId.Value) : null;
-
- List<BaseItem> items;
-
- if (command.PlayCommand == PlayCommand.PlayInstantMix)
- {
- items = command.ItemIds.SelectMany(i => TranslateItemForInstantMix(i, user))
- .Where(i => i.LocationType != LocationType.Virtual)
- .ToList();
-
- command.PlayCommand = PlayCommand.PlayNow;
- }
- else
- {
- var list = new List<BaseItem>();
- foreach (var itemId in command.ItemIds)
- {
- var subItems = await TranslateItemForPlayback(itemId, user).ConfigureAwait(false);
- list.AddRange(subItems);
- }
-
- items = list
- .Where(i => i.LocationType != LocationType.Virtual)
- .ToList();
- }
-
- if (command.PlayCommand == PlayCommand.PlayShuffle)
- {
- items = items.OrderBy(i => Guid.NewGuid()).ToList();
- command.PlayCommand = PlayCommand.PlayNow;
- }
-
- command.ItemIds = items.Select(i => i.Id.ToString("N")).ToArray();
-
- if (user != null)
- {
- if (items.Any(i => i.GetPlayAccess(user) != PlayAccess.Full))
- {
- throw new ArgumentException(string.Format("{0} is not allowed to play media.", user.Name));
- }
- }
-
- if (command.PlayCommand != PlayCommand.PlayNow)
- {
- if (items.Any(i => !session.QueueableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format("{0} is unable to queue the requested media type.", session.DeviceName ?? session.Id));
- }
- }
- else
- {
- if (items.Any(i => !session.PlayableMediaTypes.Contains(i.MediaType, StringComparer.OrdinalIgnoreCase)))
- {
- throw new ArgumentException(string.Format("{0} is unable to play the requested media type.", session.DeviceName ?? session.Id));
- }
- }
-
- if (user != null && command.ItemIds.Length == 1 && user.Configuration.EnableNextEpisodeAutoPlay)
- {
- var episode = _libraryManager.GetItemById(command.ItemIds[0]) as Episode;
- if (episode != null)
- {
- var series = episode.Series;
- if (series != null)
- {
- var episodes = series.GetEpisodes(user)
- .Where(i => !i.IsVirtualItem)
- .SkipWhile(i => i.Id != episode.Id)
- .ToList();
-
- if (episodes.Count > 0)
- {
- command.ItemIds = episodes.Select(i => i.Id.ToString("N")).ToArray();
- }
- }
- }
- }
-
- var controllingSession = GetSession(controllingSessionId);
- AssertCanControl(session, controllingSession);
- if (controllingSession.UserId.HasValue)
- {
- command.ControllingUserId = controllingSession.UserId.Value.ToString("N");
- }
-
- await session.SessionController.SendPlayCommand(command, cancellationToken).ConfigureAwait(false);
- }
-
- private async Task<List<BaseItem>> TranslateItemForPlayback(string id, User user)
- {
- var item = _libraryManager.GetItemById(id);
-
- if (item == null)
- {
- _logger.Error("A non-existant item Id {0} was passed into TranslateItemForPlayback", id);
- return new List<BaseItem>();
- }
-
- var byName = item as IItemByName;
-
- if (byName != null)
- {
- var items = byName.GetTaggedItems(new InternalItemsQuery(user)
- {
- IsFolder = false,
- Recursive = true
- });
-
- return FilterToSingleMediaType(items)
- .OrderBy(i => i.SortName)
- .ToList();
- }
-
- if (item.IsFolder)
- {
- var folder = (Folder)item;
-
- var itemsResult = await folder.GetItems(new InternalItemsQuery(user)
- {
- Recursive = true,
- IsFolder = false
-
- }).ConfigureAwait(false);
-
- return FilterToSingleMediaType(itemsResult.Items)
- .OrderBy(i => i.SortName)
- .ToList();
- }
-
- return new List<BaseItem> { item };
- }
-
- private IEnumerable<BaseItem> FilterToSingleMediaType(IEnumerable<BaseItem> items)
- {
- return items
- .Where(i => !string.IsNullOrWhiteSpace(i.MediaType))
- .ToLookup(i => i.MediaType, StringComparer.OrdinalIgnoreCase)
- .OrderByDescending(i => i.Count())
- .FirstOrDefault();
- }
-
- private IEnumerable<BaseItem> TranslateItemForInstantMix(string id, User user)
- {
- var item = _libraryManager.GetItemById(id);
-
- if (item == null)
- {
- _logger.Error("A non-existant item Id {0} was passed into TranslateItemForInstantMix", id);
- return new List<BaseItem>();
- }
-
- return _musicManager.GetInstantMixFromItem(item, user);
- }
-
- public Task SendBrowseCommand(string controllingSessionId, string sessionId, BrowseRequest command, CancellationToken cancellationToken)
- {
- var generalCommand = new GeneralCommand
- {
- Name = GeneralCommandType.DisplayContent.ToString()
- };
-
- generalCommand.Arguments["ItemId"] = command.ItemId;
- generalCommand.Arguments["ItemName"] = command.ItemName;
- generalCommand.Arguments["ItemType"] = command.ItemType;
-
- return SendGeneralCommand(controllingSessionId, sessionId, generalCommand, cancellationToken);
- }
-
- public Task SendPlaystateCommand(string controllingSessionId, string sessionId, PlaystateRequest command, CancellationToken cancellationToken)
- {
- var session = GetSessionToRemoteControl(sessionId);
-
- var controllingSession = GetSession(controllingSessionId);
- AssertCanControl(session, controllingSession);
- if (controllingSession.UserId.HasValue)
- {
- command.ControllingUserId = controllingSession.UserId.Value.ToString("N");
- }
-
- return session.SessionController.SendPlaystateCommand(command, cancellationToken);
- }
-
- private void AssertCanControl(SessionInfo session, SessionInfo controllingSession)
- {
- if (session == null)
- {
- throw new ArgumentNullException("session");
- }
- if (controllingSession == null)
- {
- throw new ArgumentNullException("controllingSession");
- }
- }
-
- /// <summary>
- /// Sends the restart required message.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public async Task SendRestartRequiredNotification(CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
-
- var info = await _appHost.GetSystemInfo().ConfigureAwait(false);
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendRestartRequiredNotification(info, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendRestartRequiredNotification.", ex);
- }
-
- }, cancellationToken));
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Sends the server shutdown notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendServerShutdownNotification(CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendServerShutdownNotification(cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendServerShutdownNotification.", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- /// <summary>
- /// Sends the server restart notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendServerRestartNotification(CancellationToken cancellationToken)
- {
- _logger.Debug("Beginning SendServerRestartNotification");
-
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendServerRestartNotification(cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendServerRestartNotification.", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- public Task SendSessionEndedNotification(SessionInfo sessionInfo, CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
- var dto = GetSessionInfoDto(sessionInfo);
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendSessionEndedNotification(dto, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendSessionEndedNotification.", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- public Task SendPlaybackStartNotification(SessionInfo sessionInfo, CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
- var dto = GetSessionInfoDto(sessionInfo);
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendPlaybackStartNotification(dto, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendPlaybackStartNotification.", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- public Task SendPlaybackStoppedNotification(SessionInfo sessionInfo, CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null).ToList();
- var dto = GetSessionInfoDto(sessionInfo);
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendPlaybackStoppedNotification(dto, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SendPlaybackStoppedNotification.", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- /// <summary>
- /// Adds the additional user.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="userId">The user identifier.</param>
- /// <exception cref="System.UnauthorizedAccessException">Cannot modify additional users without authenticating first.</exception>
- /// <exception cref="System.ArgumentException">The requested user is already the primary user of the session.</exception>
- public void AddAdditionalUser(string sessionId, string userId)
- {
- var session = GetSession(sessionId);
-
- if (session.UserId.HasValue && session.UserId.Value == new Guid(userId))
- {
- throw new ArgumentException("The requested user is already the primary user of the session.");
- }
-
- if (session.AdditionalUsers.All(i => new Guid(i.UserId) != new Guid(userId)))
- {
- var user = _userManager.GetUserById(userId);
-
- session.AdditionalUsers.Add(new SessionUserInfo
- {
- UserId = userId,
- UserName = user.Name
- });
- }
- }
-
- /// <summary>
- /// Removes the additional user.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="userId">The user identifier.</param>
- /// <exception cref="System.UnauthorizedAccessException">Cannot modify additional users without authenticating first.</exception>
- /// <exception cref="System.ArgumentException">The requested user is already the primary user of the session.</exception>
- public void RemoveAdditionalUser(string sessionId, string userId)
- {
- var session = GetSession(sessionId);
-
- if (session.UserId.HasValue && session.UserId.Value == new Guid(userId))
- {
- throw new ArgumentException("The requested user is already the primary user of the session.");
- }
-
- var user = session.AdditionalUsers.FirstOrDefault(i => new Guid(i.UserId) == new Guid(userId));
-
- if (user != null)
- {
- session.AdditionalUsers.Remove(user);
- }
- }
-
- /// <summary>
- /// Authenticates the new session.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>Task{SessionInfo}.</returns>
- public Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request)
- {
- return AuthenticateNewSessionInternal(request, true);
- }
-
- public Task<AuthenticationResult> CreateNewSession(AuthenticationRequest request)
- {
- return AuthenticateNewSessionInternal(request, false);
- }
-
- private async Task<AuthenticationResult> AuthenticateNewSessionInternal(AuthenticationRequest request, bool enforcePassword)
- {
- User user = null;
- if (!string.IsNullOrWhiteSpace(request.UserId))
- {
- var idGuid = new Guid(request.UserId);
- user = _userManager.Users
- .FirstOrDefault(i => i.Id == idGuid);
- }
-
- if (user == null)
- {
- user = _userManager.Users
- .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase));
- }
-
- if (user != null && !string.IsNullOrWhiteSpace(request.DeviceId))
- {
- if (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), request.DeviceId))
- {
- throw new SecurityException("User is not allowed access from this device.");
- }
- }
-
- if (enforcePassword)
- {
- var result = await _userManager.AuthenticateUser(request.Username, request.PasswordSha1, request.PasswordMd5, request.RemoteEndPoint).ConfigureAwait(false);
-
- if (!result)
- {
- EventHelper.FireEventIfNotNull(AuthenticationFailed, this, new GenericEventArgs<AuthenticationRequest>(request), _logger);
-
- throw new SecurityException("Invalid user or password entered.");
- }
- }
-
- var token = await GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.AppVersion, request.DeviceName).ConfigureAwait(false);
-
- EventHelper.FireEventIfNotNull(AuthenticationSucceeded, this, new GenericEventArgs<AuthenticationRequest>(request), _logger);
-
- var session = await LogSessionActivity(request.App,
- request.AppVersion,
- request.DeviceId,
- request.DeviceName,
- request.RemoteEndPoint,
- user)
- .ConfigureAwait(false);
-
- return new AuthenticationResult
- {
- User = _userManager.GetUserDto(user, request.RemoteEndPoint),
- SessionInfo = GetSessionInfoDto(session),
- AccessToken = token,
- ServerId = _appHost.SystemId
- };
- }
-
-
- private async Task<string> GetAuthorizationToken(string userId, string deviceId, string app, string appVersion, string deviceName)
- {
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- DeviceId = deviceId,
- IsActive = true,
- UserId = userId,
- Limit = 1
- });
-
- if (existing.Items.Length > 0)
- {
- var token = existing.Items[0].AccessToken;
- _logger.Info("Reissuing access token: " + token);
- return token;
- }
-
- var newToken = new AuthenticationInfo
- {
- AppName = app,
- AppVersion = appVersion,
- DateCreated = DateTime.UtcNow,
- DeviceId = deviceId,
- DeviceName = deviceName,
- UserId = userId,
- IsActive = true,
- AccessToken = Guid.NewGuid().ToString("N")
- };
-
- _logger.Info("Creating new access token for user {0}", userId);
- await _authRepo.Create(newToken, CancellationToken.None).ConfigureAwait(false);
-
- return newToken.AccessToken;
- }
-
- public async Task Logout(string accessToken)
- {
- if (string.IsNullOrWhiteSpace(accessToken))
- {
- throw new ArgumentNullException("accessToken");
- }
-
- _logger.Info("Logging out access token {0}", accessToken);
-
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- Limit = 1,
- AccessToken = accessToken
-
- }).Items.FirstOrDefault();
-
- if (existing != null)
- {
- existing.IsActive = false;
-
- await _authRepo.Update(existing, CancellationToken.None).ConfigureAwait(false);
-
- var sessions = Sessions
- .Where(i => string.Equals(i.DeviceId, existing.DeviceId, StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- foreach (var session in sessions)
- {
- try
- {
- ReportSessionEnded(session.Id);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reporting session ended", ex);
- }
- }
- }
- }
-
- public async Task RevokeUserTokens(string userId, string currentAccessToken)
- {
- var existing = _authRepo.Get(new AuthenticationInfoQuery
- {
- IsActive = true,
- UserId = userId
- });
-
- foreach (var info in existing.Items)
- {
- if (!string.Equals(currentAccessToken, info.AccessToken, StringComparison.OrdinalIgnoreCase))
- {
- await Logout(info.AccessToken).ConfigureAwait(false);
- }
- }
- }
-
- public Task RevokeToken(string token)
- {
- return Logout(token);
- }
-
- /// <summary>
- /// Reports the capabilities.
- /// </summary>
- /// <param name="sessionId">The session identifier.</param>
- /// <param name="capabilities">The capabilities.</param>
- public void ReportCapabilities(string sessionId, ClientCapabilities capabilities)
- {
- var session = GetSession(sessionId);
-
- ReportCapabilities(session, capabilities, true);
- }
-
- private async void ReportCapabilities(SessionInfo session,
- ClientCapabilities capabilities,
- bool saveCapabilities)
- {
- session.Capabilities = capabilities;
-
- if (!string.IsNullOrWhiteSpace(capabilities.MessageCallbackUrl))
- {
- var controller = session.SessionController as HttpSessionController;
-
- if (controller == null)
- {
- session.SessionController = new HttpSessionController(_httpClient, _jsonSerializer, session, capabilities.MessageCallbackUrl, this);
- }
- }
-
- EventHelper.FireEventIfNotNull(CapabilitiesChanged, this, new SessionEventArgs
- {
- SessionInfo = session
-
- }, _logger);
-
- if (saveCapabilities)
- {
- try
- {
- await SaveCapabilities(session.DeviceId, capabilities).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving device capabilities", ex);
- }
- }
- }
-
- private ClientCapabilities GetSavedCapabilities(string deviceId)
- {
- return _deviceManager.GetCapabilities(deviceId);
- }
-
- private Task SaveCapabilities(string deviceId, ClientCapabilities capabilities)
- {
- return _deviceManager.SaveCapabilities(deviceId, capabilities);
- }
-
- public SessionInfoDto GetSessionInfoDto(SessionInfo session)
- {
- var dto = new SessionInfoDto
- {
- Client = session.Client,
- DeviceId = session.DeviceId,
- DeviceName = session.DeviceName,
- Id = session.Id,
- LastActivityDate = session.LastActivityDate,
- NowViewingItem = session.NowViewingItem,
- ApplicationVersion = session.ApplicationVersion,
- QueueableMediaTypes = session.QueueableMediaTypes,
- PlayableMediaTypes = session.PlayableMediaTypes,
- AdditionalUsers = session.AdditionalUsers,
- SupportedCommands = session.SupportedCommands,
- UserName = session.UserName,
- NowPlayingItem = session.NowPlayingItem,
- SupportsRemoteControl = session.SupportsMediaControl,
- PlayState = session.PlayState,
- AppIconUrl = session.AppIconUrl,
- TranscodingInfo = session.NowPlayingItem == null ? null : session.TranscodingInfo
- };
-
- if (session.UserId.HasValue)
- {
- dto.UserId = session.UserId.Value.ToString("N");
-
- var user = _userManager.GetUserById(session.UserId.Value);
-
- if (user != null)
- {
- dto.UserPrimaryImageTag = GetImageCacheTag(user, ImageType.Primary);
- }
- }
-
- return dto;
- }
-
- /// <summary>
- /// Converts a BaseItem to a BaseItemInfo
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="chapterOwner">The chapter owner.</param>
- /// <param name="mediaSource">The media source.</param>
- /// <returns>BaseItemInfo.</returns>
- /// <exception cref="System.ArgumentNullException">item</exception>
- private BaseItemInfo GetItemInfo(BaseItem item, BaseItem chapterOwner, MediaSourceInfo mediaSource)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- var info = new BaseItemInfo
- {
- Id = GetDtoId(item),
- Name = item.Name,
- MediaType = item.MediaType,
- Type = item.GetClientTypeName(),
- RunTimeTicks = item.RunTimeTicks,
- IndexNumber = item.IndexNumber,
- ParentIndexNumber = item.ParentIndexNumber,
- PremiereDate = item.PremiereDate,
- ProductionYear = item.ProductionYear,
- IsThemeMedia = item.IsThemeMedia
- };
-
- info.PrimaryImageTag = GetImageCacheTag(item, ImageType.Primary);
- if (info.PrimaryImageTag != null)
- {
- info.PrimaryImageItemId = GetDtoId(item);
- }
-
- var episode = item as Episode;
- if (episode != null)
- {
- info.IndexNumberEnd = episode.IndexNumberEnd;
- }
-
- var hasSeries = item as IHasSeries;
- if (hasSeries != null)
- {
- info.SeriesName = hasSeries.SeriesName;
- }
-
- var recording = item as ILiveTvRecording;
- if (recording != null)
- {
- if (recording.IsSeries)
- {
- info.Name = recording.EpisodeTitle;
- info.SeriesName = recording.Name;
-
- if (string.IsNullOrWhiteSpace(info.Name))
- {
- info.Name = recording.Name;
- }
- }
- }
-
- var audio = item as Audio;
- if (audio != null)
- {
- info.Album = audio.Album;
- info.Artists = audio.Artists;
-
- if (info.PrimaryImageTag == null)
- {
- var album = audio.AlbumEntity;
-
- if (album != null && album.HasImage(ImageType.Primary))
- {
- info.PrimaryImageTag = GetImageCacheTag(album, ImageType.Primary);
- if (info.PrimaryImageTag != null)
- {
- info.PrimaryImageItemId = GetDtoId(album);
- }
- }
- }
- }
-
- var musicVideo = item as MusicVideo;
- if (musicVideo != null)
- {
- info.Album = musicVideo.Album;
- info.Artists = musicVideo.Artists.ToList();
- }
-
- var backropItem = item.HasImage(ImageType.Backdrop) ? item : null;
- var thumbItem = item.HasImage(ImageType.Thumb) ? item : null;
- var logoItem = item.HasImage(ImageType.Logo) ? item : null;
-
- if (thumbItem == null)
- {
- if (episode != null)
- {
- var series = episode.Series;
-
- if (series != null && series.HasImage(ImageType.Thumb))
- {
- thumbItem = series;
- }
- }
- }
-
- if (backropItem == null)
- {
- if (episode != null)
- {
- var series = episode.Series;
-
- if (series != null && series.HasImage(ImageType.Backdrop))
- {
- backropItem = series;
- }
- }
- }
-
- if (backropItem == null)
- {
- backropItem = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Backdrop));
- }
-
- if (thumbItem == null)
- {
- thumbItem = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Thumb));
- }
-
- if (logoItem == null)
- {
- logoItem = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Logo));
- }
-
- if (thumbItem != null)
- {
- info.ThumbImageTag = GetImageCacheTag(thumbItem, ImageType.Thumb);
- info.ThumbItemId = GetDtoId(thumbItem);
- }
-
- if (backropItem != null)
- {
- info.BackdropImageTag = GetImageCacheTag(backropItem, ImageType.Backdrop);
- info.BackdropItemId = GetDtoId(backropItem);
- }
-
- if (logoItem != null)
- {
- info.LogoImageTag = GetImageCacheTag(logoItem, ImageType.Logo);
- info.LogoItemId = GetDtoId(logoItem);
- }
-
- if (chapterOwner != null)
- {
- info.ChapterImagesItemId = chapterOwner.Id.ToString("N");
-
- info.Chapters = _dtoService.GetChapterInfoDtos(chapterOwner).ToList();
- }
-
- if (mediaSource != null)
- {
- info.MediaStreams = mediaSource.MediaStreams;
- }
-
- return info;
- }
-
- private string GetImageCacheTag(BaseItem item, ImageType type)
- {
- try
- {
- return _imageProcessor.GetImageCacheTag(item, type);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting {0} image info", ex, type);
- return null;
- }
- }
-
- private string GetDtoId(BaseItem item)
- {
- return _dtoService.GetDtoId(item);
- }
-
- public void ReportNowViewingItem(string sessionId, string itemId)
- {
- if (string.IsNullOrWhiteSpace(itemId))
- {
- throw new ArgumentNullException("itemId");
- }
-
- var item = _libraryManager.GetItemById(new Guid(itemId));
-
- var info = GetItemInfo(item, null, null);
-
- ReportNowViewingItem(sessionId, info);
- }
-
- public void ReportNowViewingItem(string sessionId, BaseItemInfo item)
- {
- var session = GetSession(sessionId);
-
- session.NowViewingItem = item;
- }
-
- public void ReportTranscodingInfo(string deviceId, TranscodingInfo info)
- {
- var session = Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId));
-
- if (session != null)
- {
- session.TranscodingInfo = info;
- }
- }
-
- public void ClearTranscodingInfo(string deviceId)
- {
- ReportTranscodingInfo(deviceId, null);
- }
-
- public SessionInfo GetSession(string deviceId, string client, string version)
- {
- return Sessions.FirstOrDefault(i => string.Equals(i.DeviceId, deviceId) &&
- string.Equals(i.Client, client));
- }
-
- public Task<SessionInfo> GetSessionByAuthenticationToken(AuthenticationInfo info, string deviceId, string remoteEndpoint, string appVersion)
- {
- if (info == null)
- {
- throw new ArgumentNullException("info");
- }
-
- var user = string.IsNullOrWhiteSpace(info.UserId)
- ? null
- : _userManager.GetUserById(info.UserId);
-
- appVersion = string.IsNullOrWhiteSpace(appVersion)
- ? info.AppVersion
- : appVersion;
-
- var deviceName = info.DeviceName;
- var appName = info.AppName;
-
- if (!string.IsNullOrWhiteSpace(deviceId))
- {
- // Replace the info from the token with more recent info
- var device = _deviceManager.GetDevice(deviceId);
- if (device != null)
- {
- deviceName = device.Name;
- appName = device.AppName;
-
- if (!string.IsNullOrWhiteSpace(device.AppVersion))
- {
- appVersion = device.AppVersion;
- }
- }
- }
- else
- {
- deviceId = info.DeviceId;
- }
-
- // Prevent argument exception
- if (string.IsNullOrWhiteSpace(appVersion))
- {
- appVersion = "1";
- }
-
- return LogSessionActivity(appName, appVersion, deviceId, deviceName, remoteEndpoint, user);
- }
-
- public Task<SessionInfo> GetSessionByAuthenticationToken(string token, string deviceId, string remoteEndpoint)
- {
- var result = _authRepo.Get(new AuthenticationInfoQuery
- {
- AccessToken = token
- });
-
- var info = result.Items.FirstOrDefault();
-
- if (info == null)
- {
- return Task.FromResult<SessionInfo>(null);
- }
-
- return GetSessionByAuthenticationToken(info, deviceId, remoteEndpoint, null);
- }
-
- public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken)
- {
- var adminUserIds = _userManager.Users.Where(i => i.Policy.IsAdministrator).Select(i => i.Id.ToString("N")).ToList();
-
- return SendMessageToUserSessions(adminUserIds, name, data, cancellationToken);
- }
-
- public Task SendMessageToUserSessions<T>(List<string> userIds, string name, T data,
- CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && userIds.Any(i.ContainsUser)).ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendMessage(name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending message", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data,
- CancellationToken cancellationToken)
- {
- var sessions = Sessions.Where(i => i.IsActive && i.SessionController != null && string.Equals(i.DeviceId, deviceId, StringComparison.OrdinalIgnoreCase)).ToList();
-
- var tasks = sessions.Select(session => Task.Run(async () =>
- {
- try
- {
- await session.SessionController.SendMessage(name, data, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending message", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs b/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
deleted file mode 100644
index 9d30135a7..000000000
--- a/MediaBrowser.Server.Implementations/Session/SessionWebSocketListener.cs
+++ /dev/null
@@ -1,485 +0,0 @@
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Session;
-using System;
-using System.Collections.Specialized;
-using System.Globalization;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Server.Implementations.Session
-{
- /// <summary>
- /// Class SessionWebSocketListener
- /// </summary>
- public class SessionWebSocketListener : IWebSocketListener, IDisposable
- {
- /// <summary>
- /// The _true task result
- /// </summary>
- private readonly Task _trueTaskResult = Task.FromResult(true);
-
- /// <summary>
- /// The _session manager
- /// </summary>
- private readonly ISessionManager _sessionManager;
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// The _dto service
- /// </summary>
- private readonly IJsonSerializer _json;
-
- private readonly IHttpServer _httpServer;
- private readonly IServerManager _serverManager;
-
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
- /// </summary>
- /// <param name="sessionManager">The session manager.</param>
- /// <param name="logManager">The log manager.</param>
- /// <param name="json">The json.</param>
- /// <param name="httpServer">The HTTP server.</param>
- /// <param name="serverManager">The server manager.</param>
- public SessionWebSocketListener(ISessionManager sessionManager, ILogManager logManager, IJsonSerializer json, IHttpServer httpServer, IServerManager serverManager)
- {
- _sessionManager = sessionManager;
- _logger = logManager.GetLogger(GetType().Name);
- _json = json;
- _httpServer = httpServer;
- _serverManager = serverManager;
- httpServer.WebSocketConnecting += _httpServer_WebSocketConnecting;
- serverManager.WebSocketConnected += _serverManager_WebSocketConnected;
- }
-
- async void _serverManager_WebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e)
- {
- var session = await GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint).ConfigureAwait(false);
-
- if (session != null)
- {
- var controller = session.SessionController as WebSocketController;
-
- if (controller == null)
- {
- controller = new WebSocketController(session, _logger, _sessionManager);
- }
-
- controller.AddWebSocket(e.Argument);
-
- session.SessionController = controller;
- }
- else
- {
- _logger.Warn("Unable to determine session based on url: {0}", e.Argument.Url);
- }
- }
-
- async void _httpServer_WebSocketConnecting(object sender, WebSocketConnectingEventArgs e)
- {
- //var token = e.QueryString["api_key"];
- //if (!string.IsNullOrWhiteSpace(token))
- //{
- // try
- // {
- // var session = await GetSession(e.QueryString, e.Endpoint).ConfigureAwait(false);
-
- // if (session == null)
- // {
- // e.AllowConnection = false;
- // }
- // }
- // catch (Exception ex)
- // {
- // _logger.ErrorException("Error getting session info", ex);
- // }
- //}
- }
-
- private Task<SessionInfo> GetSession(QueryParamCollection queryString, string remoteEndpoint)
- {
- if (queryString == null)
- {
- throw new ArgumentNullException("queryString");
- }
-
- var token = queryString["api_key"];
- if (string.IsNullOrWhiteSpace(token))
- {
- return Task.FromResult<SessionInfo>(null);
- }
- var deviceId = queryString["deviceId"];
- return _sessionManager.GetSessionByAuthenticationToken(token, deviceId, remoteEndpoint);
- }
-
- public void Dispose()
- {
- _httpServer.WebSocketConnecting -= _httpServer_WebSocketConnecting;
- _serverManager.WebSocketConnected -= _serverManager_WebSocketConnected;
- }
-
- /// <summary>
- /// Processes the message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>Task.</returns>
- public Task ProcessMessage(WebSocketMessageInfo message)
- {
- if (string.Equals(message.MessageType, "Identity", StringComparison.OrdinalIgnoreCase))
- {
- ProcessIdentityMessage(message);
- }
- else if (string.Equals(message.MessageType, "Context", StringComparison.OrdinalIgnoreCase))
- {
- ProcessContextMessage(message);
- }
- else if (string.Equals(message.MessageType, "PlaybackStart", StringComparison.OrdinalIgnoreCase))
- {
- OnPlaybackStart(message);
- }
- else if (string.Equals(message.MessageType, "PlaybackProgress", StringComparison.OrdinalIgnoreCase))
- {
- OnPlaybackProgress(message);
- }
- else if (string.Equals(message.MessageType, "PlaybackStopped", StringComparison.OrdinalIgnoreCase))
- {
- OnPlaybackStopped(message);
- }
- else if (string.Equals(message.MessageType, "ReportPlaybackStart", StringComparison.OrdinalIgnoreCase))
- {
- ReportPlaybackStart(message);
- }
- else if (string.Equals(message.MessageType, "ReportPlaybackProgress", StringComparison.OrdinalIgnoreCase))
- {
- ReportPlaybackProgress(message);
- }
- else if (string.Equals(message.MessageType, "ReportPlaybackStopped", StringComparison.OrdinalIgnoreCase))
- {
- ReportPlaybackStopped(message);
- }
-
- return _trueTaskResult;
- }
-
- /// <summary>
- /// Processes the identity message.
- /// </summary>
- /// <param name="message">The message.</param>
- private async void ProcessIdentityMessage(WebSocketMessageInfo message)
- {
- _logger.Debug("Received Identity message: " + message.Data);
-
- var vals = message.Data.Split('|');
-
- if (vals.Length < 3)
- {
- _logger.Error("Client sent invalid identity message.");
- return;
- }
-
- var client = vals[0];
- var deviceId = vals[1];
- var version = vals[2];
- var deviceName = vals.Length > 3 ? vals[3] : string.Empty;
-
- var session = _sessionManager.GetSession(deviceId, client, version);
-
- if (session == null && !string.IsNullOrEmpty(deviceName))
- {
- _logger.Debug("Logging session activity");
-
- session = await _sessionManager.LogSessionActivity(client, version, deviceId, deviceName, message.Connection.RemoteEndPoint, null).ConfigureAwait(false);
- }
-
- if (session != null)
- {
- var controller = session.SessionController as WebSocketController;
-
- if (controller == null)
- {
- controller = new WebSocketController(session, _logger, _sessionManager);
- }
-
- controller.AddWebSocket(message.Connection);
-
- session.SessionController = controller;
- }
- else
- {
- _logger.Warn("Unable to determine session based on identity message: {0}", message.Data);
- }
- }
-
- /// <summary>
- /// Processes the context message.
- /// </summary>
- /// <param name="message">The message.</param>
- private void ProcessContextMessage(WebSocketMessageInfo message)
- {
- var session = GetSessionFromMessage(message);
-
- if (session != null)
- {
- var vals = message.Data.Split('|');
-
- var itemId = vals[1];
-
- if (!string.IsNullOrWhiteSpace(itemId))
- {
- _sessionManager.ReportNowViewingItem(session.Id, itemId);
- }
- }
- }
-
- /// <summary>
- /// Gets the session from message.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>SessionInfo.</returns>
- private SessionInfo GetSessionFromMessage(WebSocketMessageInfo message)
- {
- var result = _sessionManager.Sessions.FirstOrDefault(i =>
- {
- var controller = i.SessionController as WebSocketController;
-
- if (controller != null)
- {
- if (controller.Sockets.Any(s => s.Id == message.Connection.Id))
- {
- return true;
- }
- }
-
- return false;
-
- });
-
- if (result == null)
- {
- _logger.Error("Unable to find session based on web socket message");
- }
-
- return result;
- }
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- /// <summary>
- /// Reports the playback start.
- /// </summary>
- /// <param name="message">The message.</param>
- private void OnPlaybackStart(WebSocketMessageInfo message)
- {
- _logger.Debug("Received PlaybackStart message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var vals = message.Data.Split('|');
-
- var itemId = vals[0];
-
- var queueableMediaTypes = string.Empty;
- var canSeek = true;
-
- if (vals.Length > 1)
- {
- canSeek = string.Equals(vals[1], "true", StringComparison.OrdinalIgnoreCase);
- }
- if (vals.Length > 2)
- {
- queueableMediaTypes = vals[2];
- }
-
- var info = new PlaybackStartInfo
- {
- CanSeek = canSeek,
- ItemId = itemId,
- SessionId = session.Id,
- QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
- };
-
- if (vals.Length > 3)
- {
- info.MediaSourceId = vals[3];
- }
-
- if (vals.Length > 4 && !string.IsNullOrWhiteSpace(vals[4]))
- {
- info.AudioStreamIndex = int.Parse(vals[4], _usCulture);
- }
-
- if (vals.Length > 5 && !string.IsNullOrWhiteSpace(vals[5]))
- {
- info.SubtitleStreamIndex = int.Parse(vals[5], _usCulture);
- }
-
- _sessionManager.OnPlaybackStart(info);
- }
- }
-
- private void ReportPlaybackStart(WebSocketMessageInfo message)
- {
- _logger.Debug("Received ReportPlaybackStart message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var info = _json.DeserializeFromString<PlaybackStartInfo>(message.Data);
-
- info.SessionId = session.Id;
-
- _sessionManager.OnPlaybackStart(info);
- }
- }
-
- private void ReportPlaybackProgress(WebSocketMessageInfo message)
- {
- //_logger.Debug("Received ReportPlaybackProgress message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var info = _json.DeserializeFromString<PlaybackProgressInfo>(message.Data);
-
- info.SessionId = session.Id;
-
- _sessionManager.OnPlaybackProgress(info);
- }
- }
-
- /// <summary>
- /// Reports the playback progress.
- /// </summary>
- /// <param name="message">The message.</param>
- private void OnPlaybackProgress(WebSocketMessageInfo message)
- {
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var vals = message.Data.Split('|');
-
- var itemId = vals[0];
-
- long? positionTicks = null;
-
- if (vals.Length > 1)
- {
- long pos;
-
- if (long.TryParse(vals[1], out pos))
- {
- positionTicks = pos;
- }
- }
-
- var isPaused = vals.Length > 2 && string.Equals(vals[2], "true", StringComparison.OrdinalIgnoreCase);
- var isMuted = vals.Length > 3 && string.Equals(vals[3], "true", StringComparison.OrdinalIgnoreCase);
-
- var info = new PlaybackProgressInfo
- {
- ItemId = itemId,
- PositionTicks = positionTicks,
- IsMuted = isMuted,
- IsPaused = isPaused,
- SessionId = session.Id
- };
-
- if (vals.Length > 4)
- {
- info.MediaSourceId = vals[4];
- }
-
- if (vals.Length > 5 && !string.IsNullOrWhiteSpace(vals[5]))
- {
- info.VolumeLevel = int.Parse(vals[5], _usCulture);
- }
-
- if (vals.Length > 5 && !string.IsNullOrWhiteSpace(vals[6]))
- {
- info.AudioStreamIndex = int.Parse(vals[6], _usCulture);
- }
-
- if (vals.Length > 7 && !string.IsNullOrWhiteSpace(vals[7]))
- {
- info.SubtitleStreamIndex = int.Parse(vals[7], _usCulture);
- }
-
- _sessionManager.OnPlaybackProgress(info);
- }
- }
-
- private void ReportPlaybackStopped(WebSocketMessageInfo message)
- {
- _logger.Debug("Received ReportPlaybackStopped message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var info = _json.DeserializeFromString<PlaybackStopInfo>(message.Data);
-
- info.SessionId = session.Id;
-
- _sessionManager.OnPlaybackStopped(info);
- }
- }
-
- /// <summary>
- /// Reports the playback stopped.
- /// </summary>
- /// <param name="message">The message.</param>
- private void OnPlaybackStopped(WebSocketMessageInfo message)
- {
- _logger.Debug("Received PlaybackStopped message");
-
- var session = GetSessionFromMessage(message);
-
- if (session != null && session.UserId.HasValue)
- {
- var vals = message.Data.Split('|');
-
- var itemId = vals[0];
-
- long? positionTicks = null;
-
- if (vals.Length > 1)
- {
- long pos;
-
- if (long.TryParse(vals[1], out pos))
- {
- positionTicks = pos;
- }
- }
-
- var info = new PlaybackStopInfo
- {
- ItemId = itemId,
- PositionTicks = positionTicks,
- SessionId = session.Id
- };
-
- if (vals.Length > 2)
- {
- info.MediaSourceId = vals[2];
- }
-
- _sessionManager.OnPlaybackStopped(info);
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs b/MediaBrowser.Server.Implementations/Session/WebSocketController.cs
deleted file mode 100644
index 765664299..000000000
--- a/MediaBrowser.Server.Implementations/Session/WebSocketController.cs
+++ /dev/null
@@ -1,288 +0,0 @@
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Model.System;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Session
-{
- public class WebSocketController : ISessionController, IDisposable
- {
- public SessionInfo Session { get; private set; }
- public IReadOnlyList<IWebSocketConnection> Sockets { get; private set; }
-
- private readonly ILogger _logger;
-
- private readonly ISessionManager _sessionManager;
-
- public WebSocketController(SessionInfo session, ILogger logger, ISessionManager sessionManager)
- {
- Session = session;
- _logger = logger;
- _sessionManager = sessionManager;
- Sockets = new List<IWebSocketConnection>();
- }
-
- private bool HasOpenSockets
- {
- get { return GetActiveSockets().Any(); }
- }
-
- public bool SupportsMediaControl
- {
- get { return HasOpenSockets; }
- }
-
- private bool _isActive;
- private DateTime _lastActivityDate;
- public bool IsSessionActive
- {
- get
- {
- if (HasOpenSockets)
- {
- return true;
- }
-
- //return false;
- return _isActive && (DateTime.UtcNow - _lastActivityDate).TotalMinutes <= 10;
- }
- }
-
- public void OnActivity()
- {
- _isActive = true;
- _lastActivityDate = DateTime.UtcNow;
- }
-
- private IEnumerable<IWebSocketConnection> GetActiveSockets()
- {
- return Sockets
- .OrderByDescending(i => i.LastActivityDate)
- .Where(i => i.State == WebSocketState.Open);
- }
-
- public void AddWebSocket(IWebSocketConnection connection)
- {
- var sockets = Sockets.ToList();
- sockets.Add(connection);
-
- Sockets = sockets;
-
- connection.Closed += connection_Closed;
- }
-
- void connection_Closed(object sender, EventArgs e)
- {
- if (!GetActiveSockets().Any())
- {
- _isActive = false;
-
- try
- {
- _sessionManager.ReportSessionEnded(Session.Id);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error reporting session ended.", ex);
- }
- }
- }
-
- private IWebSocketConnection GetActiveSocket()
- {
- var socket = GetActiveSockets()
- .FirstOrDefault();
-
- if (socket == null)
- {
- throw new InvalidOperationException("The requested session does not have an open web socket.");
- }
-
- return socket;
- }
-
- public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
- {
- return SendMessageInternal(new WebSocketMessage<PlayRequest>
- {
- MessageType = "Play",
- Data = command
-
- }, cancellationToken);
- }
-
- public Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
- {
- return SendMessageInternal(new WebSocketMessage<PlaystateRequest>
- {
- MessageType = "Playstate",
- Data = command
-
- }, cancellationToken);
- }
-
- public Task SendLibraryUpdateInfo(LibraryUpdateInfo info, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<LibraryUpdateInfo>
- {
- MessageType = "LibraryChanged",
- Data = info
-
- }, cancellationToken);
- }
-
- /// <summary>
- /// Sends the restart required message.
- /// </summary>
- /// <param name="info">The information.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendRestartRequiredNotification(SystemInfo info, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<SystemInfo>
- {
- MessageType = "RestartRequired",
- Data = info
-
- }, cancellationToken);
- }
-
-
- /// <summary>
- /// Sends the user data change info.
- /// </summary>
- /// <param name="info">The info.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendUserDataChangeInfo(UserDataChangeInfo info, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<UserDataChangeInfo>
- {
- MessageType = "UserDataChanged",
- Data = info
-
- }, cancellationToken);
- }
-
- /// <summary>
- /// Sends the server shutdown notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendServerShutdownNotification(CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<string>
- {
- MessageType = "ServerShuttingDown",
- Data = string.Empty
-
- }, cancellationToken);
- }
-
- /// <summary>
- /// Sends the server restart notification.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendServerRestartNotification(CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<string>
- {
- MessageType = "ServerRestarting",
- Data = string.Empty
-
- }, cancellationToken);
- }
-
- public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
- {
- return SendMessageInternal(new WebSocketMessage<GeneralCommand>
- {
- MessageType = "GeneralCommand",
- Data = command
-
- }, cancellationToken);
- }
-
- public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<SessionInfoDto>
- {
- MessageType = "SessionEnded",
- Data = sessionInfo
-
- }, cancellationToken);
- }
-
- public Task SendPlaybackStartNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<SessionInfoDto>
- {
- MessageType = "PlaybackStart",
- Data = sessionInfo
-
- }, cancellationToken);
- }
-
- public Task SendPlaybackStoppedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<SessionInfoDto>
- {
- MessageType = "PlaybackStopped",
- Data = sessionInfo
-
- }, cancellationToken);
- }
-
- public Task SendMessage<T>(string name, T data, CancellationToken cancellationToken)
- {
- return SendMessagesInternal(new WebSocketMessage<T>
- {
- Data = data,
- MessageType = name
-
- }, cancellationToken);
- }
-
- private Task SendMessageInternal<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
- {
- var socket = GetActiveSocket();
-
- return socket.SendAsync(message, cancellationToken);
- }
-
- private Task SendMessagesInternal<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
- {
- var tasks = GetActiveSockets().Select(i => Task.Run(async () =>
- {
- try
- {
- await i.SendAsync(message, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending web socket message", ex);
- }
-
- }, cancellationToken));
-
- return Task.WhenAll(tasks);
- }
-
- public void Dispose()
- {
- foreach (var socket in Sockets.ToList())
- {
- socket.Closed -= connection_Closed;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs
deleted file mode 100644
index 7e6a252cd..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/AirTimeComparer.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class AirTimeComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return DateTime.Compare(GetValue(x), GetValue(y));
- }
-
- /// <summary>
- /// Gets the value.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>System.String.</returns>
- private DateTime GetValue(BaseItem x)
- {
- var series = x as Series;
-
- if (series == null)
- {
- var season = x as Season;
-
- if (season != null)
- {
- series = season.Series;
- }
- else
- {
- var episode = x as Episode;
-
- if (episode != null)
- {
- series = episode.Series;
- }
- }
- }
-
- if (series != null)
- {
- DateTime result;
- if (DateTime.TryParse(series.AirTime, out result))
- {
- return result;
- }
- }
-
- return DateTime.MinValue;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.AirTime; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
deleted file mode 100644
index 91abbe34c..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- class AiredEpisodeOrderComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- if (x.PremiereDate.HasValue && y.PremiereDate.HasValue)
- {
- var val = DateTime.Compare(x.PremiereDate.Value, y.PremiereDate.Value);
-
- if (val != 0)
- {
- //return val;
- }
- }
-
- var episode1 = x as Episode;
- var episode2 = y as Episode;
-
- if (episode1 == null)
- {
- if (episode2 == null)
- {
- return 0;
- }
-
- return 1;
- }
-
- if (episode2 == null)
- {
- return -1;
- }
-
- return Compare(episode1, episode2);
- }
-
- private int Compare(Episode x, Episode y)
- {
- var isXSpecial = (x.ParentIndexNumber ?? -1) == 0;
- var isYSpecial = (y.ParentIndexNumber ?? -1) == 0;
-
- if (isXSpecial && isYSpecial)
- {
- return CompareSpecials(x, y);
- }
-
- if (!isXSpecial && !isYSpecial)
- {
- return CompareEpisodes(x, y);
- }
-
- if (!isXSpecial)
- {
- return CompareEpisodeToSpecial(x, y);
- }
-
- return CompareEpisodeToSpecial(y, x) * -1;
- }
-
- private int CompareEpisodeToSpecial(Episode x, Episode y)
- {
- // http://thetvdb.com/wiki/index.php?title=Special_Episodes
-
- var xSeason = x.ParentIndexNumber ?? -1;
- var ySeason = y.AirsAfterSeasonNumber ?? y.AirsBeforeSeasonNumber ?? -1;
-
- if (xSeason != ySeason)
- {
- return xSeason.CompareTo(ySeason);
- }
-
- // Special comes after episode
- if (y.AirsAfterSeasonNumber.HasValue)
- {
- return -1;
- }
-
- var yEpisode = y.AirsBeforeEpisodeNumber;
-
- // Special comes before the season
- if (!yEpisode.HasValue)
- {
- return 1;
- }
-
- // Compare episode number
- var xEpisode = x.IndexNumber;
-
- if (!xEpisode.HasValue)
- {
- // Can't really compare if this happens
- return 0;
- }
-
- // Special comes before episode
- if (xEpisode.Value == yEpisode.Value)
- {
- return 1;
- }
-
- return xEpisode.Value.CompareTo(yEpisode.Value);
- }
-
- private int CompareSpecials(Episode x, Episode y)
- {
- return GetSpecialCompareValue(x).CompareTo(GetSpecialCompareValue(y));
- }
-
- private int GetSpecialCompareValue(Episode item)
- {
- // First sort by season number
- // Since there are three sort orders, pad with 9 digits (3 for each, figure 1000 episode buffer should be enough)
- var val = (item.AirsAfterSeasonNumber ?? item.AirsBeforeSeasonNumber ?? 0) * 1000000000;
-
- // Second sort order is if it airs after the season
- if (item.AirsAfterSeasonNumber.HasValue)
- {
- val += 1000000;
- }
-
- // Third level is the episode number
- val += (item.AirsBeforeEpisodeNumber ?? 0) * 1000;
-
- // Finally, if that's still the same, last resort is the special number itself
- val += item.IndexNumber ?? 0;
-
- return val;
- }
-
- private int CompareEpisodes(Episode x, Episode y)
- {
- var xValue = (x.ParentIndexNumber ?? -1) * 1000 + (x.IndexNumber ?? -1);
- var yValue = (y.ParentIndexNumber ?? -1) * 1000 + (y.IndexNumber ?? -1);
-
- return xValue.CompareTo(yValue);
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.AiredEpisodeOrder; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs
deleted file mode 100644
index 3c79b0c32..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/AlbumArtistComparer.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System.Linq;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class AlbumArtistComparer
- /// </summary>
- public class AlbumArtistComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
- }
-
- /// <summary>
- /// Gets the value.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>System.String.</returns>
- private string GetValue(BaseItem x)
- {
- var audio = x as IHasAlbumArtist;
-
- return audio != null ? audio.AlbumArtists.FirstOrDefault() : null;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.AlbumArtist; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs b/MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs
deleted file mode 100644
index f455d5c2b..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/AlbumComparer.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class AlbumComparer
- /// </summary>
- public class AlbumComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
- }
-
- /// <summary>
- /// Gets the value.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>System.String.</returns>
- private string GetValue(BaseItem x)
- {
- var audio = x as Audio;
-
- return audio == null ? string.Empty : audio.Album;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Album; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs b/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs
deleted file mode 100644
index 232bdb3b5..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/AlphanumComparator.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using System.Collections.Generic;
-using System.Text;
-using MediaBrowser.Controller.Sorting;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class AlphanumComparator : IComparer<string>
- {
- public static int CompareValues(string s1, string s2)
- {
- if (s1 == null || s2 == null)
- {
- return 0;
- }
-
- int thisMarker = 0, thisNumericChunk = 0;
- int thatMarker = 0, thatNumericChunk = 0;
-
- while ((thisMarker < s1.Length) || (thatMarker < s2.Length))
- {
- if (thisMarker >= s1.Length)
- {
- return -1;
- }
- else if (thatMarker >= s2.Length)
- {
- return 1;
- }
- char thisCh = s1[thisMarker];
- char thatCh = s2[thatMarker];
-
- StringBuilder thisChunk = new StringBuilder();
- StringBuilder thatChunk = new StringBuilder();
-
- while ((thisMarker < s1.Length) && (thisChunk.Length == 0 || SortHelper.InChunk(thisCh, thisChunk[0])))
- {
- thisChunk.Append(thisCh);
- thisMarker++;
-
- if (thisMarker < s1.Length)
- {
- thisCh = s1[thisMarker];
- }
- }
-
- while ((thatMarker < s2.Length) && (thatChunk.Length == 0 || SortHelper.InChunk(thatCh, thatChunk[0])))
- {
- thatChunk.Append(thatCh);
- thatMarker++;
-
- if (thatMarker < s2.Length)
- {
- thatCh = s2[thatMarker];
- }
- }
-
- int result = 0;
- // If both chunks contain numeric characters, sort them numerically
- if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
- {
- if (!int.TryParse(thisChunk.ToString(), out thisNumericChunk))
- {
- return 0;
- }
- if (!int.TryParse(thatChunk.ToString(), out thatNumericChunk))
- {
- return 0;
- }
-
- if (thisNumericChunk < thatNumericChunk)
- {
- result = -1;
- }
-
- if (thisNumericChunk > thatNumericChunk)
- {
- result = 1;
- }
- }
- else
- {
- result = thisChunk.ToString().CompareTo(thatChunk.ToString());
- }
-
- if (result != 0)
- {
- return result;
- }
- }
-
- return 0;
- }
-
- public int Compare(string x, string y)
- {
- return CompareValues(x, y);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs b/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs
deleted file mode 100644
index 9ff8a5ace..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class ArtistComparer
- /// </summary>
- public class ArtistComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
- }
-
- /// <summary>
- /// Gets the value.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>System.String.</returns>
- private string GetValue(BaseItem x)
- {
- var audio = x as Audio;
-
- if (audio == null)
- {
- return string.Empty;
- }
-
- return audio.Artists.Count == 0 ? null : audio.Artists[0];
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Artist; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs b/MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs
deleted file mode 100644
index 87a7325c6..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/BudgetComparer.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class BudgetComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- private double GetValue(BaseItem x)
- {
- var hasBudget = x as IHasBudget;
- if (hasBudget != null)
- {
- return hasBudget.Budget ?? 0;
- }
- return 0;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Budget; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs b/MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs
deleted file mode 100644
index bdd18a648..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/CommunityRatingComparer.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class CommunityRatingComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return (x.CommunityRating ?? 0).CompareTo(y.CommunityRating ?? 0);
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.CommunityRating; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs b/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs
deleted file mode 100644
index 9484130cb..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class CriticRatingComparer
- /// </summary>
- public class CriticRatingComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- private float GetValue(BaseItem x)
- {
- return x.CriticRating ?? 0;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.CriticRating; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs
deleted file mode 100644
index 9862f0a8a..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/DateCreatedComparer.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class DateCreatedComparer
- /// </summary>
- public class DateCreatedComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return DateTime.Compare(x.DateCreated, y.DateCreated);
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.DateCreated; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
deleted file mode 100644
index 5080edffd..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/DateLastMediaAddedComparer.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class DateLastMediaAddedComparer : IUserBaseItemComparer
- {
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- /// <value>The user.</value>
- public User User { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- public IUserDataManager UserDataRepository { get; set; }
-
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetDate(x).CompareTo(GetDate(y));
- }
-
- /// <summary>
- /// Gets the date.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>DateTime.</returns>
- private DateTime GetDate(BaseItem x)
- {
- var folder = x as Folder;
-
- if (folder != null)
- {
- if (folder.DateLastMediaAdded.HasValue)
- {
- return folder.DateLastMediaAdded.Value;
- }
- }
-
- return DateTime.MinValue;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.DateLastContentAdded; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs
deleted file mode 100644
index 3edf23020..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class DatePlayedComparer
- /// </summary>
- public class DatePlayedComparer : IUserBaseItemComparer
- {
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- /// <value>The user.</value>
- public User User { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- public IUserDataManager UserDataRepository { get; set; }
-
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetDate(x).CompareTo(GetDate(y));
- }
-
- /// <summary>
- /// Gets the date.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>DateTime.</returns>
- private DateTime GetDate(BaseItem x)
- {
- var userdata = UserDataRepository.GetUserData(User, x);
-
- if (userdata != null && userdata.LastPlayedDate.HasValue)
- {
- return userdata.LastPlayedDate.Value;
- }
-
- return DateTime.MinValue;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.DatePlayed; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs b/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs
deleted file mode 100644
index eb83b98e9..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class GameSystemComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
- }
-
- /// <summary>
- /// Gets the value.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>System.String.</returns>
- private string GetValue(BaseItem x)
- {
- var game = x as Game;
-
- if (game != null)
- {
- return game.GameSystem;
- }
-
- var system = x as GameSystem;
-
- if (system != null)
- {
- return system.GameSystemName;
- }
-
- return string.Empty;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.GameSystem; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs b/MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
deleted file mode 100644
index 658708dba..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/IsFavoriteOrLikeComparer.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class IsFavoriteOrLikeComparer : IUserBaseItemComparer
- {
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- /// <value>The user.</value>
- public User User { get; set; }
-
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- /// <summary>
- /// Gets the date.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>DateTime.</returns>
- private int GetValue(BaseItem x)
- {
- return x.IsFavoriteOrLiked(User) ? 0 : 1;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.IsFavoriteOrLiked; }
- }
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- public IUserDataManager UserDataRepository { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs b/MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs
deleted file mode 100644
index d2341d065..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/IsFolderComparer.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class IsFolderComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- /// <summary>
- /// Gets the value.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>System.String.</returns>
- private int GetValue(BaseItem x)
- {
- return x.IsFolder ? 0 : 1;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.IsFolder; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs
deleted file mode 100644
index aebfbdb1c..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/IsPlayedComparer.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class IsPlayedComparer : IUserBaseItemComparer
- {
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- /// <value>The user.</value>
- public User User { get; set; }
-
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- /// <summary>
- /// Gets the date.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>DateTime.</returns>
- private int GetValue(BaseItem x)
- {
- return x.IsPlayed(User) ? 0 : 1;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.IsUnplayed; }
- }
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- public IUserDataManager UserDataRepository { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs
deleted file mode 100644
index f1c6a5a4e..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/IsUnplayedComparer.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class IsUnplayedComparer : IUserBaseItemComparer
- {
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- /// <value>The user.</value>
- public User User { get; set; }
-
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- /// <summary>
- /// Gets the date.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>DateTime.</returns>
- private int GetValue(BaseItem x)
- {
- return x.IsUnplayed(User) ? 0 : 1;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.IsUnplayed; }
- }
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- public IUserDataManager UserDataRepository { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs b/MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs
deleted file mode 100644
index bfd162661..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/MetascoreComparer.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class MetascoreComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- private float GetValue(BaseItem x)
- {
- var hasMetascore = x as IHasMetascore;
-
- if (hasMetascore != null)
- {
- return hasMetascore.Metascore ?? 0;
- }
-
- return 0;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Metascore; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/NameComparer.cs b/MediaBrowser.Server.Implementations/Sorting/NameComparer.cs
deleted file mode 100644
index 49f86c485..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/NameComparer.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class NameComparer
- /// </summary>
- public class NameComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return string.Compare(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase);
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Name; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs b/MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs
deleted file mode 100644
index 3b1939b70..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/OfficialRatingComparer.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class OfficialRatingComparer : IBaseItemComparer
- {
- private readonly ILocalizationManager _localization;
-
- public OfficialRatingComparer(ILocalizationManager localization)
- {
- _localization = localization;
- }
-
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- var levelX = string.IsNullOrEmpty(x.OfficialRating) ? 0 : _localization.GetRatingLevel(x.OfficialRating) ?? 0;
- var levelY = string.IsNullOrEmpty(y.OfficialRating) ? 0 : _localization.GetRatingLevel(y.OfficialRating) ?? 0;
-
- return levelX.CompareTo(levelY);
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.OfficialRating; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs
deleted file mode 100644
index 8b14efffc..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class PlayCountComparer
- /// </summary>
- public class PlayCountComparer : IUserBaseItemComparer
- {
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- /// <value>The user.</value>
- public User User { get; set; }
-
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- /// <summary>
- /// Gets the date.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>DateTime.</returns>
- private int GetValue(BaseItem x)
- {
- var userdata = UserDataRepository.GetUserData(User, x);
-
- return userdata == null ? 0 : userdata.PlayCount;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.PlayCount; }
- }
-
- /// <summary>
- /// Gets or sets the user data repository.
- /// </summary>
- /// <value>The user data repository.</value>
- public IUserDataManager UserDataRepository { get; set; }
-
- /// <summary>
- /// Gets or sets the user manager.
- /// </summary>
- /// <value>The user manager.</value>
- public IUserManager UserManager { get; set; }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs
deleted file mode 100644
index 5bcd080d7..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class PlayersComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- /// <summary>
- /// Gets the value.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>System.String.</returns>
- private int GetValue(BaseItem x)
- {
- var game = x as Game;
-
- if (game != null)
- {
- return game.PlayersSupported ?? 0;
- }
-
- return 0;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Players; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs
deleted file mode 100644
index ffe1fc24a..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/PremiereDateComparer.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class PremiereDateComparer
- /// </summary>
- public class PremiereDateComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetDate(x).CompareTo(GetDate(y));
- }
-
- /// <summary>
- /// Gets the date.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>DateTime.</returns>
- private DateTime GetDate(BaseItem x)
- {
- if (x.PremiereDate.HasValue)
- {
- return x.PremiereDate.Value;
- }
-
- if (x.ProductionYear.HasValue)
- {
- try
- {
- return new DateTime(x.ProductionYear.Value, 1, 1, 0, 0, 0, DateTimeKind.Utc);
- }
- catch (ArgumentOutOfRangeException)
- {
- // Don't blow up if the item has a bad ProductionYear, just return MinValue
- }
- }
- return DateTime.MinValue;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.PremiereDate; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs b/MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs
deleted file mode 100644
index 16d531334..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/ProductionYearComparer.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class ProductionYearComparer
- /// </summary>
- public class ProductionYearComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- /// <summary>
- /// Gets the date.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>DateTime.</returns>
- private int GetValue(BaseItem x)
- {
- if (x.ProductionYear.HasValue)
- {
- return x.ProductionYear.Value;
- }
-
- if (x.PremiereDate.HasValue)
- {
- return x.PremiereDate.Value.Year;
- }
-
- return 0;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.ProductionYear; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs b/MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs
deleted file mode 100644
index b1677331a..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/RandomComparer.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class RandomComparer
- /// </summary>
- public class RandomComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return Guid.NewGuid().CompareTo(Guid.NewGuid());
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Random; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs b/MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs
deleted file mode 100644
index 6caa27ac3..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/RevenueComparer.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class RevenueComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetValue(x).CompareTo(GetValue(y));
- }
-
- private double GetValue(BaseItem x)
- {
- var hasBudget = x as IHasBudget;
- if (hasBudget != null)
- {
- return hasBudget.Revenue ?? 0;
- }
- return 0;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Revenue; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs b/MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs
deleted file mode 100644
index 793cb265e..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/RuntimeComparer.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class RuntimeComparer
- /// </summary>
- public class RuntimeComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return (x.RunTimeTicks ?? 0).CompareTo(y.RunTimeTicks ?? 0);
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Runtime; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs
deleted file mode 100644
index 6bc1264a4..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/SeriesSortNameComparer.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- class SeriesSortNameComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
- }
-
- private string GetValue(BaseItem item)
- {
- var hasSeries = item as IHasSeries;
-
- return hasSeries != null ? hasSeries.SeriesSortName : null;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.SeriesSortName; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs b/MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs
deleted file mode 100644
index 873753a2b..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/SortNameComparer.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- /// <summary>
- /// Class SortNameComparer
- /// </summary>
- public class SortNameComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return string.Compare(x.SortName, y.SortName, StringComparison.CurrentCultureIgnoreCase);
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.SortName; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs b/MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs
deleted file mode 100644
index 7e6f24ec1..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/StartDateComparer.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class StartDateComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return GetDate(x).CompareTo(GetDate(y));
- }
-
- /// <summary>
- /// Gets the date.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>DateTime.</returns>
- private DateTime GetDate(BaseItem x)
- {
- var hasStartDate = x as LiveTvProgram;
-
- if (hasStartDate != null)
- {
- return hasStartDate.StartDate;
- }
- return DateTime.MinValue;
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.StartDate; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs b/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs
deleted file mode 100644
index 83ab4dfc2..000000000
--- a/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Querying;
-using System.Linq;
-
-namespace MediaBrowser.Server.Implementations.Sorting
-{
- public class StudioComparer : IBaseItemComparer
- {
- /// <summary>
- /// Compares the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <param name="y">The y.</param>
- /// <returns>System.Int32.</returns>
- public int Compare(BaseItem x, BaseItem y)
- {
- return AlphanumComparator.CompareValues(x.Studios.FirstOrDefault() ?? string.Empty, y.Studios.FirstOrDefault() ?? string.Empty);
- }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- /// <value>The name.</value>
- public string Name
- {
- get { return ItemSortBy.Studio; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs b/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
deleted file mode 100644
index 408ec717e..000000000
--- a/MediaBrowser.Server.Implementations/Sync/AppSyncProvider.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using MediaBrowser.Controller.Devices;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Devices;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class AppSyncProvider : ISyncProvider, IHasUniqueTargetIds, IHasSyncQuality, IHasDuplicateCheck
- {
- private readonly IDeviceManager _deviceManager;
-
- public AppSyncProvider(IDeviceManager deviceManager)
- {
- _deviceManager = deviceManager;
- }
-
- public IEnumerable<SyncTarget> GetSyncTargets(string userId)
- {
- return _deviceManager.GetDevices(new DeviceQuery
- {
- SupportsSync = true,
- UserId = userId
-
- }).Items.Select(i => new SyncTarget
- {
- Id = i.Id,
- Name = i.Name
- });
- }
-
- public DeviceProfile GetDeviceProfile(SyncTarget target, string profile, string quality)
- {
- var caps = _deviceManager.GetCapabilities(target.Id);
-
- var deviceProfile = caps == null || caps.DeviceProfile == null ? new DeviceProfile() : caps.DeviceProfile;
- deviceProfile.MaxStaticBitrate = SyncHelper.AdjustBitrate(deviceProfile.MaxStaticBitrate, quality);
-
- return deviceProfile;
- }
-
- public string Name
- {
- get { return "Mobile Sync"; }
- }
-
- public IEnumerable<SyncTarget> GetAllSyncTargets()
- {
- return _deviceManager.GetDevices(new DeviceQuery
- {
- SupportsSync = true
-
- }).Items.Select(i => new SyncTarget
- {
- Id = i.Id,
- Name = i.Name
- });
- }
-
- public IEnumerable<SyncQualityOption> GetQualityOptions(SyncTarget target)
- {
- return new List<SyncQualityOption>
- {
- new SyncQualityOption
- {
- Name = "Original",
- Id = "original",
- Description = "Syncs original files as-is, regardless of whether the device is capable of playing them or not."
- },
- new SyncQualityOption
- {
- Name = "High",
- Id = "high",
- IsDefault = true
- },
- new SyncQualityOption
- {
- Name = "Medium",
- Id = "medium"
- },
- new SyncQualityOption
- {
- Name = "Low",
- Id = "low"
- },
- new SyncQualityOption
- {
- Name = "Custom",
- Id = "custom"
- }
- };
- }
-
- public IEnumerable<SyncProfileOption> GetProfileOptions(SyncTarget target)
- {
- return new List<SyncProfileOption>();
- }
-
- public SyncJobOptions GetSyncJobOptions(SyncTarget target, string profile, string quality)
- {
- var isConverting = !string.Equals(quality, "original", StringComparison.OrdinalIgnoreCase);
-
- return new SyncJobOptions
- {
- DeviceProfile = GetDeviceProfile(target, profile, quality),
- IsConverting = isConverting
- };
- }
-
- public bool AllowDuplicateJobItem(SyncJobItem original, SyncJobItem duplicate)
- {
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
deleted file mode 100644
index f40b64498..000000000
--- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
+++ /dev/null
@@ -1,302 +0,0 @@
-using MediaBrowser.Model.Dlna;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class CloudSyncProfile : DeviceProfile
- {
- public CloudSyncProfile(bool supportsAc3, bool supportsDca)
- {
- Name = "Cloud Sync";
-
- MaxStreamingBitrate = 20000000;
- MaxStaticBitrate = 20000000;
-
- var mkvAudio = "aac,mp3";
- var mp4Audio = "aac";
-
- if (supportsAc3)
- {
- mkvAudio += ",ac3";
- mp4Audio += ",ac3";
- }
-
- if (supportsDca)
- {
- mkvAudio += ",dca,dts";
- }
-
- var videoProfile = "high|main|baseline|constrained baseline";
- var videoLevel = "40";
-
- DirectPlayProfiles = new[]
- {
- //new DirectPlayProfile
- //{
- // Container = "mkv",
- // VideoCodec = "h264,mpeg4",
- // AudioCodec = mkvAudio,
- // Type = DlnaProfileType.Video
- //},
- new DirectPlayProfile
- {
- Container = "mp4,mov,m4v",
- VideoCodec = "h264,mpeg4",
- AudioCodec = mp4Audio,
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3",
- Type = DlnaProfileType.Audio
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Video,
- Conditions = new []
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.NumAudioStreams,
- Value = "0",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.EqualsAny,
- Property = ProfileConditionValue.NumVideoStreams,
- Value = "1",
- IsRequired = false
- }
- }
- }
- };
-
- var codecProfiles = new List<CodecProfile>
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new []
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitDepth,
- Value = "8",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.RefFrames,
- Value = "4",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.IsAnamorphic,
- Value = "false",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = videoLevel,
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.EqualsAny,
- Property = ProfileConditionValue.VideoProfile,
- Value = videoProfile,
- IsRequired = false
- }
- }
- },
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "mpeg4",
- Conditions = new []
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitDepth,
- Value = "8",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.RefFrames,
- Value = "4",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.IsAnamorphic,
- Value = "false",
- IsRequired = false
- }
- }
- }
- };
-
- codecProfiles.Add(new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioBitrate,
- Value = "320000",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.IsSecondaryAudio,
- Value = "false",
- IsRequired = false
- }
- }
- });
- codecProfiles.Add(new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac,mp3",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioBitrate,
- Value = "320000",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.IsSecondaryAudio,
- Value = "false",
- IsRequired = false
- }
- }
- });
-
- CodecProfiles = codecProfiles.ToArray();
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.External
- },
- new SubtitleProfile
- {
- Format = "vtt",
- Method = SubtitleDeliveryMethod.External
- }
- };
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio,
- Context = EncodingContext.Static
- },
-
- new TranscodingProfile
- {
- Container = "mp4",
- Type = DlnaProfileType.Video,
- AudioCodec = "aac",
- VideoCodec = "h264",
- Context = EncodingContext.Static
- },
-
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo,
- Context = EncodingContext.Static
- }
- };
-
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/IHasSyncQuality.cs b/MediaBrowser.Server.Implementations/Sync/IHasSyncQuality.cs
deleted file mode 100644
index e7eee0923..000000000
--- a/MediaBrowser.Server.Implementations/Sync/IHasSyncQuality.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using MediaBrowser.Model.Sync;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public interface IHasSyncQuality
- {
- /// <summary>
- /// Gets the device profile.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <param name="profile">The profile.</param>
- /// <param name="quality">The quality.</param>
- /// <returns>DeviceProfile.</returns>
- SyncJobOptions GetSyncJobOptions(SyncTarget target, string profile, string quality);
-
- /// <summary>
- /// Gets the quality options.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <returns>IEnumerable&lt;SyncQualityOption&gt;.</returns>
- IEnumerable<SyncQualityOption> GetQualityOptions(SyncTarget target);
-
- /// <summary>
- /// Gets the profile options.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <returns>IEnumerable&lt;SyncQualityOption&gt;.</returns>
- IEnumerable<SyncProfileOption> GetProfileOptions(SyncTarget target);
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs b/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
deleted file mode 100644
index b6853267e..000000000
--- a/MediaBrowser.Server.Implementations/Sync/MediaSync.cs
+++ /dev/null
@@ -1,502 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Server.Implementations.IO;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class MediaSync
- {
- private readonly ISyncManager _syncManager;
- private readonly IServerApplicationHost _appHost;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IConfigurationManager _config;
-
- public const string PathSeparatorString = "/";
- public const char PathSeparatorChar = '/';
-
- public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config)
- {
- _logger = logger;
- _syncManager = syncManager;
- _appHost = appHost;
- _fileSystem = fileSystem;
- _config = config;
- }
-
- public async Task Sync(IServerSyncProvider provider,
- ISyncDataProvider dataProvider,
- SyncTarget target,
- IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- var serverId = _appHost.SystemId;
- var serverName = _appHost.FriendlyName;
-
- await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false);
- progress.Report(3);
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(pct =>
- {
- var totalProgress = pct * .97;
- totalProgress += 1;
- progress.Report(totalProgress);
- });
- await GetNewMedia(provider, dataProvider, target, serverId, serverName, innerProgress, cancellationToken);
-
- // Do the data sync twice so the server knows what was removed from the device
- await SyncData(provider, dataProvider, serverId, target, cancellationToken).ConfigureAwait(false);
-
- progress.Report(100);
- }
-
- private async Task SyncData(IServerSyncProvider provider,
- ISyncDataProvider dataProvider,
- string serverId,
- SyncTarget target,
- CancellationToken cancellationToken)
- {
- var localItems = await dataProvider.GetLocalItems(target, serverId).ConfigureAwait(false);
- var remoteFiles = await provider.GetFiles(target, cancellationToken).ConfigureAwait(false);
- var remoteIds = remoteFiles.Items.Select(i => i.FullName).ToList();
-
- var jobItemIds = new List<string>();
-
- foreach (var localItem in localItems)
- {
- if (remoteIds.Contains(localItem.FileId, StringComparer.OrdinalIgnoreCase))
- {
- jobItemIds.Add(localItem.SyncJobItemId);
- }
- }
-
- var result = await _syncManager.SyncData(new SyncDataRequest
- {
- TargetId = target.Id,
- SyncJobItemIds = jobItemIds
-
- }).ConfigureAwait(false);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- foreach (var itemIdToRemove in result.ItemIdsToRemove)
- {
- try
- {
- await RemoveItem(provider, dataProvider, serverId, itemIdToRemove, target, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting item from device. Id: {0}", ex, itemIdToRemove);
- }
- }
- }
-
- private async Task GetNewMedia(IServerSyncProvider provider,
- ISyncDataProvider dataProvider,
- SyncTarget target,
- string serverId,
- string serverName,
- IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- var jobItems = await _syncManager.GetReadySyncItems(target.Id).ConfigureAwait(false);
-
- var numComplete = 0;
- double startingPercent = 0;
- double percentPerItem = 1;
- if (jobItems.Count > 0)
- {
- percentPerItem /= jobItems.Count;
- }
-
- foreach (var jobItem in jobItems)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var currentPercent = startingPercent;
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(pct =>
- {
- var totalProgress = pct * percentPerItem;
- totalProgress += currentPercent;
- progress.Report(totalProgress);
- });
-
- try
- {
- await GetItem(provider, dataProvider, target, serverId, serverName, jobItem, innerProgress, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error syncing item", ex);
- }
-
- numComplete++;
- startingPercent = numComplete;
- startingPercent /= jobItems.Count;
- startingPercent *= 100;
- progress.Report(startingPercent);
- }
- }
-
- private async Task GetItem(IServerSyncProvider provider,
- ISyncDataProvider dataProvider,
- SyncTarget target,
- string serverId,
- string serverName,
- SyncedItem jobItem,
- IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- var libraryItem = jobItem.Item;
- var internalSyncJobItem = _syncManager.GetJobItem(jobItem.SyncJobItemId);
- var internalSyncJob = _syncManager.GetJob(jobItem.SyncJobId);
-
- var localItem = CreateLocalItem(provider, jobItem, internalSyncJob, target, libraryItem, serverId, serverName, jobItem.OriginalFileName);
-
- await _syncManager.ReportSyncJobItemTransferBeginning(internalSyncJobItem.Id);
-
- var transferSuccess = false;
- Exception transferException = null;
-
- var options = _config.GetSyncOptions();
-
- try
- {
- var fileTransferProgress = new ActionableProgress<double>();
- fileTransferProgress.RegisterAction(pct => progress.Report(pct * .92));
-
- var sendFileResult = await SendFile(provider, internalSyncJobItem.OutputPath, localItem.LocalPath.Split(PathSeparatorChar), target, options, fileTransferProgress, cancellationToken).ConfigureAwait(false);
-
- if (localItem.Item.MediaSources != null)
- {
- var mediaSource = localItem.Item.MediaSources.FirstOrDefault();
- if (mediaSource != null)
- {
- mediaSource.Path = sendFileResult.Path;
- mediaSource.Protocol = sendFileResult.Protocol;
- mediaSource.RequiredHttpHeaders = sendFileResult.RequiredHttpHeaders;
- mediaSource.SupportsTranscoding = false;
- }
- }
-
- localItem.FileId = sendFileResult.Id;
-
- // Create db record
- await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false);
-
- if (localItem.Item.MediaSources != null)
- {
- var mediaSource = localItem.Item.MediaSources.FirstOrDefault();
- if (mediaSource != null)
- {
- await SendSubtitles(localItem, mediaSource, provider, dataProvider, target, options, cancellationToken).ConfigureAwait(false);
- }
- }
-
- progress.Report(92);
-
- transferSuccess = true;
-
- progress.Report(99);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error transferring sync job file", ex);
- transferException = ex;
- }
-
- if (transferSuccess)
- {
- await _syncManager.ReportSyncJobItemTransferred(jobItem.SyncJobItemId).ConfigureAwait(false);
- }
- else
- {
- await _syncManager.ReportSyncJobItemTransferFailed(jobItem.SyncJobItemId).ConfigureAwait(false);
-
- throw transferException;
- }
- }
-
- private async Task SendSubtitles(LocalItem localItem, MediaSourceInfo mediaSource, IServerSyncProvider provider, ISyncDataProvider dataProvider, SyncTarget target, SyncOptions options, CancellationToken cancellationToken)
- {
- var failedSubtitles = new List<MediaStream>();
- var requiresSave = false;
-
- foreach (var mediaStream in mediaSource.MediaStreams
- .Where(i => i.Type == MediaStreamType.Subtitle && i.IsExternal)
- .ToList())
- {
- try
- {
- var remotePath = GetRemoteSubtitlePath(localItem, mediaStream, provider, target);
- var sendFileResult = await SendFile(provider, mediaStream.Path, remotePath, target, options, new Progress<double>(), cancellationToken).ConfigureAwait(false);
-
- // This is the path that will be used when talking to the provider
- mediaStream.ExternalId = sendFileResult.Id;
-
- // Keep track of all additional files for cleanup later.
- localItem.AdditionalFiles.Add(sendFileResult.Id);
-
- // This is the public path clients will use
- mediaStream.Path = sendFileResult.Path;
- requiresSave = true;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending subtitle stream", ex);
- failedSubtitles.Add(mediaStream);
- }
- }
-
- if (failedSubtitles.Count > 0)
- {
- mediaSource.MediaStreams = mediaSource.MediaStreams.Except(failedSubtitles).ToList();
- requiresSave = true;
- }
-
- if (requiresSave)
- {
- await dataProvider.AddOrUpdate(target, localItem).ConfigureAwait(false);
- }
- }
-
- private string[] GetRemoteSubtitlePath(LocalItem item, MediaStream stream, IServerSyncProvider provider, SyncTarget target)
- {
- var filename = GetSubtitleSaveFileName(item, stream.Language, stream.IsForced) + "." + stream.Codec.ToLower();
-
- var pathParts = item.LocalPath.Split(PathSeparatorChar);
- var list = pathParts.Take(pathParts.Length - 1).ToList();
- list.Add(filename);
-
- return list.ToArray();
- }
-
- private string GetSubtitleSaveFileName(LocalItem item, string language, bool isForced)
- {
- var path = item.LocalPath;
-
- var name = Path.GetFileNameWithoutExtension(path);
-
- if (!string.IsNullOrWhiteSpace(language))
- {
- name += "." + language.ToLower();
- }
-
- if (isForced)
- {
- name += ".foreign";
- }
-
- return name;
- }
-
- private async Task RemoveItem(IServerSyncProvider provider,
- ISyncDataProvider dataProvider,
- string serverId,
- string syncJobItemId,
- SyncTarget target,
- CancellationToken cancellationToken)
- {
- var localItems = await dataProvider.GetItemsBySyncJobItemId(target, serverId, syncJobItemId);
-
- foreach (var localItem in localItems)
- {
- var files = localItem.AdditionalFiles.ToList();
-
- foreach (var file in files)
- {
- _logger.Debug("Removing {0} from {1}.", file, target.Name);
- await provider.DeleteFile(file, target, cancellationToken).ConfigureAwait(false);
- }
-
- _logger.Debug("Removing {0} from {1}.", localItem.FileId, target.Name);
- await provider.DeleteFile(localItem.FileId, target, cancellationToken).ConfigureAwait(false);
-
- await dataProvider.Delete(target, localItem.Id).ConfigureAwait(false);
- }
- }
-
- private async Task<SyncedFileInfo> SendFile(IServerSyncProvider provider, string inputPath, string[] pathParts, SyncTarget target, SyncOptions options, IProgress<double> progress, CancellationToken cancellationToken)
- {
- _logger.Debug("Sending {0} to {1}. Remote path: {2}", inputPath, provider.Name, string.Join("/", pathParts));
- var supportsDirectCopy = provider as ISupportsDirectCopy;
- if (supportsDirectCopy != null)
- {
- return await supportsDirectCopy.SendFile(inputPath, pathParts, target, progress, cancellationToken).ConfigureAwait(false);
- }
-
- using (var fileStream = _fileSystem.GetFileStream(inputPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
- {
- Stream stream = fileStream;
-
- if (options.UploadSpeedLimitBytes > 0 && provider is IRemoteSyncProvider)
- {
- stream = new ThrottledStream(stream, options.UploadSpeedLimitBytes);
- }
-
- return await provider.SendFile(stream, pathParts, target, progress, cancellationToken).ConfigureAwait(false);
- }
- }
-
- private static string GetLocalId(string jobItemId, string itemId)
- {
- var bytes = Encoding.UTF8.GetBytes(jobItemId + itemId);
- bytes = CreateMd5(bytes);
- return BitConverter.ToString(bytes, 0, bytes.Length).Replace("-", string.Empty);
- }
-
- private static byte[] CreateMd5(byte[] value)
- {
- using (var provider = MD5.Create())
- {
- return provider.ComputeHash(value);
- }
- }
-
- public LocalItem CreateLocalItem(IServerSyncProvider provider, SyncedItem syncedItem, SyncJob job, SyncTarget target, BaseItemDto libraryItem, string serverId, string serverName, string originalFileName)
- {
- var path = GetDirectoryPath(provider, job, syncedItem, libraryItem, serverName);
- path.Add(GetLocalFileName(provider, libraryItem, originalFileName));
-
- var localPath = string.Join(PathSeparatorString, path.ToArray());
-
- foreach (var mediaSource in libraryItem.MediaSources)
- {
- mediaSource.Path = localPath;
- mediaSource.Protocol = MediaProtocol.File;
- }
-
- return new LocalItem
- {
- Item = libraryItem,
- ItemId = libraryItem.Id,
- ServerId = serverId,
- LocalPath = localPath,
- Id = GetLocalId(syncedItem.SyncJobItemId, libraryItem.Id),
- SyncJobItemId = syncedItem.SyncJobItemId
- };
- }
-
- private List<string> GetDirectoryPath(IServerSyncProvider provider, SyncJob job, SyncedItem syncedItem, BaseItemDto item, string serverName)
- {
- var parts = new List<string>
- {
- serverName
- };
-
- var profileOption = _syncManager.GetProfileOptions(job.TargetId)
- .FirstOrDefault(i => string.Equals(i.Id, job.Profile, StringComparison.OrdinalIgnoreCase));
-
- string name;
-
- if (profileOption != null && !string.IsNullOrWhiteSpace(profileOption.Name))
- {
- name = profileOption.Name;
-
- if (job.Bitrate.HasValue)
- {
- name += "-" + job.Bitrate.Value.ToString(CultureInfo.InvariantCulture);
- }
- else
- {
- var qualityOption = _syncManager.GetQualityOptions(job.TargetId)
- .FirstOrDefault(i => string.Equals(i.Id, job.Quality, StringComparison.OrdinalIgnoreCase));
-
- if (qualityOption != null && !string.IsNullOrWhiteSpace(qualityOption.Name))
- {
- name += "-" + qualityOption.Name;
- }
- }
- }
- else
- {
- name = syncedItem.SyncJobName + "-" + syncedItem.SyncJobDateCreated
- .ToLocalTime()
- .ToString("g")
- .Replace(" ", "-");
- }
-
- name = GetValidFilename(provider, name);
- parts.Add(name);
-
- if (item.IsType("episode"))
- {
- parts.Add("TV");
- if (!string.IsNullOrWhiteSpace(item.SeriesName))
- {
- parts.Add(item.SeriesName);
- }
- }
- else if (item.IsVideo)
- {
- parts.Add("Videos");
- parts.Add(item.Name);
- }
- else if (item.IsAudio)
- {
- parts.Add("Music");
-
- if (!string.IsNullOrWhiteSpace(item.AlbumArtist))
- {
- parts.Add(item.AlbumArtist);
- }
-
- if (!string.IsNullOrWhiteSpace(item.Album))
- {
- parts.Add(item.Album);
- }
- }
- else if (string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase))
- {
- parts.Add("Photos");
-
- if (!string.IsNullOrWhiteSpace(item.Album))
- {
- parts.Add(item.Album);
- }
- }
-
- return parts.Select(i => GetValidFilename(provider, i)).ToList();
- }
-
- private string GetLocalFileName(IServerSyncProvider provider, BaseItemDto item, string originalFileName)
- {
- var filename = originalFileName;
-
- if (string.IsNullOrWhiteSpace(filename))
- {
- filename = item.Name;
- }
-
- return GetValidFilename(provider, filename);
- }
-
- private string GetValidFilename(IServerSyncProvider provider, string filename)
- {
- // We can always add this method to the sync provider if it's really needed
- return _fileSystem.GetValidFilename(filename);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs b/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs
deleted file mode 100644
index 471604117..000000000
--- a/MediaBrowser.Server.Implementations/Sync/MultiProviderSync.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class MultiProviderSync
- {
- private readonly SyncManager _syncManager;
- private readonly IServerApplicationHost _appHost;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IConfigurationManager _config;
-
- public MultiProviderSync(SyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem, IConfigurationManager config)
- {
- _syncManager = syncManager;
- _appHost = appHost;
- _logger = logger;
- _fileSystem = fileSystem;
- _config = config;
- }
-
- public async Task Sync(IEnumerable<IServerSyncProvider> providers, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var targets = providers
- .SelectMany(i => i.GetAllSyncTargets().Select(t => new Tuple<IServerSyncProvider, SyncTarget>(i, t)))
- .ToList();
-
- var numComplete = 0;
- double startingPercent = 0;
- double percentPerItem = 1;
- if (targets.Count > 0)
- {
- percentPerItem /= targets.Count;
- }
-
- foreach (var target in targets)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var currentPercent = startingPercent;
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(pct =>
- {
- var totalProgress = pct * percentPerItem;
- totalProgress += currentPercent;
- progress.Report(totalProgress);
- });
-
- var dataProvider = _syncManager.GetDataProvider(target.Item1, target.Item2);
-
- await new MediaSync(_logger, _syncManager, _appHost, _fileSystem, _config)
- .Sync(target.Item1, dataProvider, target.Item2, innerProgress, cancellationToken)
- .ConfigureAwait(false);
-
- numComplete++;
- startingPercent = numComplete;
- startingPercent /= targets.Count;
- startingPercent *= 100;
- progress.Report(startingPercent);
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs b/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs
deleted file mode 100644
index dc7f925a0..000000000
--- a/MediaBrowser.Server.Implementations/Sync/ServerSyncScheduledTask.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- class ServerSyncScheduledTask : IScheduledTask, IConfigurableScheduledTask
- {
- private readonly ISyncManager _syncManager;
- private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IServerApplicationHost _appHost;
- private readonly IConfigurationManager _config;
-
- public ServerSyncScheduledTask(ISyncManager syncManager, ILogger logger, IFileSystem fileSystem, IServerApplicationHost appHost, IConfigurationManager config)
- {
- _syncManager = syncManager;
- _logger = logger;
- _fileSystem = fileSystem;
- _appHost = appHost;
- _config = config;
- }
-
- public string Name
- {
- get { return "Cloud & Folder Sync"; }
- }
-
- public string Description
- {
- get { return "Sync media to the cloud"; }
- }
-
- public string Category
- {
- get
- {
- return "Sync";
- }
- }
-
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- return new MultiProviderSync((SyncManager)_syncManager, _appHost, _logger, _fileSystem, _config)
- .Sync(ServerSyncProviders, progress, cancellationToken);
- }
-
- public IEnumerable<IServerSyncProvider> ServerSyncProviders
- {
- get { return ((SyncManager)_syncManager).ServerSyncProviders; }
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(3).Ticks}
- };
- }
- public bool IsHidden
- {
- get { return !IsEnabled; }
- }
-
- public bool IsEnabled
- {
- get { return ServerSyncProviders.Any(); }
- }
-
- public bool IsLogged
- {
- get { return true; }
- }
-
- public string Key
- {
- get { return "ServerSync"; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncConfig.cs b/MediaBrowser.Server.Implementations/Sync/SyncConfig.cs
deleted file mode 100644
index 52c774330..000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncConfig.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Sync;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncConfigurationFactory : IConfigurationFactory
- {
- public IEnumerable<ConfigurationStore> GetConfigurations()
- {
- return new List<ConfigurationStore>
- {
- new ConfigurationStore
- {
- ConfigurationType = typeof(SyncOptions),
- Key = "sync"
- }
- };
- }
- }
-
- public static class SyncExtensions
- {
- public static SyncOptions GetSyncOptions(this IConfigurationManager config)
- {
- return config.GetConfiguration<SyncOptions>("sync");
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs b/MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs
deleted file mode 100644
index 3a5023fe5..000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncConvertScheduledTask.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Controller.TV;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncConvertScheduledTask : IScheduledTask
- {
- private readonly ILibraryManager _libraryManager;
- private readonly ISyncRepository _syncRepo;
- private readonly ISyncManager _syncManager;
- private readonly ILogger _logger;
- private readonly IUserManager _userManager;
- private readonly ITVSeriesManager _tvSeriesManager;
- private readonly IMediaEncoder _mediaEncoder;
- private readonly ISubtitleEncoder _subtitleEncoder;
- private readonly IConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
- private readonly IMediaSourceManager _mediaSourceManager;
-
- public SyncConvertScheduledTask(ILibraryManager libraryManager, ISyncRepository syncRepo, ISyncManager syncManager, ILogger logger, IUserManager userManager, ITVSeriesManager tvSeriesManager, IMediaEncoder mediaEncoder, ISubtitleEncoder subtitleEncoder, IConfigurationManager config, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager)
- {
- _libraryManager = libraryManager;
- _syncRepo = syncRepo;
- _syncManager = syncManager;
- _logger = logger;
- _userManager = userManager;
- _tvSeriesManager = tvSeriesManager;
- _mediaEncoder = mediaEncoder;
- _subtitleEncoder = subtitleEncoder;
- _config = config;
- _fileSystem = fileSystem;
- _mediaSourceManager = mediaSourceManager;
- }
-
- public string Name
- {
- get { return "Convert media"; }
- }
-
- public string Description
- {
- get { return "Runs scheduled sync jobs"; }
- }
-
- public string Category
- {
- get
- {
- return "Sync";
- }
- }
-
- public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
- {
- return new SyncJobProcessor(_libraryManager, _syncRepo, (SyncManager)_syncManager, _logger, _userManager, _tvSeriesManager, _mediaEncoder, _subtitleEncoder, _config, _fileSystem, _mediaSourceManager)
- .Sync(progress, cancellationToken);
- }
-
- /// <summary>
- /// Creates the triggers that define when the task will run
- /// </summary>
- /// <returns>IEnumerable{BaseTaskTrigger}.</returns>
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- return new[] {
-
- // Every so often
- new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(3).Ticks}
- };
- }
-
- public string Key
- {
- get { return "SyncPrepare"; }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncHelper.cs b/MediaBrowser.Server.Implementations/Sync/SyncHelper.cs
deleted file mode 100644
index fb4e0c6be..000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncHelper.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncHelper
- {
- public static int? AdjustBitrate(int? profileBitrate, string quality)
- {
- if (profileBitrate.HasValue)
- {
- if (string.Equals(quality, "medium", StringComparison.OrdinalIgnoreCase))
- {
- profileBitrate = Math.Min(profileBitrate.Value, 4000000);
- }
- else if (string.Equals(quality, "low", StringComparison.OrdinalIgnoreCase))
- {
- profileBitrate = Math.Min(profileBitrate.Value, 1500000);
- }
- }
-
- return profileBitrate;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobOptions.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobOptions.cs
deleted file mode 100644
index cb8141c89..000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncJobOptions.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using MediaBrowser.Model.Dlna;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncJobOptions
- {
- /// <summary>
- /// Gets or sets the conversion options.
- /// </summary>
- /// <value>The conversion options.</value>
- public DeviceProfile DeviceProfile { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether this instance is converting.
- /// </summary>
- /// <value><c>true</c> if this instance is converting; otherwise, <c>false</c>.</value>
- public bool IsConverting { get; set; }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs b/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
deleted file mode 100644
index 8d2c0b20c..000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncJobProcessor.cs
+++ /dev/null
@@ -1,988 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Controller.TV;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Session;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncJobProcessor
- {
- private readonly ILibraryManager _libraryManager;
- private readonly ISyncRepository _syncRepo;
- private readonly SyncManager _syncManager;
- private readonly ILogger _logger;
- private readonly IUserManager _userManager;
- private readonly ITVSeriesManager _tvSeriesManager;
- private readonly IMediaEncoder _mediaEncoder;
- private readonly ISubtitleEncoder _subtitleEncoder;
- private readonly IConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
- private readonly IMediaSourceManager _mediaSourceManager;
-
- public SyncJobProcessor(ILibraryManager libraryManager, ISyncRepository syncRepo, SyncManager syncManager, ILogger logger, IUserManager userManager, ITVSeriesManager tvSeriesManager, IMediaEncoder mediaEncoder, ISubtitleEncoder subtitleEncoder, IConfigurationManager config, IFileSystem fileSystem, IMediaSourceManager mediaSourceManager)
- {
- _libraryManager = libraryManager;
- _syncRepo = syncRepo;
- _syncManager = syncManager;
- _logger = logger;
- _userManager = userManager;
- _tvSeriesManager = tvSeriesManager;
- _mediaEncoder = mediaEncoder;
- _subtitleEncoder = subtitleEncoder;
- _config = config;
- _fileSystem = fileSystem;
- _mediaSourceManager = mediaSourceManager;
- }
-
- public async Task EnsureJobItems(SyncJob job)
- {
- var user = _userManager.GetUserById(job.UserId);
-
- if (user == null)
- {
- throw new InvalidOperationException("Cannot proceed with sync because user no longer exists.");
- }
-
- var items = (await GetItemsForSync(job.Category, job.ParentId, job.RequestedItemIds, user, job.UnwatchedOnly).ConfigureAwait(false))
- .ToList();
-
- var jobItems = _syncManager.GetJobItems(new SyncJobItemQuery
- {
- JobId = job.Id,
- AddMetadata = false
-
- }).Items.ToList();
-
- foreach (var item in items)
- {
- // Respect ItemLimit, if set
- if (job.ItemLimit.HasValue)
- {
- if (jobItems.Count(j => j.Status != SyncJobItemStatus.RemovedFromDevice && j.Status != SyncJobItemStatus.Failed) >= job.ItemLimit.Value)
- {
- break;
- }
- }
-
- var itemId = item.Id.ToString("N");
-
- var jobItem = jobItems.FirstOrDefault(i => string.Equals(i.ItemId, itemId, StringComparison.OrdinalIgnoreCase));
-
- if (jobItem != null)
- {
- continue;
- }
-
- var index = jobItems.Count == 0 ?
- 0 :
- jobItems.Select(i => i.JobItemIndex).Max() + 1;
-
- jobItem = new SyncJobItem
- {
- Id = Guid.NewGuid().ToString("N"),
- ItemId = itemId,
- ItemName = GetSyncJobItemName(item),
- JobId = job.Id,
- TargetId = job.TargetId,
- DateCreated = DateTime.UtcNow,
- JobItemIndex = index
- };
-
- await _syncRepo.Create(jobItem).ConfigureAwait(false);
- _syncManager.OnSyncJobItemCreated(jobItem);
-
- jobItems.Add(jobItem);
- }
-
- jobItems = jobItems
- .OrderBy(i => i.DateCreated)
- .ToList();
-
- await UpdateJobStatus(job, jobItems).ConfigureAwait(false);
- }
-
- private string GetSyncJobItemName(BaseItem item)
- {
- var name = item.Name;
- var episode = item as Episode;
-
- if (episode != null)
- {
- if (episode.IndexNumber.HasValue)
- {
- name = "E" + episode.IndexNumber.Value.ToString(CultureInfo.InvariantCulture) + " - " + name;
- }
-
- if (episode.ParentIndexNumber.HasValue)
- {
- name = "S" + episode.ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture) + ", " + name;
- }
- }
-
- return name;
- }
-
- public Task UpdateJobStatus(string id)
- {
- var job = _syncRepo.GetJob(id);
-
- if (job == null)
- {
- return Task.FromResult(true);
- }
-
- var result = _syncManager.GetJobItems(new SyncJobItemQuery
- {
- JobId = job.Id,
- AddMetadata = false
- });
-
- return UpdateJobStatus(job, result.Items.ToList());
- }
-
- private async Task UpdateJobStatus(SyncJob job, List<SyncJobItem> jobItems)
- {
- job.ItemCount = jobItems.Count;
-
- double pct = 0;
-
- foreach (var item in jobItems)
- {
- if (item.Status == SyncJobItemStatus.Failed || item.Status == SyncJobItemStatus.Synced || item.Status == SyncJobItemStatus.RemovedFromDevice || item.Status == SyncJobItemStatus.Cancelled)
- {
- pct += 100;
- }
- else
- {
- pct += item.Progress ?? 0;
- }
- }
-
- if (job.ItemCount > 0)
- {
- pct /= job.ItemCount;
- job.Progress = pct;
- }
- else
- {
- job.Progress = null;
- }
-
- if (jobItems.Any(i => i.Status == SyncJobItemStatus.Transferring))
- {
- job.Status = SyncJobStatus.Transferring;
- }
- else if (jobItems.Any(i => i.Status == SyncJobItemStatus.Converting))
- {
- job.Status = SyncJobStatus.Converting;
- }
- else if (jobItems.All(i => i.Status == SyncJobItemStatus.Failed))
- {
- job.Status = SyncJobStatus.Failed;
- }
- else if (jobItems.All(i => i.Status == SyncJobItemStatus.Cancelled))
- {
- job.Status = SyncJobStatus.Cancelled;
- }
- else if (jobItems.All(i => i.Status == SyncJobItemStatus.ReadyToTransfer))
- {
- job.Status = SyncJobStatus.ReadyToTransfer;
- }
- else if (jobItems.All(i => i.Status == SyncJobItemStatus.Cancelled || i.Status == SyncJobItemStatus.Failed || i.Status == SyncJobItemStatus.Synced || i.Status == SyncJobItemStatus.RemovedFromDevice))
- {
- if (jobItems.Any(i => i.Status == SyncJobItemStatus.Failed))
- {
- job.Status = SyncJobStatus.CompletedWithError;
- }
- else
- {
- job.Status = SyncJobStatus.Completed;
- }
- }
- else
- {
- job.Status = SyncJobStatus.Queued;
- }
-
- await _syncRepo.Update(job).ConfigureAwait(false);
-
- _syncManager.OnSyncJobUpdated(job);
- }
-
- public async Task<IEnumerable<BaseItem>> GetItemsForSync(SyncCategory? category, string parentId, IEnumerable<string> itemIds, User user, bool unwatchedOnly)
- {
- var list = new List<BaseItem>();
-
- if (category.HasValue)
- {
- list = (await GetItemsForSync(category.Value, parentId, user).ConfigureAwait(false)).ToList();
- }
- else
- {
- foreach (var itemId in itemIds)
- {
- var subList = await GetItemsForSync(itemId, user).ConfigureAwait(false);
- list.AddRange(subList);
- }
- }
-
- IEnumerable<BaseItem> items = list;
- items = items.Where(_syncManager.SupportsSync);
-
- if (unwatchedOnly)
- {
- // Avoid implicitly captured closure
- var currentUser = user;
-
- items = items.Where(i =>
- {
- var video = i as Video;
-
- if (video != null)
- {
- return !video.IsPlayed(currentUser);
- }
-
- return true;
- });
- }
-
- return items.DistinctBy(i => i.Id);
- }
-
- private async Task<IEnumerable<BaseItem>> GetItemsForSync(SyncCategory category, string parentId, User user)
- {
- var parent = string.IsNullOrWhiteSpace(parentId)
- ? user.RootFolder
- : (Folder)_libraryManager.GetItemById(parentId);
-
- InternalItemsQuery query;
-
- switch (category)
- {
- case SyncCategory.Latest:
- query = new InternalItemsQuery
- {
- IsFolder = false,
- SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName },
- SortOrder = SortOrder.Descending,
- Recursive = true
- };
- break;
- case SyncCategory.Resume:
- query = new InternalItemsQuery
- {
- IsFolder = false,
- SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName },
- SortOrder = SortOrder.Descending,
- Recursive = true,
- IsResumable = true,
- MediaTypes = new[] { MediaType.Video }
- };
- break;
-
- case SyncCategory.NextUp:
- return _tvSeriesManager.GetNextUp(new NextUpQuery
- {
- ParentId = parentId,
- UserId = user.Id.ToString("N")
- }).Items;
-
- default:
- throw new ArgumentException("Unrecognized category: " + category);
- }
-
- if (parent == null)
- {
- return new List<BaseItem>();
- }
-
- query.User = user;
-
- var result = await parent.GetItems(query).ConfigureAwait(false);
- return result.Items;
- }
-
- private async Task<List<BaseItem>> GetItemsForSync(string id, User user)
- {
- var item = _libraryManager.GetItemById(id);
-
- if (item == null)
- {
- return new List<BaseItem>();
- }
-
- var itemByName = item as IItemByName;
- if (itemByName != null)
- {
- return itemByName.GetTaggedItems(new InternalItemsQuery(user)
- {
- IsFolder = false,
- Recursive = true
- }).ToList();
- }
-
- if (item.IsFolder)
- {
- var folder = (Folder)item;
- var itemsResult = await folder.GetItems(new InternalItemsQuery(user)
- {
- Recursive = true,
- IsFolder = false
-
- }).ConfigureAwait(false);
-
- var items = itemsResult.Items;
-
- if (!folder.IsPreSorted)
- {
- items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
- .ToArray();
- }
-
- return items.ToList();
- }
-
- return new List<BaseItem> { item };
- }
-
- private async Task EnsureSyncJobItems(string targetId, CancellationToken cancellationToken)
- {
- var jobResult = _syncRepo.GetJobs(new SyncJobQuery
- {
- SyncNewContent = true,
- TargetId = targetId
- });
-
- foreach (var job in jobResult.Items)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (job.SyncNewContent)
- {
- await EnsureJobItems(job).ConfigureAwait(false);
- }
- }
- }
-
- public async Task Sync(IProgress<double> progress, CancellationToken cancellationToken)
- {
- 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
- {
- Statuses = new[] { SyncJobItemStatus.Queued, SyncJobItemStatus.Converting },
- AddMetadata = false
- });
-
- await SyncJobItems(result.Items, true, progress, cancellationToken).ConfigureAwait(false);
-
- 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
- // Clean files in sync temp folder that are not linked to any sync jobs
- }
-
- public async Task SyncJobItems(string targetId, bool enableConversion, IProgress<double> progress,
- CancellationToken cancellationToken)
- {
- await EnsureSyncJobItems(targetId, cancellationToken).ConfigureAwait(false);
-
- // If it already has a converting status then is must have been aborted during conversion
- var result = _syncManager.GetJobItems(new SyncJobItemQuery
- {
- Statuses = new[] { SyncJobItemStatus.Queued, SyncJobItemStatus.Converting },
- TargetId = targetId,
- AddMetadata = false
- });
-
- await SyncJobItems(result.Items, enableConversion, progress, cancellationToken).ConfigureAwait(false);
- }
-
- public async Task SyncJobItems(SyncJobItem[] items, bool enableConversion, IProgress<double> progress, CancellationToken cancellationToken)
- {
- if (items.Length > 0)
- {
- if (!SyncRegistrationInfo.Instance.IsRegistered)
- {
- _logger.Debug("Cancelling sync job processing. Please obtain a supporter membership.");
- return;
- }
- }
-
- var numComplete = 0;
-
- foreach (var item in items)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- double percentPerItem = 1;
- percentPerItem /= items.Length;
- var startingPercent = numComplete * percentPerItem * 100;
-
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(p => progress.Report(startingPercent + percentPerItem * p));
-
- // Pull it fresh from the db just to make sure it wasn't deleted or cancelled while another item was converting
- var jobItem = enableConversion ? _syncRepo.GetJobItem(item.Id) : item;
-
- if (jobItem != null)
- {
- if (jobItem.Status != SyncJobItemStatus.Cancelled)
- {
- await ProcessJobItem(jobItem, enableConversion, innerProgress, cancellationToken).ConfigureAwait(false);
- }
-
- await UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
- }
-
- numComplete++;
- double percent = numComplete;
- percent /= items.Length;
- progress.Report(100 * percent);
- }
- }
-
- private async Task ProcessJobItem(SyncJobItem jobItem, bool enableConversion, IProgress<double> progress, CancellationToken cancellationToken)
- {
- if (jobItem == null)
- {
- throw new ArgumentNullException("jobItem");
- }
-
- var item = _libraryManager.GetItemById(jobItem.ItemId);
- if (item == null)
- {
- jobItem.Status = SyncJobItemStatus.Failed;
- _logger.Error("Unable to locate library item for JobItem {0}, ItemId {1}", jobItem.Id, jobItem.ItemId);
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- return;
- }
-
- jobItem.Progress = 0;
-
- var syncOptions = _config.GetSyncOptions();
- var job = _syncManager.GetJob(jobItem.JobId);
- var user = _userManager.GetUserById(job.UserId);
- if (user == null)
- {
- jobItem.Status = SyncJobItemStatus.Failed;
- _logger.Error("User not found. Cannot complete the sync job.");
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- return;
- }
-
- // See if there's already another active job item for the same target
- var existingJobItems = _syncManager.GetJobItems(new SyncJobItemQuery
- {
- AddMetadata = false,
- ItemId = jobItem.ItemId,
- TargetId = jobItem.TargetId,
- Statuses = new[] { SyncJobItemStatus.Converting, SyncJobItemStatus.Queued, SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Synced, SyncJobItemStatus.Transferring }
- });
-
- var duplicateJobItems = existingJobItems.Items
- .Where(i => !string.Equals(i.Id, jobItem.Id, StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- if (duplicateJobItems.Count > 0)
- {
- var syncProvider = _syncManager.GetSyncProvider(jobItem) as IHasDuplicateCheck;
-
- if (!duplicateJobItems.Any(i => AllowDuplicateJobItem(syncProvider, i, jobItem)))
- {
- _logger.Debug("Cancelling sync job item because there is already another active job for the same target.");
- jobItem.Status = SyncJobItemStatus.Cancelled;
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- return;
- }
- }
-
- var video = item as Video;
- if (video != null)
- {
- await Sync(jobItem, video, user, enableConversion, syncOptions, progress, cancellationToken).ConfigureAwait(false);
- }
-
- else if (item is Audio)
- {
- await Sync(jobItem, (Audio)item, user, enableConversion, syncOptions, progress, cancellationToken).ConfigureAwait(false);
- }
-
- else if (item is Photo)
- {
- await Sync(jobItem, (Photo)item, cancellationToken).ConfigureAwait(false);
- }
-
- else
- {
- await SyncGeneric(jobItem, item, cancellationToken).ConfigureAwait(false);
- }
- }
-
- private bool AllowDuplicateJobItem(IHasDuplicateCheck provider, SyncJobItem original, SyncJobItem duplicate)
- {
- if (provider != null)
- {
- return provider.AllowDuplicateJobItem(original, duplicate);
- }
-
- return true;
- }
-
- private async Task Sync(SyncJobItem jobItem, Video item, User user, bool enableConversion, SyncOptions syncOptions, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var job = _syncManager.GetJob(jobItem.JobId);
- var jobOptions = _syncManager.GetVideoOptions(jobItem, job);
- var conversionOptions = new VideoOptions
- {
- Profile = jobOptions.DeviceProfile
- };
-
- conversionOptions.DeviceId = jobItem.TargetId;
- conversionOptions.Context = EncodingContext.Static;
- conversionOptions.ItemId = item.Id.ToString("N");
- conversionOptions.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, false, user).ToList();
-
- var streamInfo = new StreamBuilder(_mediaEncoder, _logger).BuildVideoItem(conversionOptions);
- var mediaSource = streamInfo.MediaSource;
-
- // No sense creating external subs if we're already burning one into the video
- var externalSubs = streamInfo.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode ?
- new List<SubtitleStreamInfo>() :
- streamInfo.GetExternalSubtitles(false, true, null, null);
-
- // Mark as requiring conversion if transcoding the video, or if any subtitles need to be extracted
- var requiresVideoTranscoding = streamInfo.PlayMethod == PlayMethod.Transcode && jobOptions.IsConverting;
- var requiresConversion = requiresVideoTranscoding || externalSubs.Any(i => RequiresExtraction(i, mediaSource));
-
- if (requiresConversion && !enableConversion)
- {
- return;
- }
-
- jobItem.MediaSourceId = streamInfo.MediaSourceId;
- jobItem.TemporaryPath = GetTemporaryPath(jobItem);
-
- if (requiresConversion)
- {
- jobItem.Status = SyncJobItemStatus.Converting;
- }
-
- if (requiresVideoTranscoding)
- {
- // Save the job item now since conversion could take a while
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- await UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
-
- try
- {
- var lastJobUpdate = DateTime.MinValue;
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(async pct =>
- {
- progress.Report(pct);
-
- if ((DateTime.UtcNow - lastJobUpdate).TotalSeconds >= DatabaseProgressUpdateIntervalSeconds)
- {
- jobItem.Progress = pct / 2;
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- await UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
- }
- });
-
- jobItem.OutputPath = await _mediaEncoder.EncodeVideo(new EncodingJobOptions(streamInfo, conversionOptions.Profile)
- {
- OutputDirectory = jobItem.TemporaryPath,
- CpuCoreLimit = syncOptions.TranscodingCpuCoreLimit,
- ReadInputAtNativeFramerate = !syncOptions.EnableFullSpeedTranscoding
-
- }, innerProgress, cancellationToken);
-
- jobItem.ItemDateModifiedTicks = item.DateModified.Ticks;
- _syncManager.OnConversionComplete(jobItem);
- }
- catch (OperationCanceledException)
- {
- jobItem.Status = SyncJobItemStatus.Queued;
- jobItem.Progress = 0;
- }
- catch (Exception ex)
- {
- jobItem.Status = SyncJobItemStatus.Failed;
- _logger.ErrorException("Error during sync transcoding", ex);
- }
-
- if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
- {
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- return;
- }
-
- jobItem.MediaSource = await GetEncodedMediaSource(jobItem.OutputPath, user, true).ConfigureAwait(false);
- }
- else
- {
- if (mediaSource.Protocol == MediaProtocol.File)
- {
- jobItem.OutputPath = mediaSource.Path;
- }
- else if (mediaSource.Protocol == MediaProtocol.Http)
- {
- jobItem.OutputPath = await DownloadFile(jobItem, mediaSource, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol));
- }
-
- jobItem.ItemDateModifiedTicks = item.DateModified.Ticks;
- jobItem.MediaSource = mediaSource;
- }
-
- jobItem.MediaSource.SupportsTranscoding = false;
-
- if (externalSubs.Count > 0)
- {
- // Save the job item now since conversion could take a while
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
-
- await ConvertSubtitles(jobItem, externalSubs, streamInfo, cancellationToken).ConfigureAwait(false);
- }
-
- jobItem.Progress = 50;
- jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- }
-
- private bool RequiresExtraction(SubtitleStreamInfo stream, MediaSourceInfo mediaSource)
- {
- var originalStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Subtitle && i.Index == stream.Index);
-
- return originalStream != null && !originalStream.IsExternal;
- }
-
- private async Task ConvertSubtitles(SyncJobItem jobItem,
- IEnumerable<SubtitleStreamInfo> subtitles,
- StreamInfo streamInfo,
- CancellationToken cancellationToken)
- {
- var files = new List<ItemFileInfo>();
-
- var mediaStreams = jobItem.MediaSource.MediaStreams
- .Where(i => i.Type != MediaStreamType.Subtitle || !i.IsExternal)
- .ToList();
-
- var startingIndex = mediaStreams.Count == 0 ?
- 0 :
- mediaStreams.Select(i => i.Index).Max() + 1;
-
- foreach (var subtitle in subtitles)
- {
- var fileInfo = await ConvertSubtitles(jobItem.TemporaryPath, streamInfo, subtitle, cancellationToken).ConfigureAwait(false);
-
- // Reset this to a value that will be based on the output media
- fileInfo.Index = startingIndex;
- files.Add(fileInfo);
-
- mediaStreams.Add(new MediaStream
- {
- Index = startingIndex,
- Codec = subtitle.Format,
- IsForced = subtitle.IsForced,
- IsExternal = true,
- Language = subtitle.Language,
- Path = fileInfo.Path,
- SupportsExternalStream = true,
- Type = MediaStreamType.Subtitle
- });
-
- startingIndex++;
- }
-
- jobItem.AdditionalFiles.AddRange(files);
-
- jobItem.MediaSource.MediaStreams = mediaStreams;
- }
-
- private async Task<ItemFileInfo> ConvertSubtitles(string temporaryPath, StreamInfo streamInfo, SubtitleStreamInfo subtitleStreamInfo, CancellationToken cancellationToken)
- {
- var subtitleStreamIndex = subtitleStreamInfo.Index;
-
- var filename = Guid.NewGuid() + "." + subtitleStreamInfo.Format.ToLower();
-
- var path = Path.Combine(temporaryPath, filename);
-
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- using (var stream = await _subtitleEncoder.GetSubtitles(streamInfo.ItemId, streamInfo.MediaSourceId, subtitleStreamIndex, subtitleStreamInfo.Format, 0, null, false, cancellationToken).ConfigureAwait(false))
- {
- using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
- {
- await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
- }
- }
-
- return new ItemFileInfo
- {
- Name = Path.GetFileName(path),
- Path = path,
- Type = ItemFileType.Subtitles,
- Index = subtitleStreamIndex
- };
- }
-
- private const int DatabaseProgressUpdateIntervalSeconds = 2;
-
- private async Task Sync(SyncJobItem jobItem, Audio item, User user, bool enableConversion, SyncOptions syncOptions, IProgress<double> progress, CancellationToken cancellationToken)
- {
- var job = _syncManager.GetJob(jobItem.JobId);
- var jobOptions = _syncManager.GetAudioOptions(jobItem, job);
- var conversionOptions = new AudioOptions
- {
- Profile = jobOptions.DeviceProfile
- };
-
- conversionOptions.DeviceId = jobItem.TargetId;
- conversionOptions.Context = EncodingContext.Static;
- conversionOptions.ItemId = item.Id.ToString("N");
- conversionOptions.MediaSources = _mediaSourceManager.GetStaticMediaSources(item, false, user).ToList();
-
- var streamInfo = new StreamBuilder(_mediaEncoder, _logger).BuildAudioItem(conversionOptions);
- var mediaSource = streamInfo.MediaSource;
-
- jobItem.MediaSourceId = streamInfo.MediaSourceId;
- jobItem.TemporaryPath = GetTemporaryPath(jobItem);
-
- if (streamInfo.PlayMethod == PlayMethod.Transcode && jobOptions.IsConverting)
- {
- if (!enableConversion)
- {
- return;
- }
-
- jobItem.Status = SyncJobItemStatus.Converting;
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- await UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
-
- try
- {
- var lastJobUpdate = DateTime.MinValue;
- var innerProgress = new ActionableProgress<double>();
- innerProgress.RegisterAction(async pct =>
- {
- progress.Report(pct);
-
- if ((DateTime.UtcNow - lastJobUpdate).TotalSeconds >= DatabaseProgressUpdateIntervalSeconds)
- {
- jobItem.Progress = pct / 2;
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- await UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
- }
- });
-
- jobItem.OutputPath = await _mediaEncoder.EncodeAudio(new EncodingJobOptions(streamInfo, conversionOptions.Profile)
- {
- OutputDirectory = jobItem.TemporaryPath,
- CpuCoreLimit = syncOptions.TranscodingCpuCoreLimit
-
- }, innerProgress, cancellationToken);
-
- jobItem.ItemDateModifiedTicks = item.DateModified.Ticks;
- _syncManager.OnConversionComplete(jobItem);
- }
- catch (OperationCanceledException)
- {
- jobItem.Status = SyncJobItemStatus.Queued;
- jobItem.Progress = 0;
- }
- catch (Exception ex)
- {
- jobItem.Status = SyncJobItemStatus.Failed;
- _logger.ErrorException("Error during sync transcoding", ex);
- }
-
- if (jobItem.Status == SyncJobItemStatus.Failed || jobItem.Status == SyncJobItemStatus.Queued)
- {
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- return;
- }
-
- jobItem.MediaSource = await GetEncodedMediaSource(jobItem.OutputPath, user, false).ConfigureAwait(false);
- }
- else
- {
- if (mediaSource.Protocol == MediaProtocol.File)
- {
- jobItem.OutputPath = mediaSource.Path;
- }
- else if (mediaSource.Protocol == MediaProtocol.Http)
- {
- jobItem.OutputPath = await DownloadFile(jobItem, mediaSource, cancellationToken).ConfigureAwait(false);
- }
- else
- {
- throw new InvalidOperationException(string.Format("Cannot direct stream {0} protocol", mediaSource.Protocol));
- }
-
- jobItem.ItemDateModifiedTicks = item.DateModified.Ticks;
- jobItem.MediaSource = mediaSource;
- }
-
- jobItem.MediaSource.SupportsTranscoding = false;
-
- jobItem.Progress = 50;
- jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- }
-
- private async Task Sync(SyncJobItem jobItem, Photo item, CancellationToken cancellationToken)
- {
- jobItem.OutputPath = item.Path;
-
- jobItem.Progress = 50;
- jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
- jobItem.ItemDateModifiedTicks = item.DateModified.Ticks;
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- }
-
- private async Task SyncGeneric(SyncJobItem jobItem, BaseItem item, CancellationToken cancellationToken)
- {
- jobItem.OutputPath = item.Path;
-
- jobItem.Progress = 50;
- jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
- jobItem.ItemDateModifiedTicks = item.DateModified.Ticks;
- await _syncManager.UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- }
-
- private async Task<string> DownloadFile(SyncJobItem jobItem, MediaSourceInfo mediaSource, CancellationToken cancellationToken)
- {
- // TODO: Download
- return mediaSource.Path;
- }
-
- public string GetTemporaryPath(SyncJob job)
- {
- return GetTemporaryPath(job.Id);
- }
-
- public string GetTemporaryPath(string jobId)
- {
- var basePath = _config.GetSyncOptions().TemporaryPath;
-
- if (string.IsNullOrWhiteSpace(basePath))
- {
- basePath = Path.Combine(_config.CommonApplicationPaths.ProgramDataPath, "sync");
- }
-
- return Path.Combine(basePath, jobId);
- }
-
- public string GetTemporaryPath(SyncJobItem jobItem)
- {
- return Path.Combine(GetTemporaryPath(jobItem.JobId), jobItem.Id);
- }
-
- private async Task<MediaSourceInfo> GetEncodedMediaSource(string path, User user, bool isVideo)
- {
- var item = _libraryManager.ResolvePath(_fileSystem.GetFileSystemInfo(path));
-
- await item.RefreshMetadata(CancellationToken.None).ConfigureAwait(false);
-
- var hasMediaSources = item as IHasMediaSources;
-
- var mediaSources = _mediaSourceManager.GetStaticMediaSources(hasMediaSources, false).ToList();
-
- var preferredAudio = string.IsNullOrEmpty(user.Configuration.AudioLanguagePreference)
- ? new string[] { }
- : new[] { user.Configuration.AudioLanguagePreference };
-
- var preferredSubs = string.IsNullOrEmpty(user.Configuration.SubtitleLanguagePreference)
- ? new List<string>() : new List<string> { user.Configuration.SubtitleLanguagePreference };
-
- foreach (var source in mediaSources)
- {
- if (isVideo)
- {
- source.DefaultAudioStreamIndex =
- MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.Configuration.PlayDefaultAudioTrack);
-
- var defaultAudioIndex = source.DefaultAudioStreamIndex;
- var audioLangage = defaultAudioIndex == null
- ? null
- : source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault();
-
- source.DefaultAudioStreamIndex =
- MediaStreamSelector.GetDefaultSubtitleStreamIndex(source.MediaStreams, preferredSubs, user.Configuration.SubtitleMode, audioLangage);
- }
- else
- {
- var audio = source.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
-
- if (audio != null)
- {
- source.DefaultAudioStreamIndex = audio.Index;
- }
-
- }
- }
-
- return mediaSources.FirstOrDefault();
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
deleted file mode 100644
index 7bcb7b05e..000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs
+++ /dev/null
@@ -1,1361 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Controller.TV;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Sync;
-using MediaBrowser.Model.Users;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncManager : ISyncManager
- {
- private readonly ILibraryManager _libraryManager;
- private readonly ISyncRepository _repo;
- private readonly IImageProcessor _imageProcessor;
- private readonly ILogger _logger;
- private readonly IUserManager _userManager;
- private readonly Func<IDtoService> _dtoService;
- private readonly IServerApplicationHost _appHost;
- private readonly ITVSeriesManager _tvSeriesManager;
- private readonly Func<IMediaEncoder> _mediaEncoder;
- private readonly IFileSystem _fileSystem;
- private readonly Func<ISubtitleEncoder> _subtitleEncoder;
- private readonly IConfigurationManager _config;
- private readonly IUserDataManager _userDataManager;
- private readonly Func<IMediaSourceManager> _mediaSourceManager;
- private readonly IJsonSerializer _json;
- private readonly ITaskManager _taskManager;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
-
- private ISyncProvider[] _providers = { };
-
- public event EventHandler<GenericEventArgs<SyncJobCreationResult>> SyncJobCreated;
- public event EventHandler<GenericEventArgs<SyncJob>> SyncJobCancelled;
- public event EventHandler<GenericEventArgs<SyncJob>> SyncJobUpdated;
- public event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemUpdated;
- public event EventHandler<GenericEventArgs<SyncJobItem>> SyncJobItemCreated;
-
- public SyncManager(ILibraryManager libraryManager, ISyncRepository repo, IImageProcessor imageProcessor, ILogger logger, IUserManager userManager, Func<IDtoService> dtoService, IServerApplicationHost appHost, ITVSeriesManager tvSeriesManager, Func<IMediaEncoder> mediaEncoder, IFileSystem fileSystem, Func<ISubtitleEncoder> subtitleEncoder, IConfigurationManager config, IUserDataManager userDataManager, Func<IMediaSourceManager> mediaSourceManager, IJsonSerializer json, ITaskManager taskManager, IMemoryStreamProvider memoryStreamProvider)
- {
- _libraryManager = libraryManager;
- _repo = repo;
- _imageProcessor = imageProcessor;
- _logger = logger;
- _userManager = userManager;
- _dtoService = dtoService;
- _appHost = appHost;
- _tvSeriesManager = tvSeriesManager;
- _mediaEncoder = mediaEncoder;
- _fileSystem = fileSystem;
- _subtitleEncoder = subtitleEncoder;
- _config = config;
- _userDataManager = userDataManager;
- _mediaSourceManager = mediaSourceManager;
- _json = json;
- _taskManager = taskManager;
- _memoryStreamProvider = memoryStreamProvider;
- }
-
- public void AddParts(IEnumerable<ISyncProvider> providers)
- {
- _providers = providers.ToArray();
- }
-
- public IEnumerable<IServerSyncProvider> ServerSyncProviders
- {
- get { return _providers.OfType<IServerSyncProvider>(); }
- }
-
- private readonly ConcurrentDictionary<string, ISyncDataProvider> _dataProviders =
- new ConcurrentDictionary<string, ISyncDataProvider>(StringComparer.OrdinalIgnoreCase);
-
- public ISyncDataProvider GetDataProvider(IServerSyncProvider provider, SyncTarget target)
- {
- return _dataProviders.GetOrAdd(target.Id, key => new TargetDataProvider(provider, target, _appHost, _logger, _json, _fileSystem, _config.CommonApplicationPaths, _memoryStreamProvider));
- }
-
- public async Task<SyncJobCreationResult> CreateJob(SyncJobRequest request)
- {
- var processor = GetSyncJobProcessor();
-
- var user = _userManager.GetUserById(request.UserId);
-
- var items = (await processor
- .GetItemsForSync(request.Category, request.ParentId, request.ItemIds, user, request.UnwatchedOnly).ConfigureAwait(false))
- .ToList();
-
- if (items.Any(i => !SupportsSync(i)))
- {
- throw new ArgumentException("Item does not support sync.");
- }
-
- if (string.IsNullOrWhiteSpace(request.Name))
- {
- if (request.ItemIds.Count == 1)
- {
- request.Name = GetDefaultName(_libraryManager.GetItemById(request.ItemIds[0]));
- }
- }
-
- if (string.IsNullOrWhiteSpace(request.Name))
- {
- request.Name = DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString();
- }
-
- var target = GetSyncTargets(request.UserId)
- .FirstOrDefault(i => string.Equals(request.TargetId, i.Id));
-
- if (target == null)
- {
- throw new ArgumentException("Sync target not found.");
- }
-
- var jobId = Guid.NewGuid().ToString("N");
-
- if (string.IsNullOrWhiteSpace(request.Quality))
- {
- request.Quality = GetQualityOptions(request.TargetId)
- .Where(i => i.IsDefault)
- .Select(i => i.Id)
- .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
- }
-
- var job = new SyncJob
- {
- Id = jobId,
- Name = request.Name,
- TargetId = target.Id,
- UserId = request.UserId,
- UnwatchedOnly = request.UnwatchedOnly,
- ItemLimit = request.ItemLimit,
- RequestedItemIds = request.ItemIds ?? new List<string>(),
- DateCreated = DateTime.UtcNow,
- DateLastModified = DateTime.UtcNow,
- SyncNewContent = request.SyncNewContent,
- ItemCount = items.Count,
- Category = request.Category,
- ParentId = request.ParentId,
- Quality = request.Quality,
- Profile = request.Profile,
- Bitrate = request.Bitrate
- };
-
- if (!request.Category.HasValue && request.ItemIds != null)
- {
- var requestedItems = request.ItemIds
- .Select(_libraryManager.GetItemById)
- .Where(i => i != null);
-
- // It's just a static list
- if (!requestedItems.Any(i => i.IsFolder || i is IItemByName))
- {
- job.SyncNewContent = false;
- }
- }
-
- await _repo.Create(job).ConfigureAwait(false);
-
- await processor.EnsureJobItems(job).ConfigureAwait(false);
-
- // If it already has a converting status then is must have been aborted during conversion
- var jobItemsResult = GetJobItems(new SyncJobItemQuery
- {
- Statuses = new[] { SyncJobItemStatus.Queued, SyncJobItemStatus.Converting },
- JobId = jobId,
- AddMetadata = false
- });
-
- await processor.SyncJobItems(jobItemsResult.Items, false, new Progress<double>(), CancellationToken.None)
- .ConfigureAwait(false);
-
- jobItemsResult = GetJobItems(new SyncJobItemQuery
- {
- Statuses = new[] { SyncJobItemStatus.Queued, SyncJobItemStatus.Converting },
- JobId = jobId,
- AddMetadata = false
- });
-
- var returnResult = new SyncJobCreationResult
- {
- Job = GetJob(jobId),
- JobItems = jobItemsResult.Items.ToList()
- };
-
- if (SyncJobCreated != null)
- {
- EventHelper.FireEventIfNotNull(SyncJobCreated, this, new GenericEventArgs<SyncJobCreationResult>
- {
- Argument = returnResult
-
- }, _logger);
- }
-
- if (returnResult.JobItems.Any(i => i.Status == SyncJobItemStatus.Queued || i.Status == SyncJobItemStatus.Converting))
- {
- _taskManager.QueueScheduledTask<SyncConvertScheduledTask>();
- }
-
- return returnResult;
- }
-
- public async Task UpdateJob(SyncJob job)
- {
- // Get fresh from the db and only update the fields that are supported to be changed.
- var instance = _repo.GetJob(job.Id);
-
- instance.Name = job.Name;
- instance.Quality = job.Quality;
- instance.Profile = job.Profile;
- instance.UnwatchedOnly = job.UnwatchedOnly;
- instance.SyncNewContent = job.SyncNewContent;
- instance.ItemLimit = job.ItemLimit;
-
- await _repo.Update(instance).ConfigureAwait(false);
-
- OnSyncJobUpdated(instance);
- }
-
- internal void OnSyncJobUpdated(SyncJob job)
- {
- if (SyncJobUpdated != null)
- {
- EventHelper.FireEventIfNotNull(SyncJobUpdated, this, new GenericEventArgs<SyncJob>
- {
- Argument = job
-
- }, _logger);
- }
- }
-
- internal async Task UpdateSyncJobItemInternal(SyncJobItem jobItem)
- {
- await _repo.Update(jobItem).ConfigureAwait(false);
-
- if (SyncJobUpdated != null)
- {
- EventHelper.FireEventIfNotNull(SyncJobItemUpdated, this, new GenericEventArgs<SyncJobItem>
- {
- Argument = jobItem
-
- }, _logger);
- }
- }
-
- internal void OnSyncJobItemCreated(SyncJobItem job)
- {
- if (SyncJobUpdated != null)
- {
- EventHelper.FireEventIfNotNull(SyncJobItemCreated, this, new GenericEventArgs<SyncJobItem>
- {
- Argument = job
-
- }, _logger);
- }
- }
-
- public async Task<QueryResult<SyncJob>> GetJobs(SyncJobQuery query)
- {
- var result = _repo.GetJobs(query);
-
- foreach (var item in result.Items)
- {
- await FillMetadata(item).ConfigureAwait(false);
- }
-
- return result;
- }
-
- private async Task FillMetadata(SyncJob job)
- {
- var user = _userManager.GetUserById(job.UserId);
-
- if (user == null)
- {
- return;
- }
-
- var target = GetSyncTargets(job.UserId)
- .FirstOrDefault(i => string.Equals(i.Id, job.TargetId, StringComparison.OrdinalIgnoreCase));
-
- if (target != null)
- {
- job.TargetName = target.Name;
- }
-
- var item = job.RequestedItemIds
- .Select(_libraryManager.GetItemById)
- .FirstOrDefault(i => i != null);
-
- if (item == null)
- {
- var processor = GetSyncJobProcessor();
-
- item = (await processor
- .GetItemsForSync(job.Category, job.ParentId, job.RequestedItemIds, user, job.UnwatchedOnly).ConfigureAwait(false))
- .FirstOrDefault();
- }
-
- if (item != null)
- {
- var hasSeries = item as IHasSeries;
- if (hasSeries != null)
- {
- job.ParentName = hasSeries.SeriesName;
- }
-
- var hasAlbumArtist = item as IHasAlbumArtist;
- if (hasAlbumArtist != null)
- {
- job.ParentName = hasAlbumArtist.AlbumArtists.FirstOrDefault();
- }
-
- var primaryImage = item.GetImageInfo(ImageType.Primary, 0);
- var itemWithImage = item;
-
- if (primaryImage == null)
- {
- var parentWithImage = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary));
-
- if (parentWithImage != null)
- {
- itemWithImage = parentWithImage;
- primaryImage = parentWithImage.GetImageInfo(ImageType.Primary, 0);
- }
- }
-
- if (primaryImage != null)
- {
- try
- {
- job.PrimaryImageTag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Primary);
- job.PrimaryImageItemId = itemWithImage.Id.ToString("N");
-
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting image info", ex);
- }
- }
- }
- }
-
- private void FillMetadata(SyncJobItem jobItem)
- {
- var item = _libraryManager.GetItemById(jobItem.ItemId);
-
- if (item == null)
- {
- return;
- }
-
- var primaryImage = item.GetImageInfo(ImageType.Primary, 0);
- var itemWithImage = item;
-
- if (primaryImage == null)
- {
- var parentWithImage = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary));
-
- if (parentWithImage != null)
- {
- itemWithImage = parentWithImage;
- primaryImage = parentWithImage.GetImageInfo(ImageType.Primary, 0);
- }
- }
-
- if (primaryImage != null)
- {
- try
- {
- jobItem.PrimaryImageTag = _imageProcessor.GetImageCacheTag(itemWithImage, ImageType.Primary);
- jobItem.PrimaryImageItemId = itemWithImage.Id.ToString("N");
-
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting image info", ex);
- }
- }
- }
-
- public async Task CancelJob(string id)
- {
- var job = GetJob(id);
-
- if (job == null)
- {
- throw new ArgumentException("Job not found.");
- }
-
- await _repo.DeleteJob(id).ConfigureAwait(false);
-
- var path = GetSyncJobProcessor().GetTemporaryPath(id);
-
- try
- {
- _fileSystem.DeleteDirectory(path, true);
- }
- catch (DirectoryNotFoundException)
- {
-
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting directory {0}", ex, path);
- }
-
- if (SyncJobCancelled != null)
- {
- EventHelper.FireEventIfNotNull(SyncJobCancelled, this, new GenericEventArgs<SyncJob>
- {
- Argument = job
-
- }, _logger);
- }
- }
-
- public SyncJob GetJob(string id)
- {
- return _repo.GetJob(id);
- }
-
- public IEnumerable<SyncTarget> GetSyncTargets(string userId)
- {
- return _providers
- .SelectMany(i => GetSyncTargets(i, userId))
- .OrderBy(i => i.Name);
- }
-
- private IEnumerable<SyncTarget> GetSyncTargets(ISyncProvider provider)
- {
- return provider.GetAllSyncTargets().Select(i => new SyncTarget
- {
- Name = i.Name,
- Id = GetSyncTargetId(provider, i)
- });
- }
-
- private IEnumerable<SyncTarget> GetSyncTargets(ISyncProvider provider, string userId)
- {
- return provider.GetSyncTargets(userId).Select(i => new SyncTarget
- {
- Name = i.Name,
- Id = GetSyncTargetId(provider, i)
- });
- }
-
- private string GetSyncTargetId(ISyncProvider provider, SyncTarget target)
- {
- var hasUniqueId = provider as IHasUniqueTargetIds;
-
- if (hasUniqueId != null)
- {
- return target.Id;
- }
-
- return target.Id;
- //var providerId = GetSyncProviderId(provider);
- //return (providerId + "-" + target.Id).GetMD5().ToString("N");
- }
-
- private string GetSyncProviderId(ISyncProvider provider)
- {
- return provider.GetType().Name.GetMD5().ToString("N");
- }
-
- public bool SupportsSync(BaseItem item)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- if (item is Playlist)
- {
- return true;
- }
-
- if (item is Person)
- {
- return false;
- }
-
- if (item is Year)
- {
- return false;
- }
-
- if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(item.MediaType, MediaType.Photo, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(item.MediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(item.MediaType, MediaType.Book, StringComparison.OrdinalIgnoreCase))
- {
- if (item.LocationType == LocationType.Virtual)
- {
- return false;
- }
-
- var video = item as Video;
- if (video != null)
- {
- if (video.IsPlaceHolder)
- {
- return false;
- }
-
- if (video.IsShortcut)
- {
- return false;
- }
- }
-
- if (item.SourceType != SourceType.Library)
- {
- return false;
- }
-
- return true;
- }
-
- if (item.SourceType == SourceType.Channel)
- {
- return BaseItem.ChannelManager.SupportsSync(item.ChannelId);
- }
-
- return item.LocationType == LocationType.FileSystem || item is Season;
- }
-
- private string GetDefaultName(BaseItem item)
- {
- return item.Name;
- }
-
- public async Task ReportSyncJobItemTransferred(string id)
- {
- var jobItem = _repo.GetJobItem(id);
-
- 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
- {
- _fileSystem.DeleteDirectory(jobItem.TemporaryPath, true);
- }
- catch (DirectoryNotFoundException)
- {
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting temporary job file: {0}", ex, jobItem.OutputPath);
- }
- }
- }
-
- private SyncJobProcessor GetSyncJobProcessor()
- {
- return new SyncJobProcessor(_libraryManager, _repo, this, _logger, _userManager, _tvSeriesManager, _mediaEncoder(), _subtitleEncoder(), _config, _fileSystem, _mediaSourceManager());
- }
-
- public SyncJobItem GetJobItem(string id)
- {
- return _repo.GetJobItem(id);
- }
-
- public QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query)
- {
- var result = _repo.GetJobItems(query);
-
- if (query.AddMetadata)
- {
- foreach (var item in result.Items)
- {
- FillMetadata(item);
- }
- }
-
- return result;
- }
-
- private SyncedItem GetJobItemInfo(SyncJobItem jobItem)
- {
- var job = _repo.GetJob(jobItem.JobId);
-
- if (job == null)
- {
- _logger.Error("GetJobItemInfo job id {0} no longer exists", jobItem.JobId);
- return null;
- }
-
- var libraryItem = _libraryManager.GetItemById(jobItem.ItemId);
-
- if (libraryItem == null)
- {
- _logger.Error("GetJobItemInfo library item with id {0} no longer exists", jobItem.ItemId);
- return null;
- }
-
- var syncedItem = new SyncedItem
- {
- SyncJobId = jobItem.JobId,
- SyncJobItemId = jobItem.Id,
- ServerId = _appHost.SystemId,
- UserId = job.UserId,
- SyncJobName = job.Name,
- SyncJobDateCreated = job.DateCreated,
- AdditionalFiles = jobItem.AdditionalFiles.Select(i => new ItemFileInfo
- {
- ImageType = i.ImageType,
- Name = i.Name,
- Type = i.Type,
- Index = i.Index
-
- }).ToList()
- };
-
- var dtoOptions = new DtoOptions();
-
- // Remove some bloat
- dtoOptions.Fields.Remove(ItemFields.MediaStreams);
- dtoOptions.Fields.Remove(ItemFields.IndexOptions);
- dtoOptions.Fields.Remove(ItemFields.MediaSourceCount);
- dtoOptions.Fields.Remove(ItemFields.Path);
- dtoOptions.Fields.Remove(ItemFields.SeriesGenres);
- dtoOptions.Fields.Remove(ItemFields.Settings);
- dtoOptions.Fields.Remove(ItemFields.SyncInfo);
- dtoOptions.Fields.Remove(ItemFields.BasicSyncInfo);
-
- syncedItem.Item = _dtoService().GetBaseItemDto(libraryItem, dtoOptions);
-
- var mediaSource = jobItem.MediaSource;
-
- syncedItem.Item.MediaSources = new List<MediaSourceInfo>();
-
- syncedItem.OriginalFileName = Path.GetFileName(libraryItem.Path);
- if (string.IsNullOrWhiteSpace(syncedItem.OriginalFileName))
- {
- syncedItem.OriginalFileName = Path.GetFileName(mediaSource.Path);
- }
-
- // This will be null for items that are not audio/video
- if (mediaSource != null)
- {
- syncedItem.OriginalFileName = Path.ChangeExtension(syncedItem.OriginalFileName, Path.GetExtension(mediaSource.Path));
- syncedItem.Item.MediaSources.Add(mediaSource);
- }
- if (string.IsNullOrWhiteSpace(syncedItem.OriginalFileName))
- {
- syncedItem.OriginalFileName = libraryItem.Name;
- }
-
- return syncedItem;
- }
-
- public Task ReportOfflineAction(UserAction action)
- {
- switch (action.Type)
- {
- case UserActionType.PlayedItem:
- return ReportOfflinePlayedItem(action);
- default:
- throw new ArgumentException("Unexpected action type");
- }
- }
-
- private Task ReportOfflinePlayedItem(UserAction action)
- {
- var item = _libraryManager.GetItemById(action.ItemId);
- var userData = _userDataManager.GetUserData(action.UserId, item);
-
- userData.LastPlayedDate = action.Date;
- _userDataManager.UpdatePlayState(item, userData, action.PositionTicks);
-
- return _userDataManager.SaveUserData(new Guid(action.UserId), item, userData, UserDataSaveReason.Import, CancellationToken.None);
- }
-
- public async Task<List<SyncedItem>> GetReadySyncItems(string targetId)
- {
- var processor = GetSyncJobProcessor();
-
- await processor.SyncJobItems(targetId, false, new Progress<double>(), CancellationToken.None).ConfigureAwait(false);
-
- var jobItemResult = GetJobItems(new SyncJobItemQuery
- {
- TargetId = targetId,
- Statuses = new[]
- {
- SyncJobItemStatus.ReadyToTransfer,
- SyncJobItemStatus.Transferring
- }
- });
-
- var readyItems = jobItemResult.Items
- .Select(GetJobItemInfo)
- .Where(i => i != null)
- .ToList();
-
- _logger.Debug("Returning {0} ready sync items for targetId {1}", readyItems.Count, targetId);
-
- return readyItems;
- }
-
- public async Task<SyncDataResponse> SyncData(SyncDataRequest request)
- {
- if (request.SyncJobItemIds != null)
- {
- return await SyncDataUsingSyncJobItemIds(request).ConfigureAwait(false);
- }
-
- var jobItemResult = GetJobItems(new SyncJobItemQuery
- {
- TargetId = request.TargetId,
- Statuses = new[] { SyncJobItemStatus.Synced }
- });
-
- var response = new SyncDataResponse();
-
- foreach (var jobItem in jobItemResult.Items)
- {
- var requiresSaving = false;
- var removeFromDevice = false;
-
- if (request.LocalItemIds.Contains(jobItem.ItemId, StringComparer.OrdinalIgnoreCase))
- {
- var libraryItem = _libraryManager.GetItemById(jobItem.ItemId);
-
- var job = _repo.GetJob(jobItem.JobId);
- var user = _userManager.GetUserById(job.UserId);
-
- if (jobItem.IsMarkedForRemoval)
- {
- // Tell the device to remove it since it has been marked for removal
- _logger.Info("Adding ItemIdsToRemove {0} because IsMarkedForRemoval is set.", jobItem.ItemId);
- removeFromDevice = true;
- }
- else if (user == null)
- {
- // Tell the device to remove it since the user is gone now
- _logger.Info("Adding ItemIdsToRemove {0} because the user is no longer valid.", jobItem.ItemId);
- removeFromDevice = true;
- }
- else if (!IsLibraryItemAvailable(libraryItem))
- {
- // Tell the device to remove it since it's no longer available
- _logger.Info("Adding ItemIdsToRemove {0} because it is no longer available.", jobItem.ItemId);
- removeFromDevice = true;
- }
- else if (job.UnwatchedOnly)
- {
- if (libraryItem is Video && libraryItem.IsPlayed(user))
- {
- // Tell the device to remove it since it has been played
- _logger.Info("Adding ItemIdsToRemove {0} because it has been marked played.", jobItem.ItemId);
- removeFromDevice = true;
- }
- }
- else if (libraryItem != null && libraryItem.DateModified.Ticks != jobItem.ItemDateModifiedTicks && jobItem.ItemDateModifiedTicks > 0)
- {
- _logger.Info("Setting status to Queued for {0} because the media has been modified since the original sync.", jobItem.ItemId);
- jobItem.Status = SyncJobItemStatus.Queued;
- jobItem.Progress = 0;
- requiresSaving = true;
- }
- }
- else
- {
- // Content is no longer on the device
- if (jobItem.IsMarkedForRemoval)
- {
- jobItem.Status = SyncJobItemStatus.RemovedFromDevice;
- }
- else
- {
- _logger.Info("Setting status to Queued for {0} because it is no longer on the device.", jobItem.ItemId);
- jobItem.Status = SyncJobItemStatus.Queued;
- jobItem.Progress = 0;
- }
- requiresSaving = true;
- }
-
- if (removeFromDevice)
- {
- response.ItemIdsToRemove.Add(jobItem.ItemId);
- jobItem.IsMarkedForRemoval = true;
- requiresSaving = true;
- }
-
- if (requiresSaving)
- {
- await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- }
- }
-
- // Now check each item that's on the device
- foreach (var itemId in request.LocalItemIds)
- {
- // See if it's already marked for removal
- if (response.ItemIdsToRemove.Contains(itemId, StringComparer.OrdinalIgnoreCase))
- {
- continue;
- }
-
- // If there isn't a sync job for this item, mark it for removal
- if (!jobItemResult.Items.Any(i => string.Equals(itemId, i.ItemId, StringComparison.OrdinalIgnoreCase)))
- {
- response.ItemIdsToRemove.Add(itemId);
- }
- }
-
- response.ItemIdsToRemove = response.ItemIdsToRemove.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
-
- var itemsOnDevice = request.LocalItemIds
- .Except(response.ItemIdsToRemove)
- .ToList();
-
- SetUserAccess(request, response, itemsOnDevice);
-
- return response;
- }
-
- private async Task<SyncDataResponse> SyncDataUsingSyncJobItemIds(SyncDataRequest request)
- {
- var jobItemResult = GetJobItems(new SyncJobItemQuery
- {
- TargetId = request.TargetId,
- Statuses = new[] { SyncJobItemStatus.Synced }
- });
-
- var response = new SyncDataResponse();
-
- foreach (var jobItem in jobItemResult.Items)
- {
- var requiresSaving = false;
- var removeFromDevice = false;
-
- if (request.SyncJobItemIds.Contains(jobItem.Id, StringComparer.OrdinalIgnoreCase))
- {
- var libraryItem = _libraryManager.GetItemById(jobItem.ItemId);
-
- var job = _repo.GetJob(jobItem.JobId);
- var user = _userManager.GetUserById(job.UserId);
-
- if (jobItem.IsMarkedForRemoval)
- {
- // Tell the device to remove it since it has been marked for removal
- _logger.Info("Adding ItemIdsToRemove {0} because IsMarkedForRemoval is set.", jobItem.Id);
- removeFromDevice = true;
- }
- else if (user == null)
- {
- // Tell the device to remove it since the user is gone now
- _logger.Info("Adding ItemIdsToRemove {0} because the user is no longer valid.", jobItem.Id);
- removeFromDevice = true;
- }
- else if (!IsLibraryItemAvailable(libraryItem))
- {
- // Tell the device to remove it since it's no longer available
- _logger.Info("Adding ItemIdsToRemove {0} because it is no longer available.", jobItem.Id);
- removeFromDevice = true;
- }
- else if (job.UnwatchedOnly)
- {
- if (libraryItem is Video && libraryItem.IsPlayed(user))
- {
- // Tell the device to remove it since it has been played
- _logger.Info("Adding ItemIdsToRemove {0} because it has been marked played.", jobItem.Id);
- removeFromDevice = true;
- }
- }
- else if (libraryItem != null && libraryItem.DateModified.Ticks != jobItem.ItemDateModifiedTicks && jobItem.ItemDateModifiedTicks > 0)
- {
- _logger.Info("Setting status to Queued for {0} because the media has been modified since the original sync.", jobItem.ItemId);
- jobItem.Status = SyncJobItemStatus.Queued;
- jobItem.Progress = 0;
- requiresSaving = true;
- }
- }
- else
- {
- // Content is no longer on the device
- if (jobItem.IsMarkedForRemoval)
- {
- jobItem.Status = SyncJobItemStatus.RemovedFromDevice;
- }
- else
- {
- _logger.Info("Setting status to Queued for {0} because it is no longer on the device.", jobItem.Id);
- jobItem.Status = SyncJobItemStatus.Queued;
- jobItem.Progress = 0;
- }
- requiresSaving = true;
- }
-
- if (removeFromDevice)
- {
- response.ItemIdsToRemove.Add(jobItem.Id);
- jobItem.IsMarkedForRemoval = true;
- requiresSaving = true;
- }
-
- if (requiresSaving)
- {
- await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
- }
- }
-
- // Now check each item that's on the device
- foreach (var syncJobItemId in request.SyncJobItemIds)
- {
- // See if it's already marked for removal
- if (response.ItemIdsToRemove.Contains(syncJobItemId, StringComparer.OrdinalIgnoreCase))
- {
- continue;
- }
-
- // If there isn't a sync job for this item, mark it for removal
- if (!jobItemResult.Items.Any(i => string.Equals(syncJobItemId, i.Id, StringComparison.OrdinalIgnoreCase)))
- {
- response.ItemIdsToRemove.Add(syncJobItemId);
- }
- }
-
- response.ItemIdsToRemove = response.ItemIdsToRemove.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
-
- return response;
- }
-
- private void SetUserAccess(SyncDataRequest request, SyncDataResponse response, List<string> itemIds)
- {
- var users = request.OfflineUserIds
- .Select(_userManager.GetUserById)
- .Where(i => i != null)
- .ToList();
-
- foreach (var itemId in itemIds)
- {
- var item = _libraryManager.GetItemById(itemId);
-
- if (item != null)
- {
- response.ItemUserAccess[itemId] = users
- .Where(i => IsUserVisible(item, i))
- .Select(i => i.Id.ToString("N"))
- .OrderBy(i => i)
- .ToList();
- }
- }
- }
-
- private bool IsUserVisible(BaseItem item, User user)
- {
- return item.IsVisibleStandalone(user);
- }
-
- private bool IsLibraryItemAvailable(BaseItem item)
- {
- if (item == null)
- {
- return false;
- }
-
- return true;
- }
-
- public async Task ReEnableJobItem(string id)
- {
- var jobItem = _repo.GetJobItem(id);
-
- if (jobItem.Status != SyncJobItemStatus.Failed && jobItem.Status != SyncJobItemStatus.Cancelled)
- {
- throw new ArgumentException("Operation is not valid for this job item");
- }
-
- jobItem.Status = SyncJobItemStatus.Queued;
- jobItem.Progress = 0;
- jobItem.IsMarkedForRemoval = false;
-
- await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
-
- var processor = GetSyncJobProcessor();
-
- await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
- }
-
- public async Task CancelItems(string targetId, IEnumerable<string> itemIds)
- {
- foreach (var item in itemIds)
- {
- var syncJobItemResult = GetJobItems(new SyncJobItemQuery
- {
- AddMetadata = false,
- ItemId = item,
- TargetId = targetId,
- Statuses = new[] { SyncJobItemStatus.Queued, SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Converting, SyncJobItemStatus.Synced, SyncJobItemStatus.Failed }
- });
-
- foreach (var jobItem in syncJobItemResult.Items)
- {
- await CancelJobItem(jobItem.Id).ConfigureAwait(false);
- }
- }
- }
-
- public async Task CancelJobItem(string id)
- {
- 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 && jobItem.Status != SyncJobItemStatus.Transferring)
- {
- throw new ArgumentException("Operation is not valid for this job item");
- }
-
- if (jobItem.Status != SyncJobItemStatus.Synced)
- {
- jobItem.Status = SyncJobItemStatus.Cancelled;
- }
-
- jobItem.Progress = 0;
- jobItem.IsMarkedForRemoval = true;
-
- await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
-
- var processor = GetSyncJobProcessor();
-
- await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
-
- var path = processor.GetTemporaryPath(jobItem);
-
- try
- {
- _fileSystem.DeleteDirectory(path, true);
- }
- catch (DirectoryNotFoundException)
- {
-
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error deleting directory {0}", ex, path);
- }
-
- //var jobItemsResult = GetJobItems(new SyncJobItemQuery
- //{
- // AddMetadata = false,
- // JobId = jobItem.JobId,
- // Limit = 0,
- // Statuses = new[] { SyncJobItemStatus.Converting, SyncJobItemStatus.Failed, SyncJobItemStatus.Queued, SyncJobItemStatus.ReadyToTransfer, SyncJobItemStatus.Synced, SyncJobItemStatus.Transferring }
- //});
-
- //if (jobItemsResult.TotalRecordCount == 0)
- //{
- // await CancelJob(jobItem.JobId).ConfigureAwait(false);
- //}
- }
-
- public Task MarkJobItemForRemoval(string id)
- {
- return CancelJobItem(id);
- }
-
- public async Task UnmarkJobItemForRemoval(string id)
- {
- var jobItem = _repo.GetJobItem(id);
-
- if (jobItem.Status != SyncJobItemStatus.Synced)
- {
- throw new ArgumentException("Operation is not valid for this job item");
- }
-
- jobItem.IsMarkedForRemoval = false;
-
- await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
-
- var processor = GetSyncJobProcessor();
-
- await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
- }
-
- public async Task ReportSyncJobItemTransferBeginning(string id)
- {
- var jobItem = _repo.GetJobItem(id);
-
- jobItem.Status = SyncJobItemStatus.Transferring;
-
- await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
-
- var processor = GetSyncJobProcessor();
-
- await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
- }
-
- public async Task ReportSyncJobItemTransferFailed(string id)
- {
- var jobItem = _repo.GetJobItem(id);
-
- jobItem.Status = SyncJobItemStatus.ReadyToTransfer;
-
- await UpdateSyncJobItemInternal(jobItem).ConfigureAwait(false);
-
- var processor = GetSyncJobProcessor();
-
- await processor.UpdateJobStatus(jobItem.JobId).ConfigureAwait(false);
- }
-
- public Dictionary<string, SyncedItemProgress> GetSyncedItemProgresses(SyncJobItemQuery query)
- {
- return _repo.GetSyncedItemProgresses(query);
- }
-
- public SyncJobOptions GetAudioOptions(SyncJobItem jobItem, SyncJob job)
- {
- var options = GetSyncJobOptions(jobItem.TargetId, null, null);
-
- if (job.Bitrate.HasValue)
- {
- options.DeviceProfile.MaxStaticBitrate = job.Bitrate.Value;
- }
-
- return options;
- }
-
- public ISyncProvider GetSyncProvider(SyncJobItem jobItem)
- {
- foreach (var provider in _providers)
- {
- foreach (var target in GetSyncTargets(provider))
- {
- if (string.Equals(target.Id, jobItem.TargetId, StringComparison.OrdinalIgnoreCase))
- {
- return provider;
- }
- }
- }
- return null;
- }
-
- public SyncJobOptions GetVideoOptions(SyncJobItem jobItem, SyncJob job)
- {
- var options = GetSyncJobOptions(jobItem.TargetId, job.Profile, job.Quality);
-
- if (job.Bitrate.HasValue)
- {
- options.DeviceProfile.MaxStaticBitrate = job.Bitrate.Value;
- }
-
- return options;
- }
-
- private SyncJobOptions GetSyncJobOptions(string targetId, string profile, string quality)
- {
- foreach (var provider in _providers)
- {
- foreach (var target in GetSyncTargets(provider))
- {
- if (string.Equals(target.Id, targetId, StringComparison.OrdinalIgnoreCase))
- {
- return GetSyncJobOptions(provider, target, profile, quality);
- }
- }
- }
-
- return GetDefaultSyncJobOptions(profile, quality);
- }
-
- private SyncJobOptions GetSyncJobOptions(ISyncProvider provider, SyncTarget target, string profile, string quality)
- {
- var hasProfile = provider as IHasSyncQuality;
-
- if (hasProfile != null)
- {
- return hasProfile.GetSyncJobOptions(target, profile, quality);
- }
-
- return GetDefaultSyncJobOptions(profile, quality);
- }
-
- private SyncJobOptions GetDefaultSyncJobOptions(string profile, string quality)
- {
- var supportsAc3 = string.Equals(profile, "general", StringComparison.OrdinalIgnoreCase);
-
- var deviceProfile = new CloudSyncProfile(supportsAc3, false);
- deviceProfile.MaxStaticBitrate = SyncHelper.AdjustBitrate(deviceProfile.MaxStaticBitrate, quality);
-
- return new SyncJobOptions
- {
- DeviceProfile = deviceProfile,
- IsConverting = IsConverting(profile, quality)
- };
- }
-
- private bool IsConverting(string profile, string quality)
- {
- return !string.Equals(profile, "original", StringComparison.OrdinalIgnoreCase);
- }
-
- public IEnumerable<SyncQualityOption> GetQualityOptions(string targetId)
- {
- return GetQualityOptions(targetId, null);
- }
-
- public IEnumerable<SyncQualityOption> GetQualityOptions(string targetId, User user)
- {
- foreach (var provider in _providers)
- {
- foreach (var target in GetSyncTargets(provider))
- {
- if (string.Equals(target.Id, targetId, StringComparison.OrdinalIgnoreCase))
- {
- return GetQualityOptions(provider, target, user);
- }
- }
- }
-
- return new List<SyncQualityOption>();
- }
-
- private IEnumerable<SyncQualityOption> GetQualityOptions(ISyncProvider provider, SyncTarget target, User user)
- {
- var hasQuality = provider as IHasSyncQuality;
- if (hasQuality != null)
- {
- var options = hasQuality.GetQualityOptions(target);
-
- if (user != null && !user.Policy.EnableSyncTranscoding)
- {
- options = options.Where(i => i.IsOriginalQuality);
- }
-
- return options;
- }
-
- // Default options for providers that don't override
- return new List<SyncQualityOption>
- {
- new SyncQualityOption
- {
- Name = "High",
- Id = "high",
- IsDefault = true
- },
- new SyncQualityOption
- {
- Name = "Medium",
- Id = "medium"
- },
- new SyncQualityOption
- {
- Name = "Low",
- Id = "low"
- },
- new SyncQualityOption
- {
- Name = "Custom",
- Id = "custom"
- }
- };
- }
-
- public IEnumerable<SyncProfileOption> GetProfileOptions(string targetId, User user)
- {
- foreach (var provider in _providers)
- {
- foreach (var target in GetSyncTargets(provider))
- {
- if (string.Equals(target.Id, targetId, StringComparison.OrdinalIgnoreCase))
- {
- return GetProfileOptions(provider, target, user);
- }
- }
- }
-
- return new List<SyncProfileOption>();
- }
-
- public IEnumerable<SyncProfileOption> GetProfileOptions(string targetId)
- {
- return GetProfileOptions(targetId, null);
- }
-
- private IEnumerable<SyncProfileOption> GetProfileOptions(ISyncProvider provider, SyncTarget target, User user)
- {
- var hasQuality = provider as IHasSyncQuality;
- if (hasQuality != null)
- {
- return hasQuality.GetProfileOptions(target);
- }
-
- var list = new List<SyncProfileOption>();
-
- list.Add(new SyncProfileOption
- {
- Name = "Original",
- Id = "Original",
- Description = "Syncs original files as-is.",
- EnableQualityOptions = false
- });
-
- if (user == null || user.Policy.EnableSyncTranscoding)
- {
- list.Add(new SyncProfileOption
- {
- Name = "Baseline",
- Id = "baseline",
- Description = "Designed for compatibility with all devices, including web browsers. Targets H264/AAC video and MP3 audio."
- });
-
- list.Add(new SyncProfileOption
- {
- Name = "General",
- Id = "general",
- Description = "Designed for compatibility with Chromecast, Roku, Smart TV's, and other similar devices. Targets H264/AAC/AC3 video and MP3 audio.",
- IsDefault = true
- });
- }
-
- return list;
- }
-
- protected internal void OnConversionComplete(SyncJobItem item)
- {
- var syncProvider = GetSyncProvider(item);
- if (syncProvider is AppSyncProvider)
- {
- return;
- }
-
- _taskManager.QueueIfNotRunning<ServerSyncScheduledTask>();
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncNotificationEntryPoint.cs b/MediaBrowser.Server.Implementations/Sync/SyncNotificationEntryPoint.cs
deleted file mode 100644
index 7017b422e..000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncNotificationEntryPoint.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Threading;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Sync;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncNotificationEntryPoint : IServerEntryPoint
- {
- private readonly ISessionManager _sessionManager;
- private readonly ISyncManager _syncManager;
-
- public SyncNotificationEntryPoint(ISyncManager syncManager, ISessionManager sessionManager)
- {
- _syncManager = syncManager;
- _sessionManager = sessionManager;
- }
-
- public void Run()
- {
- _syncManager.SyncJobItemUpdated += _syncManager_SyncJobItemUpdated;
- }
-
- private async void _syncManager_SyncJobItemUpdated(object sender, GenericEventArgs<SyncJobItem> e)
- {
- var item = e.Argument;
-
- if (item.Status == SyncJobItemStatus.ReadyToTransfer)
- {
- try
- {
- await _sessionManager.SendMessageToUserDeviceSessions(item.TargetId, "SyncJobItemReady", item, CancellationToken.None).ConfigureAwait(false);
- }
- catch
- {
-
- }
- }
- }
-
- public void Dispose()
- {
- _syncManager.SyncJobItemUpdated -= _syncManager_SyncJobItemUpdated;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncRegistrationInfo.cs b/MediaBrowser.Server.Implementations/Sync/SyncRegistrationInfo.cs
deleted file mode 100644
index 40b84b1c2..000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncRegistrationInfo.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using MediaBrowser.Common.Security;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncRegistrationInfo : IRequiresRegistration
- {
- private readonly ISecurityManager _securityManager;
-
- public static SyncRegistrationInfo Instance;
-
- public SyncRegistrationInfo(ISecurityManager securityManager)
- {
- _securityManager = securityManager;
- Instance = this;
- }
-
- private bool _registered;
- public bool IsRegistered
- {
- get { return _registered; }
- }
-
- public async Task LoadRegistrationInfoAsync()
- {
- var info = await _securityManager.GetRegistrationStatus("sync").ConfigureAwait(false);
-
- _registered = info.IsValid;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
deleted file mode 100644
index e0553b1b1..000000000
--- a/MediaBrowser.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class SyncedMediaSourceProvider : IMediaSourceProvider
- {
- private readonly SyncManager _syncManager;
- private readonly IServerApplicationHost _appHost;
- private readonly ILogger _logger;
-
- public SyncedMediaSourceProvider(ISyncManager syncManager, IServerApplicationHost appHost, ILogger logger)
- {
- _appHost = appHost;
- _logger = logger;
- _syncManager = (SyncManager)syncManager;
- }
-
- public async Task<IEnumerable<MediaSourceInfo>> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken)
- {
- var jobItemResult = _syncManager.GetJobItems(new SyncJobItemQuery
- {
- AddMetadata = false,
- Statuses = new[] { SyncJobItemStatus.Synced },
- ItemId = item.Id.ToString("N")
- });
-
- var list = new List<MediaSourceInfo>();
-
- if (jobItemResult.Items.Length > 0)
- {
- var targets = _syncManager.ServerSyncProviders
- .SelectMany(i => i.GetAllSyncTargets().Select(t => new Tuple<IServerSyncProvider, SyncTarget>(i, t)))
- .ToList();
-
- var serverId = _appHost.SystemId;
-
- foreach (var jobItem in jobItemResult.Items)
- {
- var targetTuple = targets.FirstOrDefault(i => string.Equals(i.Item2.Id, jobItem.TargetId, StringComparison.OrdinalIgnoreCase));
-
- if (targetTuple != null)
- {
- var syncTarget = targetTuple.Item2;
- var syncProvider = targetTuple.Item1;
- var dataProvider = _syncManager.GetDataProvider(targetTuple.Item1, syncTarget);
-
- var localItems = await dataProvider.GetItems(syncTarget, serverId, item.Id.ToString("N")).ConfigureAwait(false);
-
- foreach (var localItem in localItems)
- {
- foreach (var mediaSource in localItem.Item.MediaSources)
- {
- AddMediaSource(list, localItem, mediaSource, syncProvider, syncTarget);
- }
- }
- }
- }
- }
-
- return list;
- }
-
- private void AddMediaSource(List<MediaSourceInfo> list,
- LocalItem item,
- MediaSourceInfo mediaSource,
- IServerSyncProvider provider,
- SyncTarget target)
- {
- SetStaticMediaSourceInfo(item, mediaSource);
-
- var requiresDynamicAccess = provider as IHasDynamicAccess;
-
- if (requiresDynamicAccess != null)
- {
- mediaSource.RequiresOpening = true;
-
- var keyList = new List<string>();
- keyList.Add(provider.GetType().FullName.GetMD5().ToString("N"));
- keyList.Add(target.Id.GetMD5().ToString("N"));
- keyList.Add(item.Id);
- mediaSource.OpenToken = string.Join(StreamIdDelimeterString, keyList.ToArray());
- }
-
- list.Add(mediaSource);
- }
-
- // Do not use a pipe here because Roku http requests to the server will fail, without any explicit error message.
- private const string StreamIdDelimeterString = "_";
-
- public async Task<Tuple<MediaSourceInfo, IDirectStreamProvider>> OpenMediaSource(string openToken, CancellationToken cancellationToken)
- {
- var openKeys = openToken.Split(new[] { StreamIdDelimeterString[0] }, 3);
-
- var provider = _syncManager.ServerSyncProviders
- .FirstOrDefault(i => string.Equals(openKeys[0], i.GetType().FullName.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase));
-
- var target = provider.GetAllSyncTargets()
- .FirstOrDefault(i => string.Equals(openKeys[1], i.Id.GetMD5().ToString("N"), StringComparison.OrdinalIgnoreCase));
-
- var dataProvider = _syncManager.GetDataProvider(provider, target);
- var localItem = await dataProvider.Get(target, openKeys[2]).ConfigureAwait(false);
-
- var fileId = localItem.FileId;
- if (string.IsNullOrWhiteSpace(fileId))
- {
- }
-
- var requiresDynamicAccess = (IHasDynamicAccess)provider;
- var dynamicInfo = await requiresDynamicAccess.GetSyncedFileInfo(fileId, target, cancellationToken).ConfigureAwait(false);
-
- var mediaSource = localItem.Item.MediaSources.First();
- mediaSource.LiveStreamId = Guid.NewGuid().ToString();
- SetStaticMediaSourceInfo(localItem, mediaSource);
-
- foreach (var stream in mediaSource.MediaStreams)
- {
- if (!string.IsNullOrWhiteSpace(stream.ExternalId))
- {
- var dynamicStreamInfo = await requiresDynamicAccess.GetSyncedFileInfo(stream.ExternalId, target, cancellationToken).ConfigureAwait(false);
- stream.Path = dynamicStreamInfo.Path;
- }
- }
-
- mediaSource.Path = dynamicInfo.Path;
- mediaSource.Protocol = dynamicInfo.Protocol;
- mediaSource.RequiredHttpHeaders = dynamicInfo.RequiredHttpHeaders;
-
- return new Tuple<MediaSourceInfo, IDirectStreamProvider>(mediaSource, null);
- }
-
- private void SetStaticMediaSourceInfo(LocalItem item, MediaSourceInfo mediaSource)
- {
- mediaSource.Id = item.Id;
- mediaSource.SupportsTranscoding = false;
- if (mediaSource.Protocol == Model.MediaInfo.MediaProtocol.File)
- {
- mediaSource.ETag = item.Id;
- }
- }
-
- public Task CloseMediaSource(string liveStreamId)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs b/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs
deleted file mode 100644
index 03df0d4e6..000000000
--- a/MediaBrowser.Server.Implementations/Sync/TargetDataProvider.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Sync;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-
-namespace MediaBrowser.Server.Implementations.Sync
-{
- public class TargetDataProvider : ISyncDataProvider
- {
- private readonly SyncTarget _target;
- private readonly IServerSyncProvider _provider;
-
- private readonly SemaphoreSlim _dataLock = new SemaphoreSlim(1, 1);
- private List<LocalItem> _items;
-
- private readonly ILogger _logger;
- private readonly IJsonSerializer _json;
- private readonly IFileSystem _fileSystem;
- private readonly IApplicationPaths _appPaths;
- private readonly IServerApplicationHost _appHost;
- private readonly IMemoryStreamProvider _memoryStreamProvider;
-
- public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths, IMemoryStreamProvider memoryStreamProvider)
- {
- _logger = logger;
- _json = json;
- _provider = provider;
- _target = target;
- _fileSystem = fileSystem;
- _appPaths = appPaths;
- _memoryStreamProvider = memoryStreamProvider;
- _appHost = appHost;
- }
-
- private string[] GetRemotePath()
- {
- var parts = new List<string>
- {
- _appHost.FriendlyName,
- "data.json"
- };
-
- parts = parts.Select(i => GetValidFilename(_provider, i)).ToList();
-
- return parts.ToArray();
- }
-
- private string GetValidFilename(IServerSyncProvider provider, string filename)
- {
- // We can always add this method to the sync provider if it's really needed
- return _fileSystem.GetValidFilename(filename);
- }
-
- private async Task<List<LocalItem>> RetrieveItems(CancellationToken cancellationToken)
- {
- _logger.Debug("Getting {0} from {1}", string.Join(MediaSync.PathSeparatorString, GetRemotePath().ToArray()), _provider.Name);
-
- var fileResult = await _provider.GetFiles(GetRemotePath().ToArray(), _target, cancellationToken).ConfigureAwait(false);
-
- if (fileResult.Items.Length > 0)
- {
- using (var stream = await _provider.GetFile(fileResult.Items[0].FullName, _target, new Progress<double>(), cancellationToken))
- {
- return _json.DeserializeFromStream<List<LocalItem>>(stream);
- }
- }
-
- return new List<LocalItem>();
- }
-
- private async Task EnsureData(CancellationToken cancellationToken)
- {
- if (_items == null)
- {
- _items = await RetrieveItems(cancellationToken).ConfigureAwait(false);
- }
- }
-
- private async Task SaveData(List<LocalItem> items, CancellationToken cancellationToken)
- {
- using (var stream = _memoryStreamProvider.CreateNew())
- {
- _json.SerializeToStream(items, stream);
-
- // Save to sync provider
- stream.Position = 0;
- var remotePath = GetRemotePath();
- _logger.Debug("Saving data.json to {0}. Remote path: {1}", _provider.Name, string.Join("/", remotePath));
-
- await _provider.SendFile(stream, remotePath, _target, new Progress<double>(), cancellationToken).ConfigureAwait(false);
- }
- }
-
- private async Task<T> GetData<T>(bool enableCache, Func<List<LocalItem>, T> dataFactory)
- {
- if (!enableCache)
- {
- var items = await RetrieveItems(CancellationToken.None).ConfigureAwait(false);
- var newCache = items.ToList();
- var result = dataFactory(items);
- await UpdateCache(newCache).ConfigureAwait(false);
- return result;
- }
-
- await _dataLock.WaitAsync().ConfigureAwait(false);
-
- try
- {
- await EnsureData(CancellationToken.None).ConfigureAwait(false);
-
- return dataFactory(_items);
- }
- finally
- {
- _dataLock.Release();
- }
- }
-
- private async Task UpdateData(Func<List<LocalItem>, List<LocalItem>> action)
- {
- var items = await RetrieveItems(CancellationToken.None).ConfigureAwait(false);
- items = action(items);
- await SaveData(items.ToList(), CancellationToken.None).ConfigureAwait(false);
-
- await UpdateCache(null).ConfigureAwait(false);
- }
-
- private async Task UpdateCache(List<LocalItem> list)
- {
- await _dataLock.WaitAsync().ConfigureAwait(false);
-
- try
- {
- _items = list;
- }
- finally
- {
- _dataLock.Release();
- }
- }
-
- public Task<List<LocalItem>> GetLocalItems(SyncTarget target, string serverId)
- {
- return GetData(false, items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase)).ToList());
- }
-
- public Task AddOrUpdate(SyncTarget target, LocalItem item)
- {
- return UpdateData(items =>
- {
- var list = items.Where(i => !string.Equals(i.Id, item.Id, StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- list.Add(item);
-
- return list;
- });
- }
-
- public Task Delete(SyncTarget target, string id)
- {
- return UpdateData(items => items.Where(i => !string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)).ToList());
- }
-
- public Task<LocalItem> Get(SyncTarget target, string id)
- {
- return GetData(true, items => items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)));
- }
-
- public Task<List<LocalItem>> GetItems(SyncTarget target, string serverId, string itemId)
- {
- return GetData(true, items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase) && string.Equals(i.ItemId, itemId, StringComparison.OrdinalIgnoreCase)).ToList());
- }
-
- public Task<List<LocalItem>> GetItemsBySyncJobItemId(SyncTarget target, string serverId, string syncJobItemId)
- {
- return GetData(false, items => items.Where(i => string.Equals(i.ServerId, serverId, StringComparison.OrdinalIgnoreCase) && string.Equals(i.SyncJobItemId, syncJobItemId, StringComparison.OrdinalIgnoreCase)).ToList());
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/TV/SeriesPostScanTask.cs b/MediaBrowser.Server.Implementations/TV/SeriesPostScanTask.cs
deleted file mode 100644
index a498dfec3..000000000
--- a/MediaBrowser.Server.Implementations/TV/SeriesPostScanTask.cs
+++ /dev/null
@@ -1,238 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Tasks;
-using MediaBrowser.Model.Xml;
-using MediaBrowser.Providers.TV;
-
-namespace MediaBrowser.Server.Implementations.TV
-{
- class SeriesGroup : List<Series>, IGrouping<string, Series>
- {
- public string Key { get; set; }
- }
-
- class SeriesPostScanTask : ILibraryPostScanTask, IHasOrder
- {
- /// <summary>
- /// The _library manager
- /// </summary>
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly ILocalizationManager _localization;
- private readonly IFileSystem _fileSystem;
- private readonly IXmlReaderSettingsFactory _xmlSettings;
-
- public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, ILocalizationManager localization, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings)
- {
- _libraryManager = libraryManager;
- _logger = logger;
- _config = config;
- _localization = localization;
- _fileSystem = fileSystem;
- _xmlSettings = xmlSettings;
- }
-
- public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
- {
- return RunInternal(progress, cancellationToken);
- }
-
- private Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Recursive = true,
- GroupByPresentationUniqueKey = false
-
- }).Cast<Series>().ToList();
-
- var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
-
- return new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem, _xmlSettings).Run(seriesGroups, true, cancellationToken);
- }
-
- internal static IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList)
- {
- var links = seriesList.ToDictionary(s => s, s => seriesList.Where(c => c != s && ShareProviderId(s, c)).ToList());
-
- var visited = new HashSet<Series>();
-
- foreach (var series in seriesList)
- {
- if (!visited.Contains(series))
- {
- var group = new SeriesGroup();
- FindAllLinked(series, visited, links, group);
-
- group.Key = group.Select(s => s.GetProviderId(MetadataProviders.Tvdb)).FirstOrDefault(id => !string.IsNullOrEmpty(id));
-
- yield return group;
- }
- }
- }
-
- private static void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results)
- {
- results.Add(series);
- visited.Add(series);
-
- var links = linksMap[series];
-
- foreach (var s in links)
- {
- if (!visited.Contains(s))
- {
- FindAllLinked(s, visited, linksMap, results);
- }
- }
- }
-
- private static bool ShareProviderId(Series a, Series b)
- {
- return a.ProviderIds.Any(id =>
- {
- string value;
- return b.ProviderIds.TryGetValue(id.Key, out value) && id.Value == value;
- });
- }
-
- public int Order
- {
- get
- {
- // Run after tvdb update task
- return 1;
- }
- }
- }
-
- public class CleanMissingEpisodesEntryPoint : IServerEntryPoint
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly ILocalizationManager _localization;
- private readonly IFileSystem _fileSystem;
- private readonly object _libraryChangedSyncLock = new object();
- private const int LibraryUpdateDuration = 180000;
- private readonly ITaskManager _taskManager;
- private readonly IXmlReaderSettingsFactory _xmlSettings;
-
- public CleanMissingEpisodesEntryPoint(ILibraryManager libraryManager, IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, IFileSystem fileSystem, ITaskManager taskManager, IXmlReaderSettingsFactory xmlSettings)
- {
- _libraryManager = libraryManager;
- _config = config;
- _logger = logger;
- _localization = localization;
- _fileSystem = fileSystem;
- _taskManager = taskManager;
- _xmlSettings = xmlSettings;
- }
-
- private Timer LibraryUpdateTimer { get; set; }
-
- public void Run()
- {
- _libraryManager.ItemAdded += _libraryManager_ItemAdded;
- }
-
- private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
- {
- if (!FilterItem(e.Item))
- {
- return;
- }
-
- lock (_libraryChangedSyncLock)
- {
- if (LibraryUpdateTimer == null)
- {
- LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite);
- }
- else
- {
- LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite);
- }
- }
- }
-
- private async void LibraryUpdateTimerCallback(object state)
- {
- try
- {
- if (MissingEpisodeProvider.IsRunning)
- {
- return;
- }
-
- if (_libraryManager.IsScanRunning)
- {
- return;
- }
-
- var seriesList = _libraryManager.GetItemList(new InternalItemsQuery()
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- Recursive = true,
- GroupByPresentationUniqueKey = false
-
- }).Cast<Series>().ToList();
-
- var seriesGroups = SeriesPostScanTask.FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
-
- await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem, _xmlSettings)
- .Run(seriesGroups, false, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error in SeriesPostScanTask", ex);
- }
- }
-
- private bool FilterItem(BaseItem item)
- {
- return item is Episode && item.LocationType != LocationType.Virtual;
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (dispose)
- {
- if (LibraryUpdateTimer != null)
- {
- LibraryUpdateTimer.Dispose();
- LibraryUpdateTimer = null;
- }
-
- _libraryManager.ItemAdded -= _libraryManager_ItemAdded;
- }
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs
deleted file mode 100644
index 03e8a9178..000000000
--- a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs
+++ /dev/null
@@ -1,226 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.TV;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Querying;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Controller.Configuration;
-
-namespace MediaBrowser.Server.Implementations.TV
-{
- public class TVSeriesManager : ITVSeriesManager
- {
- private readonly IUserManager _userManager;
- private readonly IUserDataManager _userDataManager;
- private readonly ILibraryManager _libraryManager;
- private readonly IServerConfigurationManager _config;
-
- public TVSeriesManager(IUserManager userManager, IUserDataManager userDataManager, ILibraryManager libraryManager, IServerConfigurationManager config)
- {
- _userManager = userManager;
- _userDataManager = userDataManager;
- _libraryManager = libraryManager;
- _config = config;
- }
-
- public QueryResult<BaseItem> GetNextUp(NextUpQuery request)
- {
- var user = _userManager.GetUserById(request.UserId);
-
- if (user == null)
- {
- throw new ArgumentException("User not found");
- }
-
- var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
-
- string presentationUniqueKey = null;
- int? limit = null;
- if (!string.IsNullOrWhiteSpace(request.SeriesId))
- {
- var series = _libraryManager.GetItemById(request.SeriesId);
-
- if (series != null)
- {
- presentationUniqueKey = GetUniqueSeriesKey(series);
- limit = 1;
- }
- }
-
- if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue)
- {
- limit = limit.Value + 10;
- }
-
- var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- SortOrder = SortOrder.Ascending,
- PresentationUniqueKey = presentationUniqueKey,
- Limit = limit,
- ParentId = parentIdGuid,
- Recursive = true
-
- }).Cast<Series>();
-
- // Avoid implicitly captured closure
- var episodes = GetNextUpEpisodes(request, user, items);
-
- return GetResult(episodes, null, request);
- }
-
- public QueryResult<BaseItem> GetNextUp(NextUpQuery request, IEnumerable<Folder> parentsFolders)
- {
- var user = _userManager.GetUserById(request.UserId);
-
- if (user == null)
- {
- throw new ArgumentException("User not found");
- }
-
- string presentationUniqueKey = null;
- int? limit = null;
- if (!string.IsNullOrWhiteSpace(request.SeriesId))
- {
- var series = _libraryManager.GetItemById(request.SeriesId);
-
- if (series != null)
- {
- presentationUniqueKey = GetUniqueSeriesKey(series);
- limit = 1;
- }
- }
-
- if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue)
- {
- limit = limit.Value + 10;
- }
-
- var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- IncludeItemTypes = new[] { typeof(Series).Name },
- SortOrder = SortOrder.Ascending,
- PresentationUniqueKey = presentationUniqueKey,
- Limit = limit
-
- }, parentsFolders.Select(i => i.Id.ToString("N"))).Cast<Series>();
-
- // Avoid implicitly captured closure
- var episodes = GetNextUpEpisodes(request, user, items);
-
- return GetResult(episodes, null, request);
- }
-
- public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<Series> series)
- {
- // Avoid implicitly captured closure
- var currentUser = user;
-
- var allNextUp = series
- .Select(i => GetNextUp(i, currentUser))
- .Where(i => i.Item1 != null)
- // Include if an episode was found, and either the series is not unwatched or the specific series was requested
- .OrderByDescending(i => i.Item2)
- .ThenByDescending(i => i.Item1.PremiereDate ?? DateTime.MinValue)
- .ToList();
-
- // If viewing all next up for all series, remove first episodes
- if (string.IsNullOrWhiteSpace(request.SeriesId))
- {
- var withoutFirstEpisode = allNextUp
- .Where(i => !i.Item3)
- .ToList();
-
- // But if that returns empty, keep those first episodes (avoid completely empty view)
- if (withoutFirstEpisode.Count > 0)
- {
- allNextUp = withoutFirstEpisode;
- }
- }
-
- return allNextUp
- .Select(i => i.Item1)
- .Take(request.Limit ?? int.MaxValue);
- }
-
- private string GetUniqueSeriesKey(BaseItem series)
- {
- if (_config.Configuration.SchemaVersion < 97)
- {
- return series.Id.ToString("N");
- }
- return series.GetPresentationUniqueKey();
- }
-
- /// <summary>
- /// Gets the next up.
- /// </summary>
- /// <param name="series">The series.</param>
- /// <param name="user">The user.</param>
- /// <returns>Task{Episode}.</returns>
- private Tuple<Episode, DateTime, bool> GetNextUp(Series series, User user)
- {
- var lastWatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(series),
- IncludeItemTypes = new[] { typeof(Episode).Name },
- SortBy = new[] { ItemSortBy.SortName },
- SortOrder = SortOrder.Descending,
- IsPlayed = true,
- Limit = 1,
- ParentIndexNumberNotEquals = 0
-
- }).FirstOrDefault();
-
- var firstUnwatchedEpisode = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(series),
- IncludeItemTypes = new[] { typeof(Episode).Name },
- SortBy = new[] { ItemSortBy.SortName },
- SortOrder = SortOrder.Ascending,
- Limit = 1,
- IsPlayed = false,
- IsVirtualItem = false,
- ParentIndexNumberNotEquals = 0,
- MinSortName = lastWatchedEpisode == null ? null : lastWatchedEpisode.SortName
-
- }).Cast<Episode>().FirstOrDefault();
-
- if (lastWatchedEpisode != null && firstUnwatchedEpisode != null)
- {
- var userData = _userDataManager.GetUserData(user, lastWatchedEpisode);
-
- var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1);
-
- return new Tuple<Episode, DateTime, bool>(firstUnwatchedEpisode, lastWatchedDate, false);
- }
-
- // Return the first episode
- return new Tuple<Episode, DateTime, bool>(firstUnwatchedEpisode, DateTime.MinValue, true);
- }
-
- private QueryResult<BaseItem> GetResult(IEnumerable<BaseItem> items, int? totalRecordLimit, NextUpQuery query)
- {
- var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();
- var totalCount = itemsArray.Length;
-
- if (query.Limit.HasValue)
- {
- itemsArray = itemsArray.Skip(query.StartIndex ?? 0).Take(query.Limit.Value).ToArray();
- }
- else if (query.StartIndex.HasValue)
- {
- itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray();
- }
-
- return new QueryResult<BaseItem>
- {
- TotalRecordCount = totalCount,
- Items = itemsArray
- };
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs b/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs
deleted file mode 100644
index 4c047b7d5..000000000
--- a/MediaBrowser.Server.Implementations/TextEncoding/TextEncoding.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.Text;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.TextEncoding;
-
-namespace MediaBrowser.Server.Implementations.TextEncoding
-{
- public class TextEncoding : IEncoding
- {
- private readonly IFileSystem _fileSystem;
-
- public TextEncoding(IFileSystem fileSystem)
- {
- _fileSystem = fileSystem;
- }
-
- public byte[] GetASCIIBytes(string text)
- {
- return Encoding.ASCII.GetBytes(text);
- }
-
- public string GetASCIIString(byte[] bytes, int startIndex, int length)
- {
- return Encoding.ASCII.GetString(bytes, 0, bytes.Length);
- }
-
- public Encoding GetFileEncoding(string srcFile)
- {
- // *** Detect byte order mark if any - otherwise assume default
- var buffer = new byte[5];
-
- using (var file = _fileSystem.OpenRead(srcFile))
- {
- file.Read(buffer, 0, 5);
- }
-
- if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf)
- return Encoding.UTF8;
- if (buffer[0] == 0xfe && buffer[1] == 0xff)
- return Encoding.Unicode;
- if (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff)
- return Encoding.UTF32;
- if (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76)
- return Encoding.UTF7;
-
- // It's ok - anything aside from utf is ok since that's what we're looking for
- return Encoding.Default;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Threading/PeriodicTimer.cs b/MediaBrowser.Server.Implementations/Threading/PeriodicTimer.cs
deleted file mode 100644
index 057a84483..000000000
--- a/MediaBrowser.Server.Implementations/Threading/PeriodicTimer.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using System;
-using System.Threading;
-using Microsoft.Win32;
-
-namespace MediaBrowser.Server.Implementations.Threading
-{
- public class PeriodicTimer : IDisposable
- {
- public Action<object> Callback { get; set; }
- private Timer _timer;
- private readonly object _state;
- private readonly object _timerLock = new object();
- private readonly TimeSpan _period;
-
- public PeriodicTimer(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period)
- {
- if (callback == null)
- {
- throw new ArgumentNullException("callback");
- }
-
- Callback = callback;
- _period = period;
- _state = state;
-
- StartTimer(dueTime);
- }
-
- void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
- {
- if (e.Mode == PowerModes.Resume)
- {
- DisposeTimer();
- StartTimer(Timeout.InfiniteTimeSpan);
- }
- }
-
- private void TimerCallback(object state)
- {
- Callback(state);
- }
-
- private void StartTimer(TimeSpan dueTime)
- {
- lock (_timerLock)
- {
- _timer = new Timer(TimerCallback, _state, dueTime, _period);
-
- SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
- }
- }
-
- private void DisposeTimer()
- {
- SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
-
- lock (_timerLock)
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-
- public void Dispose()
- {
- DisposeTimer();
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Updates/InstallationManager.cs b/MediaBrowser.Server.Implementations/Updates/InstallationManager.cs
deleted file mode 100644
index c6930e487..000000000
--- a/MediaBrowser.Server.Implementations/Updates/InstallationManager.cs
+++ /dev/null
@@ -1,685 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Progress;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Updates;
-
-namespace MediaBrowser.Server.Implementations.Updates
-{
- /// <summary>
- /// Manages all install, uninstall and update operations (both plugins and system)
- /// </summary>
- public class InstallationManager : IInstallationManager
- {
- public event EventHandler<InstallationEventArgs> PackageInstalling;
- public event EventHandler<InstallationEventArgs> PackageInstallationCompleted;
- public event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed;
- public event EventHandler<InstallationEventArgs> PackageInstallationCancelled;
-
- /// <summary>
- /// The current installations
- /// </summary>
- public List<Tuple<InstallationInfo, CancellationTokenSource>> CurrentInstallations { get; set; }
-
- /// <summary>
- /// The completed installations
- /// </summary>
- public ConcurrentBag<InstallationInfo> CompletedInstallations { get; set; }
-
- #region PluginUninstalled Event
- /// <summary>
- /// Occurs when [plugin uninstalled].
- /// </summary>
- public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
-
- /// <summary>
- /// Called when [plugin uninstalled].
- /// </summary>
- /// <param name="plugin">The plugin.</param>
- private void OnPluginUninstalled(IPlugin plugin)
- {
- EventHelper.FireEventIfNotNull(PluginUninstalled, this, new GenericEventArgs<IPlugin> { Argument = plugin }, _logger);
- }
- #endregion
-
- #region PluginUpdated Event
- /// <summary>
- /// Occurs when [plugin updated].
- /// </summary>
- public event EventHandler<GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>>> PluginUpdated;
- /// <summary>
- /// Called when [plugin updated].
- /// </summary>
- /// <param name="plugin">The plugin.</param>
- /// <param name="newVersion">The new version.</param>
- private void OnPluginUpdated(IPlugin plugin, PackageVersionInfo newVersion)
- {
- _logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.versionStr ?? string.Empty, newVersion.classification);
-
- EventHelper.FireEventIfNotNull(PluginUpdated, this, new GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> { Argument = new Tuple<IPlugin, PackageVersionInfo>(plugin, newVersion) }, _logger);
-
- _applicationHost.NotifyPendingRestart();
- }
- #endregion
-
- #region PluginInstalled Event
- /// <summary>
- /// Occurs when [plugin updated].
- /// </summary>
- public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
- /// <summary>
- /// Called when [plugin installed].
- /// </summary>
- /// <param name="package">The package.</param>
- private void OnPluginInstalled(PackageVersionInfo package)
- {
- _logger.Info("New plugin installed: {0} {1} {2}", package.name, package.versionStr ?? string.Empty, package.classification);
-
- EventHelper.FireEventIfNotNull(PluginInstalled, this, new GenericEventArgs<PackageVersionInfo> { Argument = package }, _logger);
-
- _applicationHost.NotifyPendingRestart();
- }
- #endregion
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- private readonly IApplicationPaths _appPaths;
- private readonly IHttpClient _httpClient;
- private readonly IJsonSerializer _jsonSerializer;
- private readonly ISecurityManager _securityManager;
- private readonly IConfigurationManager _config;
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Gets the application host.
- /// </summary>
- /// <value>The application host.</value>
- private readonly IApplicationHost _applicationHost;
-
- public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem)
- {
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
- CurrentInstallations = new List<Tuple<InstallationInfo, CancellationTokenSource>>();
- CompletedInstallations = new ConcurrentBag<InstallationInfo>();
-
- _applicationHost = appHost;
- _appPaths = appPaths;
- _httpClient = httpClient;
- _jsonSerializer = jsonSerializer;
- _securityManager = securityManager;
- _config = config;
- _fileSystem = fileSystem;
- _logger = logger;
- }
-
- private Version GetPackageVersion(PackageVersionInfo version)
- {
- return new Version(ValueOrDefault(version.versionStr, "0.0.0.1"));
- }
-
- private static string ValueOrDefault(string str, string def)
- {
- return string.IsNullOrEmpty(str) ? def : str;
- }
-
- /// <summary>
- /// Gets all available packages.
- /// </summary>
- /// <returns>Task{List{PackageInfo}}.</returns>
- public async Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
- bool withRegistration = true,
- string packageType = null,
- Version applicationVersion = null)
- {
- var data = new Dictionary<string, string>
- {
- { "key", _securityManager.SupporterKey },
- { "mac", _applicationHost.SystemId },
- { "systemid", _applicationHost.SystemId }
- };
-
- if (withRegistration)
- {
- using (var json = await _httpClient.Post("https://www.mb3admin.com/admin/service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var packages = _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(json).ToList();
-
- return FilterPackages(packages, packageType, applicationVersion);
- }
- }
- else
- {
- var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
-
- return FilterPackages(packages.ToList(), packageType, applicationVersion);
- }
- }
-
- private DateTime _lastPackageUpdateTime;
-
- /// <summary>
- /// Gets all available packages.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{List{PackageInfo}}.</returns>
- public async Task<IEnumerable<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken)
- {
- _logger.Info("Opening {0}", PackageCachePath);
- try
- {
- using (var stream = _fileSystem.OpenRead(PackageCachePath))
- {
- var packages = _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(stream).ToList();
-
- if (DateTime.UtcNow - _lastPackageUpdateTime > GetCacheLength())
- {
- UpdateCachedPackages(CancellationToken.None, false);
- }
-
- return packages;
- }
- }
- catch (Exception)
- {
-
- }
-
- _lastPackageUpdateTime = DateTime.MinValue;
- await UpdateCachedPackages(cancellationToken, true).ConfigureAwait(false);
- using (var stream = _fileSystem.OpenRead(PackageCachePath))
- {
- return _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(stream).ToList();
- }
- }
-
- private string PackageCachePath
- {
- get { return Path.Combine(_appPaths.CachePath, "serverpackages.json"); }
- }
-
- private readonly SemaphoreSlim _updateSemaphore = new SemaphoreSlim(1, 1);
- private async Task UpdateCachedPackages(CancellationToken cancellationToken, bool throwErrors)
- {
- await _updateSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- if (DateTime.UtcNow - _lastPackageUpdateTime < GetCacheLength())
- {
- return;
- }
-
- var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = "https://www.mb3admin.com/admin/service/MB3Packages.json",
- CancellationToken = cancellationToken,
- Progress = new Progress<Double>()
-
- }).ConfigureAwait(false);
-
- _fileSystem.CreateDirectory(Path.GetDirectoryName(PackageCachePath));
-
- _fileSystem.CopyFile(tempFile, PackageCachePath, true);
- _lastPackageUpdateTime = DateTime.UtcNow;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error updating package cache", ex);
-
- if (throwErrors)
- {
- throw;
- }
- }
- finally
- {
- _updateSemaphore.Release();
- }
- }
-
- private TimeSpan GetCacheLength()
- {
- switch (_config.CommonConfiguration.SystemUpdateLevel)
- {
- case PackageVersionClass.Beta:
- return TimeSpan.FromMinutes(30);
- case PackageVersionClass.Dev:
- return TimeSpan.FromMinutes(3);
- default:
- return TimeSpan.FromHours(24);
- }
- }
-
- protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages)
- {
- foreach (var package in packages)
- {
- package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl))
- .OrderByDescending(GetPackageVersion).ToList();
- }
-
- // Remove packages with no versions
- packages = packages.Where(p => p.versions.Any()).ToList();
-
- return packages;
- }
-
- protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages, string packageType, Version applicationVersion)
- {
- foreach (var package in packages)
- {
- package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl))
- .OrderByDescending(GetPackageVersion).ToList();
- }
-
- if (!string.IsNullOrWhiteSpace(packageType))
- {
- packages = packages.Where(p => string.Equals(p.type, packageType, StringComparison.OrdinalIgnoreCase)).ToList();
- }
-
- // If an app version was supplied, filter the versions for each package to only include supported versions
- if (applicationVersion != null)
- {
- foreach (var package in packages)
- {
- package.versions = package.versions.Where(v => IsPackageVersionUpToDate(v, applicationVersion)).ToList();
- }
- }
-
- // Remove packages with no versions
- packages = packages.Where(p => p.versions.Any()).ToList();
-
- return packages;
- }
-
- /// <summary>
- /// Determines whether [is package version up to date] [the specified package version info].
- /// </summary>
- /// <param name="packageVersionInfo">The package version info.</param>
- /// <param name="currentServerVersion">The current server version.</param>
- /// <returns><c>true</c> if [is package version up to date] [the specified package version info]; otherwise, <c>false</c>.</returns>
- private bool IsPackageVersionUpToDate(PackageVersionInfo packageVersionInfo, Version currentServerVersion)
- {
- if (string.IsNullOrEmpty(packageVersionInfo.requiredVersionStr))
- {
- return true;
- }
-
- Version requiredVersion;
-
- return Version.TryParse(packageVersionInfo.requiredVersionStr, out requiredVersion) && currentServerVersion >= requiredVersion;
- }
-
- /// <summary>
- /// Gets the package.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="guid">The assembly guid</param>
- /// <param name="classification">The classification.</param>
- /// <param name="version">The version.</param>
- /// <returns>Task{PackageVersionInfo}.</returns>
- public async Task<PackageVersionInfo> GetPackage(string name, string guid, PackageVersionClass classification, Version version)
- {
- var packages = await GetAvailablePackages(CancellationToken.None).ConfigureAwait(false);
-
- var package = packages.FirstOrDefault(p => string.Equals(p.guid, guid ?? "none", StringComparison.OrdinalIgnoreCase))
- ?? packages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase));
-
- if (package == null)
- {
- return null;
- }
-
- return package.versions.FirstOrDefault(v => GetPackageVersion(v).Equals(version) && v.classification == classification);
- }
-
- /// <summary>
- /// Gets the latest compatible version.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="guid">The assembly guid if this is a plug-in</param>
- /// <param name="currentServerVersion">The current server version.</param>
- /// <param name="classification">The classification.</param>
- /// <returns>Task{PackageVersionInfo}.</returns>
- public async Task<PackageVersionInfo> GetLatestCompatibleVersion(string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release)
- {
- var packages = await GetAvailablePackages(CancellationToken.None).ConfigureAwait(false);
-
- return GetLatestCompatibleVersion(packages, name, guid, currentServerVersion, classification);
- }
-
- /// <summary>
- /// Gets the latest compatible version.
- /// </summary>
- /// <param name="availablePackages">The available packages.</param>
- /// <param name="name">The name.</param>
- /// <param name="currentServerVersion">The current server version.</param>
- /// <param name="classification">The classification.</param>
- /// <returns>PackageVersionInfo.</returns>
- public PackageVersionInfo GetLatestCompatibleVersion(IEnumerable<PackageInfo> availablePackages, string name, string guid, Version currentServerVersion, PackageVersionClass classification = PackageVersionClass.Release)
- {
- var package = availablePackages.FirstOrDefault(p => string.Equals(p.guid, guid ?? "none", StringComparison.OrdinalIgnoreCase))
- ?? availablePackages.FirstOrDefault(p => p.name.Equals(name, StringComparison.OrdinalIgnoreCase));
-
- if (package == null)
- {
- return null;
- }
-
- return package.versions
- .OrderByDescending(GetPackageVersion)
- .FirstOrDefault(v => v.classification <= classification && IsPackageVersionUpToDate(v, currentServerVersion));
- }
-
- /// <summary>
- /// Gets the available plugin updates.
- /// </summary>
- /// <param name="applicationVersion">The current server version.</param>
- /// <param name="withAutoUpdateEnabled">if set to <c>true</c> [with auto update enabled].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{PackageVersionInfo}}.</returns>
- public async Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(Version applicationVersion, bool withAutoUpdateEnabled, CancellationToken cancellationToken)
- {
- var catalog = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
-
- var plugins = _applicationHost.Plugins.ToList();
-
- if (withAutoUpdateEnabled)
- {
- plugins = plugins
- .Where(p => _config.CommonConfiguration.EnableAutoUpdate)
- .ToList();
- }
-
- // Figure out what needs to be installed
- var packages = plugins.Select(p =>
- {
- var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, _config.CommonConfiguration.SystemUpdateLevel);
-
- return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null;
-
- }).Where(i => i != null).ToList();
-
- return packages
- .Where(p => !string.IsNullOrWhiteSpace(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
- }
-
- /// <summary>
- /// Installs the package.
- /// </summary>
- /// <param name="package">The package.</param>
- /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">package</exception>
- public async Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
- {
- if (package == null)
- {
- throw new ArgumentNullException("package");
- }
-
- if (progress == null)
- {
- throw new ArgumentNullException("progress");
- }
-
- var installationInfo = new InstallationInfo
- {
- Id = Guid.NewGuid().ToString("N"),
- Name = package.name,
- AssemblyGuid = package.guid,
- UpdateClass = package.classification,
- Version = package.versionStr
- };
-
- var innerCancellationTokenSource = new CancellationTokenSource();
-
- var tuple = new Tuple<InstallationInfo, CancellationTokenSource>(installationInfo, innerCancellationTokenSource);
-
- // Add it to the in-progress list
- lock (CurrentInstallations)
- {
- CurrentInstallations.Add(tuple);
- }
-
- var innerProgress = new ActionableProgress<double>();
-
- // Whenever the progress updates, update the outer progress object and InstallationInfo
- innerProgress.RegisterAction(percent =>
- {
- progress.Report(percent);
-
- installationInfo.PercentComplete = percent;
- });
-
- var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token;
-
- var installationEventArgs = new InstallationEventArgs
- {
- InstallationInfo = installationInfo,
- PackageVersionInfo = package
- };
-
- EventHelper.FireEventIfNotNull(PackageInstalling, this, installationEventArgs, _logger);
-
- try
- {
- await InstallPackageInternal(package, isPlugin, innerProgress, linkedToken).ConfigureAwait(false);
-
- lock (CurrentInstallations)
- {
- CurrentInstallations.Remove(tuple);
- }
-
- progress.Report(100);
-
- CompletedInstallations.Add(installationInfo);
-
- EventHelper.FireEventIfNotNull(PackageInstallationCompleted, this, installationEventArgs, _logger);
- }
- catch (OperationCanceledException)
- {
- lock (CurrentInstallations)
- {
- CurrentInstallations.Remove(tuple);
- }
-
- _logger.Info("Package installation cancelled: {0} {1}", package.name, package.versionStr);
-
- EventHelper.FireEventIfNotNull(PackageInstallationCancelled, this, installationEventArgs, _logger);
-
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Package installation failed", ex);
-
- lock (CurrentInstallations)
- {
- CurrentInstallations.Remove(tuple);
- }
-
- EventHelper.FireEventIfNotNull(PackageInstallationFailed, this, new InstallationFailedEventArgs
- {
- InstallationInfo = installationInfo,
- Exception = ex
-
- }, _logger);
-
- throw;
- }
- finally
- {
- // Dispose the progress object and remove the installation from the in-progress list
- innerProgress.Dispose();
- tuple.Item2.Dispose();
- }
- }
-
- /// <summary>
- /// Installs the package internal.
- /// </summary>
- /// <param name="package">The package.</param>
- /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
- /// <param name="progress">The progress.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
- {
- // Do the install
- await PerformPackageInstallation(progress, package, cancellationToken).ConfigureAwait(false);
-
- // Do plugin-specific processing
- if (isPlugin)
- {
- // Set last update time if we were installed before
- var plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))
- ?? _applicationHost.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase));
-
- if (plugin != null)
- {
- OnPluginUpdated(plugin, package);
- }
- else
- {
- OnPluginInstalled(package);
- }
- }
- }
-
- private async Task PerformPackageInstallation(IProgress<double> progress, PackageVersionInfo package, CancellationToken cancellationToken)
- {
- // Target based on if it is an archive or single assembly
- // zip archives are assumed to contain directory structures relative to our ProgramDataPath
- var extension = Path.GetExtension(package.targetFilename);
- var isArchive = string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase);
- var target = Path.Combine(isArchive ? _appPaths.TempUpdatePath : _appPaths.PluginsPath, package.targetFilename);
-
- // Download to temporary file so that, if interrupted, it won't destroy the existing installation
- var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
- {
- Url = package.sourceUrl,
- CancellationToken = cancellationToken,
- Progress = progress
-
- }).ConfigureAwait(false);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- // Validate with a checksum
- var packageChecksum = string.IsNullOrWhiteSpace(package.checksum) ? Guid.Empty : new Guid(package.checksum);
- if (packageChecksum != Guid.Empty) // support for legacy uploads for now
- {
- using (var crypto = new MD5CryptoServiceProvider())
- using (var stream = new BufferedStream(_fileSystem.OpenRead(tempFile), 100000))
- {
- var check = Guid.Parse(BitConverter.ToString(crypto.ComputeHash(stream)).Replace("-", String.Empty));
- if (check != packageChecksum)
- {
- throw new ApplicationException(string.Format("Download validation failed for {0}. Probably corrupted during transfer.", package.name));
- }
- }
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- // Success - move it to the real target
- try
- {
- _fileSystem.CreateDirectory(Path.GetDirectoryName(target));
- _fileSystem.CopyFile(tempFile, target, true);
- //If it is an archive - write out a version file so we know what it is
- if (isArchive)
- {
- _fileSystem.WriteAllText(target + ".ver", package.versionStr);
- }
- }
- catch (IOException e)
- {
- _logger.ErrorException("Error attempting to move file from {0} to {1}", e, tempFile, target);
- throw;
- }
-
- try
- {
- _fileSystem.DeleteFile(tempFile);
- }
- catch (IOException e)
- {
- // Don't fail because of this
- _logger.ErrorException("Error deleting temp file {0]", e, tempFile);
- }
- }
-
- /// <summary>
- /// Uninstalls a plugin
- /// </summary>
- /// <param name="plugin">The plugin.</param>
- /// <exception cref="System.ArgumentException"></exception>
- public void UninstallPlugin(IPlugin plugin)
- {
- plugin.OnUninstalling();
-
- // Remove it the quick way for now
- _applicationHost.RemovePlugin(plugin);
-
- _fileSystem.DeleteFile(plugin.AssemblyFilePath);
-
- OnPluginUninstalled(plugin);
-
- _applicationHost.NotifyPendingRestart();
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
- {
- if (dispose)
- {
- lock (CurrentInstallations)
- {
- foreach (var tuple in CurrentInstallations)
- {
- tuple.Item2.Dispose();
- }
-
- CurrentInstallations.Clear();
- }
- }
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
deleted file mode 100644
index 33a7b6725..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/CollectionFolderImageProvider.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Server.Implementations.Photos;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.Collections;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.Querying;
-
-namespace MediaBrowser.Server.Implementations.UserViews
-{
- public class CollectionFolderImageProvider : BaseDynamicImageProvider<CollectionFolder>
- {
- public CollectionFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
- {
- }
-
- public override IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- return new List<ImageType>
- {
- ImageType.Primary
- };
- }
-
- protected override async Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
- {
- var view = (CollectionFolder)item;
-
- var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
-
- var result = await view.GetItems(new InternalItemsQuery
- {
- CollapseBoxSetItems = false,
- Recursive = recursive,
- ExcludeItemTypes = new[] { "UserView", "CollectionFolder", "Playlist" }
-
- }).ConfigureAwait(false);
-
- var items = result.Items.Select(i =>
- {
- var episode = i as Episode;
- if (episode != null)
- {
- var series = episode.Series;
- if (series != null)
- {
- return series;
- }
-
- return episode;
- }
-
- var season = i as Season;
- if (season != null)
- {
- var series = season.Series;
- if (series != null)
- {
- return series;
- }
-
- return season;
- }
-
- var audio = i as Audio;
- if (audio != null)
- {
- var album = audio.AlbumEntity;
- if (album != null && album.HasImage(ImageType.Primary))
- {
- return album;
- }
- }
-
- return i;
-
- }).DistinctBy(i => i.Id);
-
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8);
- }
-
- protected override bool Supports(IHasImages item)
- {
- return item is CollectionFolder;
- }
-
- protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
- {
- var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
-
- if (imageType == ImageType.Primary)
- {
- if (itemsWithImages.Count == 0)
- {
- return null;
- }
-
- return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false);
- }
-
- return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
- }
- }
-
- public class ManualCollectionFolderImageProvider : BaseDynamicImageProvider<ManualCollectionsFolder>
- {
- private readonly ILibraryManager _libraryManager;
-
- public ManualCollectionFolderImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
- {
- _libraryManager = libraryManager;
- }
-
- public override IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- return new List<ImageType>
- {
- ImageType.Primary
- };
- }
-
- protected override async Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
- {
- var view = (ManualCollectionsFolder)item;
-
- var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
-
- var items = _libraryManager.GetItemList(new InternalItemsQuery
- {
- Recursive = recursive,
- IncludeItemTypes = new[] { typeof(BoxSet).Name },
- Limit = 20,
- SortBy = new[] { ItemSortBy.Random }
- });
-
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8);
- }
-
- protected override bool Supports(IHasImages item)
- {
- return item is ManualCollectionsFolder;
- }
-
- protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
- {
- var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
-
- if (imageType == ImageType.Primary)
- {
- if (itemsWithImages.Count == 0)
- {
- return null;
- }
-
- return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false);
- }
-
- return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
- }
- }
-
-}
diff --git a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs b/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
deleted file mode 100644
index 61f3c77f0..000000000
--- a/MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs
+++ /dev/null
@@ -1,190 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Server.Implementations.Photos;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Model.Extensions;
-
-namespace MediaBrowser.Server.Implementations.UserViews
-{
- public class DynamicImageProvider : BaseDynamicImageProvider<UserView>
- {
- private readonly IUserManager _userManager;
- private readonly ILibraryManager _libraryManager;
-
- public DynamicImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, IUserManager userManager, ILibraryManager libraryManager)
- : base(fileSystem, providerManager, applicationPaths, imageProcessor)
- {
- _userManager = userManager;
- _libraryManager = libraryManager;
- }
-
- public override IEnumerable<ImageType> GetSupportedImages(IHasImages item)
- {
- var view = (UserView)item;
- if (IsUsingCollectionStrip(view))
- {
- return new List<ImageType>
- {
- ImageType.Primary
- };
- }
-
- return new List<ImageType>
- {
- ImageType.Primary
- };
- }
-
- protected override async Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
- {
- var view = (UserView)item;
-
- if (string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
- {
- var programs = _libraryManager.GetItemList(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
- ImageTypes = new[] { ImageType.Primary },
- Limit = 30,
- IsMovie = true
- }).ToList();
-
- return GetFinalItems(programs).ToList();
- }
-
- if (string.Equals(view.ViewType, SpecialFolder.MovieGenre, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(view.ViewType, SpecialFolder.TvGenre, StringComparison.OrdinalIgnoreCase))
- {
- var userItemsResult = await view.GetItems(new InternalItemsQuery
- {
- CollapseBoxSetItems = false
- });
-
- return userItemsResult.Items.ToList();
- }
-
- var isUsingCollectionStrip = IsUsingCollectionStrip(view);
- var recursive = isUsingCollectionStrip && !new[] { CollectionType.Channels, CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
-
- var result = await view.GetItems(new InternalItemsQuery
- {
- User = view.UserId.HasValue ? _userManager.GetUserById(view.UserId.Value) : null,
- CollapseBoxSetItems = false,
- Recursive = recursive,
- ExcludeItemTypes = new[] { "UserView", "CollectionFolder", "Person" },
-
- }).ConfigureAwait(false);
-
- var items = result.Items.Select(i =>
- {
- var episode = i as Episode;
- if (episode != null)
- {
- var series = episode.Series;
- if (series != null)
- {
- return series;
- }
-
- return episode;
- }
-
- var season = i as Season;
- if (season != null)
- {
- var series = season.Series;
- if (series != null)
- {
- return series;
- }
-
- return season;
- }
-
- var audio = i as Audio;
- if (audio != null)
- {
- var album = audio.AlbumEntity;
- if (album != null && album.HasImage(ImageType.Primary))
- {
- return album;
- }
- }
-
- return i;
-
- }).DistinctBy(i => i.Id);
-
- if (isUsingCollectionStrip)
- {
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb)).ToList(), 8);
- }
-
- return GetFinalItems(items.Where(i => i.HasImage(ImageType.Primary)).ToList());
- }
-
- protected override bool Supports(IHasImages item)
- {
- var view = item as UserView;
- if (view != null)
- {
- return IsUsingCollectionStrip(view);
- }
-
- return false;
- }
-
- private bool IsUsingCollectionStrip(UserView view)
- {
- string[] collectionStripViewTypes =
- {
- CollectionType.Movies,
- CollectionType.TvShows,
- CollectionType.Music,
- CollectionType.Games,
- CollectionType.Books,
- CollectionType.MusicVideos,
- CollectionType.HomeVideos,
- CollectionType.BoxSets,
- CollectionType.LiveTv,
- CollectionType.Playlists,
- CollectionType.Photos,
- string.Empty
- };
-
- return collectionStripViewTypes.Contains(view.ViewType ?? string.Empty);
- }
-
- protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
- {
- var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ".png");
-
- var view = (UserView)item;
- if (imageType == ImageType.Primary && IsUsingCollectionStrip(view))
- {
- if (itemsWithImages.Count == 0)
- {
- return null;
- }
-
- return await CreateThumbCollage(item, itemsWithImages, outputPath, 960, 540).ConfigureAwait(false);
- }
-
- return await base.CreateImage(item, itemsWithImages, outputPath, imageType, imageIndex).ConfigureAwait(false);
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/Xml/XmlReaderSettingsFactory.cs b/MediaBrowser.Server.Implementations/Xml/XmlReaderSettingsFactory.cs
deleted file mode 100644
index ae7018ad4..000000000
--- a/MediaBrowser.Server.Implementations/Xml/XmlReaderSettingsFactory.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Xml;
-using MediaBrowser.Model.Xml;
-
-namespace MediaBrowser.Server.Implementations.Xml
-{
- public class XmlReaderSettingsFactory : IXmlReaderSettingsFactory
- {
- public XmlReaderSettings Create(bool enableValidation)
- {
- var settings = new XmlReaderSettings();
-
- if (!enableValidation)
- {
- settings.ValidationType = ValidationType.None;
- }
-
- return settings;
- }
- }
-}
diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config
index add5b29aa..5a43abe0f 100644
--- a/MediaBrowser.Server.Implementations/packages.config
+++ b/MediaBrowser.Server.Implementations/packages.config
@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="Emby.XmlTv" version="1.0.0.58" targetFramework="net46" />
+ <package id="Emby.XmlTv" version="1.0.0.63" targetFramework="net46" />
<package id="ini-parser" version="2.3.0" targetFramework="net45" />
- <package id="MediaBrowser.Naming" version="1.0.0.57" targetFramework="net46" />
<package id="Microsoft.IO.RecyclableMemoryStream" version="1.1.0.0" targetFramework="net46" />
- <package id="Patterns.Logging" version="1.0.0.4" targetFramework="net46" />
+ <package id="Patterns.Logging" version="1.0.0.6" targetFramework="net46" />
<package id="SocketHttpListener" version="1.0.0.40" targetFramework="net45" />
<package id="UniversalDetector" version="1.0.1" targetFramework="net46" />
</packages> \ No newline at end of file