aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2017-02-24 15:21:37 -0500
committerLuke Pulverenti <luke.pulverenti@gmail.com>2017-02-24 15:21:37 -0500
commita73eeac6869edc199c4d0915d07b6847d0c95524 (patch)
tree378241f264bbd6106ea6e958e5bf346d1f76b86e
parentc456490de43082f008dd592345e76cf75db1ad0a (diff)
parentcd6b7f3bdc5bcbc6c68131cc40b71b68ac1b73a6 (diff)
Merge branch 'master' of https://github.com/MediaBrowser/Emby
-rw-r--r--Emby.Common.Implementations/BaseApplicationHost.cs10
-rw-r--r--Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs2
-rw-r--r--Emby.Server.Core/ApplicationHost.cs147
-rw-r--r--Emby.Server.Core/Emby.Server.Core.xproj1
-rw-r--r--Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs26
-rw-r--r--Emby.Server.Core/Logging/ConsoleLogger.cs16
-rw-r--r--Emby.Server.Core/project.json6
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs (renamed from Emby.Common.Implementations/BaseApplicationPaths.cs)14
-rw-r--r--Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs (renamed from Emby.Common.Implementations/Configuration/BaseConfigurationManager.cs)3
-rw-r--r--Emby.Server.Implementations/AppBase/ConfigurationHelper.cs (renamed from Emby.Common.Implementations/Configuration/ConfigurationHelper.cs)2
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs (renamed from Emby.Server.Core/Configuration/ServerConfigurationManager.cs)6
-rw-r--r--Emby.Server.Implementations/Connect/ConnectData.cs36
-rw-r--r--Emby.Server.Implementations/Connect/ConnectEntryPoint.cs218
-rw-r--r--Emby.Server.Implementations/Connect/ConnectManager.cs1193
-rw-r--r--Emby.Server.Implementations/Connect/Responses.cs85
-rw-r--r--Emby.Server.Implementations/Connect/Validator.cs29
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs46
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs92
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj48
-rw-r--r--Emby.Server.Implementations/EntryPoints/UsageReporter.cs3
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs187
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs9
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs38
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs41
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs24
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs8
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs38
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs13
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs7
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs29
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs21
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs6
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs34
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs4
-rw-r--r--Emby.Server.Implementations/Logging/UnhandledExceptionWriter.cs (renamed from Emby.Server.Core/UnhandledExceptionWriter.cs)19
-rw-r--r--Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs22
-rw-r--r--Emby.Server.Implementations/Security/PluginSecurityManager.cs3
-rw-r--r--Emby.Server.Implementations/ServerApplicationPaths.cs (renamed from Emby.Server.Core/ServerApplicationPaths.cs)11
-rw-r--r--Emby.Server.Implementations/ServerManager/ServerManager.cs4
-rw-r--r--Emby.Server.Implementations/Services/HttpResult.cs (renamed from ServiceStack/HttpResult.cs)7
-rw-r--r--Emby.Server.Implementations/Services/RequestHelper.cs51
-rw-r--r--Emby.Server.Implementations/Services/ResponseHelper.cs (renamed from ServiceStack/HttpResponseExtensionsInternal.cs)45
-rw-r--r--Emby.Server.Implementations/Services/ServiceController.cs (renamed from ServiceStack/Host/ServiceController.cs)117
-rw-r--r--Emby.Server.Implementations/Services/ServiceExec.cs (renamed from ServiceStack/Host/ServiceExec.cs)49
-rw-r--r--Emby.Server.Implementations/Services/ServiceHandler.cs296
-rw-r--r--Emby.Server.Implementations/Services/ServiceMethod.cs (renamed from ServiceStack/Host/ActionContext.cs)7
-rw-r--r--Emby.Server.Implementations/Services/ServicePath.cs (renamed from ServiceStack/Host/RestPath.cs)209
-rw-r--r--Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs (renamed from ServiceStack/StringMapTypeDeserializer.cs)22
-rw-r--r--Emby.Server.Implementations/Services/UrlExtensions.cs (renamed from ServiceStack/UrlExtensions.cs)4
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs29
-rw-r--r--Emby.Server.Implementations/Sync/AppSyncProvider.cs118
-rw-r--r--Emby.Server.Implementations/Sync/CloudSyncProfile.cs288
-rw-r--r--Emby.Server.Implementations/Sync/IHasSyncQuality.cs31
-rw-r--r--Emby.Server.Implementations/Sync/MediaSync.cs500
-rw-r--r--Emby.Server.Implementations/Sync/MultiProviderSync.cs79
-rw-r--r--Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs95
-rw-r--r--Emby.Server.Implementations/Sync/SyncConfig.cs29
-rw-r--r--Emby.Server.Implementations/Sync/SyncConvertScheduledTask.cs89
-rw-r--r--Emby.Server.Implementations/Sync/SyncHelper.cs24
-rw-r--r--Emby.Server.Implementations/Sync/SyncJobOptions.cs18
-rw-r--r--Emby.Server.Implementations/Sync/SyncJobProcessor.cs998
-rw-r--r--Emby.Server.Implementations/Sync/SyncManager.cs1372
-rw-r--r--Emby.Server.Implementations/Sync/SyncNotificationEntryPoint.cs60
-rw-r--r--Emby.Server.Implementations/Sync/SyncRegistrationInfo.cs31
-rw-r--r--Emby.Server.Implementations/Sync/SyncRepository.cs847
-rw-r--r--Emby.Server.Implementations/Sync/SyncedMediaSourceProvider.cs158
-rw-r--r--Emby.Server.Implementations/Sync/TargetDataProvider.cs208
-rw-r--r--Emby.Server.Implementations/packages.config4
-rw-r--r--MediaBrowser.Api/ConnectService.cs177
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs20
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs2
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj6
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs21
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs3
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs40
-rw-r--r--MediaBrowser.Api/Playback/Progressive/AudioService.cs2
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs2
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs3
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs2
-rw-r--r--MediaBrowser.Api/Playback/UniversalAudioService.cs248
-rw-r--r--MediaBrowser.Api/Reports/ReportsService.cs6
-rw-r--r--MediaBrowser.Api/Sync/SyncHelper.cs4
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs68
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs2
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs20
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs5
-rw-r--r--MediaBrowser.Controller/Entities/AudioBook.cs5
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs33
-rw-r--r--MediaBrowser.Controller/Entities/Game.cs11
-rw-r--r--MediaBrowser.Controller/Entities/IHasImages.cs2
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs4
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs8
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs24
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs9
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs12
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs8
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs5
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs47
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs10
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs2
-rw-r--r--MediaBrowser.Controller/LiveTv/ITunerHost.cs5
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs5
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs13
-rw-r--r--MediaBrowser.Controller/LiveTv/TimerInfo.cs2
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs (renamed from MediaBrowser.MediaEncoding/Encoder/EncodingHelper.cs)67
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs (renamed from MediaBrowser.MediaEncoding/Encoder/EncodingJobInfo.cs)6
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs5
-rw-r--r--MediaBrowser.Controller/Providers/EpisodeInfo.cs1
-rw-r--r--MediaBrowser.Controller/Providers/SeasonInfo.cs1
-rw-r--r--MediaBrowser.Controller/Providers/SeriesInfo.cs1
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs15
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj2
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs45
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs1
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs13
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs224
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs10
-rw-r--r--MediaBrowser.Model/Logging/IConsoleLogger.cs7
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj1
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs11
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs6
-rw-r--r--MediaBrowser.Mono.sln18
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs25
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs2
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbItemProvider.cs5
-rw-r--r--MediaBrowser.Providers/Omdb/OmdbProvider.cs11
-rw-r--r--MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs12
-rw-r--r--MediaBrowser.Providers/TV/MissingEpisodeProvider.cs42
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs1
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs4
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs4
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs20
-rw-r--r--MediaBrowser.Server.Mac.sln22
-rw-r--r--MediaBrowser.Server.Mac/Emby.Server.Mac.csproj53
-rw-r--r--MediaBrowser.Server.Mac/MacAppHost.cs16
-rw-r--r--MediaBrowser.Server.Mac/Main.cs14
-rw-r--r--MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj20
-rw-r--r--MediaBrowser.Server.Mono/MonoAppHost.cs97
-rw-r--r--MediaBrowser.Server.Mono/Program.cs12
-rw-r--r--MediaBrowser.Server.Mono/app.config4
-rw-r--r--MediaBrowser.Server.Mono/packages.config8
-rw-r--r--MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj195
-rw-r--r--MediaBrowser.Server.Startup.Common/packages.config1
-rw-r--r--MediaBrowser.ServerApplication/App.config2
-rw-r--r--MediaBrowser.ServerApplication/MainStartup.cs18
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj20
-rw-r--r--MediaBrowser.ServerApplication/WindowsAppHost.cs110
-rw-r--r--MediaBrowser.ServerApplication/packages.config8
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs16
-rw-r--r--MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs8
-rw-r--r--MediaBrowser.sln42
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec2
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
-rw-r--r--ServiceStack/FilterAttributeCache.cs27
-rw-r--r--ServiceStack/Host/ContentTypes.cs63
-rw-r--r--ServiceStack/Host/RestHandler.cs176
-rw-r--r--ServiceStack/Host/ServiceMetadata.cs27
-rw-r--r--ServiceStack/HttpHandlerFactory.cs32
-rw-r--r--ServiceStack/HttpRequestExtensions.cs127
-rw-r--r--ServiceStack/HttpUtils.cs34
-rw-r--r--ServiceStack/Properties/AssemblyInfo.cs25
-rw-r--r--ServiceStack/ReflectionExtensions.cs270
-rw-r--r--ServiceStack/ServiceStack.csproj130
-rw-r--r--ServiceStack/ServiceStack.nuget.targets6
-rw-r--r--ServiceStack/ServiceStackHost.Runtime.cs57
-rw-r--r--ServiceStack/ServiceStackHost.cs104
-rw-r--r--ServiceStack/packages.config3
-rw-r--r--ServiceStack/project.json17
-rw-r--r--src/Emby.Server/CoreAppHost.cs76
-rw-r--r--src/Emby.Server/Emby.Server.xproj1
-rw-r--r--src/Emby.Server/Program.cs2
-rw-r--r--src/Emby.Server/project.json3
186 files changed, 2027 insertions, 9586 deletions
diff --git a/Emby.Common.Implementations/BaseApplicationHost.cs b/Emby.Common.Implementations/BaseApplicationHost.cs
index 147a43fa1..13bb85087 100644
--- a/Emby.Common.Implementations/BaseApplicationHost.cs
+++ b/Emby.Common.Implementations/BaseApplicationHost.cs
@@ -147,7 +147,7 @@ namespace Emby.Common.Implementations
/// <value>The configuration manager.</value>
protected IConfigurationManager ConfigurationManager { get; private set; }
- protected IFileSystem FileSystemManager { get; private set; }
+ public IFileSystem FileSystemManager { get; private set; }
protected IIsoManager IsoManager { get; private set; }
@@ -873,7 +873,13 @@ return null;
/// Gets or sets a value indicating whether this instance can self update.
/// </summary>
/// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value>
- public abstract bool CanSelfUpdate { get; }
+ public virtual bool CanSelfUpdate
+ {
+ get
+ {
+ return false;
+ }
+ }
/// <summary>
/// Checks for update.
diff --git a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
index de528a94f..f0518f69e 100644
--- a/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
+++ b/Emby.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs
@@ -379,7 +379,7 @@ namespace Emby.Common.Implementations.ScheduledTasks
/// <exception cref="System.InvalidOperationException">Cannot execute a Task that is already running</exception>
public async Task Execute(TaskExecutionOptions options)
{
- var task = ExecuteInternal(options);
+ var task = Task.Run(async () => await ExecuteInternal(options).ConfigureAwait(false));
_currentTask = task;
diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs
index a3c228a58..4425d1a0b 100644
--- a/Emby.Server.Core/ApplicationHost.cs
+++ b/Emby.Server.Core/ApplicationHost.cs
@@ -83,7 +83,6 @@ using Emby.Dlna.MediaReceiverRegistrar;
using Emby.Dlna.Ssdp;
using Emby.Server.Core;
using Emby.Server.Implementations.Activity;
-using Emby.Server.Core.Configuration;
using Emby.Server.Implementations.Devices;
using Emby.Server.Implementations.FFMpeg;
using Emby.Server.Core.IO;
@@ -91,10 +90,8 @@ using Emby.Server.Core.Localization;
using Emby.Server.Implementations.Migrations;
using Emby.Server.Implementations.Security;
using Emby.Server.Implementations.Social;
-using Emby.Server.Implementations.Sync;
using Emby.Server.Implementations.Channels;
using Emby.Server.Implementations.Collections;
-using Emby.Server.Implementations.Connect;
using Emby.Server.Implementations.Dto;
using Emby.Server.Implementations.EntryPoints;
using Emby.Server.Implementations.FileOrganization;
@@ -111,7 +108,6 @@ using Emby.Server.Implementations;
using Emby.Server.Implementations.ServerManager;
using Emby.Server.Implementations.Session;
using Emby.Server.Implementations.Social;
-using Emby.Server.Implementations.Sync;
using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates;
using MediaBrowser.Model.Activity;
@@ -134,6 +130,7 @@ using Emby.Drawing;
using Emby.Server.Implementations.Migrations;
using MediaBrowser.Model.Diagnostics;
using Emby.Common.Implementations.Diagnostics;
+using Emby.Server.Implementations.Configuration;
namespace Emby.Server.Core
{
@@ -312,7 +309,13 @@ namespace Emby.Server.Core
}
}
- public abstract bool SupportsRunningAsService { get; }
+ public virtual bool SupportsRunningAsService
+ {
+ get
+ {
+ return false;
+ }
+ }
/// <summary>
/// Gets the name.
@@ -326,14 +329,26 @@ namespace Emby.Server.Core
}
}
- public abstract bool IsRunningAsService { get; }
+ public virtual bool IsRunningAsService
+ {
+ get
+ {
+ return false;
+ }
+ }
private Assembly GetAssembly(Type type)
{
return type.GetTypeInfo().Assembly;
}
- public abstract bool SupportsAutoRunAtStartup { get; }
+ public virtual bool SupportsAutoRunAtStartup
+ {
+ get
+ {
+ return EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows;
+ }
+ }
private void SetBaseExceptionMessage()
{
@@ -508,6 +523,9 @@ namespace Emby.Server.Core
}
}
+ protected abstract IConnectManager CreateConnectManager();
+ protected abstract ISyncManager CreateSyncManager();
+
/// <summary>
/// Registers resources that classes will depend on
/// </summary>
@@ -568,9 +586,6 @@ namespace Emby.Server.Core
AuthenticationRepository = await GetAuthenticationRepository().ConfigureAwait(false);
RegisterSingleInstance(AuthenticationRepository);
- SyncRepository = GetSyncRepository();
- RegisterSingleInstance(SyncRepository);
-
UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, () => ConnectManager, this, JsonSerializer, FileSystemManager, CryptographyProvider, _defaultUserNameFactory());
RegisterSingleInstance(UserManager);
@@ -608,7 +623,7 @@ namespace Emby.Server.Core
TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager);
RegisterSingleInstance(TVSeriesManager);
- SyncManager = new SyncManager(LibraryManager, SyncRepository, ImageProcessor, LogManager.GetLogger("SyncManager"), UserManager, () => DtoService, this, TVSeriesManager, () => MediaEncoder, FileSystemManager, () => SubtitleEncoder, ServerConfigurationManager, UserDataManager, () => MediaSourceManager, JsonSerializer, TaskManager, MemoryStreamFactory);
+ SyncManager = CreateSyncManager();
RegisterSingleInstance(SyncManager);
DtoService = new DtoService(LogManager.GetLogger("DtoService"), LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, SyncManager, this, () => DeviceManager, () => MediaSourceManager, () => LiveTvManager);
@@ -617,7 +632,7 @@ namespace Emby.Server.Core
var encryptionManager = new EncryptionManager();
RegisterSingleInstance<IEncryptionManager>(encryptionManager);
- ConnectManager = new ConnectManager(LogManager.GetLogger("ConnectManager"), ApplicationPaths, JsonSerializer, encryptionManager, HttpClient, this, ServerConfigurationManager, UserManager, ProviderManager, SecurityManager, FileSystemManager);
+ ConnectManager = CreateConnectManager();
RegisterSingleInstance(ConnectManager);
DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer, LogManager.GetLogger("DeviceManager"), FileSystemManager), UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager);
@@ -716,7 +731,13 @@ namespace Emby.Server.Core
await ((UserManager)UserManager).Initialize().ConfigureAwait(false);
}
- protected abstract bool SupportsDualModeSockets { get; }
+ protected virtual bool SupportsDualModeSockets
+ {
+ get
+ {
+ return true;
+ }
+ }
private ICertificate GetCertificate(string certificateLocation)
{
@@ -761,7 +782,77 @@ namespace Emby.Server.Core
return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, maxConcurrentImageProcesses, () => LibraryManager, TimerFactory);
}
- protected abstract FFMpegInstallInfo GetFfmpegInstallInfo();
+ protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
+ {
+ var info = new FFMpegInstallInfo();
+
+ // Windows builds: http://ffmpeg.zeranoe.com/builds/
+ // Linux builds: http://johnvansickle.com/ffmpeg/
+ // OS X builds: http://ffmpegmac.net/
+ // OS X x64: http://www.evermeet.cx/ffmpeg/
+
+ if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Linux)
+ {
+ info.FFMpegFilename = "ffmpeg";
+ info.FFProbeFilename = "ffprobe";
+ info.ArchiveType = "7z";
+ info.Version = "20160215";
+ info.DownloadUrls = GetLinuxDownloadUrls();
+ }
+ else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
+ {
+ info.FFMpegFilename = "ffmpeg.exe";
+ info.FFProbeFilename = "ffprobe.exe";
+ info.Version = "20160410";
+ info.ArchiveType = "7z";
+ info.DownloadUrls = GetWindowsDownloadUrls();
+ }
+ else
+ {
+ // No version available - user requirement
+ info.DownloadUrls = new string[] { };
+ }
+
+ return info;
+ }
+
+ private string[] GetWindowsDownloadUrls()
+ {
+ switch (EnvironmentInfo.SystemArchitecture)
+ {
+ case Architecture.X64:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win64.7z"
+ };
+ case Architecture.X86:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win32.7z"
+ };
+ }
+
+ return new string[] { };
+ }
+
+ private string[] GetLinuxDownloadUrls()
+ {
+ switch (EnvironmentInfo.SystemArchitecture)
+ {
+ case Architecture.X64:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
+ };
+ case Architecture.X86:
+ return new[]
+ {
+ "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z"
+ };
+ }
+
+ return new string[] { };
+ }
/// <summary>
/// Registers the media encoder.
@@ -849,21 +940,12 @@ namespace Emby.Server.Core
return repo;
}
- private ISyncRepository GetSyncRepository()
- {
- var repo = new SyncRepository(LogManager.GetLogger("SyncRepository"), JsonSerializer, ServerConfigurationManager.ApplicationPaths);
-
- repo.Initialize();
-
- return repo;
- }
-
/// <summary>
/// Configures the repositories.
/// </summary>
private void ConfigureNotificationsRepository()
{
- var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths);
+ var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths, FileSystemManager);
repo.Initialize();
@@ -1158,7 +1240,7 @@ namespace Emby.Server.Core
list.Add(GetAssembly(typeof(InstallationManager)));
// Emby.Server.Core
- list.Add(GetAssembly(typeof(ServerApplicationPaths)));
+ list.Add(GetAssembly(typeof(ApplicationHost)));
// MediaEncoding
list.Add(GetAssembly(typeof(MediaEncoder)));
@@ -1489,7 +1571,10 @@ namespace Emby.Server.Core
}
}
- protected abstract void AuthorizeServer();
+ protected virtual void AuthorizeServer()
+ {
+ throw new NotImplementedException();
+ }
public event EventHandler HasUpdateAvailableChanged;
@@ -1565,7 +1650,10 @@ namespace Emby.Server.Core
}
}
- protected abstract void ConfigureAutoRunInternal(bool autorun);
+ protected virtual void ConfigureAutoRunInternal(bool autorun)
+ {
+ throw new NotImplementedException();
+ }
/// <summary>
/// This returns localhost in the case of no external dns, and the hostname if the
@@ -1631,7 +1719,10 @@ namespace Emby.Server.Core
EnableLoopbackInternal(appName);
}
- protected abstract void EnableLoopbackInternal(string appName);
+ protected virtual void EnableLoopbackInternal(string appName)
+ {
+
+ }
private void RegisterModules()
{
diff --git a/Emby.Server.Core/Emby.Server.Core.xproj b/Emby.Server.Core/Emby.Server.Core.xproj
index 00f7664bd..fefaa6284 100644
--- a/Emby.Server.Core/Emby.Server.Core.xproj
+++ b/Emby.Server.Core/Emby.Server.Core.xproj
@@ -16,7 +16,6 @@
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
- <ProjectReference Include="..\ServiceStack\ServiceStack.csproj" />
<ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" />
<ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
<ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj" />
diff --git a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs
index eb3a71465..2c7e6a487 100644
--- a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs
+++ b/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs
@@ -187,7 +187,10 @@ namespace Emby.Server.Core.EntryPoints
private void ClearCreatedRules(object state)
{
- _createdRules = new List<string>();
+ lock (_createdRules)
+ {
+ _createdRules.Clear();
+ }
lock (_usnsHandled)
{
_usnsHandled.Clear();
@@ -236,16 +239,23 @@ namespace Emby.Server.Core.EntryPoints
var address = device.LocalAddress.ToString();
- if (!_createdRules.Contains(address))
+ lock (_createdRules)
{
- _createdRules.Add(address);
-
- var success = await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false);
-
- if (success)
+ if (!_createdRules.Contains(address))
{
- await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false);
+ _createdRules.Add(address);
}
+ else
+ {
+ return;
+ }
+ }
+
+ var success = await CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort).ConfigureAwait(false);
+
+ if (success)
+ {
+ await CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort).ConfigureAwait(false);
}
}
diff --git a/Emby.Server.Core/Logging/ConsoleLogger.cs b/Emby.Server.Core/Logging/ConsoleLogger.cs
new file mode 100644
index 000000000..01eca7b7e
--- /dev/null
+++ b/Emby.Server.Core/Logging/ConsoleLogger.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using MediaBrowser.Model.Logging;
+
+namespace Emby.Server.Core.Logging
+{
+ public class ConsoleLogger : IConsoleLogger
+ {
+ public void WriteLine(string message)
+ {
+ Console.WriteLine(message);
+ }
+ }
+}
diff --git a/Emby.Server.Core/project.json b/Emby.Server.Core/project.json
index e987da6aa..70543d7df 100644
--- a/Emby.Server.Core/project.json
+++ b/Emby.Server.Core/project.json
@@ -56,9 +56,6 @@
"Emby.Drawing": {
"target": "project"
},
- "ServiceStack": {
- "target": "project"
- },
"SocketHttpListener.Portable": {
"target": "project"
}
@@ -121,9 +118,6 @@
},
"SocketHttpListener.Portable": {
"target": "project"
- },
- "ServiceStack": {
- "target": "project"
}
}
}
diff --git a/Emby.Common.Implementations/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index 8792778ba..54d1d5302 100644
--- a/Emby.Common.Implementations/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -1,7 +1,8 @@
-using System.IO;
+using System;
+using System.IO;
using MediaBrowser.Common.Configuration;
-namespace Emby.Common.Implementations
+namespace Emby.Server.Implementations.AppBase
{
/// <summary>
/// Provides a base class to hold common application paths used by both the Ui and Server.
@@ -12,12 +13,15 @@ namespace Emby.Common.Implementations
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths"/> class.
/// </summary>
- protected BaseApplicationPaths(string programDataPath, string appFolderPath)
+ protected BaseApplicationPaths(string programDataPath, string appFolderPath, Action<string> createDirectoryFn)
{
ProgramDataPath = programDataPath;
ProgramSystemPath = appFolderPath;
+ CreateDirectoryFn = createDirectoryFn;
}
+ protected Action<string> CreateDirectoryFn;
+
public string ProgramDataPath { get; private set; }
/// <summary>
@@ -41,7 +45,7 @@ namespace Emby.Common.Implementations
{
_dataDirectory = Path.Combine(ProgramDataPath, "data");
- Directory.CreateDirectory(_dataDirectory);
+ CreateDirectoryFn(_dataDirectory);
}
return _dataDirectory;
@@ -148,7 +152,7 @@ namespace Emby.Common.Implementations
{
_cachePath = Path.Combine(ProgramDataPath, "cache");
- Directory.CreateDirectory(_cachePath);
+ CreateDirectoryFn(_cachePath);
}
return _cachePath;
diff --git a/Emby.Common.Implementations/Configuration/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
index 27c9fe615..13874223c 100644
--- a/Emby.Common.Implementations/Configuration/BaseConfigurationManager.cs
+++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
@@ -7,13 +7,12 @@ using System.Threading;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
-using Emby.Common.Implementations;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
-namespace Emby.Common.Implementations.Configuration
+namespace Emby.Server.Implementations.AppBase
{
/// <summary>
/// Class BaseConfigurationManager
diff --git a/Emby.Common.Implementations/Configuration/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
index 0d43a651e..ad2f45945 100644
--- a/Emby.Common.Implementations/Configuration/ConfigurationHelper.cs
+++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs
@@ -4,7 +4,7 @@ using System.Linq;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
-namespace Emby.Common.Implementations.Configuration
+namespace Emby.Server.Implementations.AppBase
{
/// <summary>
/// Class ConfigurationHelper
diff --git a/Emby.Server.Core/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
index eb3d8b9f9..2241e9377 100644
--- a/Emby.Server.Core/Configuration/ServerConfigurationManager.cs
+++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using Emby.Common.Implementations.Configuration;
+using Emby.Server.Implementations.AppBase;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Controller;
@@ -17,7 +17,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
-namespace Emby.Server.Core.Configuration
+namespace Emby.Server.Implementations.Configuration
{
/// <summary>
/// Class ServerConfigurationManager
@@ -187,7 +187,7 @@ namespace Emby.Server.Core.Configuration
// Validate
if (!FileSystem.DirectoryExists(newPath))
{
- throw new DirectoryNotFoundException(string.Format("{0} does not exist.", newPath));
+ throw new FileNotFoundException(string.Format("{0} does not exist.", newPath));
}
EnsureWriteAccess(newPath);
diff --git a/Emby.Server.Implementations/Connect/ConnectData.cs b/Emby.Server.Implementations/Connect/ConnectData.cs
deleted file mode 100644
index 41b89ce52..000000000
--- a/Emby.Server.Implementations/Connect/ConnectData.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Emby.Server.Implementations.Connect
-{
- public class ConnectData
- {
- /// <summary>
- /// Gets or sets the server identifier.
- /// </summary>
- /// <value>The server identifier.</value>
- public string ServerId { get; set; }
- /// <summary>
- /// Gets or sets the access key.
- /// </summary>
- /// <value>The access key.</value>
- public string AccessKey { get; set; }
-
- /// <summary>
- /// Gets or sets the authorizations.
- /// </summary>
- /// <value>The authorizations.</value>
- public List<ConnectAuthorizationInternal> PendingAuthorizations { get; set; }
-
- /// <summary>
- /// Gets or sets the last authorizations refresh.
- /// </summary>
- /// <value>The last authorizations refresh.</value>
- public DateTime LastAuthorizationsRefresh { get; set; }
-
- public ConnectData()
- {
- PendingAuthorizations = new List<ConnectAuthorizationInternal>();
- }
- }
-}
diff --git a/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs b/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs
deleted file mode 100644
index b5639773b..000000000
--- a/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs
+++ /dev/null
@@ -1,218 +0,0 @@
-using MediaBrowser.Common;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Connect;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using System;
-using System.IO;
-using System.Text;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Threading;
-
-namespace Emby.Server.Implementations.Connect
-{
- public class ConnectEntryPoint : IServerEntryPoint
- {
- private ITimer _timer;
- private IpAddressInfo _cachedIpAddress;
- private readonly IHttpClient _httpClient;
- private readonly IApplicationPaths _appPaths;
- private readonly ILogger _logger;
- private readonly IConnectManager _connectManager;
-
- private readonly INetworkManager _networkManager;
- private readonly IApplicationHost _appHost;
- private readonly IFileSystem _fileSystem;
- private readonly ITimerFactory _timerFactory;
- private readonly IEncryptionManager _encryption;
-
- public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory, IEncryptionManager encryption)
- {
- _httpClient = httpClient;
- _appPaths = appPaths;
- _logger = logger;
- _networkManager = networkManager;
- _connectManager = connectManager;
- _appHost = appHost;
- _fileSystem = fileSystem;
- _timerFactory = timerFactory;
- _encryption = encryption;
- }
-
- public void Run()
- {
- LoadCachedAddress();
-
- _timer = _timerFactory.Create(TimerCallback, null, TimeSpan.FromSeconds(5), TimeSpan.FromHours(1));
- ((ConnectManager)_connectManager).Start();
- }
-
- private readonly string[] _ipLookups =
- {
- "http://bot.whatismyipaddress.com",
- "https://connect.emby.media/service/ip"
- };
-
- private async void TimerCallback(object state)
- {
- IpAddressInfo validIpAddress = null;
-
- foreach (var ipLookupUrl in _ipLookups)
- {
- try
- {
- validIpAddress = await GetIpAddress(ipLookupUrl).ConfigureAwait(false);
-
- // Try to find the ipv4 address, if present
- if (validIpAddress.AddressFamily != IpAddressFamily.InterNetworkV6)
- {
- break;
- }
- }
- catch (HttpException)
- {
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting connection info", ex);
- }
- }
-
- // If this produced an ipv6 address, try again
- if (validIpAddress != null && validIpAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
- {
- foreach (var ipLookupUrl in _ipLookups)
- {
- try
- {
- var newAddress = await GetIpAddress(ipLookupUrl, true).ConfigureAwait(false);
-
- // Try to find the ipv4 address, if present
- if (newAddress.AddressFamily != IpAddressFamily.InterNetworkV6)
- {
- validIpAddress = newAddress;
- break;
- }
- }
- catch (HttpException)
- {
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting connection info", ex);
- }
- }
- }
-
- if (validIpAddress != null)
- {
- ((ConnectManager)_connectManager).OnWanAddressResolved(validIpAddress);
- CacheAddress(validIpAddress);
- }
- }
-
- private async Task<IpAddressInfo> GetIpAddress(string lookupUrl, bool preferIpv4 = false)
- {
- // Sometimes whatismyipaddress might fail, but it won't do us any good having users raise alarms over it.
- var logErrors = false;
-
-#if DEBUG
- logErrors = true;
-#endif
- using (var stream = await _httpClient.Get(new HttpRequestOptions
- {
- Url = lookupUrl,
- UserAgent = "Emby/" + _appHost.ApplicationVersion,
- LogErrors = logErrors,
-
- // Seeing block length errors with our server
- EnableHttpCompression = false,
- PreferIpv4 = preferIpv4,
- BufferContent = false
-
- }).ConfigureAwait(false))
- {
- using (var reader = new StreamReader(stream))
- {
- var addressString = await reader.ReadToEndAsync().ConfigureAwait(false);
-
- return _networkManager.ParseIpAddress(addressString);
- }
- }
- }
-
- private string CacheFilePath
- {
- get { return Path.Combine(_appPaths.DataPath, "wan.dat"); }
- }
-
- private void CacheAddress(IpAddressInfo address)
- {
- if (_cachedIpAddress != null && _cachedIpAddress.Equals(address))
- {
- // no need to update the file if the address has not changed
- return;
- }
-
- var path = CacheFilePath;
-
- try
- {
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
- }
- catch (Exception ex)
- {
- }
-
- try
- {
- _fileSystem.WriteAllText(path, _encryption.EncryptString(address.ToString()), Encoding.UTF8);
- _cachedIpAddress = address;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving data", ex);
- }
- }
-
- private void LoadCachedAddress()
- {
- var path = CacheFilePath;
-
- _logger.Info("Loading data from {0}", path);
-
- try
- {
- var endpoint = _encryption.DecryptString(_fileSystem.ReadAllText(path, Encoding.UTF8));
- IpAddressInfo ipAddress;
-
- if (_networkManager.TryParseIpAddress(endpoint, out ipAddress))
- {
- _cachedIpAddress = ipAddress;
- ((ConnectManager)_connectManager).OnWanAddressResolved(ipAddress);
- }
- }
- catch (IOException)
- {
- // File isn't there. no biggie
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error loading data", ex);
- }
- }
-
- public void Dispose()
- {
- if (_timer != null)
- {
- _timer.Dispose();
- _timer = null;
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Connect/ConnectManager.cs b/Emby.Server.Implementations/Connect/ConnectManager.cs
deleted file mode 100644
index 8aac2a8c4..000000000
--- a/Emby.Server.Implementations/Connect/ConnectManager.cs
+++ /dev/null
@@ -1,1193 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Security;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Connect;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Security;
-using MediaBrowser.Model.Connect;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
-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 MediaBrowser.Model.IO;
-using MediaBrowser.Common.Extensions;
-
-namespace Emby.Server.Implementations.Connect
-{
- public class ConnectManager : IConnectManager
- {
- private readonly SemaphoreSlim _operationLock = new SemaphoreSlim(1, 1);
-
- private readonly ILogger _logger;
- private readonly IApplicationPaths _appPaths;
- private readonly IJsonSerializer _json;
- private readonly IEncryptionManager _encryption;
- private readonly IHttpClient _httpClient;
- private readonly IServerApplicationHost _appHost;
- private readonly IServerConfigurationManager _config;
- private readonly IUserManager _userManager;
- private readonly IProviderManager _providerManager;
- private readonly ISecurityManager _securityManager;
- private readonly IFileSystem _fileSystem;
-
- private ConnectData _data = new ConnectData();
-
- public string ConnectServerId
- {
- get { return _data.ServerId; }
- }
- public string ConnectAccessKey
- {
- get { return _data.AccessKey; }
- }
-
- private IpAddressInfo DiscoveredWanIpAddress { get; set; }
-
- public string WanIpAddress
- {
- get
- {
- var address = _config.Configuration.WanDdns;
-
- if (!string.IsNullOrWhiteSpace(address))
- {
- Uri newUri;
-
- if (Uri.TryCreate(address, UriKind.Absolute, out newUri))
- {
- address = newUri.Host;
- }
- }
-
- if (string.IsNullOrWhiteSpace(address) && DiscoveredWanIpAddress != null)
- {
- if (DiscoveredWanIpAddress.AddressFamily == IpAddressFamily.InterNetworkV6)
- {
- address = "[" + DiscoveredWanIpAddress + "]";
- }
- else
- {
- address = DiscoveredWanIpAddress.ToString();
- }
- }
-
- return address;
- }
- }
-
- public string WanApiAddress
- {
- get
- {
- var ip = WanIpAddress;
-
- if (!string.IsNullOrEmpty(ip))
- {
- if (!ip.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
- !ip.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
- {
- ip = (_appHost.EnableHttps ? "https://" : "http://") + ip;
- }
-
- ip += ":";
- ip += _appHost.EnableHttps ? _config.Configuration.PublicHttpsPort.ToString(CultureInfo.InvariantCulture) : _config.Configuration.PublicPort.ToString(CultureInfo.InvariantCulture);
-
- return ip;
- }
-
- return null;
- }
- }
-
- private string XApplicationValue
- {
- get { return _appHost.Name + "/" + _appHost.ApplicationVersion; }
- }
-
- public ConnectManager(ILogger logger,
- IApplicationPaths appPaths,
- IJsonSerializer json,
- IEncryptionManager encryption,
- IHttpClient httpClient,
- IServerApplicationHost appHost,
- IServerConfigurationManager config, IUserManager userManager, IProviderManager providerManager, ISecurityManager securityManager, IFileSystem fileSystem)
- {
- _logger = logger;
- _appPaths = appPaths;
- _json = json;
- _encryption = encryption;
- _httpClient = httpClient;
- _appHost = appHost;
- _config = config;
- _userManager = userManager;
- _providerManager = providerManager;
- _securityManager = securityManager;
- _fileSystem = fileSystem;
-
- LoadCachedData();
- }
-
- internal void Start()
- {
- _config.ConfigurationUpdated += _config_ConfigurationUpdated;
- }
-
- internal void OnWanAddressResolved(IpAddressInfo address)
- {
- DiscoveredWanIpAddress = address;
-
- var task = UpdateConnectInfo();
- }
-
- private async Task UpdateConnectInfo()
- {
- await _operationLock.WaitAsync().ConfigureAwait(false);
-
- try
- {
- await UpdateConnectInfoInternal().ConfigureAwait(false);
- }
- finally
- {
- _operationLock.Release();
- }
- }
-
- private async Task UpdateConnectInfoInternal()
- {
- var wanApiAddress = WanApiAddress;
-
- if (string.IsNullOrWhiteSpace(wanApiAddress))
- {
- _logger.Warn("Cannot update Emby Connect information without a WanApiAddress");
- return;
- }
-
- try
- {
- var localAddress = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
-
- var hasExistingRecord = !string.IsNullOrWhiteSpace(ConnectServerId) &&
- !string.IsNullOrWhiteSpace(ConnectAccessKey);
-
- var createNewRegistration = !hasExistingRecord;
-
- if (hasExistingRecord)
- {
- try
- {
- await UpdateServerRegistration(wanApiAddress, localAddress).ConfigureAwait(false);
- }
- catch (HttpException ex)
- {
- if (!ex.StatusCode.HasValue || !new[] { HttpStatusCode.NotFound, HttpStatusCode.Unauthorized }.Contains(ex.StatusCode.Value))
- {
- throw;
- }
-
- createNewRegistration = true;
- }
- }
-
- if (createNewRegistration)
- {
- await CreateServerRegistration(wanApiAddress, localAddress).ConfigureAwait(false);
- }
-
- _lastReportedIdentifier = GetConnectReportingIdentifier(localAddress, wanApiAddress);
-
- await RefreshAuthorizationsInternal(true, CancellationToken.None).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error registering with Connect", ex);
- }
- }
-
- private string _lastReportedIdentifier;
- private async Task<string> GetConnectReportingIdentifier()
- {
- var url = await _appHost.GetLocalApiUrl().ConfigureAwait(false);
- return GetConnectReportingIdentifier(url, WanApiAddress);
- }
- private string GetConnectReportingIdentifier(string localAddress, string remoteAddress)
- {
- return (remoteAddress ?? string.Empty) + (localAddress ?? string.Empty);
- }
-
- async void _config_ConfigurationUpdated(object sender, EventArgs e)
- {
- // If info hasn't changed, don't report anything
- var connectIdentifier = await GetConnectReportingIdentifier().ConfigureAwait(false);
- if (string.Equals(_lastReportedIdentifier, connectIdentifier, StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
-
- await UpdateConnectInfo().ConfigureAwait(false);
- }
-
- private async Task CreateServerRegistration(string wanApiAddress, string localAddress)
- {
- if (string.IsNullOrWhiteSpace(wanApiAddress))
- {
- throw new ArgumentNullException("wanApiAddress");
- }
-
- var url = "Servers";
- url = GetConnectUrl(url);
-
- var postData = new Dictionary<string, string>
- {
- {"name", _appHost.FriendlyName},
- {"url", wanApiAddress},
- {"systemId", _appHost.SystemId}
- };
-
- if (!string.IsNullOrWhiteSpace(localAddress))
- {
- postData["localAddress"] = localAddress;
- }
-
- var options = new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None,
- BufferContent = false
- };
-
- options.SetPostData(postData);
- SetApplicationHeader(options);
-
- using (var response = await _httpClient.Post(options).ConfigureAwait(false))
- {
- var data = _json.DeserializeFromStream<ServerRegistrationResponse>(response.Content);
-
- _data.ServerId = data.Id;
- _data.AccessKey = data.AccessKey;
-
- CacheData();
- }
- }
-
- private async Task UpdateServerRegistration(string wanApiAddress, string localAddress)
- {
- if (string.IsNullOrWhiteSpace(wanApiAddress))
- {
- throw new ArgumentNullException("wanApiAddress");
- }
-
- if (string.IsNullOrWhiteSpace(ConnectServerId))
- {
- throw new ArgumentNullException("ConnectServerId");
- }
-
- var url = "Servers";
- url = GetConnectUrl(url);
- url += "?id=" + ConnectServerId;
-
- var postData = new Dictionary<string, string>
- {
- {"name", _appHost.FriendlyName},
- {"url", wanApiAddress},
- {"systemId", _appHost.SystemId}
- };
-
- if (!string.IsNullOrWhiteSpace(localAddress))
- {
- postData["localAddress"] = localAddress;
- }
-
- var options = new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None,
- BufferContent = false
- };
-
- options.SetPostData(postData);
-
- SetServerAccessToken(options);
- SetApplicationHeader(options);
-
- // No need to examine the response
- using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
- {
- }
- }
-
- private readonly object _dataFileLock = new object();
- private string CacheFilePath
- {
- get { return Path.Combine(_appPaths.DataPath, "connect.txt"); }
- }
-
- private void CacheData()
- {
- var path = CacheFilePath;
-
- try
- {
- _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
-
- var json = _json.SerializeToString(_data);
-
- var encrypted = _encryption.EncryptString(json);
-
- lock (_dataFileLock)
- {
- _fileSystem.WriteAllText(path, encrypted, Encoding.UTF8);
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving data", ex);
- }
- }
-
- private void LoadCachedData()
- {
- var path = CacheFilePath;
-
- _logger.Info("Loading data from {0}", path);
-
- try
- {
- lock (_dataFileLock)
- {
- var encrypted = _fileSystem.ReadAllText(path, Encoding.UTF8);
-
- var json = _encryption.DecryptString(encrypted);
-
- _data = _json.DeserializeFromString<ConnectData>(json);
- }
- }
- catch (IOException)
- {
- // File isn't there. no biggie
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error loading data", ex);
- }
- }
-
- private User GetUser(string id)
- {
- var user = _userManager.GetUserById(id);
-
- if (user == null)
- {
- throw new ArgumentException("User not found.");
- }
-
- return user;
- }
-
- private string GetConnectUrl(string handler)
- {
- return "https://connect.emby.media/service/" + handler;
- }
-
- public async Task<UserLinkResult> LinkUser(string userId, string connectUsername)
- {
- if (string.IsNullOrWhiteSpace(userId))
- {
- throw new ArgumentNullException("userId");
- }
- if (string.IsNullOrWhiteSpace(connectUsername))
- {
- throw new ArgumentNullException("connectUsername");
- }
- if (string.IsNullOrWhiteSpace(ConnectServerId))
- {
- await UpdateConnectInfo().ConfigureAwait(false);
- }
-
- await _operationLock.WaitAsync().ConfigureAwait(false);
-
- try
- {
- return await LinkUserInternal(userId, connectUsername).ConfigureAwait(false);
- }
- finally
- {
- _operationLock.Release();
- }
- }
-
- private async Task<UserLinkResult> LinkUserInternal(string userId, string connectUsername)
- {
- if (string.IsNullOrWhiteSpace(ConnectServerId))
- {
- throw new ArgumentNullException("ConnectServerId");
- }
-
- var connectUser = await GetConnectUser(new ConnectUserQuery
- {
- NameOrEmail = connectUsername
-
- }, CancellationToken.None).ConfigureAwait(false);
-
- if (!connectUser.IsActive)
- {
- throw new ArgumentException("The Emby account has been disabled.");
- }
-
- var existingUser = _userManager.Users.FirstOrDefault(i => string.Equals(i.ConnectUserId, connectUser.Id) && !string.IsNullOrWhiteSpace(i.ConnectAccessKey));
- if (existingUser != null)
- {
- throw new InvalidOperationException("This connect user is already linked to local user " + existingUser.Name);
- }
-
- var user = GetUser(userId);
-
- if (!string.IsNullOrWhiteSpace(user.ConnectUserId))
- {
- await RemoveConnect(user, user.ConnectUserId).ConfigureAwait(false);
- }
-
- var url = GetConnectUrl("ServerAuthorizations");
-
- var options = new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None,
- BufferContent = false
- };
-
- var accessToken = Guid.NewGuid().ToString("N");
-
- var postData = new Dictionary<string, string>
- {
- {"serverId", ConnectServerId},
- {"userId", connectUser.Id},
- {"userType", "Linked"},
- {"accessToken", accessToken}
- };
-
- options.SetPostData(postData);
-
- SetServerAccessToken(options);
- SetApplicationHeader(options);
-
- var result = new UserLinkResult();
-
- // No need to examine the response
- using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
- {
- var response = _json.DeserializeFromStream<ServerUserAuthorizationResponse>(stream);
-
- result.IsPending = string.Equals(response.AcceptStatus, "waiting", StringComparison.OrdinalIgnoreCase);
- }
-
- user.ConnectAccessKey = accessToken;
- user.ConnectUserName = connectUser.Name;
- user.ConnectUserId = connectUser.Id;
- user.ConnectLinkType = UserLinkType.LinkedUser;
-
- await user.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
-
- await _userManager.UpdateConfiguration(user.Id.ToString("N"), user.Configuration);
-
- await RefreshAuthorizationsInternal(false, CancellationToken.None).ConfigureAwait(false);
-
- return result;
- }
-
- public async Task<UserLinkResult> InviteUser(ConnectAuthorizationRequest request)
- {
- if (string.IsNullOrWhiteSpace(ConnectServerId))
- {
- await UpdateConnectInfo().ConfigureAwait(false);
- }
-
- await _operationLock.WaitAsync().ConfigureAwait(false);
-
- try
- {
- return await InviteUserInternal(request).ConfigureAwait(false);
- }
- finally
- {
- _operationLock.Release();
- }
- }
-
- private async Task<UserLinkResult> InviteUserInternal(ConnectAuthorizationRequest request)
- {
- var connectUsername = request.ConnectUserName;
- var sendingUserId = request.SendingUserId;
-
- if (string.IsNullOrWhiteSpace(connectUsername))
- {
- throw new ArgumentNullException("connectUsername");
- }
- if (string.IsNullOrWhiteSpace(ConnectServerId))
- {
- throw new ArgumentNullException("ConnectServerId");
- }
-
- var sendingUser = GetUser(sendingUserId);
- var requesterUserName = sendingUser.ConnectUserName;
-
- if (string.IsNullOrWhiteSpace(requesterUserName))
- {
- throw new ArgumentException("A Connect account is required in order to send invitations.");
- }
-
- string connectUserId = null;
- var result = new UserLinkResult();
-
- try
- {
- var connectUser = await GetConnectUser(new ConnectUserQuery
- {
- NameOrEmail = connectUsername
-
- }, CancellationToken.None).ConfigureAwait(false);
-
- if (!connectUser.IsActive)
- {
- throw new ArgumentException("The Emby account is not active. Please ensure the account has been activated by following the instructions within the email confirmation.");
- }
-
- connectUserId = connectUser.Id;
- result.GuestDisplayName = connectUser.Name;
- }
- catch (HttpException ex)
- {
- if (!ex.StatusCode.HasValue || ex.IsTimedOut)
- {
- throw new Exception("Unable to invite guest, " + ex.Message, ex);
- }
-
- // If they entered a username, then whatever the error is just throw it, for example, user not found
- if (!Validator.EmailIsValid(connectUsername))
- {
- if (ex.StatusCode.Value == HttpStatusCode.NotFound)
- {
- throw new ResourceNotFoundException();
- }
- throw;
- }
-
- if (ex.StatusCode.Value != HttpStatusCode.NotFound)
- {
- throw;
- }
- }
-
- if (string.IsNullOrWhiteSpace(connectUserId))
- {
- return await SendNewUserInvitation(requesterUserName, connectUsername).ConfigureAwait(false);
- }
-
- var url = GetConnectUrl("ServerAuthorizations");
-
- var options = new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None,
- BufferContent = false
- };
-
- var accessToken = Guid.NewGuid().ToString("N");
-
- var postData = new Dictionary<string, string>
- {
- {"serverId", ConnectServerId},
- {"userId", connectUserId},
- {"userType", "Guest"},
- {"accessToken", accessToken},
- {"requesterUserName", requesterUserName}
- };
-
- options.SetPostData(postData);
-
- SetServerAccessToken(options);
- SetApplicationHeader(options);
-
- // No need to examine the response
- using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
- {
- var response = _json.DeserializeFromStream<ServerUserAuthorizationResponse>(stream);
-
- result.IsPending = string.Equals(response.AcceptStatus, "waiting", StringComparison.OrdinalIgnoreCase);
-
- _data.PendingAuthorizations.Add(new ConnectAuthorizationInternal
- {
- ConnectUserId = response.UserId,
- Id = response.Id,
- ImageUrl = response.UserImageUrl,
- UserName = response.UserName,
- EnabledLibraries = request.EnabledLibraries,
- EnabledChannels = request.EnabledChannels,
- EnableLiveTv = request.EnableLiveTv,
- AccessToken = accessToken
- });
-
- CacheData();
- }
-
- await RefreshAuthorizationsInternal(false, CancellationToken.None).ConfigureAwait(false);
-
- return result;
- }
-
- private async Task<UserLinkResult> SendNewUserInvitation(string fromName, string email)
- {
- var url = GetConnectUrl("users/invite");
-
- var options = new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None,
- BufferContent = false
- };
-
- var postData = new Dictionary<string, string>
- {
- {"email", email},
- {"requesterUserName", fromName}
- };
-
- options.SetPostData(postData);
- SetApplicationHeader(options);
-
- // No need to examine the response
- using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
- {
- }
-
- return new UserLinkResult
- {
- IsNewUserInvitation = true,
- GuestDisplayName = email
- };
- }
-
- public Task RemoveConnect(string userId)
- {
- var user = GetUser(userId);
-
- return RemoveConnect(user, user.ConnectUserId);
- }
-
- private async Task RemoveConnect(User user, string connectUserId)
- {
- if (!string.IsNullOrWhiteSpace(connectUserId))
- {
- await CancelAuthorizationByConnectUserId(connectUserId).ConfigureAwait(false);
- }
-
- user.ConnectAccessKey = null;
- user.ConnectUserName = null;
- user.ConnectUserId = null;
- user.ConnectLinkType = null;
-
- await user.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
- }
-
- private async Task<ConnectUser> GetConnectUser(ConnectUserQuery query, CancellationToken cancellationToken)
- {
- var url = GetConnectUrl("user");
-
- if (!string.IsNullOrWhiteSpace(query.Id))
- {
- url = url + "?id=" + WebUtility.UrlEncode(query.Id);
- }
- else if (!string.IsNullOrWhiteSpace(query.NameOrEmail))
- {
- url = url + "?nameOrEmail=" + WebUtility.UrlEncode(query.NameOrEmail);
- }
- else if (!string.IsNullOrWhiteSpace(query.Name))
- {
- url = url + "?name=" + WebUtility.UrlEncode(query.Name);
- }
- else if (!string.IsNullOrWhiteSpace(query.Email))
- {
- url = url + "?name=" + WebUtility.UrlEncode(query.Email);
- }
- else
- {
- throw new ArgumentException("Empty ConnectUserQuery supplied");
- }
-
- var options = new HttpRequestOptions
- {
- CancellationToken = cancellationToken,
- Url = url,
- BufferContent = false
- };
-
- SetServerAccessToken(options);
- SetApplicationHeader(options);
-
- using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
- {
- var response = _json.DeserializeFromStream<GetConnectUserResponse>(stream);
-
- return new ConnectUser
- {
- Email = response.Email,
- Id = response.Id,
- Name = response.Name,
- IsActive = response.IsActive,
- ImageUrl = response.ImageUrl
- };
- }
- }
-
- private void SetApplicationHeader(HttpRequestOptions options)
- {
- options.RequestHeaders.Add("X-Application", XApplicationValue);
- }
-
- private void SetServerAccessToken(HttpRequestOptions options)
- {
- if (string.IsNullOrWhiteSpace(ConnectAccessKey))
- {
- throw new ArgumentNullException("ConnectAccessKey");
- }
-
- options.RequestHeaders.Add("X-Connect-Token", ConnectAccessKey);
- }
-
- public async Task RefreshAuthorizations(CancellationToken cancellationToken)
- {
- await _operationLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- await RefreshAuthorizationsInternal(true, cancellationToken).ConfigureAwait(false);
- }
- finally
- {
- _operationLock.Release();
- }
- }
-
- private async Task RefreshAuthorizationsInternal(bool refreshImages, CancellationToken cancellationToken)
- {
- if (string.IsNullOrWhiteSpace(ConnectServerId))
- {
- throw new ArgumentNullException("ConnectServerId");
- }
-
- var url = GetConnectUrl("ServerAuthorizations");
-
- url += "?serverId=" + ConnectServerId;
-
- var options = new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- BufferContent = false
- };
-
- SetServerAccessToken(options);
- SetApplicationHeader(options);
-
- try
- {
- using (var stream = (await _httpClient.SendAsync(options, "GET").ConfigureAwait(false)).Content)
- {
- var list = _json.DeserializeFromStream<List<ServerUserAuthorizationResponse>>(stream);
-
- await RefreshAuthorizations(list, refreshImages).ConfigureAwait(false);
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing server authorizations.", ex);
- }
- }
-
- private async Task RefreshAuthorizations(List<ServerUserAuthorizationResponse> list, bool refreshImages)
- {
- var users = _userManager.Users.ToList();
-
- // Handle existing authorizations that were removed by the Connect server
- // Handle existing authorizations whose status may have been updated
- foreach (var user in users)
- {
- if (!string.IsNullOrWhiteSpace(user.ConnectUserId))
- {
- var connectEntry = list.FirstOrDefault(i => string.Equals(i.UserId, user.ConnectUserId, StringComparison.OrdinalIgnoreCase));
-
- if (connectEntry == null)
- {
- var deleteUser = user.ConnectLinkType.HasValue &&
- user.ConnectLinkType.Value == UserLinkType.Guest;
-
- user.ConnectUserId = null;
- user.ConnectAccessKey = null;
- user.ConnectUserName = null;
- user.ConnectLinkType = null;
-
- await _userManager.UpdateUser(user).ConfigureAwait(false);
-
- if (deleteUser)
- {
- _logger.Debug("Deleting guest user {0}", user.Name);
- await _userManager.DeleteUser(user).ConfigureAwait(false);
- }
- }
- else
- {
- var changed = !string.Equals(user.ConnectAccessKey, connectEntry.AccessToken, StringComparison.OrdinalIgnoreCase);
-
- if (changed)
- {
- user.ConnectUserId = connectEntry.UserId;
- user.ConnectAccessKey = connectEntry.AccessToken;
-
- await _userManager.UpdateUser(user).ConfigureAwait(false);
- }
- }
- }
- }
-
- var currentPendingList = _data.PendingAuthorizations.ToList();
- var newPendingList = new List<ConnectAuthorizationInternal>();
-
- foreach (var connectEntry in list)
- {
- if (string.Equals(connectEntry.UserType, "guest", StringComparison.OrdinalIgnoreCase))
- {
- var currentPendingEntry = currentPendingList.FirstOrDefault(i => string.Equals(i.Id, connectEntry.Id, StringComparison.OrdinalIgnoreCase));
-
- if (string.Equals(connectEntry.AcceptStatus, "accepted", StringComparison.OrdinalIgnoreCase))
- {
- var user = _userManager.Users
- .FirstOrDefault(i => string.Equals(i.ConnectUserId, connectEntry.UserId, StringComparison.OrdinalIgnoreCase));
-
- if (user == null)
- {
- // Add user
- user = await _userManager.CreateUser(_userManager.MakeValidUsername(connectEntry.UserName)).ConfigureAwait(false);
-
- user.ConnectUserName = connectEntry.UserName;
- user.ConnectUserId = connectEntry.UserId;
- user.ConnectLinkType = UserLinkType.Guest;
- user.ConnectAccessKey = connectEntry.AccessToken;
-
- await _userManager.UpdateUser(user).ConfigureAwait(false);
-
- user.Policy.IsHidden = true;
- user.Policy.EnableLiveTvManagement = false;
- user.Policy.EnableContentDeletion = false;
- user.Policy.EnableRemoteControlOfOtherUsers = false;
- user.Policy.EnableSharedDeviceControl = false;
- user.Policy.IsAdministrator = false;
-
- if (currentPendingEntry != null)
- {
- user.Policy.EnabledFolders = currentPendingEntry.EnabledLibraries;
- user.Policy.EnableAllFolders = false;
-
- user.Policy.EnabledChannels = currentPendingEntry.EnabledChannels;
- user.Policy.EnableAllChannels = false;
-
- user.Policy.EnableLiveTvAccess = currentPendingEntry.EnableLiveTv;
- }
-
- await _userManager.UpdateConfiguration(user.Id.ToString("N"), user.Configuration);
- }
- }
- else if (string.Equals(connectEntry.AcceptStatus, "waiting", StringComparison.OrdinalIgnoreCase))
- {
- currentPendingEntry = currentPendingEntry ?? new ConnectAuthorizationInternal();
-
- currentPendingEntry.ConnectUserId = connectEntry.UserId;
- currentPendingEntry.ImageUrl = connectEntry.UserImageUrl;
- currentPendingEntry.UserName = connectEntry.UserName;
- currentPendingEntry.Id = connectEntry.Id;
- currentPendingEntry.AccessToken = connectEntry.AccessToken;
-
- newPendingList.Add(currentPendingEntry);
- }
- }
- }
-
- _data.PendingAuthorizations = newPendingList;
-
- if (!newPendingList.Select(i => i.Id).SequenceEqual(currentPendingList.Select(i => i.Id), StringComparer.Ordinal))
- {
- CacheData();
- }
-
- await RefreshGuestNames(list, refreshImages).ConfigureAwait(false);
- }
-
- private async Task RefreshGuestNames(List<ServerUserAuthorizationResponse> list, bool refreshImages)
- {
- var users = _userManager.Users
- .Where(i => !string.IsNullOrEmpty(i.ConnectUserId) && i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.Guest)
- .ToList();
-
- foreach (var user in users)
- {
- var authorization = list.FirstOrDefault(i => string.Equals(i.UserId, user.ConnectUserId, StringComparison.Ordinal));
-
- if (authorization == null)
- {
- _logger.Warn("Unable to find connect authorization record for user {0}", user.Name);
- continue;
- }
-
- var syncConnectName = true;
- var syncConnectImage = true;
-
- if (syncConnectName)
- {
- var changed = !string.Equals(authorization.UserName, user.Name, StringComparison.OrdinalIgnoreCase);
-
- if (changed)
- {
- await user.Rename(authorization.UserName).ConfigureAwait(false);
- }
- }
-
- if (syncConnectImage)
- {
- var imageUrl = authorization.UserImageUrl;
-
- if (!string.IsNullOrWhiteSpace(imageUrl))
- {
- var changed = false;
-
- if (!user.HasImage(ImageType.Primary))
- {
- changed = true;
- }
- else if (refreshImages)
- {
- using (var response = await _httpClient.SendAsync(new HttpRequestOptions
- {
- Url = imageUrl,
- BufferContent = false
-
- }, "HEAD").ConfigureAwait(false))
- {
- var length = response.ContentLength;
-
- if (length != _fileSystem.GetFileInfo(user.GetImageInfo(ImageType.Primary, 0).Path).Length)
- {
- changed = true;
- }
- }
- }
-
- if (changed)
- {
- await _providerManager.SaveImage(user, imageUrl, ImageType.Primary, null, CancellationToken.None).ConfigureAwait(false);
-
- await user.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
- {
- ForceSave = true,
-
- }, CancellationToken.None).ConfigureAwait(false);
- }
- }
- }
- }
- }
-
- public async Task<List<ConnectAuthorization>> GetPendingGuests()
- {
- var time = DateTime.UtcNow - _data.LastAuthorizationsRefresh;
-
- if (time.TotalMinutes >= 5)
- {
- await _operationLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
-
- try
- {
- await RefreshAuthorizationsInternal(false, CancellationToken.None).ConfigureAwait(false);
-
- _data.LastAuthorizationsRefresh = DateTime.UtcNow;
- CacheData();
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error refreshing authorization", ex);
- }
- finally
- {
- _operationLock.Release();
- }
- }
-
- return _data.PendingAuthorizations.Select(i => new ConnectAuthorization
- {
- ConnectUserId = i.ConnectUserId,
- EnableLiveTv = i.EnableLiveTv,
- EnabledChannels = i.EnabledChannels,
- EnabledLibraries = i.EnabledLibraries,
- Id = i.Id,
- ImageUrl = i.ImageUrl,
- UserName = i.UserName
-
- }).ToList();
- }
-
- public async Task CancelAuthorization(string id)
- {
- await _operationLock.WaitAsync().ConfigureAwait(false);
-
- try
- {
- await CancelAuthorizationInternal(id).ConfigureAwait(false);
- }
- finally
- {
- _operationLock.Release();
- }
- }
-
- private async Task CancelAuthorizationInternal(string id)
- {
- var connectUserId = _data.PendingAuthorizations
- .First(i => string.Equals(i.Id, id, StringComparison.Ordinal))
- .ConnectUserId;
-
- await CancelAuthorizationByConnectUserId(connectUserId).ConfigureAwait(false);
-
- await RefreshAuthorizationsInternal(false, CancellationToken.None).ConfigureAwait(false);
- }
-
- private async Task CancelAuthorizationByConnectUserId(string connectUserId)
- {
- if (string.IsNullOrWhiteSpace(connectUserId))
- {
- throw new ArgumentNullException("connectUserId");
- }
- if (string.IsNullOrWhiteSpace(ConnectServerId))
- {
- throw new ArgumentNullException("ConnectServerId");
- }
-
- var url = GetConnectUrl("ServerAuthorizations");
-
- var options = new HttpRequestOptions
- {
- Url = url,
- CancellationToken = CancellationToken.None,
- BufferContent = false
- };
-
- var postData = new Dictionary<string, string>
- {
- {"serverId", ConnectServerId},
- {"userId", connectUserId}
- };
-
- options.SetPostData(postData);
-
- SetServerAccessToken(options);
- SetApplicationHeader(options);
-
- try
- {
- // No need to examine the response
- using (var stream = (await _httpClient.SendAsync(options, "DELETE").ConfigureAwait(false)).Content)
- {
- }
- }
- catch (HttpException ex)
- {
- // If connect says the auth doesn't exist, we can handle that gracefully since this is a remove operation
-
- if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
- {
- throw;
- }
-
- _logger.Debug("Connect returned a 404 when removing a user auth link. Handling it.");
- }
- }
-
- public async Task<ConnectAuthenticationResult> Authenticate(string username, string passwordMd5)
- {
- if (string.IsNullOrWhiteSpace(username))
- {
- throw new ArgumentNullException("username");
- }
-
- if (string.IsNullOrWhiteSpace(passwordMd5))
- {
- throw new ArgumentNullException("passwordMd5");
- }
-
- var options = new HttpRequestOptions
- {
- Url = GetConnectUrl("user/authenticate"),
- BufferContent = false
- };
-
- options.SetPostData(new Dictionary<string, string>
- {
- {"userName",username},
- {"password",passwordMd5}
- });
-
- SetApplicationHeader(options);
-
- // No need to examine the response
- using (var response = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content)
- {
- return _json.DeserializeFromStream<ConnectAuthenticationResult>(response);
- }
- }
-
- public async Task<User> GetLocalUser(string connectUserId)
- {
- var user = _userManager.Users
- .FirstOrDefault(i => string.Equals(i.ConnectUserId, connectUserId, StringComparison.OrdinalIgnoreCase));
-
- if (user == null)
- {
- await RefreshAuthorizations(CancellationToken.None).ConfigureAwait(false);
- }
-
- return _userManager.Users
- .FirstOrDefault(i => string.Equals(i.ConnectUserId, connectUserId, StringComparison.OrdinalIgnoreCase));
- }
-
- public User GetUserFromExchangeToken(string token)
- {
- if (string.IsNullOrWhiteSpace(token))
- {
- throw new ArgumentNullException("token");
- }
-
- return _userManager.Users.FirstOrDefault(u => string.Equals(token, u.ConnectAccessKey, StringComparison.OrdinalIgnoreCase));
- }
-
- public bool IsAuthorizationTokenValid(string token)
- {
- if (string.IsNullOrWhiteSpace(token))
- {
- throw new ArgumentNullException("token");
- }
-
- return _userManager.Users.Any(u => string.Equals(token, u.ConnectAccessKey, StringComparison.OrdinalIgnoreCase)) ||
- _data.PendingAuthorizations.Select(i => i.AccessToken).Contains(token, StringComparer.OrdinalIgnoreCase);
- }
- }
-}
diff --git a/Emby.Server.Implementations/Connect/Responses.cs b/Emby.Server.Implementations/Connect/Responses.cs
deleted file mode 100644
index 87cb6cdf9..000000000
--- a/Emby.Server.Implementations/Connect/Responses.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Connect;
-
-namespace Emby.Server.Implementations.Connect
-{
- public class ServerRegistrationResponse
- {
- public string Id { get; set; }
- public string Url { get; set; }
- public string Name { get; set; }
- public string AccessKey { get; set; }
- }
-
- public class UpdateServerRegistrationResponse
- {
- public string Id { get; set; }
- public string Url { get; set; }
- public string Name { get; set; }
- }
-
- public class GetConnectUserResponse
- {
- public string Id { get; set; }
- public string Name { get; set; }
- public string DisplayName { get; set; }
- public string Email { get; set; }
- public bool IsActive { get; set; }
- public string ImageUrl { get; set; }
- }
-
- public class ServerUserAuthorizationResponse
- {
- public string Id { get; set; }
- public string ServerId { get; set; }
- public string UserId { get; set; }
- public string AccessToken { get; set; }
- public string DateCreated { get; set; }
- public bool IsActive { get; set; }
- public string AcceptStatus { get; set; }
- public string UserType { get; set; }
- public string UserImageUrl { get; set; }
- public string UserName { get; set; }
- }
-
- public class ConnectUserPreferences
- {
- public string[] PreferredAudioLanguages { get; set; }
- public bool PlayDefaultAudioTrack { get; set; }
- public string[] PreferredSubtitleLanguages { get; set; }
- public SubtitlePlaybackMode SubtitleMode { get; set; }
- public bool GroupMoviesIntoBoxSets { get; set; }
-
- public ConnectUserPreferences()
- {
- PreferredAudioLanguages = new string[] { };
- PreferredSubtitleLanguages = new string[] { };
- }
-
- public static ConnectUserPreferences FromUserConfiguration(UserConfiguration config)
- {
- return new ConnectUserPreferences
- {
- PlayDefaultAudioTrack = config.PlayDefaultAudioTrack,
- SubtitleMode = config.SubtitleMode,
- PreferredAudioLanguages = string.IsNullOrWhiteSpace(config.AudioLanguagePreference) ? new string[] { } : new[] { config.AudioLanguagePreference },
- PreferredSubtitleLanguages = string.IsNullOrWhiteSpace(config.SubtitleLanguagePreference) ? new string[] { } : new[] { config.SubtitleLanguagePreference }
- };
- }
-
- public void MergeInto(UserConfiguration config)
- {
-
- }
- }
-
- public class UserPreferencesDto<T>
- {
- public T data { get; set; }
- }
-
- public class ConnectAuthorizationInternal : ConnectAuthorization
- {
- public string AccessToken { get; set; }
- }
-}
diff --git a/Emby.Server.Implementations/Connect/Validator.cs b/Emby.Server.Implementations/Connect/Validator.cs
deleted file mode 100644
index 5c94fa71c..000000000
--- a/Emby.Server.Implementations/Connect/Validator.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System.Text.RegularExpressions;
-
-namespace Emby.Server.Implementations.Connect
-{
- public static class Validator
- {
- static readonly Regex ValidEmailRegex = CreateValidEmailRegex();
-
- /// <summary>
- /// Taken from http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx
- /// </summary>
- /// <returns></returns>
- private static Regex CreateValidEmailRegex()
- {
- const string validEmailPattern = @"^(?!\.)(""([^""\r\\]|\\[""\r\\])*""|"
- + @"([-a-z0-9!#$%&'*+/=?^_`{|}~]|(?<!\.)\.)*)(?<!\.)"
- + @"@[a-z0-9][\w\.-]*[a-z0-9]\.[a-z][a-z\.]*[a-z]$";
-
- return new Regex(validEmailPattern, RegexOptions.IgnoreCase);
- }
-
- internal static bool EmailIsValid(string emailAddress)
- {
- bool isValid = ValidEmailRegex.IsMatch(emailAddress);
-
- return isValid;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 4a4a1a6bf..1f72ebd54 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -2872,7 +2872,8 @@ namespace Emby.Server.Implementations.Data
}
if (string.Equals(name, ItemSortBy.IsFavoriteOrLiked, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("IsFavorite", true);
+ // (Select Case When Abs(COALESCE(ProductionYear, 0) - @ItemProductionYear) < 10 Then 2 Else 0 End )
+ return new Tuple<string, bool>("(Select Case When IsFavorite is null Then 0 Else IsFavorite End )", true);
}
if (string.Equals(name, ItemSortBy.IsFolder, StringComparison.OrdinalIgnoreCase))
{
@@ -3874,6 +3875,25 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add(clause);
}
+ if (query.AlbumIds.Length > 0)
+ {
+ var clauses = new List<string>();
+ var index = 0;
+ foreach (var albumId in query.AlbumIds)
+ {
+ var paramName = "@AlbumIds" + index;
+
+ clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")");
+ if (statement != null)
+ {
+ statement.TryBind(paramName, albumId.ToGuidParamValue());
+ }
+ index++;
+ }
+ var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
+ whereClauses.Add(clause);
+ }
+
if (query.ExcludeArtistIds.Length > 0)
{
var clauses = new List<string>();
@@ -4227,30 +4247,6 @@ namespace Emby.Server.Implementations.Data
{
whereClauses.Add("ProviderIds like '%tvdb=%'");
}
-
- if (query.AlbumNames.Length > 0)
- {
- var clause = "(";
-
- var index = 0;
- foreach (var name in query.AlbumNames)
- {
- if (index > 0)
- {
- clause += " OR ";
- }
- clause += "Album=@AlbumName" + index;
-
- if (statement != null)
- {
- statement.TryBind("@AlbumName" + index, name);
- }
- index++;
- }
-
- clause += ")";
- whereClauses.Add(clause);
- }
if (query.HasThemeSong.HasValue)
{
if (query.HasThemeSong.Value)
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 9c50ad5da..4ee3df7f5 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -360,7 +360,6 @@ namespace Emby.Server.Implementations.Dto
var collectionFolder = item as ICollectionFolder;
if (collectionFolder != null)
{
- dto.OriginalCollectionType = collectionFolder.CollectionType;
dto.CollectionType = collectionFolder.CollectionType;
}
@@ -492,7 +491,10 @@ namespace Emby.Server.Implementations.Dto
}
}
- dto.PlayAccess = item.GetPlayAccess(user);
+ //if (!(item is LiveTvProgram))
+ {
+ dto.PlayAccess = item.GetPlayAccess(user);
+ }
if (fields.Contains(ItemFields.BasicSyncInfo) || fields.Contains(ItemFields.SyncInfo))
{
@@ -502,33 +504,6 @@ namespace Emby.Server.Implementations.Dto
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)
@@ -879,20 +854,6 @@ namespace Emby.Server.Implementations.Dto
}
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))
@@ -994,7 +955,12 @@ namespace Emby.Server.Implementations.Dto
}
dto.MediaType = item.MediaType;
- dto.LocationType = item.LocationType;
+
+ if (!(item is LiveTvProgram))
+ {
+ dto.LocationType = item.LocationType;
+ }
+
if (item.IsHD.HasValue && item.IsHD.Value)
{
dto.IsHD = item.IsHD;
@@ -1065,7 +1031,7 @@ namespace Emby.Server.Implementations.Dto
if (fields.Contains(ItemFields.Path))
{
- dto.Path = GetMappedPath(item);
+ dto.Path = GetMappedPath(item, owner);
}
dto.PremiereDate = item.PremiereDate;
@@ -1102,7 +1068,10 @@ namespace Emby.Server.Implementations.Dto
}
dto.Type = item.GetClientTypeName();
- dto.CommunityRating = item.CommunityRating;
+ if ((item.CommunityRating ?? 0) > 0)
+ {
+ dto.CommunityRating = item.CommunityRating;
+ }
if (fields.Contains(ItemFields.VoteCount))
{
@@ -1396,7 +1365,7 @@ namespace Emby.Server.Implementations.Dto
}
catch (Exception ex)
{
-
+
}
}
}
@@ -1409,9 +1378,7 @@ namespace Emby.Server.Implementations.Dto
{
dto.AirDays = series.AirDays;
dto.AirTime = series.AirTime;
- dto.SeriesStatus = series.Status;
-
- dto.AnimeSeriesIndex = series.AnimeSeriesIndex;
+ dto.Status = series.Status.HasValue ? series.Status.Value.ToString() : null;
}
// Add SeasonInfo
@@ -1473,9 +1440,12 @@ namespace Emby.Server.Implementations.Dto
SetBookProperties(dto, book);
}
- if (item.ProductionLocations.Count > 0 || item is Movie)
+ if (fields.Contains(ItemFields.ProductionLocations))
{
- dto.ProductionLocations = item.ProductionLocations.ToArray();
+ if (item.ProductionLocations.Count > 0 || item is Movie)
+ {
+ dto.ProductionLocations = item.ProductionLocations.ToArray();
+ }
}
var photo = item as Photo;
@@ -1596,7 +1566,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- private string GetMappedPath(BaseItem item)
+ private string GetMappedPath(BaseItem item, BaseItem ownerItem)
{
var path = item.Path;
@@ -1604,7 +1574,7 @@ namespace Emby.Server.Implementations.Dto
if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
{
- path = _libraryManager.GetPathAfterNetworkSubstitution(path, item);
+ path = _libraryManager.GetPathAfterNetworkSubstitution(path, ownerItem ?? item);
}
return path;
@@ -1630,8 +1600,20 @@ namespace Emby.Server.Implementations.Dto
return null;
}
+ var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList();
+
ImageSize size;
+ if (supportedEnhancers.Count == 0)
+ {
+ var defaultAspectRatio = item.GetDefaultPrimaryImageAspectRatio();
+
+ if (defaultAspectRatio.HasValue)
+ {
+ return defaultAspectRatio.Value;
+ }
+ }
+
try
{
size = _imageProcessor.GetImageSize(imageInfo);
@@ -1642,8 +1624,6 @@ namespace Emby.Server.Implementations.Dto
return null;
}
- var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList();
-
foreach (var enhancer in supportedEnhancers)
{
try
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index a1bd67f1f..13efb7bf9 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -35,6 +35,9 @@
<Compile Include="Activity\ActivityLogEntryPoint.cs" />
<Compile Include="Activity\ActivityManager.cs" />
<Compile Include="Activity\ActivityRepository.cs" />
+ <Compile Include="AppBase\BaseApplicationPaths.cs" />
+ <Compile Include="AppBase\BaseConfigurationManager.cs" />
+ <Compile Include="AppBase\ConfigurationHelper.cs" />
<Compile Include="Branding\BrandingConfigurationFactory.cs" />
<Compile Include="Browser\BrowserLauncher.cs" />
<Compile Include="Channels\ChannelConfigurations.cs" />
@@ -46,11 +49,7 @@
<Compile Include="Collections\CollectionImageProvider.cs" />
<Compile Include="Collections\CollectionManager.cs" />
<Compile Include="Collections\CollectionsDynamicFolder.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="Configuration\ServerConfigurationManager.cs" />
<Compile Include="Data\ManagedConnection.cs" />
<Compile Include="Data\SqliteDisplayPreferencesRepository.cs" />
<Compile Include="Data\SqliteFileOrganizationRepository.cs" />
@@ -179,6 +178,7 @@
<Compile Include="LiveTv\TunerHosts\MulticastStream.cs" />
<Compile Include="LiveTv\TunerHosts\QueueStream.cs" />
<Compile Include="Localization\LocalizationManager.cs" />
+ <Compile Include="Logging\UnhandledExceptionWriter.cs" />
<Compile Include="MediaEncoder\EncodingManager.cs" />
<Compile Include="Migrations\IVersionMigration.cs" />
<Compile Include="Migrations\LibraryScanMigration.cs" />
@@ -213,8 +213,19 @@
<Compile Include="Security\MBLicenseFile.cs" />
<Compile Include="Security\PluginSecurityManager.cs" />
<Compile Include="Security\RegRecord.cs" />
+ <Compile Include="ServerApplicationPaths.cs" />
<Compile Include="ServerManager\ServerManager.cs" />
<Compile Include="ServerManager\WebSocketConnection.cs" />
+ <Compile Include="Services\ServicePath.cs" />
+ <Compile Include="Services\ServiceMethod.cs" />
+ <Compile Include="Services\ResponseHelper.cs" />
+ <Compile Include="Services\HttpResult.cs" />
+ <Compile Include="Services\RequestHelper.cs" />
+ <Compile Include="Services\ServiceHandler.cs" />
+ <Compile Include="Services\ServiceController.cs" />
+ <Compile Include="Services\ServiceExec.cs" />
+ <Compile Include="Services\StringMapTypeDeserializer.cs" />
+ <Compile Include="Services\UrlExtensions.cs" />
<Compile Include="Session\HttpSessionController.cs" />
<Compile Include="Session\SessionManager.cs" />
<Compile Include="Session\SessionWebSocketListener.cs" />
@@ -253,23 +264,6 @@
<Compile Include="Sorting\StartDateComparer.cs" />
<Compile Include="Sorting\StudioComparer.cs" />
<Compile Include="StartupOptions.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\SyncConfig.cs" />
- <Compile Include="Sync\SyncConvertScheduledTask.cs" />
- <Compile Include="Sync\SyncedMediaSourceProvider.cs" />
- <Compile Include="Sync\SyncHelper.cs" />
- <Compile Include="Sync\SyncJobOptions.cs" />
- <Compile Include="Sync\SyncJobProcessor.cs" />
- <Compile Include="Sync\SyncManager.cs" />
- <Compile Include="Sync\SyncNotificationEntryPoint.cs" />
- <Compile Include="Sync\SyncRegistrationInfo.cs" />
- <Compile Include="Sync\SyncRepository.cs" />
- <Compile Include="Sync\TargetDataProvider.cs" />
<Compile Include="TV\SeriesPostScanTask.cs" />
<Compile Include="TV\TVSeriesManager.cs" />
<Compile Include="Udp\UdpServer.cs" />
@@ -301,16 +295,12 @@
<Project>{2e781478-814d-4a48-9d80-bff206441a65}</Project>
<Name>MediaBrowser.Server.Implementations</Name>
</ProjectReference>
- <ProjectReference Include="..\ServiceStack\ServiceStack.csproj">
- <Project>{680a1709-25eb-4d52-a87f-ee03ffd94baa}</Project>
- <Name>ServiceStack</Name>
- </ProjectReference>
<ProjectReference Include="..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj">
<Project>{4f26d5d8-a7b0-42b3-ba42-7cb7d245934e}</Project>
<Name>SocketHttpListener.Portable</Name>
</ProjectReference>
- <Reference Include="Emby.XmlTv, Version=1.0.6249.32870, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\Emby.XmlTv.1.0.6\lib\portable-net45+win8\Emby.XmlTv.dll</HintPath>
+ <Reference Include="Emby.XmlTv, Version=1.0.6251.29080, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\Emby.XmlTv.1.0.7\lib\portable-net45+win8\Emby.XmlTv.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MediaBrowser.Naming, Version=1.0.6201.24431, Culture=neutral, processorArchitecture=MSIL">
@@ -322,7 +312,7 @@
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.1\lib\portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLitePCLRaw.core.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.2\lib\portable-net45+netcore45+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\SQLitePCLRaw.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="UniversalDetector, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
diff --git a/Emby.Server.Implementations/EntryPoints/UsageReporter.cs b/Emby.Server.Implementations/EntryPoints/UsageReporter.cs
index 31254c6c2..778c8a6ce 100644
--- a/Emby.Server.Implementations/EntryPoints/UsageReporter.cs
+++ b/Emby.Server.Implementations/EntryPoints/UsageReporter.cs
@@ -40,8 +40,7 @@ namespace Emby.Server.Implementations.EntryPoints
{ "serverid", _applicationHost.SystemId },
{ "deviceid", _applicationHost.SystemId },
{ "ver", _applicationHost.ApplicationVersion.ToString() },
- { "platform", _applicationHost.OperatingSystemDisplayName },
- { "isservice", _applicationHost.IsRunningAsService.ToString().ToLower()}
+ { "platform", _applicationHost.OperatingSystemDisplayName }
};
var users = _userManager.Users.ToList();
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 322cdf4f0..6fcdab874 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -2,8 +2,6 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
-using ServiceStack;
-using ServiceStack.Host;
using System;
using System.Collections.Generic;
using System.IO;
@@ -13,6 +11,7 @@ using System.Text;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.HttpServer.SocketSharp;
+using Emby.Server.Implementations.Services;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Security;
using MediaBrowser.Controller;
@@ -29,7 +28,7 @@ using SocketHttpListener.Primitives;
namespace Emby.Server.Implementations.HttpServer
{
- public class HttpListenerHost : ServiceStackHost, IHttpServer
+ public class HttpListenerHost : IHttpServer, IDisposable
{
private string DefaultRedirectPath { get; set; }
@@ -61,13 +60,21 @@ namespace Emby.Server.Implementations.HttpServer
private readonly Func<Type, Func<string, object>> _funcParseFn;
private readonly bool _enableDualModeSockets;
+ public List<Action<IRequest, IResponse, object>> RequestFilters { get; set; }
+ public List<Action<IRequest, IResponse, object>> ResponseFilters { get; set; }
+
+ private readonly Dictionary<Type, Type> ServiceOperationsMap = new Dictionary<Type, Type>();
+ public static HttpListenerHost Instance { get; protected set; }
+
public HttpListenerHost(IServerApplicationHost applicationHost,
ILogger logger,
IServerConfigurationManager config,
string serviceName,
string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func<Type, Func<string, object>> funcParseFn, bool enableDualModeSockets)
- : base(serviceName)
+ : base()
{
+ Instance = this;
+
_appHost = applicationHost;
DefaultRedirectPath = defaultRedirectPath;
_networkManager = networkManager;
@@ -85,6 +92,9 @@ namespace Emby.Server.Implementations.HttpServer
_config = config;
_logger = logger;
+
+ RequestFilters = new List<Action<IRequest, IResponse, object>>();
+ ResponseFilters = new List<Action<IRequest, IResponse, object>>();
}
public string GlobalResponse { get; set; }
@@ -99,18 +109,7 @@ namespace Emby.Server.Implementations.HttpServer
{typeof (ArgumentException), 400}
};
- public override void Configure()
- {
- var requestFilters = _appHost.GetExports<IRequestFilter>().ToList();
- foreach (var filter in requestFilters)
- {
- GlobalRequestFilters.Add(filter.Filter);
- }
-
- GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse);
- }
-
- protected override ILogger Logger
+ protected ILogger Logger
{
get
{
@@ -118,32 +117,73 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- public override T Resolve<T>()
+ public object CreateInstance(Type type)
{
- return _appHost.Resolve<T>();
+ return _appHost.CreateInstance(type);
}
- public override T TryResolve<T>()
+ private ServiceController CreateServiceController()
{
- return _appHost.TryResolve<T>();
+ var types = _restServices.Select(r => r.GetType()).ToArray();
+
+ return new ServiceController(() => types);
}
- public override object CreateInstance(Type type)
+ /// <summary>
+ /// Applies the request filters. Returns whether or not the request has been handled
+ /// and no more processing should be done.
+ /// </summary>
+ /// <returns></returns>
+ public void ApplyRequestFilters(IRequest req, IResponse res, object requestDto)
{
- return _appHost.CreateInstance(type);
+ //Exec all RequestFilter attributes with Priority < 0
+ var attributes = GetRequestFilterAttributes(requestDto.GetType());
+ var i = 0;
+ for (; i < attributes.Length && attributes[i].Priority < 0; i++)
+ {
+ var attribute = attributes[i];
+ attribute.RequestFilter(req, res, requestDto);
+ }
+
+ //Exec global filters
+ foreach (var requestFilter in RequestFilters)
+ {
+ requestFilter(req, res, requestDto);
+ }
+
+ //Exec remaining RequestFilter attributes with Priority >= 0
+ for (; i < attributes.Length && attributes[i].Priority >= 0; i++)
+ {
+ var attribute = attributes[i];
+ attribute.RequestFilter(req, res, requestDto);
+ }
}
- protected override ServiceController CreateServiceController()
+ public Type GetServiceTypeByRequest(Type requestType)
{
- var types = _restServices.Select(r => r.GetType()).ToArray();
+ Type serviceType;
+ ServiceOperationsMap.TryGetValue(requestType, out serviceType);
+ return serviceType;
+ }
- return new ServiceController(() => types);
+ public void AddServiceInfo(Type serviceType, Type requestType, Type responseType)
+ {
+ ServiceOperationsMap[requestType] = serviceType;
}
- public override ServiceStackHost Start(string listeningAtUrlBase)
+ private IHasRequestFilter[] GetRequestFilterAttributes(Type requestDtoType)
{
- StartListener();
- return this;
+ var attributes = requestDtoType.GetTypeInfo().GetCustomAttributes(true).OfType<IHasRequestFilter>().ToList();
+
+ var serviceType = GetServiceTypeByRequest(requestDtoType);
+ if (serviceType != null)
+ {
+ attributes.AddRange(serviceType.GetTypeInfo().GetCustomAttributes(true).OfType<IHasRequestFilter>());
+ }
+
+ attributes.Sort((x, y) => x.Priority - y.Priority);
+
+ return attributes.ToArray();
}
/// <summary>
@@ -531,11 +571,11 @@ namespace Emby.Server.Implementations.HttpServer
return;
}
- var handler = HttpHandlerFactory.GetHandler(httpReq, _logger);
+ var handler = GetServiceHandler(httpReq);
if (handler != null)
{
- await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false);
+ await handler.ProcessRequestAsync(this, httpReq, httpRes, Logger, operationName).ConfigureAwait(false);
}
else
{
@@ -565,6 +605,35 @@ namespace Emby.Server.Implementations.HttpServer
}
}
+ // Entry point for HttpListener
+ public ServiceHandler GetServiceHandler(IHttpRequest httpReq)
+ {
+ var pathInfo = httpReq.PathInfo;
+
+ var pathParts = pathInfo.TrimStart('/').Split('/');
+ if (pathParts.Length == 0)
+ {
+ _logger.Error("Path parts empty for PathInfo: {0}, Url: {1}", pathInfo, httpReq.RawUrl);
+ return null;
+ }
+
+ string contentType;
+ var restPath = ServiceHandler.FindMatchingRestPath(httpReq.HttpMethod, pathInfo, _logger, out contentType);
+
+ if (restPath != null)
+ {
+ return new ServiceHandler
+ {
+ RestPath = restPath,
+ ResponseContentType = contentType
+ };
+ }
+
+ _logger.Error("Could not find handler for {0}", pathInfo);
+ return null;
+ }
+
+
private void Write(IResponse response, string text)
{
var bOutput = Encoding.UTF8.GetBytes(text);
@@ -580,6 +649,7 @@ namespace Emby.Server.Implementations.HttpServer
httpRes.AddHeader("Location", url);
}
+ public ServiceController ServiceController { get; private set; }
/// <summary>
/// Adds the rest handlers.
@@ -593,12 +663,20 @@ namespace Emby.Server.Implementations.HttpServer
_logger.Info("Calling ServiceStack AppHost.Init");
- base.Init();
+ ServiceController.Init(this);
+
+ var requestFilters = _appHost.GetExports<IRequestFilter>().ToList();
+ foreach (var filter in requestFilters)
+ {
+ RequestFilters.Add(filter.Filter);
+ }
+
+ ResponseFilters.Add(new ResponseFilter(_logger).FilterResponse);
}
- public override RouteAttribute[] GetRouteAttributes(Type requestType)
+ public RouteAttribute[] GetRouteAttributes(Type requestType)
{
- var routes = base.GetRouteAttributes(requestType).ToList();
+ var routes = requestType.GetTypeInfo().GetCustomAttributes<RouteAttribute>(true).ToList();
var clone = routes.ToList();
foreach (var route in clone)
@@ -628,54 +706,27 @@ namespace Emby.Server.Implementations.HttpServer
return routes.ToArray();
}
- public override object GetTaskResult(Task task, string requestName)
- {
- try
- {
- var taskObject = task as Task<object>;
- if (taskObject != null)
- {
- return taskObject.Result;
- }
-
- task.Wait();
-
- var type = task.GetType().GetTypeInfo();
- if (!type.IsGenericType)
- {
- return null;
- }
-
- Logger.Warn("Getting task result from " + requestName + " using reflection. For better performance have your api return Task<object>");
- return type.GetDeclaredProperty("Result").GetValue(task);
- }
- catch (TypeAccessException)
- {
- return null; //return null for void Task's
- }
- }
-
- public override Func<string, object> GetParseFn(Type propertyType)
+ public Func<string, object> GetParseFn(Type propertyType)
{
return _funcParseFn(propertyType);
}
- public override void SerializeToJson(object o, Stream stream)
+ public void SerializeToJson(object o, Stream stream)
{
_jsonSerializer.SerializeToStream(o, stream);
}
- public override void SerializeToXml(object o, Stream stream)
+ public void SerializeToXml(object o, Stream stream)
{
_xmlSerializer.SerializeToStream(o, stream);
}
- public override object DeserializeXml(Type type, Stream stream)
+ public object DeserializeXml(Type type, Stream stream)
{
return _xmlSerializer.DeserializeFromStream(type, stream);
}
- public override object DeserializeJson(Type type, Stream stream)
+ public object DeserializeJson(Type type, Stream stream)
{
return _jsonSerializer.DeserializeFromStream(stream, type);
}
@@ -716,8 +767,6 @@ namespace Emby.Server.Implementations.HttpServer
{
if (_disposed) return;
- base.Dispose();
-
lock (_disposeLock)
{
if (_disposed) return;
@@ -731,7 +780,7 @@ namespace Emby.Server.Implementations.HttpServer
}
}
- public override void Dispose()
+ public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
@@ -740,7 +789,7 @@ namespace Emby.Server.Implementations.HttpServer
public void StartServer(IEnumerable<string> urlPrefixes)
{
UrlPrefixes = urlPrefixes.ToList();
- Start(UrlPrefixes.First());
+ StartListener();
}
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index e78446bc8..6bfd83110 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -13,10 +13,9 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Emby.Server.Implementations.HttpServer;
+using Emby.Server.Implementations.Services;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
-using ServiceStack;
-using ServiceStack.Host;
using IRequest = MediaBrowser.Model.Services.IRequest;
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter;
@@ -203,7 +202,11 @@ namespace Emby.Server.Implementations.HttpServer
// Do not use the memoryStreamFactory here, they don't place nice with compression
using (var ms = new MemoryStream())
{
- ContentTypes.Instance.SerializeToStream(request, dto, ms);
+ var contentType = request.ResponseContentType;
+ var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
+
+ writerFn(dto, ms);
+
ms.Position = 0;
var responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 616c6c1a2..56bffc233 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -409,17 +409,17 @@ namespace Emby.Server.Implementations.Library
if (options.DeleteFileLocation && locationType != LocationType.Remote && locationType != LocationType.Virtual)
{
- foreach (var path in item.GetDeletePaths().ToList())
+ foreach (var fileSystemInfo in item.GetDeletePaths().ToList())
{
- if (_fileSystem.DirectoryExists(path))
+ if (fileSystemInfo.IsDirectory)
{
- _logger.Debug("Deleting path {0}", path);
- _fileSystem.DeleteDirectory(path, true);
+ _logger.Debug("Deleting path {0}", fileSystemInfo.FullName);
+ _fileSystem.DeleteDirectory(fileSystemInfo.FullName, true);
}
- else if (_fileSystem.FileExists(path))
+ else
{
- _logger.Debug("Deleting path {0}", path);
- _fileSystem.DeleteFile(path);
+ _logger.Debug("Deleting path {0}", fileSystemInfo.FullName);
+ _fileSystem.DeleteFile(fileSystemInfo.FullName);
}
}
@@ -818,30 +818,6 @@ namespace Emby.Server.Implementations.Library
return _userRootFolder;
}
- public Guid? FindIdByPath(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
- };
-
- var id = GetItemIds(query);
-
- if (id.Count == 0)
- {
- return null;
- }
-
- return id[0];
- }
-
public BaseItem FindByPath(string path, bool? isFolder)
{
// If this returns multiple items it could be tricky figuring out which one is correct.
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index f11cbd498..9e1291847 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -248,6 +248,13 @@ namespace Emby.Server.Implementations.Library
}
}
+ var isPlayed = request.IsPlayed;
+
+ if (parents.OfType<ICollectionFolder>().Any(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)))
+ {
+ isPlayed = null;
+ }
+
if (parents.Count == 0)
{
parents = user.RootFolder.GetChildren(user, true)
@@ -282,7 +289,7 @@ namespace Emby.Server.Implementations.Library
IsVirtualItem = false,
Limit = limit * 5,
SourceTypes = parents.Count == 0 ? new[] { SourceType.Library } : new SourceType[] { },
- IsPlayed = request.IsPlayed
+ IsPlayed = isPlayed
}, parents);
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index bbb060203..926d82f94 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -474,17 +474,30 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public ChannelInfo GetEpgChannelFromTunerChannel(List<NameValuePair> mappings, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
{
- var tunerChannelId = string.IsNullOrWhiteSpace(tunerChannel.TunerChannelId)
- ? tunerChannel.Id
- : tunerChannel.TunerChannelId;
+ if (!string.IsNullOrWhiteSpace(tunerChannel.Id))
+ {
+ var mappedTunerChannelId = GetMappedChannel(tunerChannel.Id, mappings);
+
+ if (string.IsNullOrWhiteSpace(mappedTunerChannelId))
+ {
+ mappedTunerChannelId = tunerChannel.Id;
+ }
+
+ var channel = epgChannels.FirstOrDefault(i => string.Equals(mappedTunerChannelId, i.Id, StringComparison.OrdinalIgnoreCase));
+
+ if (channel != null)
+ {
+ return channel;
+ }
+ }
- if (!string.IsNullOrWhiteSpace(tunerChannelId))
+ if (!string.IsNullOrWhiteSpace(tunerChannel.TunerChannelId))
{
- var mappedTunerChannelId = GetMappedChannel(tunerChannelId, mappings);
+ var mappedTunerChannelId = GetMappedChannel(tunerChannel.TunerChannelId, mappings);
if (string.IsNullOrWhiteSpace(mappedTunerChannelId))
{
- mappedTunerChannelId = tunerChannelId;
+ mappedTunerChannelId = tunerChannel.TunerChannelId;
}
var channel = epgChannels.FirstOrDefault(i => string.Equals(mappedTunerChannelId, i.Id, StringComparison.OrdinalIgnoreCase));
@@ -635,6 +648,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
existingTimer.Status == RecordingStatus.Completed)
{
existingTimer.Status = RecordingStatus.New;
+ existingTimer.IsManual = true;
_timerProvider.Update(existingTimer);
return Task.FromResult(existingTimer.Id);
}
@@ -663,6 +677,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
RecordingHelper.CopyProgramInfoToTimerInfo(programInfo, timer);
}
+ timer.IsManual = true;
_timerProvider.Add(timer);
return Task.FromResult(timer.Id);
}
@@ -758,6 +773,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
existingTimer.PostPaddingSeconds = updatedTimer.PostPaddingSeconds;
existingTimer.IsPostPaddingRequired = updatedTimer.IsPostPaddingRequired;
existingTimer.IsPrePaddingRequired = updatedTimer.IsPrePaddingRequired;
+
+ _timerProvider.Update(existingTimer);
}
return Task.FromResult(true);
@@ -986,6 +1003,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
foreach (var program in programs)
{
program.ChannelId = channelId;
+
+ if (provider.Item2.EnableNewProgramIds)
+ {
+ program.Id += "_" + channelId;
+ }
}
if (programs.Count > 0)
@@ -1172,7 +1194,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
};
var isAudio = false;
- await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, false, cancellationToken).ConfigureAwait(false);
+ await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
return new List<MediaSourceInfo>
{
@@ -2198,6 +2220,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private bool ShouldCancelTimerForSeriesTimer(SeriesTimerInfo seriesTimer, TimerInfo timer)
{
+ if (timer.IsManual)
+ {
+ return false;
+ }
+
if (!seriesTimer.RecordAnyTime)
{
if (Math.Abs(seriesTimer.StartDate.TimeOfDay.Ticks - timer.StartDate.TimeOfDay.Ticks) >= TimeSpan.FromMinutes(5).Ticks)
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
index 9a8282250..7460a9f06 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs
@@ -65,6 +65,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
+ private bool CopySubtitles
+ {
+ get
+ {
+ return false;
+ //return string.Equals(OutputFormat, "mkv", StringComparison.OrdinalIgnoreCase);
+ }
+ }
+
public string GetOutputPath(MediaSourceInfo mediaSource, string targetFile)
{
return Path.ChangeExtension(targetFile, "." + OutputFormat);
@@ -154,9 +163,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
var inputModifiers = "-fflags +genpts -async 1 -vsync -1";
- var mapArgs = string.Equals(OutputFormat, "mkv", StringComparison.OrdinalIgnoreCase) ? "-map 0" : "-sn";
- mapArgs = "-sn";
- var commandLineArgs = "-i \"{0}\"{4} " + mapArgs + " {2} -map_metadata -1 -threads 0 {3} -y \"{1}\"";
+ var mapArgs = "-map 0 -ignore_unknown";
+ var commandLineArgs = "-i \"{0}\"{5} " + mapArgs + " {2} -map_metadata -1 -threads 0 {3}{4} -y \"{1}\"";
long startTimeTicks = 0;
//if (mediaSource.DateLiveStreamOpened.HasValue)
@@ -184,7 +192,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
(analyzeDurationSeconds * 1000000).ToString(CultureInfo.InvariantCulture);
inputModifiers += analyzeDuration;
- commandLineArgs = string.Format(commandLineArgs, inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource), durationParam);
+ var subtitleArgs = CopySubtitles ? " -codec:s copy" : " -sn";
+
+ commandLineArgs = string.Format(commandLineArgs, inputTempFile, targetFile, videoArgs, GetAudioArgs(mediaSource), subtitleArgs, durationParam);
return inputModifiers + " " + commandLineArgs;
}
@@ -197,7 +207,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
// 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";
+ return "-codec:a copy";
}
var audioChannels = 2;
@@ -206,7 +216,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
audioChannels = audioStream.Channels ?? audioChannels;
}
- return "-codec:a:0 aac -strict experimental -ab 320000";
+ return "-codec:a aac -strict experimental -ab 320000";
}
private bool EncodeVideo(MediaSourceInfo mediaSource)
@@ -261,7 +271,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
_logger.Info("Calling recording process.WaitForExit for {0}", _targetPath);
- if (_process.WaitForExit(5000))
+ if (_process.WaitForExit(10000))
{
return;
}
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 0d7a26553..f76735030 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -81,12 +81,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return programsInfo;
}
- if (string.IsNullOrWhiteSpace(info.ListingsId))
- {
- _logger.Warn("ListingsId is null, returning empty program list");
- return programsInfo;
- }
-
var dates = GetScheduleRequestDates(startDateUtc, endDateUtc);
string stationID = channelId;
@@ -156,7 +150,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID)
.ToList();
- var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken);
+ var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
var schedules = dailySchedules.SelectMany(d => d.programs);
foreach (ScheduleDirect.Program schedule in schedules)
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index d7803f9e3..c22bb1171 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
@@ -26,13 +27,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
+ private readonly IZipClient _zipClient;
- public XmlTvListingsProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IFileSystem fileSystem)
+ public XmlTvListingsProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IFileSystem fileSystem, IZipClient zipClient)
{
_config = config;
_httpClient = httpClient;
_logger = logger;
_fileSystem = fileSystem;
+ _zipClient = zipClient;
}
public string Name
@@ -63,7 +66,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename);
if (_fileSystem.FileExists(cacheFile))
{
- return cacheFile;
+ return UnzipIfNeeded(path, cacheFile);
}
_logger.Info("Downloading xmltv listings from {0}", path);
@@ -103,7 +106,30 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
_logger.Debug("Returning xmltv path {0}", cacheFile);
- return cacheFile;
+ return UnzipIfNeeded(path, cacheFile);
+ }
+
+ private string UnzipIfNeeded(string originalUrl, string file)
+ {
+ //var ext = Path.GetExtension(originalUrl);
+
+ //if (string.Equals(ext, ".gz", StringComparison.OrdinalIgnoreCase))
+ //{
+ // using (var stream = _fileSystem.OpenRead(file))
+ // {
+ // var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
+ // _fileSystem.CreateDirectory(tempFolder);
+
+ // _zipClient.ExtractAllFromZip(stream, tempFolder, true);
+
+ // return _fileSystem.GetFiles(tempFolder, true)
+ // .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
+ // .Select(i => i.FullName)
+ // .FirstOrDefault();
+ // }
+ //}
+
+ return file;
}
public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
@@ -122,6 +148,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
}
+ _logger.Debug("Getting xmltv programs for channel {0}", channelId);
+
var path = await GetXml(info.Path, cancellationToken).ConfigureAwait(false);
var reader = new XmlTvReader(path, GetLanguage());
@@ -140,7 +168,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
EpisodeNumber = p.Episode == null ? null : p.Episode.Episode,
EpisodeTitle = episodeTitle,
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,
@@ -180,6 +207,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programInfo.ShowId = uniqueString.GetMD5().ToString("N");
}
+ // Construct an id from the channel and start date
+ programInfo.Id = String.Format("{0}_{1:O}", p.ChannelId, p.StartDate);
+
if (programInfo.IsMovie)
{
programInfo.IsSeries = false;
diff --git a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
index 2ee6869f6..e2f973699 100644
--- a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
@@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.LiveTv
_logger = logger;
}
- public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, bool assumeInterlaced, CancellationToken cancellationToken)
+ public async Task AddMediaInfoWithProbe(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken)
{
var originalRuntime = mediaSource.RunTimeTicks;
@@ -96,17 +96,6 @@ namespace Emby.Server.Implementations.LiveTv
videoStream.IsAVC = null;
}
- if (assumeInterlaced)
- {
- foreach (var mediaStream in mediaSource.MediaStreams)
- {
- if (mediaStream.Type == MediaStreamType.Video)
- {
- mediaStream.IsInterlaced = true;
- }
- }
- }
-
// Try to estimate this
mediaSource.InferTotalBitrate(true);
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
index e73378dde..43b055098 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
@@ -278,6 +278,13 @@ namespace Emby.Server.Implementations.LiveTv
{
var program = _libraryManager.GetItemList(new InternalItemsQuery
{
+ IncludeItemTypes = new string[] { typeof(Series).Name },
+ Name = seriesName,
+ Limit = 1,
+ ImageTypes = new ImageType[] { ImageType.Primary }
+
+ }).FirstOrDefault() ?? _libraryManager.GetItemList(new InternalItemsQuery
+ {
IncludeItemTypes = new string[] { typeof(LiveTvProgram).Name },
ExternalSeriesId = programSeriesId,
Limit = 1,
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index e30280967..654777b3c 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -598,15 +598,15 @@ namespace Emby.Server.Implementations.LiveTv
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;
+ if ((item.CommunityRating ?? 0).Equals(0))
+ {
+ item.CommunityRating = null;
+ }
item.EpisodeTitle = info.EpisodeTitle;
item.ExternalId = info.Id;
@@ -1307,7 +1307,7 @@ namespace Emby.Server.Implementations.LiveTv
var isKids = false;
var iSSeries = false;
- var channelPrograms = await service.GetProgramsAsync(GetItemExternalId(currentChannel), start, end, cancellationToken).ConfigureAwait(false);
+ var channelPrograms = (await service.GetProgramsAsync(GetItemExternalId(currentChannel), start, end, cancellationToken).ConfigureAwait(false)).ToList();
var existingPrograms = _libraryManager.GetItemList(new InternalItemsQuery
{
@@ -1405,7 +1405,7 @@ namespace Emby.Server.Implementations.LiveTv
double percent = numComplete;
percent /= allChannelsList.Count;
- progress.Report(80 * percent + 10);
+ progress.Report(85 * percent + 15);
}
progress.Report(100);
@@ -1477,12 +1477,7 @@ namespace Emby.Server.Implementations.LiveTv
private DateTime _lastRecordingRefreshTime;
private async Task RefreshRecordings(CancellationToken cancellationToken)
{
- const int cacheMinutes = 3;
-
- if ((DateTime.UtcNow - _lastRecordingRefreshTime).TotalMinutes < cacheMinutes)
- {
- return;
- }
+ const int cacheMinutes = 2;
await _refreshRecordingsLock.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -1885,7 +1880,7 @@ namespace Emby.Server.Implementations.LiveTv
: _tvDtoService.GetInternalTimerId(service.Name, info.TimerId).ToString("N");
dto.StartDate = info.StartDate;
- dto.RecordingStatus = info.Status;
+ dto.Status = info.Status.ToString();
dto.IsRepeat = info.IsRepeat;
dto.EpisodeTitle = info.EpisodeTitle;
dto.IsMovie = info.IsMovie;
@@ -2866,6 +2861,7 @@ namespace Emby.Server.Implementations.LiveTv
{
info.Id = Guid.NewGuid().ToString("N");
config.ListingProviders.Add(info);
+ info.EnableNewProgramIds = true;
}
else
{
@@ -2930,7 +2926,7 @@ namespace Emby.Server.Implementations.LiveTv
var result = new TunerChannelMapping
{
Name = tunerChannel.Name,
- Id = tunerChannel.TunerChannelId
+ Id = tunerChannel.Id
};
if (!string.IsNullOrWhiteSpace(tunerChannel.Number))
@@ -2938,11 +2934,6 @@ namespace Emby.Server.Implementations.LiveTv
result.Name = tunerChannel.Number + " " + result.Name;
}
- if (string.IsNullOrWhiteSpace(result.Id))
- {
- result.Id = tunerChannel.Id;
- }
-
var providerChannel = EmbyTV.EmbyTV.Current.GetEpgChannelFromTunerChannel(mappings, tunerChannel, epgChannels);
if (providerChannel != null)
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
index e25e28484..747e0fdd3 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
@@ -126,14 +126,12 @@ namespace Emby.Server.Implementations.LiveTv
var keys = openToken.Split(new[] { StreamIdDelimeter }, 3);
var mediaSourceId = keys.Length >= 3 ? keys[2] : null;
IDirectStreamProvider directStreamProvider = null;
- var assumeInterlaced = false;
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;
- assumeInterlaced = info.Item3;
}
else
{
@@ -148,7 +146,7 @@ namespace Emby.Server.Implementations.LiveTv
}
else
{
- await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, assumeInterlaced, cancellationToken).ConfigureAwait(false);
+ await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception ex)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 5fae3f666..5ac3812b0 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -35,10 +35,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
MediaEncoder = mediaEncoder;
}
- protected abstract Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
+ protected abstract Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo tuner, CancellationToken cancellationToken);
public abstract string Type { get; }
- public async Task<IEnumerable<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
+ public async Task<List<ChannelInfo>> GetChannels(TunerHostInfo tuner, bool enableCache, CancellationToken cancellationToken)
{
ChannelCache cache = null;
var key = tuner.Id;
@@ -73,7 +73,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
.ToList();
}
- public async Task<IEnumerable<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken)
+ public async Task<List<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken)
{
var list = new List<ChannelInfo>();
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 1c7c0828c..2e70e1ac1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -9,17 +9,14 @@ 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;
@@ -66,7 +63,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return id;
}
- private async Task<IEnumerable<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
+ private async Task<List<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
{
var options = new HttpRequestOptions
{
@@ -74,7 +71,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
CancellationToken = cancellationToken,
BufferContent = false
};
- using (var stream = await _httpClient.Get(options))
+ using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
{
var lineup = JsonSerializer.DeserializeFromStream<List<Channels>>(stream) ?? new List<Channels>();
@@ -87,7 +84,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
- protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
+ protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
{
var lineup = await GetLineup(info, cancellationToken).ConfigureAwait(false);
@@ -102,7 +99,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
AudioCodec = i.AudioCodec,
VideoCodec = i.VideoCodec,
ChannelType = ChannelType.TV
- });
+
+ }).ToList();
}
private readonly Dictionary<string, DiscoverResponse> _modelCache = new Dictionary<string, DiscoverResponse>();
@@ -127,7 +125,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
CacheMode = CacheMode.Unconditional,
TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
BufferContent = false
- }))
+
+ }).ConfigureAwait(false))
{
var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
@@ -169,7 +168,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
CancellationToken = cancellationToken,
TimeoutMs = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds),
BufferContent = false
- }))
+
+ }).ConfigureAwait(false))
{
var tuners = new List<LiveTvTunerInfo>();
using (var sr = new StreamReader(stream, System.Text.Encoding.UTF8))
@@ -536,7 +536,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
Url = string.Format("{0}/discover.json", GetApiUrl(info, false)),
CancellationToken = CancellationToken.None,
BufferContent = false
- }))
+
+ }).ConfigureAwait(false))
{
var response = JsonSerializer.DeserializeFromStream<DiscoverResponse>(stream);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 601cb2666..8366c2d57 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -48,9 +48,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private const string ChannelIdPrefix = "m3u_";
- protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
+ protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
{
- return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, !info.EnableTvgId, cancellationToken).ConfigureAwait(false);
+ var result = await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, !info.EnableTvgId, cancellationToken).ConfigureAwait(false);
+
+ return result.Cast<ChannelInfo>().ToList();
}
public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index 34d0dd853..8c4b9bf60 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -136,11 +136,26 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
channel.Name = GetChannelName(extInf, attributes);
channel.Number = GetChannelNumber(extInf, attributes, mediaUrl);
- var channelId = GetTunerChannelId(attributes);
+ string tvgId;
+ attributes.TryGetValue("tvg-id", out tvgId);
+
+ string channelId;
+ attributes.TryGetValue("channel-id", out channelId);
+
+ channel.TunerChannelId = string.IsNullOrWhiteSpace(tvgId) ? channelId : tvgId;
+
+ var channelIdValues = new List<string>();
if (!string.IsNullOrWhiteSpace(channelId))
{
- channel.Id = channelId;
- channel.TunerChannelId = channelId;
+ channelIdValues.Add(channelId);
+ }
+ if (!string.IsNullOrWhiteSpace(tvgId))
+ {
+ channelIdValues.Add(tvgId);
+ }
+ if (channelIdValues.Count > 0)
+ {
+ channel.Id = string.Join("_", channelIdValues.ToArray());
}
return channel;
@@ -296,19 +311,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return name;
}
- private string GetTunerChannelId(Dictionary<string, string> attributes)
- {
- string result;
- attributes.TryGetValue("tvg-id", out result);
-
- if (string.IsNullOrWhiteSpace(result))
- {
- attributes.TryGetValue("channel-id", out result);
- }
-
- return result;
- }
-
private Dictionary<string, string> ParseExtInf(string line, out string remaining)
{
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
index a7e1b3cf3..df83d4341 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
@@ -74,10 +74,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
OnFinished = OnFinished
};
- var initial = _sharedBuffer.ToList();
var list = new List<byte>();
-
- foreach (var bytes in initial)
+ foreach (var bytes in _sharedBuffer)
{
list.AddRange(bytes);
}
diff --git a/Emby.Server.Core/UnhandledExceptionWriter.cs b/Emby.Server.Implementations/Logging/UnhandledExceptionWriter.cs
index 5147be9e7..5183f3a0b 100644
--- a/Emby.Server.Core/UnhandledExceptionWriter.cs
+++ b/Emby.Server.Implementations/Logging/UnhandledExceptionWriter.cs
@@ -2,20 +2,25 @@
using MediaBrowser.Model.Logging;
using System;
using System.IO;
+using MediaBrowser.Model.IO;
-namespace Emby.Server.Core
+namespace Emby.Server.Implementations.Logging
{
public class UnhandledExceptionWriter
{
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
private readonly ILogManager _logManager;
+ private readonly IFileSystem _fileSystem;
+ private readonly IConsoleLogger _console;
- public UnhandledExceptionWriter(IApplicationPaths appPaths, ILogger logger, ILogManager logManager)
+ public UnhandledExceptionWriter(IApplicationPaths appPaths, ILogger logger, ILogManager logManager, IFileSystem fileSystem, IConsoleLogger console)
{
_appPaths = appPaths;
_logger = logger;
_logManager = logManager;
+ _fileSystem = fileSystem;
+ _console = console;
}
public void Log(Exception ex)
@@ -24,15 +29,15 @@ namespace Emby.Server.Core
_logManager.Flush();
var path = Path.Combine(_appPaths.LogDirectoryPath, "unhandled_" + Guid.NewGuid() + ".txt");
- Directory.CreateDirectory(Path.GetDirectoryName(path));
+ _fileSystem.CreateDirectory(Path.GetDirectoryName(path));
var builder = LogHelper.GetLogMessage(ex);
// Write to console just in case file logging fails
- Console.WriteLine("UnhandledException");
- Console.WriteLine(builder.ToString());
-
- File.WriteAllText(path, builder.ToString());
+ _console.WriteLine("UnhandledException");
+ _console.WriteLine(builder.ToString());
+
+ _fileSystem.WriteAllText(path, builder.ToString());
}
}
}
diff --git a/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs b/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
index f18278cb2..76c7a7d77 100644
--- a/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
+++ b/Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Data;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Notifications;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Notifications;
using SQLitePCL.pretty;
@@ -16,8 +17,11 @@ namespace Emby.Server.Implementations.Notifications
{
public class SqliteNotificationsRepository : BaseSqliteRepository, INotificationsRepository
{
- public SqliteNotificationsRepository(ILogger logger, IServerApplicationPaths appPaths) : base(logger)
+ protected IFileSystem FileSystem { get; private set; }
+
+ public SqliteNotificationsRepository(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem) : base(logger)
{
+ FileSystem = fileSystem;
DbFilePath = Path.Combine(appPaths.DataPath, "notifications.db");
}
@@ -27,6 +31,22 @@ namespace Emby.Server.Implementations.Notifications
public void Initialize()
{
+ try
+ {
+ InitializeInternal();
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error loading notifications database file. Will reset and retry.", ex);
+
+ FileSystem.DeleteFile(DbFilePath);
+
+ InitializeInternal();
+ }
+ }
+
+ private void InitializeInternal()
+ {
using (var connection = CreateConnection())
{
RunDefaultInitialization(connection);
diff --git a/Emby.Server.Implementations/Security/PluginSecurityManager.cs b/Emby.Server.Implementations/Security/PluginSecurityManager.cs
index f21259137..a26df7625 100644
--- a/Emby.Server.Implementations/Security/PluginSecurityManager.cs
+++ b/Emby.Server.Implementations/Security/PluginSecurityManager.cs
@@ -277,8 +277,7 @@ namespace Emby.Server.Implementations.Security
{ "systemid", _appHost.SystemId },
{ "mb2equiv", mb2Equivalent },
{ "ver", version },
- { "platform", _appHost.OperatingSystemDisplayName },
- { "isservice", _appHost.IsRunningAsService.ToString().ToLower() }
+ { "platform", _appHost.OperatingSystemDisplayName }
};
try
diff --git a/Emby.Server.Core/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs
index dc80b773c..b4b2bb139 100644
--- a/Emby.Server.Core/ServerApplicationPaths.cs
+++ b/Emby.Server.Implementations/ServerApplicationPaths.cs
@@ -1,8 +1,9 @@
-using System.IO;
-using Emby.Common.Implementations;
+using System;
+using System.IO;
+using Emby.Server.Implementations.AppBase;
using MediaBrowser.Controller;
-namespace Emby.Server.Core
+namespace Emby.Server.Implementations
{
/// <summary>
/// Extends BaseApplicationPaths to add paths that are only applicable on the server
@@ -12,8 +13,8 @@ namespace Emby.Server.Core
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationPaths" /> class.
/// </summary>
- public ServerApplicationPaths(string programDataPath, string appFolderPath, string applicationResourcesPath)
- : base(programDataPath, appFolderPath)
+ public ServerApplicationPaths(string programDataPath, string appFolderPath, string applicationResourcesPath, Action<string> createDirectoryFn)
+ : base(programDataPath, appFolderPath, createDirectoryFn)
{
ApplicationResourcesPath = applicationResourcesPath;
}
diff --git a/Emby.Server.Implementations/ServerManager/ServerManager.cs b/Emby.Server.Implementations/ServerManager/ServerManager.cs
index f7e4c0ce2..4c9228e54 100644
--- a/Emby.Server.Implementations/ServerManager/ServerManager.cs
+++ b/Emby.Server.Implementations/ServerManager/ServerManager.cs
@@ -303,6 +303,7 @@ namespace Emby.Server.Implementations.ServerManager
/// </summary>
private void DisposeHttpServer()
{
+ _logger.Info("Disposing web socket connections");
foreach (var socket in _webSocketConnections)
{
// Dispose the connection
@@ -314,6 +315,9 @@ namespace Emby.Server.Implementations.ServerManager
if (HttpServer != null)
{
HttpServer.WebSocketConnected -= HttpServer_WebSocketConnected;
+
+ _logger.Info("Disposing http server");
+
HttpServer.Dispose();
}
}
diff --git a/ServiceStack/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs
index 3f86ffdf7..dfad09f7b 100644
--- a/ServiceStack/HttpResult.cs
+++ b/Emby.Server.Implementations/Services/HttpResult.cs
@@ -1,14 +1,11 @@
-using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
-using ServiceStack.Host;
-namespace ServiceStack
+namespace Emby.Server.Implementations.Services
{
public class HttpResult
: IHttpResult, IAsyncStreamWriter
@@ -55,7 +52,7 @@ namespace ServiceStack
return;
}
- await HttpResponseExtensionsInternal.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false);
+ await ResponseHelper.WriteObject(this.RequestContext, this.Response, response).ConfigureAwait(false);
}
}
}
diff --git a/Emby.Server.Implementations/Services/RequestHelper.cs b/Emby.Server.Implementations/Services/RequestHelper.cs
new file mode 100644
index 000000000..7538d3102
--- /dev/null
+++ b/Emby.Server.Implementations/Services/RequestHelper.cs
@@ -0,0 +1,51 @@
+using System;
+using System.IO;
+using Emby.Server.Implementations.HttpServer;
+
+namespace Emby.Server.Implementations.Services
+{
+ public class RequestHelper
+ {
+ public static Func<Type, Stream, object> GetRequestReader(HttpListenerHost host, string contentType)
+ {
+ switch (GetContentTypeWithoutEncoding(contentType))
+ {
+ case "application/xml":
+ case "text/xml":
+ case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
+ return host.DeserializeXml;
+
+ case "application/json":
+ case "text/json":
+ return host.DeserializeJson;
+ }
+
+ return null;
+ }
+
+ public static Action<object, Stream> GetResponseWriter(HttpListenerHost host, string contentType)
+ {
+ switch (GetContentTypeWithoutEncoding(contentType))
+ {
+ case "application/xml":
+ case "text/xml":
+ case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
+ return host.SerializeToXml;
+
+ case "application/json":
+ case "text/json":
+ return host.SerializeToJson;
+ }
+
+ return null;
+ }
+
+ private static string GetContentTypeWithoutEncoding(string contentType)
+ {
+ return contentType == null
+ ? null
+ : contentType.Split(';')[0].ToLower().Trim();
+ }
+
+ }
+} \ No newline at end of file
diff --git a/ServiceStack/HttpResponseExtensionsInternal.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs
index feb18081a..d4ce1cabf 100644
--- a/ServiceStack/HttpResponseExtensionsInternal.cs
+++ b/Emby.Server.Implementations/Services/ResponseHelper.cs
@@ -1,21 +1,18 @@
-//Copyright (c) Service Stack LLC. All Rights Reserved.
-//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
-
using System;
+using System.Collections.Generic;
using System.IO;
using System.Net;
-using System.Threading.Tasks;
-using System.Collections.Generic;
using System.Text;
using System.Threading;
+using System.Threading.Tasks;
+using Emby.Server.Implementations.HttpServer;
using MediaBrowser.Model.Services;
-using ServiceStack.Host;
-namespace ServiceStack
+namespace Emby.Server.Implementations.Services
{
- public static class HttpResponseExtensionsInternal
+ public static class ResponseHelper
{
- public static async Task<bool> WriteToOutputStream(IResponse response, object result)
+ private static async Task<bool> WriteToOutputStream(IResponse response, object result)
{
var asyncStreamWriter = result as IAsyncStreamWriter;
if (asyncStreamWriter != null)
@@ -54,24 +51,16 @@ namespace ServiceStack
return false;
}
- /// <summary>
- /// End a ServiceStack Request with no content
- /// </summary>
- public static void EndRequestWithNoContent(this IResponse httpRes)
- {
- if (httpRes.StatusCode == (int)HttpStatusCode.OK)
- {
- httpRes.StatusCode = (int)HttpStatusCode.NoContent;
- }
-
- httpRes.SetContentLength(0);
- }
-
- public static Task WriteToResponse(this IResponse httpRes, IRequest httpReq, object result)
+ public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result)
{
if (result == null)
{
- httpRes.EndRequestWithNoContent();
+ if (httpRes.StatusCode == (int)HttpStatusCode.OK)
+ {
+ httpRes.StatusCode = (int)HttpStatusCode.NoContent;
+ }
+
+ httpRes.SetContentLength(0);
return Task.FromResult(true);
}
@@ -80,10 +69,10 @@ namespace ServiceStack
{
httpResult.RequestContext = httpReq;
httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType;
- return httpRes.WriteToResponseInternal(httpResult, httpReq);
+ return WriteToResponseInternal(httpRes, httpResult, httpReq);
}
- return httpRes.WriteToResponseInternal(result, httpReq);
+ return WriteToResponseInternal(httpRes, result, httpReq);
}
/// <summary>
@@ -94,7 +83,7 @@ namespace ServiceStack
/// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
/// <param name="request">The serialization context.</param>
/// <returns></returns>
- private static async Task WriteToResponseInternal(this IResponse response, object result, IRequest request)
+ private static async Task WriteToResponseInternal(IResponse response, object result, IRequest request)
{
var defaultContentType = request.ResponseContentType;
@@ -173,7 +162,7 @@ namespace ServiceStack
public static async Task WriteObject(IRequest request, object result, IResponse response)
{
var contentType = request.ResponseContentType;
- var serializer = ContentTypes.GetStreamSerializer(contentType);
+ var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
using (var ms = new MemoryStream())
{
diff --git a/ServiceStack/Host/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs
index 378c21d5d..d283bf81f 100644
--- a/ServiceStack/Host/ServiceController.cs
+++ b/Emby.Server.Implementations/Services/ServiceController.cs
@@ -1,13 +1,12 @@
using System;
-using System.Collections;
using System.Collections.Generic;
-using System.Linq;
using System.Reflection;
-using System.Text;
using System.Threading.Tasks;
+using Emby.Server.Implementations.HttpServer;
+using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;
-namespace ServiceStack.Host
+namespace Emby.Server.Implementations.Services
{
public delegate Task<object> InstanceExecFn(IRequest requestContext, object intance, object request);
public delegate object ActionInvokerFn(object intance, object request);
@@ -15,21 +14,20 @@ namespace ServiceStack.Host
public class ServiceController
{
+ public static ServiceController Instance;
private readonly Func<IEnumerable<Type>> _resolveServicesFn;
public ServiceController(Func<IEnumerable<Type>> resolveServicesFn)
{
+ Instance = this;
_resolveServicesFn = resolveServicesFn;
- this.RequestTypeFactoryMap = new Dictionary<Type, Func<IRequest, object>>();
}
- public Dictionary<Type, Func<IRequest, object>> RequestTypeFactoryMap { get; set; }
-
- public void Init()
+ public void Init(HttpListenerHost appHost)
{
foreach (var serviceType in _resolveServicesFn())
{
- RegisterService(serviceType);
+ RegisterService(appHost, serviceType);
}
}
@@ -40,15 +38,12 @@ namespace ServiceStack.Host
: type.GetTypeInfo().GenericTypeArguments;
}
- public void RegisterService(Type serviceType)
+ public void RegisterService(HttpListenerHost appHost, Type serviceType)
{
var processedReqs = new HashSet<Type>();
var actions = ServiceExecGeneral.Reset(serviceType);
- var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
-
- var appHost = ServiceStackHost.Instance;
foreach (var mi in serviceType.GetActions())
{
var requestType = mi.GetParameters()[0].ParameterType;
@@ -57,45 +52,62 @@ namespace ServiceStack.Host
ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
- var returnMarker = requestType.GetTypeWithGenericTypeDefinitionOf(typeof(IReturn<>));
+ var returnMarker = GetTypeWithGenericTypeDefinitionOf(requestType, typeof(IReturn<>));
var responseType = returnMarker != null ?
GetGenericArguments(returnMarker)[0]
: mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void) ?
mi.ReturnType
: Type.GetType(requestType.FullName + "Response");
- RegisterRestPaths(requestType);
+ RegisterRestPaths(appHost, requestType);
- appHost.Metadata.Add(serviceType, requestType, responseType);
+ appHost.AddServiceInfo(serviceType, requestType, responseType);
+ }
+ }
- if (requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo()))
+ private static Type GetTypeWithGenericTypeDefinitionOf(Type type, Type genericTypeDefinition)
+ {
+ foreach (var t in type.GetTypeInfo().ImplementedInterfaces)
+ {
+ if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == genericTypeDefinition)
{
- this.RequestTypeFactoryMap[requestType] = req =>
- {
- var restPath = req.GetRoute();
- var request = RestHandler.CreateRequest(req, restPath, req.GetRequestParams(), ServiceStackHost.Instance.CreateInstance(requestType));
-
- var rawReq = (IRequiresRequestStream)request;
- rawReq.RequestStream = req.InputStream;
- return rawReq;
- };
+ return t;
}
}
+
+ var genericType = FirstGenericType(type);
+ if (genericType != null && genericType.GetGenericTypeDefinition() == genericTypeDefinition)
+ {
+ return genericType;
+ }
+
+ return null;
+ }
+
+ public static Type FirstGenericType(Type type)
+ {
+ while (type != null)
+ {
+ if (type.GetTypeInfo().IsGenericType)
+ return type;
+
+ type = type.GetTypeInfo().BaseType;
+ }
+ return null;
}
public readonly Dictionary<string, List<RestPath>> RestPathMap = new Dictionary<string, List<RestPath>>(StringComparer.OrdinalIgnoreCase);
- public void RegisterRestPaths(Type requestType)
+ public void RegisterRestPaths(HttpListenerHost appHost, Type requestType)
{
- var appHost = ServiceStackHost.Instance;
var attrs = appHost.GetRouteAttributes(requestType);
- foreach (MediaBrowser.Model.Services.RouteAttribute attr in attrs)
+ foreach (RouteAttribute attr in attrs)
{
- var restPath = new RestPath(requestType, attr.Path, attr.Verbs, attr.Summary, attr.Notes);
+ var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, attr.Path, attr.Verbs, attr.Summary, attr.Notes);
if (!restPath.IsValid)
throw new NotSupportedException(string.Format(
- "RestPath '{0}' on Type '{1}' is not Valid", attr.Path, requestType.GetOperationName()));
+ "RestPath '{0}' on Type '{1}' is not Valid", attr.Path, requestType.GetMethodName()));
RegisterRestPath(restPath);
}
@@ -106,10 +118,10 @@ namespace ServiceStack.Host
public void RegisterRestPath(RestPath restPath)
{
if (!restPath.Path.StartsWith("/"))
- throw new ArgumentException(string.Format("Route '{0}' on '{1}' must start with a '/'", restPath.Path, restPath.RequestType.GetOperationName()));
+ throw new ArgumentException(string.Format("Route '{0}' on '{1}' must start with a '/'", restPath.Path, restPath.RequestType.GetMethodName()));
if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
throw new ArgumentException(string.Format("Route '{0}' on '{1}' contains invalid chars. " +
- "See https://github.com/ServiceStack/ServiceStack/wiki/Routing for info on valid routes.", restPath.Path, restPath.RequestType.GetOperationName()));
+ "See https://github.com/ServiceStack/ServiceStack/wiki/Routing for info on valid routes.", restPath.Path, restPath.RequestType.GetMethodName()));
List<RestPath> pathsAtFirstMatch;
if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out pathsAtFirstMatch))
@@ -120,22 +132,7 @@ namespace ServiceStack.Host
pathsAtFirstMatch.Add(restPath);
}
- public void AfterInit()
- {
- var appHost = ServiceStackHost.Instance;
-
- //Register any routes configured on Metadata.Routes
- foreach (var restPath in appHost.RestPaths)
- {
- RegisterRestPath(restPath);
- }
-
- //Sync the RestPaths collections
- appHost.RestPaths.Clear();
- appHost.RestPaths.AddRange(RestPathMap.Values.SelectMany(x => x));
- }
-
- public RestPath GetRestPathForRequest(string httpMethod, string pathInfo)
+ public RestPath GetRestPathForRequest(string httpMethod, string pathInfo, ILogger logger)
{
var matchUsingPathParts = RestPath.GetPathPartsForMatching(pathInfo);
@@ -144,19 +141,23 @@ namespace ServiceStack.Host
var yieldedHashMatches = RestPath.GetFirstMatchHashKeys(matchUsingPathParts);
foreach (var potentialHashMatch in yieldedHashMatches)
{
- if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches)) continue;
+ if (!this.RestPathMap.TryGetValue(potentialHashMatch, out firstMatches))
+ {
+ continue;
+ }
var bestScore = -1;
foreach (var restPath in firstMatches)
{
- var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
+ var score = restPath.MatchScore(httpMethod, matchUsingPathParts, logger);
if (score > bestScore) bestScore = score;
}
+
if (bestScore > 0)
{
foreach (var restPath in firstMatches)
{
- if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts))
+ if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts, logger))
return restPath;
}
}
@@ -170,14 +171,14 @@ namespace ServiceStack.Host
var bestScore = -1;
foreach (var restPath in firstMatches)
{
- var score = restPath.MatchScore(httpMethod, matchUsingPathParts);
+ var score = restPath.MatchScore(httpMethod, matchUsingPathParts, logger);
if (score > bestScore) bestScore = score;
}
if (bestScore > 0)
{
foreach (var restPath in firstMatches)
{
- if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts))
+ if (bestScore == restPath.MatchScore(httpMethod, matchUsingPathParts, logger))
return restPath;
}
}
@@ -186,15 +187,15 @@ namespace ServiceStack.Host
return null;
}
- public async Task<object> Execute(object requestDto, IRequest req)
+ public async Task<object> Execute(HttpListenerHost appHost, object requestDto, IRequest req)
{
req.Dto = requestDto;
var requestType = requestDto.GetType();
req.OperationName = requestType.Name;
- var serviceType = ServiceStackHost.Instance.Metadata.GetServiceTypeByRequest(requestType);
+ var serviceType = appHost.GetServiceTypeByRequest(requestType);
- var service = ServiceStackHost.Instance.CreateInstance(serviceType);
+ var service = appHost.CreateInstance(serviceType);
//var service = typeFactory.CreateInstance(serviceType);
@@ -208,7 +209,7 @@ namespace ServiceStack.Host
req.Dto = requestDto;
//Executes the service and returns the result
- var response = await ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetOperationName()).ConfigureAwait(false);
+ var response = await ServiceExecGeneral.Execute(serviceType, req, service, requestDto, requestType.GetMethodName()).ConfigureAwait(false);
return response;
}
diff --git a/ServiceStack/Host/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs
index cb501a3ad..e0b5e69c0 100644
--- a/ServiceStack/Host/ServiceExec.cs
+++ b/Emby.Server.Implementations/Services/ServiceExec.cs
@@ -1,6 +1,3 @@
-//Copyright (c) Service Stack LLC. All Rights Reserved.
-//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
-
using System;
using System.Collections.Generic;
using System.Linq;
@@ -9,10 +6,23 @@ using System.Reflection;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
-namespace ServiceStack.Host
+namespace Emby.Server.Implementations.Services
{
public static class ServiceExecExtensions
{
+ public static HashSet<string> AllVerbs = new HashSet<string>(new[] {
+ "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", // RFC 2616
+ "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", // RFC 2518
+ "VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT",
+ "MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY", // RFC 3253
+ "ORDERPATCH", // RFC 3648
+ "ACL", // RFC 3744
+ "PATCH", // https://datatracker.ietf.org/doc/draft-dusseault-http-patch/
+ "SEARCH", // https://datatracker.ietf.org/doc/draft-reschke-webdav-search/
+ "BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "NOTIFY",
+ "POLL", "SUBSCRIBE", "UNSUBSCRIBE"
+ });
+
public static IEnumerable<MethodInfo> GetActions(this Type serviceType)
{
foreach (var mi in serviceType.GetRuntimeMethods().Where(i => i.IsPublic && !i.IsStatic))
@@ -20,8 +30,8 @@ namespace ServiceStack.Host
if (mi.GetParameters().Length != 1)
continue;
- var actionName = mi.Name.ToUpper();
- if (!HttpMethods.AllVerbs.Contains(actionName) && actionName != ActionContext.AnyAction)
+ var actionName = mi.Name;
+ if (!AllVerbs.Contains(actionName, StringComparer.OrdinalIgnoreCase) && !string.Equals(actionName, ServiceMethod.AnyAction, StringComparison.OrdinalIgnoreCase))
continue;
yield return mi;
@@ -31,9 +41,9 @@ namespace ServiceStack.Host
internal static class ServiceExecGeneral
{
- public static Dictionary<string, ActionContext> execMap = new Dictionary<string, ActionContext>();
+ public static Dictionary<string, ServiceMethod> execMap = new Dictionary<string, ServiceMethod>();
- public static void CreateServiceRunnersFor(Type requestType, List<ActionContext> actions)
+ public static void CreateServiceRunnersFor(Type requestType, List<ServiceMethod> actions)
{
foreach (var actionCtx in actions)
{
@@ -45,12 +55,11 @@ namespace ServiceStack.Host
public static async Task<object> Execute(Type serviceType, IRequest request, object instance, object requestDto, string requestName)
{
- var actionName = request.Verb
- ?? HttpMethods.Post; //MQ Services
+ var actionName = request.Verb ?? "POST";
- ActionContext actionContext;
- if (ServiceExecGeneral.execMap.TryGetValue(ActionContext.Key(serviceType, actionName, requestName), out actionContext)
- || ServiceExecGeneral.execMap.TryGetValue(ActionContext.AnyKey(serviceType, requestName), out actionContext))
+ ServiceMethod actionContext;
+ if (ServiceExecGeneral.execMap.TryGetValue(ServiceMethod.Key(serviceType, actionName, requestName), out actionContext)
+ || ServiceExecGeneral.execMap.TryGetValue(ServiceMethod.AnyKey(serviceType, requestName), out actionContext))
{
if (actionContext.RequestFilters != null)
{
@@ -67,29 +76,29 @@ namespace ServiceStack.Host
if (taskResponse != null)
{
await taskResponse.ConfigureAwait(false);
- response = ServiceStackHost.Instance.GetTaskResult(taskResponse, requestName);
+ response = ServiceHandler.GetTaskResult(taskResponse);
}
return response;
}
var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLower();
- throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetOperationName(), expectedMethodName, serviceType.GetOperationName()));
+ throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetMethodName(), expectedMethodName, serviceType.GetMethodName()));
}
- public static List<ActionContext> Reset(Type serviceType)
+ public static List<ServiceMethod> Reset(Type serviceType)
{
- var actions = new List<ActionContext>();
+ var actions = new List<ServiceMethod>();
foreach (var mi in serviceType.GetActions())
{
- var actionName = mi.Name.ToUpper();
+ var actionName = mi.Name;
var args = mi.GetParameters();
var requestType = args[0].ParameterType;
- var actionCtx = new ActionContext
+ var actionCtx = new ServiceMethod
{
- Id = ActionContext.Key(serviceType, actionName, requestType.GetOperationName())
+ Id = ServiceMethod.Key(serviceType, actionName, requestType.GetMethodName())
};
try
diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs
new file mode 100644
index 000000000..8b59b4843
--- /dev/null
+++ b/Emby.Server.Implementations/Services/ServiceHandler.cs
@@ -0,0 +1,296 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Threading.Tasks;
+using Emby.Server.Implementations.HttpServer;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Services;
+
+namespace Emby.Server.Implementations.Services
+{
+ public class ServiceHandler
+ {
+ public async Task<object> HandleResponseAsync(object response)
+ {
+ var taskResponse = response as Task;
+
+ if (taskResponse == null)
+ {
+ return response;
+ }
+
+ await taskResponse.ConfigureAwait(false);
+
+ var taskResult = GetTaskResult(taskResponse);
+
+ var subTask = taskResult as Task;
+ if (subTask != null)
+ {
+ taskResult = GetTaskResult(subTask);
+ }
+
+ return taskResult;
+ }
+
+ internal static object GetTaskResult(Task task)
+ {
+ try
+ {
+ var taskObject = task as Task<object>;
+ if (taskObject != null)
+ {
+ return taskObject.Result;
+ }
+
+ task.Wait();
+
+ var type = task.GetType().GetTypeInfo();
+ if (!type.IsGenericType)
+ {
+ return null;
+ }
+
+ return type.GetDeclaredProperty("Result").GetValue(task);
+ }
+ catch (TypeAccessException)
+ {
+ return null; //return null for void Task's
+ }
+ }
+
+ protected static object CreateContentTypeRequest(HttpListenerHost host, IRequest httpReq, Type requestType, string contentType)
+ {
+ if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
+ {
+ var deserializer = RequestHelper.GetRequestReader(host, contentType);
+ if (deserializer != null)
+ {
+ return deserializer(requestType, httpReq.InputStream);
+ }
+ }
+ return host.CreateInstance(requestType);
+ }
+
+ public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, ILogger logger, out string contentType)
+ {
+ pathInfo = GetSanitizedPathInfo(pathInfo, out contentType);
+
+ return ServiceController.Instance.GetRestPathForRequest(httpMethod, pathInfo, logger);
+ }
+
+ public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
+ {
+ contentType = null;
+ var pos = pathInfo.LastIndexOf('.');
+ if (pos >= 0)
+ {
+ var format = pathInfo.Substring(pos + 1);
+ contentType = GetFormatContentType(format);
+ if (contentType != null)
+ {
+ pathInfo = pathInfo.Substring(0, pos);
+ }
+ }
+ return pathInfo;
+ }
+
+ private static string GetFormatContentType(string format)
+ {
+ //built-in formats
+ if (format == "json")
+ return "application/json";
+ if (format == "xml")
+ return "application/xml";
+
+ return null;
+ }
+
+ public RestPath GetRestPath(string httpMethod, string pathInfo)
+ {
+ if (this.RestPath == null)
+ {
+ string contentType;
+ this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, new NullLogger(), out contentType);
+
+ if (contentType != null)
+ ResponseContentType = contentType;
+ }
+ return this.RestPath;
+ }
+
+ public RestPath RestPath { get; set; }
+
+ // Set from SSHHF.GetHandlerForPathInfo()
+ public string ResponseContentType { get; set; }
+
+ public async Task ProcessRequestAsync(HttpListenerHost appHost, IRequest httpReq, IResponse httpRes, ILogger logger, string operationName)
+ {
+ var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
+ if (restPath == null)
+ {
+ throw new NotSupportedException("No RestPath found for: " + httpReq.Verb + " " + httpReq.PathInfo);
+ }
+
+ SetRoute(httpReq, restPath);
+
+ if (ResponseContentType != null)
+ httpReq.ResponseContentType = ResponseContentType;
+
+ var request = httpReq.Dto = CreateRequest(appHost, httpReq, restPath, logger);
+
+ appHost.ApplyRequestFilters(httpReq, httpRes, request);
+
+ var rawResponse = await appHost.ServiceController.Execute(appHost, request, httpReq).ConfigureAwait(false);
+
+ var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
+
+ // Apply response filters
+ foreach (var responseFilter in appHost.ResponseFilters)
+ {
+ responseFilter(httpReq, httpRes, response);
+ }
+
+ await ResponseHelper.WriteToResponse(httpRes, httpReq, response).ConfigureAwait(false);
+ }
+
+ public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
+ {
+ var requestType = restPath.RequestType;
+
+ if (RequireqRequestStream(requestType))
+ {
+ // Used by IRequiresRequestStream
+ return CreateRequiresRequestStreamRequest(host, httpReq, requestType);
+ }
+
+ var requestParams = GetFlattenedRequestParams(httpReq);
+ return CreateRequest(host, httpReq, restPath, requestParams);
+ }
+
+ private static bool RequireqRequestStream(Type requestType)
+ {
+ var requiresRequestStreamTypeInfo = typeof(IRequiresRequestStream).GetTypeInfo();
+
+ return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo());
+ }
+
+ private static IRequiresRequestStream CreateRequiresRequestStreamRequest(HttpListenerHost host, IRequest req, Type requestType)
+ {
+ var restPath = GetRoute(req);
+ var request = ServiceHandler.CreateRequest(req, restPath, GetRequestParams(req), host.CreateInstance(requestType));
+
+ var rawReq = (IRequiresRequestStream)request;
+ rawReq.RequestStream = req.InputStream;
+ return rawReq;
+ }
+
+ public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams)
+ {
+ var requestDto = CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType);
+
+ return CreateRequest(httpReq, restPath, requestParams, requestDto);
+ }
+
+ public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
+ {
+ string contentType;
+ var pathInfo = !restPath.IsWildCardPath
+ ? GetSanitizedPathInfo(httpReq.PathInfo, out contentType)
+ : httpReq.PathInfo;
+
+ return restPath.CreateRequest(pathInfo, requestParams, requestDto);
+ }
+
+ /// <summary>
+ /// Duplicate Params are given a unique key by appending a #1 suffix
+ /// </summary>
+ private static Dictionary<string, string> GetRequestParams(IRequest request)
+ {
+ var map = new Dictionary<string, string>();
+
+ foreach (var name in request.QueryString.Keys)
+ {
+ if (name == null) continue; //thank you ASP.NET
+
+ var values = request.QueryString.GetValues(name);
+ if (values.Length == 1)
+ {
+ map[name] = values[0];
+ }
+ else
+ {
+ for (var i = 0; i < values.Length; i++)
+ {
+ map[name + (i == 0 ? "" : "#" + i)] = values[i];
+ }
+ }
+ }
+
+ if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null)
+ {
+ foreach (var name in request.FormData.Keys)
+ {
+ if (name == null) continue; //thank you ASP.NET
+
+ var values = request.FormData.GetValues(name);
+ if (values.Length == 1)
+ {
+ map[name] = values[0];
+ }
+ else
+ {
+ for (var i = 0; i < values.Length; i++)
+ {
+ map[name + (i == 0 ? "" : "#" + i)] = values[i];
+ }
+ }
+ }
+ }
+
+ return map;
+ }
+
+ private static bool IsMethod(string method, string expected)
+ {
+ return string.Equals(method, expected, StringComparison.OrdinalIgnoreCase);
+ }
+
+ /// <summary>
+ /// Duplicate params have their values joined together in a comma-delimited string
+ /// </summary>
+ private static Dictionary<string, string> GetFlattenedRequestParams(IRequest request)
+ {
+ var map = new Dictionary<string, string>();
+
+ foreach (var name in request.QueryString.Keys)
+ {
+ if (name == null) continue; //thank you ASP.NET
+ map[name] = request.QueryString[name];
+ }
+
+ if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null)
+ {
+ foreach (var name in request.FormData.Keys)
+ {
+ if (name == null) continue; //thank you ASP.NET
+ map[name] = request.FormData[name];
+ }
+ }
+
+ return map;
+ }
+
+ private static void SetRoute(IRequest req, RestPath route)
+ {
+ req.Items["__route"] = route;
+ }
+
+ private static RestPath GetRoute(IRequest req)
+ {
+ object route;
+ req.Items.TryGetValue("__route", out route);
+ return route as RestPath;
+ }
+ }
+
+}
diff --git a/ServiceStack/Host/ActionContext.cs b/Emby.Server.Implementations/Services/ServiceMethod.cs
index 9f165cff1..bcbc6fb57 100644
--- a/ServiceStack/Host/ActionContext.cs
+++ b/Emby.Server.Implementations/Services/ServiceMethod.cs
@@ -1,11 +1,8 @@
using System;
-namespace ServiceStack.Host
+namespace Emby.Server.Implementations.Services
{
- /// <summary>
- /// Context to capture IService action
- /// </summary>
- public class ActionContext
+ public class ServiceMethod
{
public const string AnyAction = "ANY";
diff --git a/ServiceStack/Host/RestPath.cs b/Emby.Server.Implementations/Services/ServicePath.cs
index 5bbd03a21..255b20919 100644
--- a/ServiceStack/Host/RestPath.cs
+++ b/Emby.Server.Implementations/Services/ServicePath.cs
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
-using ServiceStack.Serialization;
+using MediaBrowser.Model.Logging;
-namespace ServiceStack.Host
+namespace Emby.Server.Implementations.Services
{
public class RestPath
{
@@ -46,11 +47,11 @@ namespace ServiceStack.Host
public string[] Verbs
{
- get
- {
- return allowsAllVerbs
- ? new[] { ActionContext.AnyAction }
- : AllowedVerbs.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ get
+ {
+ return allowsAllVerbs
+ ? new[] { "ANY" }
+ : AllowedVerbs.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
}
}
@@ -71,45 +72,50 @@ namespace ServiceStack.Host
public static string[] GetPathPartsForMatching(string pathInfo)
{
var parts = pathInfo.ToLower().Split(PathSeperatorChar)
- .Where(x => !string.IsNullOrEmpty(x)).ToArray();
+ .Where(x => !String.IsNullOrEmpty(x)).ToArray();
return parts;
}
- public static IEnumerable<string> GetFirstMatchHashKeys(string[] pathPartsForMatching)
+ public static List<string> GetFirstMatchHashKeys(string[] pathPartsForMatching)
{
var hashPrefix = pathPartsForMatching.Length + PathSeperator;
return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching);
}
- public static IEnumerable<string> GetFirstMatchWildCardHashKeys(string[] pathPartsForMatching)
+ public static List<string> GetFirstMatchWildCardHashKeys(string[] pathPartsForMatching)
{
const string hashPrefix = WildCard + PathSeperator;
return GetPotentialMatchesWithPrefix(hashPrefix, pathPartsForMatching);
}
- private static IEnumerable<string> GetPotentialMatchesWithPrefix(string hashPrefix, string[] pathPartsForMatching)
+ private static List<string> GetPotentialMatchesWithPrefix(string hashPrefix, string[] pathPartsForMatching)
{
+ var list = new List<string>();
+
foreach (var part in pathPartsForMatching)
{
- yield return hashPrefix + part;
+ list.Add(hashPrefix + part);
+
var subParts = part.Split(ComponentSeperator);
if (subParts.Length == 1) continue;
foreach (var subPart in subParts)
{
- yield return hashPrefix + subPart;
+ list.Add(hashPrefix + subPart);
}
}
+
+ return list;
}
- public RestPath(Type requestType, string path, string verbs, string summary = null, string notes = null)
+ public RestPath(Func<Type, object> createInstanceFn, Func<Type, Func<string, object>> getParseFn, Type requestType, string path, string verbs, string summary = null, string notes = null)
{
this.RequestType = requestType;
this.Summary = summary;
this.Notes = notes;
this.restPath = path;
- this.allowsAllVerbs = verbs == null || string.Equals(verbs, WildCard, StringComparison.OrdinalIgnoreCase);
+ this.allowsAllVerbs = verbs == null || String.Equals(verbs, WildCard, StringComparison.OrdinalIgnoreCase);
if (!this.allowsAllVerbs)
{
this.allowedVerbs = verbs.ToUpper();
@@ -121,7 +127,7 @@ namespace ServiceStack.Host
var hasSeparators = new List<bool>();
foreach (var component in this.restPath.Split(PathSeperatorChar))
{
- if (string.IsNullOrEmpty(component)) continue;
+ if (String.IsNullOrEmpty(component)) continue;
if (StringContains(component, VariablePrefix)
&& component.IndexOf(ComponentSeperator) != -1)
@@ -194,19 +200,95 @@ namespace ServiceStack.Host
this.IsValid = sbHashKey.Length > 0;
this.UniqueMatchHashKey = sbHashKey.ToString();
- this.typeDeserializer = new StringMapTypeDeserializer(this.RequestType);
+ this.typeDeserializer = new StringMapTypeDeserializer(createInstanceFn, getParseFn, this.RequestType);
RegisterCaseInsenstivePropertyNameMappings();
}
private void RegisterCaseInsenstivePropertyNameMappings()
{
- foreach (var propertyInfo in RequestType.GetSerializableProperties())
+ foreach (var propertyInfo in GetSerializableProperties(RequestType))
{
var propertyName = propertyInfo.Name;
propertyNamesMap.Add(propertyName.ToLower(), propertyName);
}
}
+ internal static string[] IgnoreAttributesNamed = new[] {
+ "IgnoreDataMemberAttribute",
+ "JsonIgnoreAttribute"
+ };
+
+
+ private static List<Type> _excludeTypes = new List<Type> { typeof(Stream) };
+
+ internal static PropertyInfo[] GetSerializableProperties(Type type)
+ {
+ var properties = GetPublicProperties(type);
+ var readableProperties = properties.Where(x => x.GetMethod != null);
+
+ // else return those properties that are not decorated with IgnoreDataMember
+ return readableProperties
+ .Where(prop => prop.GetCustomAttributes(true)
+ .All(attr =>
+ {
+ var name = attr.GetType().Name;
+ return !IgnoreAttributesNamed.Contains(name);
+ }))
+ .Where(prop => !_excludeTypes.Contains(prop.PropertyType))
+ .ToArray();
+ }
+
+ private static PropertyInfo[] GetPublicProperties(Type type)
+ {
+ if (type.GetTypeInfo().IsInterface)
+ {
+ var propertyInfos = new List<PropertyInfo>();
+
+ var considered = new List<Type>();
+ var queue = new Queue<Type>();
+ considered.Add(type);
+ queue.Enqueue(type);
+
+ while (queue.Count > 0)
+ {
+ var subType = queue.Dequeue();
+ foreach (var subInterface in subType.GetTypeInfo().ImplementedInterfaces)
+ {
+ if (considered.Contains(subInterface)) continue;
+
+ considered.Add(subInterface);
+ queue.Enqueue(subInterface);
+ }
+
+ var typeProperties = GetTypesPublicProperties(subType);
+
+ var newPropertyInfos = typeProperties
+ .Where(x => !propertyInfos.Contains(x));
+
+ propertyInfos.InsertRange(0, newPropertyInfos);
+ }
+
+ return propertyInfos.ToArray();
+ }
+
+ return GetTypesPublicProperties(type)
+ .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties
+ .ToArray();
+ }
+
+ private static PropertyInfo[] GetTypesPublicProperties(Type subType)
+ {
+ var pis = new List<PropertyInfo>();
+ foreach (var pi in subType.GetRuntimeProperties())
+ {
+ var mi = pi.GetMethod ?? pi.SetMethod;
+ if (mi != null && mi.IsStatic) continue;
+ pis.Add(pi);
+ }
+ return pis.ToArray();
+ }
+
+
public bool IsValid { get; set; }
/// <summary>
@@ -220,16 +302,14 @@ namespace ServiceStack.Host
private readonly Dictionary<string, string> propertyNamesMap = new Dictionary<string, string>();
- public static Func<RestPath, string, string[], int> CalculateMatchScore { get; set; }
-
- public int MatchScore(string httpMethod, string[] withPathInfoParts)
+ public int MatchScore(string httpMethod, string[] withPathInfoParts, ILogger logger)
{
- if (CalculateMatchScore != null)
- return CalculateMatchScore(this, httpMethod, withPathInfoParts);
-
int wildcardMatchCount;
- var isMatch = IsMatch(httpMethod, withPathInfoParts, out wildcardMatchCount);
- if (!isMatch) return -1;
+ var isMatch = IsMatch(httpMethod, withPathInfoParts, logger, out wildcardMatchCount);
+ if (!isMatch)
+ {
+ return -1;
+ }
var score = 0;
@@ -240,7 +320,7 @@ namespace ServiceStack.Host
score += Math.Max((10 - VariableArgsCount), 1) * 100;
//Exact verb match is better than ANY
- var exactVerb = string.Equals(httpMethod, AllowedVerbs, StringComparison.OrdinalIgnoreCase);
+ var exactVerb = String.Equals(httpMethod, AllowedVerbs, StringComparison.OrdinalIgnoreCase);
score += exactVerb ? 10 : 1;
return score;
@@ -255,19 +335,33 @@ namespace ServiceStack.Host
/// For performance withPathInfoParts should already be a lower case string
/// to minimize redundant matching operations.
/// </summary>
- /// <param name="httpMethod"></param>
- /// <param name="withPathInfoParts"></param>
- /// <param name="wildcardMatchCount"></param>
- /// <returns></returns>
- public bool IsMatch(string httpMethod, string[] withPathInfoParts, out int wildcardMatchCount)
+ public bool IsMatch(string httpMethod, string[] withPathInfoParts, ILogger logger, out int wildcardMatchCount)
{
wildcardMatchCount = 0;
- if (withPathInfoParts.Length != this.PathComponentsCount && !this.IsWildCardPath) return false;
- if (!this.allowsAllVerbs && !StringContains(this.allowedVerbs, httpMethod)) return false;
+ if (withPathInfoParts.Length != this.PathComponentsCount && !this.IsWildCardPath)
+ {
+ //logger.Info("withPathInfoParts mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts));
+ return false;
+ }
+
+ if (!this.allowsAllVerbs && !StringContains(this.allowedVerbs, httpMethod))
+ {
+ //logger.Info("allowsAllVerbs mismatch for {0} for {1} allowedverbs {2}", httpMethod, string.Join("/", withPathInfoParts), this.allowedVerbs);
+ return false;
+ }
+
+ if (!ExplodeComponents(ref withPathInfoParts))
+ {
+ //logger.Info("ExplodeComponents mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts));
+ return false;
+ }
- if (!ExplodeComponents(ref withPathInfoParts)) return false;
- if (this.TotalComponentsCount != withPathInfoParts.Length && !this.IsWildCardPath) return false;
+ if (this.TotalComponentsCount != withPathInfoParts.Length && !this.IsWildCardPath)
+ {
+ //logger.Info("TotalComponentsCount mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts));
+ return false;
+ }
int pathIx = 0;
for (var i = 0; i < this.TotalComponentsCount; i++)
@@ -277,7 +371,7 @@ namespace ServiceStack.Host
if (i < this.TotalComponentsCount - 1)
{
// Continue to consume up until a match with the next literal
- while (pathIx < withPathInfoParts.Length && withPathInfoParts[pathIx] != this.literalsToMatch[i + 1])
+ while (pathIx < withPathInfoParts.Length && !LiteralsEqual(withPathInfoParts[pathIx], this.literalsToMatch[i + 1]))
{
pathIx++;
wildcardMatchCount++;
@@ -286,6 +380,7 @@ namespace ServiceStack.Host
// Ensure there are still enough parts left to match the remainder
if ((withPathInfoParts.Length - pathIx) < (this.TotalComponentsCount - i - 1))
{
+ //logger.Info("withPathInfoParts length mismatch for {0} for {1}", httpMethod, string.Join("/", withPathInfoParts));
return false;
}
}
@@ -306,7 +401,11 @@ namespace ServiceStack.Host
continue;
}
- if (withPathInfoParts.Length <= pathIx || withPathInfoParts[pathIx] != literalToMatch) return false;
+ if (withPathInfoParts.Length <= pathIx || !LiteralsEqual(withPathInfoParts[pathIx], literalToMatch))
+ {
+ //logger.Info("withPathInfoParts2 length mismatch for {0} for {1}. not equals: {2} != {3}.", httpMethod, string.Join("/", withPathInfoParts), withPathInfoParts[pathIx], literalToMatch);
+ return false;
+ }
pathIx++;
}
}
@@ -314,13 +413,29 @@ namespace ServiceStack.Host
return pathIx == withPathInfoParts.Length;
}
+ private bool LiteralsEqual(string str1, string str2)
+ {
+ // Most cases
+ if (String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ // Handle turkish i
+ str1 = str1.ToUpperInvariant();
+ str2 = str2.ToUpperInvariant();
+
+ // Invariant IgnoreCase would probably be better but it's not available in PCL
+ return String.Equals(str1, str2, StringComparison.CurrentCultureIgnoreCase);
+ }
+
private bool ExplodeComponents(ref string[] withPathInfoParts)
{
var totalComponents = new List<string>();
for (var i = 0; i < withPathInfoParts.Length; i++)
{
var component = withPathInfoParts[i];
- if (string.IsNullOrEmpty(component)) continue;
+ if (String.IsNullOrEmpty(component)) continue;
if (this.PathComponentsCount != this.TotalComponentsCount
&& this.componentsWithSeparators[i])
@@ -342,7 +457,7 @@ namespace ServiceStack.Host
public object CreateRequest(string pathInfo, Dictionary<string, string> queryStringAndFormData, object fromInstance)
{
var requestComponents = pathInfo.Split(PathSeperatorChar)
- .Where(x => !string.IsNullOrEmpty(x)).ToArray();
+ .Where(x => !String.IsNullOrEmpty(x)).ToArray();
ExplodeComponents(ref requestComponents);
@@ -352,7 +467,7 @@ namespace ServiceStack.Host
&& requestComponents.Length >= this.TotalComponentsCount - this.wildcardCount;
if (!isValidWildCardPath)
- throw new ArgumentException(string.Format(
+ throw new ArgumentException(String.Format(
"Path Mismatch: Request Path '{0}' has invalid number of components compared to: '{1}'",
pathInfo, this.restPath));
}
@@ -371,14 +486,14 @@ namespace ServiceStack.Host
string propertyNameOnRequest;
if (!this.propertyNamesMap.TryGetValue(variableName.ToLower(), out propertyNameOnRequest))
{
- if (string.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase))
+ if (String.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase))
{
pathIx++;
- continue;
+ continue;
}
-
+
throw new ArgumentException("Could not find property "
- + variableName + " on " + RequestType.GetOperationName());
+ + variableName + " on " + RequestType.GetMethodName());
}
var value = requestComponents.Length > pathIx ? requestComponents[pathIx] : null; //wildcard has arg mismatch
@@ -401,12 +516,12 @@ namespace ServiceStack.Host
// hits a match for the next element in the definition (which must be a literal)
// It may consume 0 or more path parts
var stopLiteral = i == this.TotalComponentsCount - 1 ? null : this.literalsToMatch[i + 1];
- if (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
+ if (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
{
var sb = new StringBuilder();
sb.Append(value);
pathIx++;
- while (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
+ while (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase))
{
sb.Append(PathSeperatorChar + requestComponents[pathIx++]);
}
diff --git a/ServiceStack/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
index 762e8aaff..fc1cf4ed9 100644
--- a/ServiceStack/StringMapTypeDeserializer.cs
+++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs
@@ -1,10 +1,9 @@
using System;
using System.Collections.Generic;
-using System.Runtime.Serialization;
using System.Linq;
using System.Reflection;
-namespace ServiceStack.Serialization
+namespace Emby.Server.Implementations.Services
{
/// <summary>
/// Serializer cache of delegates required to create a type from a string map (e.g. for REST urls)
@@ -30,29 +29,28 @@ namespace ServiceStack.Serialization
public Func<string, object> GetParseFn(Type propertyType)
{
- //Don't JSV-decode string values for string properties
if (propertyType == typeof(string))
return s => s;
- return ServiceStackHost.Instance.GetParseFn(propertyType);
+ return _GetParseFn(propertyType);
}
- public StringMapTypeDeserializer(Type type)
+ private readonly Func<Type, object> _CreateInstanceFn;
+ private readonly Func<Type, Func<string, object>> _GetParseFn;
+
+ public StringMapTypeDeserializer(Func<Type, object> createInstanceFn, Func<Type, Func<string, object>> getParseFn, Type type)
{
+ _CreateInstanceFn = createInstanceFn;
+ _GetParseFn = getParseFn;
this.type = type;
- foreach (var propertyInfo in type.GetSerializableProperties())
+ foreach (var propertyInfo in RestPath.GetSerializableProperties(type))
{
var propertySetFn = TypeAccessor.GetSetPropertyMethod(type, propertyInfo);
var propertyType = propertyInfo.PropertyType;
var propertyParseStringFn = GetParseFn(propertyType);
var propertySerializer = new PropertySerializerEntry(propertySetFn, propertyParseStringFn) { PropertyType = propertyType };
- var attr = propertyInfo.AllAttributes<DataMemberAttribute>().FirstOrDefault();
- if (attr != null && attr.Name != null)
- {
- propertySetterMap[attr.Name] = propertySerializer;
- }
propertySetterMap[propertyInfo.Name] = propertySerializer;
}
}
@@ -64,7 +62,7 @@ namespace ServiceStack.Serialization
PropertySerializerEntry propertySerializerEntry = null;
if (instance == null)
- instance = ServiceStackHost.Instance.CreateInstance(type);
+ instance = _CreateInstanceFn(type);
foreach (var pair in keyValuePairs.Where(x => !string.IsNullOrEmpty(x.Value)))
{
diff --git a/ServiceStack/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs
index 7b5a50ef1..c7346789a 100644
--- a/ServiceStack/UrlExtensions.cs
+++ b/Emby.Server.Implementations/Services/UrlExtensions.cs
@@ -1,6 +1,6 @@
using System;
-namespace ServiceStack
+namespace Emby.Server.Implementations.Services
{
/// <summary>
/// Donated by Ivan Korneliuk from his post:
@@ -10,7 +10,7 @@ namespace ServiceStack
/// </summary>
public static class UrlExtensions
{
- public static string GetOperationName(this Type type)
+ public static string GetMethodName(this Type type)
{
var typeName = type.FullName != null //can be null, e.g. generic types
? LeftPart(type.FullName, "[[") //Generic Fullname
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index a20fb67b2..8051b7848 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -954,8 +954,11 @@ namespace Emby.Server.Implementations.Session
{
var session = GetSessionToRemoteControl(sessionId);
- var controllingSession = GetSession(controllingSessionId);
- AssertCanControl(session, controllingSession);
+ if (!string.IsNullOrWhiteSpace(controllingSessionId))
+ {
+ var controllingSession = GetSession(controllingSessionId);
+ AssertCanControl(session, controllingSession);
+ }
return session.SessionController.SendGeneralCommand(command, cancellationToken);
}
@@ -1042,11 +1045,14 @@ namespace Emby.Server.Implementations.Session
}
}
- var controllingSession = GetSession(controllingSessionId);
- AssertCanControl(session, controllingSession);
- if (controllingSession.UserId.HasValue)
+ if (!string.IsNullOrWhiteSpace(controllingSessionId))
{
- command.ControllingUserId = controllingSession.UserId.Value.ToString("N");
+ 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);
@@ -1136,11 +1142,14 @@ namespace Emby.Server.Implementations.Session
{
var session = GetSessionToRemoteControl(sessionId);
- var controllingSession = GetSession(controllingSessionId);
- AssertCanControl(session, controllingSession);
- if (controllingSession.UserId.HasValue)
+ if (!string.IsNullOrWhiteSpace(controllingSessionId))
{
- command.ControllingUserId = controllingSession.UserId.Value.ToString("N");
+ var controllingSession = GetSession(controllingSessionId);
+ AssertCanControl(session, controllingSession);
+ if (controllingSession.UserId.HasValue)
+ {
+ command.ControllingUserId = controllingSession.UserId.Value.ToString("N");
+ }
}
return session.SessionController.SendPlaystateCommand(command, cancellationToken);
diff --git a/Emby.Server.Implementations/Sync/AppSyncProvider.cs b/Emby.Server.Implementations/Sync/AppSyncProvider.cs
deleted file mode 100644
index d405a0ff9..000000000
--- a/Emby.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 Emby.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/Emby.Server.Implementations/Sync/CloudSyncProfile.cs b/Emby.Server.Implementations/Sync/CloudSyncProfile.cs
deleted file mode 100644
index c0675df81..000000000
--- a/Emby.Server.Implementations/Sync/CloudSyncProfile.cs
+++ /dev/null
@@ -1,288 +0,0 @@
-using MediaBrowser.Model.Dlna;
-using System.Collections.Generic;
-
-namespace Emby.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.Equals,
- 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.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/Emby.Server.Implementations/Sync/IHasSyncQuality.cs b/Emby.Server.Implementations/Sync/IHasSyncQuality.cs
deleted file mode 100644
index bec8b37a7..000000000
--- a/Emby.Server.Implementations/Sync/IHasSyncQuality.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using MediaBrowser.Model.Sync;
-using System.Collections.Generic;
-
-namespace Emby.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/Emby.Server.Implementations/Sync/MediaSync.cs b/Emby.Server.Implementations/Sync/MediaSync.cs
deleted file mode 100644
index fa8388b6c..000000000
--- a/Emby.Server.Implementations/Sync/MediaSync.cs
+++ /dev/null
@@ -1,500 +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.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.IO;
-using MediaBrowser.Model.Cryptography;
-using MediaBrowser.Model.IO;
-
-namespace Emby.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;
- private readonly ICryptoProvider _cryptographyProvider;
-
- public const string PathSeparatorString = "/";
- public const char PathSeparatorChar = '/';
-
- public MediaSync(ILogger logger, ISyncManager syncManager, IServerApplicationHost appHost, IFileSystem fileSystem, IConfigurationManager config, ICryptoProvider cryptographyProvider)
- {
- _logger = logger;
- _syncManager = syncManager;
- _appHost = appHost;
- _fileSystem = fileSystem;
- _config = config;
- _cryptographyProvider = cryptographyProvider;
- }
-
- 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 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 byte[] CreateMd5(byte[] value)
- {
- return _cryptographyProvider.ComputeMD5(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/Emby.Server.Implementations/Sync/MultiProviderSync.cs b/Emby.Server.Implementations/Sync/MultiProviderSync.cs
deleted file mode 100644
index 8189b8550..000000000
--- a/Emby.Server.Implementations/Sync/MultiProviderSync.cs
+++ /dev/null
@@ -1,79 +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.Cryptography;
-using MediaBrowser.Model.IO;
-
-namespace Emby.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;
- private readonly ICryptoProvider _cryptographyProvider;
-
- public MultiProviderSync(SyncManager syncManager, IServerApplicationHost appHost, ILogger logger, IFileSystem fileSystem, IConfigurationManager config, ICryptoProvider cryptographyProvider)
- {
- _syncManager = syncManager;
- _appHost = appHost;
- _logger = logger;
- _fileSystem = fileSystem;
- _config = config;
- _cryptographyProvider = cryptographyProvider;
- }
-
- 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, _cryptographyProvider)
- .Sync(target.Item1, dataProvider, target.Item2, innerProgress, cancellationToken)
- .ConfigureAwait(false);
-
- numComplete++;
- startingPercent = numComplete;
- startingPercent /= targets.Count;
- startingPercent *= 100;
- progress.Report(startingPercent);
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs b/Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs
deleted file mode 100644
index 09a0bfde4..000000000
--- a/Emby.Server.Implementations/Sync/ServerSyncScheduledTask.cs
+++ /dev/null
@@ -1,95 +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.Cryptography;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace Emby.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;
- private readonly ICryptoProvider _cryptographyProvider;
-
- public ServerSyncScheduledTask(ISyncManager syncManager, ILogger logger, IFileSystem fileSystem, IServerApplicationHost appHost, IConfigurationManager config, ICryptoProvider cryptographyProvider)
- {
- _syncManager = syncManager;
- _logger = logger;
- _fileSystem = fileSystem;
- _appHost = appHost;
- _config = config;
- _cryptographyProvider = cryptographyProvider;
- }
-
- 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, _cryptographyProvider)
- .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/Emby.Server.Implementations/Sync/SyncConfig.cs b/Emby.Server.Implementations/Sync/SyncConfig.cs
deleted file mode 100644
index 8a97326bd..000000000
--- a/Emby.Server.Implementations/Sync/SyncConfig.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Model.Sync;
-using System.Collections.Generic;
-
-namespace Emby.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/Emby.Server.Implementations/Sync/SyncConvertScheduledTask.cs b/Emby.Server.Implementations/Sync/SyncConvertScheduledTask.cs
deleted file mode 100644
index 8dafac7e1..000000000
--- a/Emby.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 Emby.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/Emby.Server.Implementations/Sync/SyncHelper.cs b/Emby.Server.Implementations/Sync/SyncHelper.cs
deleted file mode 100644
index 7fe703796..000000000
--- a/Emby.Server.Implementations/Sync/SyncHelper.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-
-namespace Emby.Server.Implementations.Sync
-{
- public class SyncHelper
- {
- public static long? AdjustBitrate(long? 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/Emby.Server.Implementations/Sync/SyncJobOptions.cs b/Emby.Server.Implementations/Sync/SyncJobOptions.cs
deleted file mode 100644
index 8e4d8e2ed..000000000
--- a/Emby.Server.Implementations/Sync/SyncJobOptions.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.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/Emby.Server.Implementations/Sync/SyncJobProcessor.cs b/Emby.Server.Implementations/Sync/SyncJobProcessor.cs
deleted file mode 100644
index 17cdef5fc..000000000
--- a/Emby.Server.Implementations/Sync/SyncJobProcessor.cs
+++ /dev/null
@@ -1,998 +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 Emby.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, User user)
- {
- 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)
- {
- var user = _userManager.GetUserById(job.UserId);
-
- if (user == null)
- {
- await _syncManager.CancelJob(job.Id).ConfigureAwait(false);
- }
- else
- {
- await EnsureJobItems(job, user).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 job = _syncManager.GetJob(jobItem.JobId);
- if (job == null)
- {
- _logger.Error("Job not found. Cannot complete the sync job.");
- await _syncManager.CancelJobItem(jobItem.Id).ConfigureAwait(false);
- return;
- }
-
- 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 syncOptions = _config.GetSyncOptions();
-
- 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/Emby.Server.Implementations/Sync/SyncManager.cs b/Emby.Server.Implementations/Sync/SyncManager.cs
deleted file mode 100644
index 418d42c9a..000000000
--- a/Emby.Server.Implementations/Sync/SyncManager.cs
+++ /dev/null
@@ -1,1372 +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.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-
-namespace Emby.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 IMemoryStreamFactory _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, IMemoryStreamFactory 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.ToString("f1", CultureInfo.CurrentCulture);
- }
-
- 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, user).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 (IOException)
- {
-
- }
- 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);
-
- if (jobItem == null)
- {
- _logger.Debug("ReportSyncJobItemTransferred: SyncJobItem {0} doesn't exist anymore", id);
- return;
- }
-
- 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 (IOException)
- {
- }
- 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);
- }
-
- var syncJobResult = await GetJobs(new SyncJobQuery
- {
- ItemId = item,
- TargetId = targetId
-
- }).ConfigureAwait(false);
-
- foreach (var job in syncJobResult.Items)
- {
- await CancelJob(job.Id).ConfigureAwait(false);
- }
- }
- }
-
- public async Task CancelJobItem(string id)
- {
- var jobItem = _repo.GetJobItem(id);
-
- 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 (IOException)
- {
-
- }
- 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.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/Emby.Server.Implementations/Sync/SyncNotificationEntryPoint.cs b/Emby.Server.Implementations/Sync/SyncNotificationEntryPoint.cs
deleted file mode 100644
index 06e0e66a9..000000000
--- a/Emby.Server.Implementations/Sync/SyncNotificationEntryPoint.cs
+++ /dev/null
@@ -1,60 +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 Emby.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
- {
-
- }
- }
-
- if (item.Status == SyncJobItemStatus.Cancelled)
- {
- try
- {
- await _sessionManager.SendMessageToUserDeviceSessions(item.TargetId, "SyncJobItemCancelled", item, CancellationToken.None).ConfigureAwait(false);
- }
- catch
- {
-
- }
- }
- }
-
- public void Dispose()
- {
- _syncManager.SyncJobItemUpdated -= _syncManager_SyncJobItemUpdated;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Sync/SyncRegistrationInfo.cs b/Emby.Server.Implementations/Sync/SyncRegistrationInfo.cs
deleted file mode 100644
index c2658c5c5..000000000
--- a/Emby.Server.Implementations/Sync/SyncRegistrationInfo.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using MediaBrowser.Common.Security;
-using System.Threading.Tasks;
-
-namespace Emby.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/Emby.Server.Implementations/Sync/SyncRepository.cs b/Emby.Server.Implementations/Sync/SyncRepository.cs
deleted file mode 100644
index aafce3500..000000000
--- a/Emby.Server.Implementations/Sync/SyncRepository.cs
+++ /dev/null
@@ -1,847 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Emby.Server.Implementations.Data;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Sync;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Sync;
-using SQLitePCL.pretty;
-
-namespace Emby.Server.Implementations.Sync
-{
- public class SyncRepository : BaseSqliteRepository, ISyncRepository
- {
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- private readonly IJsonSerializer _json;
-
- public SyncRepository(ILogger logger, IJsonSerializer json, IServerApplicationPaths appPaths)
- : base(logger)
- {
- _json = json;
- DbFilePath = Path.Combine(appPaths.DataPath, "sync14.db");
- }
-
- private class SyncSummary
- {
- public Dictionary<string, int> Items { get; set; }
-
- public SyncSummary()
- {
- Items = new Dictionary<string, int>();
- }
- }
-
- public void Initialize()
- {
- using (var connection = CreateConnection())
- {
- RunDefaultInitialization(connection);
-
- string[] queries = {
-
- "create table if not exists SyncJobs (Id GUID PRIMARY KEY, TargetId TEXT NOT NULL, Name TEXT NOT NULL, Profile TEXT, Quality TEXT, Bitrate INT, Status TEXT NOT NULL, Progress FLOAT, UserId TEXT NOT NULL, ItemIds TEXT NOT NULL, Category TEXT, ParentId TEXT, UnwatchedOnly BIT, ItemLimit INT, SyncNewContent BIT, DateCreated DateTime, DateLastModified DateTime, ItemCount int)",
-
- "create table if not exists SyncJobItems (Id GUID PRIMARY KEY, ItemId TEXT, ItemName TEXT, MediaSourceId TEXT, JobId TEXT, TemporaryPath TEXT, OutputPath TEXT, Status TEXT, TargetId TEXT, DateCreated DateTime, Progress FLOAT, AdditionalFiles TEXT, MediaSource TEXT, IsMarkedForRemoval BIT, JobItemIndex INT, ItemDateModifiedTicks BIGINT)",
-
- "drop index if exists idx_SyncJobItems2",
- "drop index if exists idx_SyncJobItems3",
- "drop index if exists idx_SyncJobs1",
- "drop index if exists idx_SyncJobs",
- "drop index if exists idx_SyncJobItems1",
- "create index if not exists idx_SyncJobItems4 on SyncJobItems(TargetId,ItemId,Status,Progress,DateCreated)",
- "create index if not exists idx_SyncJobItems5 on SyncJobItems(TargetId,Status,ItemId,Progress)",
-
- "create index if not exists idx_SyncJobs2 on SyncJobs(TargetId,Status,ItemIds,Progress)",
-
- "pragma shrink_memory"
- };
-
- connection.RunQueries(queries);
-
- connection.RunInTransaction(db =>
- {
- var existingColumnNames = GetColumnNames(db, "SyncJobs");
- AddColumn(db, "SyncJobs", "Profile", "TEXT", existingColumnNames);
- AddColumn(db, "SyncJobs", "Bitrate", "INT", existingColumnNames);
-
- existingColumnNames = GetColumnNames(db, "SyncJobItems");
- AddColumn(db, "SyncJobItems", "ItemDateModifiedTicks", "BIGINT", existingColumnNames);
- }, TransactionMode);
- }
- }
-
- protected override bool EnableTempStoreMemory
- {
- get
- {
- return true;
- }
- }
-
- private const string BaseJobSelectText = "select Id, TargetId, Name, Profile, Quality, Bitrate, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount from SyncJobs";
- private const string BaseJobItemSelectText = "select Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex, ItemDateModifiedTicks from SyncJobItems";
-
- public SyncJob GetJob(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- CheckDisposed();
-
- var guid = new Guid(id);
-
- if (guid == Guid.Empty)
- {
- throw new ArgumentNullException("id");
- }
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var commandText = BaseJobSelectText + " where Id=?";
- var paramList = new List<object>();
-
- paramList.Add(guid.ToGuidParamValue());
-
- foreach (var row in connection.Query(commandText, paramList.ToArray()))
- {
- return GetJob(row);
- }
-
- return null;
- }
- }
- }
-
- private SyncJob GetJob(IReadOnlyList<IResultSetValue> reader)
- {
- var info = new SyncJob
- {
- Id = reader[0].ReadGuid().ToString("N"),
- TargetId = reader[1].ToString(),
- Name = reader[2].ToString()
- };
-
- if (reader[3].SQLiteType != SQLiteType.Null)
- {
- info.Profile = reader[3].ToString();
- }
-
- if (reader[4].SQLiteType != SQLiteType.Null)
- {
- info.Quality = reader[4].ToString();
- }
-
- if (reader[5].SQLiteType != SQLiteType.Null)
- {
- info.Bitrate = reader[5].ToInt();
- }
-
- if (reader[6].SQLiteType != SQLiteType.Null)
- {
- info.Status = (SyncJobStatus)Enum.Parse(typeof(SyncJobStatus), reader[6].ToString(), true);
- }
-
- if (reader[7].SQLiteType != SQLiteType.Null)
- {
- info.Progress = reader[7].ToDouble();
- }
-
- if (reader[8].SQLiteType != SQLiteType.Null)
- {
- info.UserId = reader[8].ToString();
- }
-
- if (reader[9].SQLiteType != SQLiteType.Null)
- {
- info.RequestedItemIds = reader[9].ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
- }
-
- if (reader[10].SQLiteType != SQLiteType.Null)
- {
- info.Category = (SyncCategory)Enum.Parse(typeof(SyncCategory), reader[10].ToString(), true);
- }
-
- if (reader[11].SQLiteType != SQLiteType.Null)
- {
- info.ParentId = reader[11].ToString();
- }
-
- if (reader[12].SQLiteType != SQLiteType.Null)
- {
- info.UnwatchedOnly = reader[12].ToBool();
- }
-
- if (reader[13].SQLiteType != SQLiteType.Null)
- {
- info.ItemLimit = reader[13].ToInt();
- }
-
- info.SyncNewContent = reader[14].ToBool();
-
- info.DateCreated = reader[15].ReadDateTime();
- info.DateLastModified = reader[16].ReadDateTime();
- info.ItemCount = reader[17].ToInt();
-
- return info;
- }
-
- public Task Create(SyncJob job)
- {
- return InsertOrUpdate(job, true);
- }
-
- public Task Update(SyncJob job)
- {
- return InsertOrUpdate(job, false);
- }
-
- private async Task InsertOrUpdate(SyncJob job, bool insert)
- {
- if (job == null)
- {
- throw new ArgumentNullException("job");
- }
-
- CheckDisposed();
-
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- string commandText;
-
- if (insert)
- {
- commandText = "insert into SyncJobs (Id, TargetId, Name, Profile, Quality, Bitrate, Status, Progress, UserId, ItemIds, Category, ParentId, UnwatchedOnly, ItemLimit, SyncNewContent, DateCreated, DateLastModified, ItemCount) values (@Id, @TargetId, @Name, @Profile, @Quality, @Bitrate, @Status, @Progress, @UserId, @ItemIds, @Category, @ParentId, @UnwatchedOnly, @ItemLimit, @SyncNewContent, @DateCreated, @DateLastModified, @ItemCount)";
- }
- else
- {
- commandText = "update SyncJobs set TargetId=@TargetId,Name=@Name,Profile=@Profile,Quality=@Quality,Bitrate=@Bitrate,Status=@Status,Progress=@Progress,UserId=@UserId,ItemIds=@ItemIds,Category=@Category,ParentId=@ParentId,UnwatchedOnly=@UnwatchedOnly,ItemLimit=@ItemLimit,SyncNewContent=@SyncNewContent,DateCreated=@DateCreated,DateLastModified=@DateLastModified,ItemCount=@ItemCount where Id=@Id";
- }
-
- connection.RunInTransaction(conn =>
- {
- using (var statement = PrepareStatementSafe(connection, commandText))
- {
- statement.TryBind("@TargetId", job.TargetId);
- statement.TryBind("@Name", job.Name);
- statement.TryBind("@Profile", job.Profile);
- statement.TryBind("@Quality", job.Quality);
- statement.TryBind("@Bitrate", job.Bitrate);
- statement.TryBind("@Status", job.Status.ToString());
- statement.TryBind("@Progress", job.Progress);
- statement.TryBind("@UserId", job.UserId);
-
- statement.TryBind("@ItemIds", string.Join(",", job.RequestedItemIds.ToArray()));
-
- if (job.Category.HasValue)
- {
- statement.TryBind("@Category", job.Category.Value.ToString());
- }
- else
- {
- statement.TryBindNull("@Category");
- }
-
- if (!string.IsNullOrWhiteSpace(job.ParentId))
- {
- statement.TryBind("@ParentId", job.ParentId);
- }
- else
- {
- statement.TryBindNull("@ParentId");
- }
-
- statement.TryBind("@UnwatchedOnly", job.UnwatchedOnly);
-
- if (job.ItemLimit.HasValue)
- {
- statement.TryBind("@ItemLimit", job.ItemLimit);
- }
- else
- {
- statement.TryBindNull("@ItemLimit");
- }
-
- statement.TryBind("@SyncNewContent", job.SyncNewContent);
-
- statement.TryBind("@DateCreated", job.DateCreated.ToDateTimeParamValue());
- statement.TryBind("@DateLastModified", job.DateLastModified.ToDateTimeParamValue());
-
- statement.TryBind("@ItemCount", job.ItemCount);
- statement.TryBind("@Id", job.Id.ToGuidParamValue());
-
- statement.MoveNext();
- }
- }, TransactionMode);
- }
- }
- }
-
- public async Task DeleteJob(string id)
- {
- if (string.IsNullOrWhiteSpace(id))
- {
- throw new ArgumentNullException("id");
- }
-
- CheckDisposed();
-
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- connection.RunInTransaction(conn =>
- {
- conn.Execute("delete from SyncJobs where Id=?", id.ToGuidParamValue());
- conn.Execute("delete from SyncJobItems where JobId=?", id);
- }, TransactionMode);
- }
- }
- }
-
- public QueryResult<SyncJob> GetJobs(SyncJobQuery query)
- {
- if (query == null)
- {
- throw new ArgumentNullException("query");
- }
-
- CheckDisposed();
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var commandText = BaseJobSelectText;
- var paramList = new List<object>();
-
- var whereClauses = new List<string>();
-
- if (query.Statuses.Length > 0)
- {
- var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
-
- whereClauses.Add(string.Format("Status in ({0})", statuses));
- }
- if (!string.IsNullOrWhiteSpace(query.TargetId))
- {
- whereClauses.Add("TargetId=?");
- paramList.Add(query.TargetId);
- }
- if (!string.IsNullOrWhiteSpace(query.ExcludeTargetIds))
- {
- var excludeIds = (query.ExcludeTargetIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- if (excludeIds.Length == 1)
- {
- whereClauses.Add("TargetId<>?");
- paramList.Add(excludeIds[0]);
- }
- else if (excludeIds.Length > 1)
- {
- whereClauses.Add("TargetId<>?");
- paramList.Add(excludeIds[0]);
- }
- }
- if (!string.IsNullOrWhiteSpace(query.UserId))
- {
- whereClauses.Add("UserId=?");
- paramList.Add(query.UserId);
- }
- if (!string.IsNullOrWhiteSpace(query.ItemId))
- {
- whereClauses.Add("ItemIds like ?");
- paramList.Add("%" + query.ItemId + "%");
- }
- if (query.SyncNewContent.HasValue)
- {
- whereClauses.Add("SyncNewContent=?");
- paramList.Add(query.SyncNewContent.Value);
- }
-
- commandText += " mainTable";
-
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- var startIndex = query.StartIndex ?? 0;
- if (startIndex > 0)
- {
- whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobs ORDER BY (Select Max(DateLastModified) from SyncJobs where TargetId=mainTable.TargetId) DESC, DateLastModified DESC LIMIT {0})",
- startIndex.ToString(_usCulture)));
- }
-
- if (whereClauses.Count > 0)
- {
- commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
- }
-
- commandText += " ORDER BY (Select Max(DateLastModified) from SyncJobs where TargetId=mainTable.TargetId) DESC, DateLastModified DESC";
-
- if (query.Limit.HasValue)
- {
- commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
- }
-
- var list = new List<SyncJob>();
- var count = connection.Query("select count (Id) from SyncJobs" + whereTextWithoutPaging, paramList.ToArray())
- .SelectScalarInt()
- .First();
-
- foreach (var row in connection.Query(commandText, paramList.ToArray()))
- {
- list.Add(GetJob(row));
- }
-
- return new QueryResult<SyncJob>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
- }
- }
- }
-
- public SyncJobItem GetJobItem(string id)
- {
- if (string.IsNullOrEmpty(id))
- {
- throw new ArgumentNullException("id");
- }
-
- CheckDisposed();
-
- var guid = new Guid(id);
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var commandText = BaseJobItemSelectText + " where Id=?";
- var paramList = new List<object>();
-
- paramList.Add(guid.ToGuidParamValue());
-
- foreach (var row in connection.Query(commandText, paramList.ToArray()))
- {
- return GetJobItem(row);
- }
-
- return null;
- }
- }
- }
-
- private QueryResult<T> GetJobItemReader<T>(SyncJobItemQuery query, string baseSelectText, Func<IReadOnlyList<IResultSetValue>, T> itemFactory)
- {
- if (query == null)
- {
- throw new ArgumentNullException("query");
- }
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var commandText = baseSelectText;
- var paramList = new List<object>();
-
- var whereClauses = new List<string>();
-
- if (!string.IsNullOrWhiteSpace(query.JobId))
- {
- whereClauses.Add("JobId=?");
- paramList.Add(query.JobId);
- }
- if (!string.IsNullOrWhiteSpace(query.ItemId))
- {
- whereClauses.Add("ItemId=?");
- paramList.Add(query.ItemId);
- }
- if (!string.IsNullOrWhiteSpace(query.TargetId))
- {
- whereClauses.Add("TargetId=?");
- paramList.Add(query.TargetId);
- }
-
- if (query.Statuses.Length > 0)
- {
- var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
-
- whereClauses.Add(string.Format("Status in ({0})", statuses));
- }
-
- var whereTextWithoutPaging = whereClauses.Count == 0 ?
- string.Empty :
- " where " + string.Join(" AND ", whereClauses.ToArray());
-
- var startIndex = query.StartIndex ?? 0;
- if (startIndex > 0)
- {
- whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM SyncJobItems ORDER BY JobItemIndex, DateCreated LIMIT {0})",
- startIndex.ToString(_usCulture)));
- }
-
- if (whereClauses.Count > 0)
- {
- commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
- }
-
- commandText += " ORDER BY JobItemIndex, DateCreated";
-
- if (query.Limit.HasValue)
- {
- commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
- }
-
- var list = new List<T>();
- var count = connection.Query("select count (Id) from SyncJobItems" + whereTextWithoutPaging, paramList.ToArray())
- .SelectScalarInt()
- .First();
-
- foreach (var row in connection.Query(commandText, paramList.ToArray()))
- {
- list.Add(itemFactory(row));
- }
-
- return new QueryResult<T>()
- {
- Items = list.ToArray(),
- TotalRecordCount = count
- };
- }
- }
- }
-
- public Dictionary<string, SyncedItemProgress> GetSyncedItemProgresses(SyncJobItemQuery query)
- {
- var result = new Dictionary<string, SyncedItemProgress>();
-
- var now = DateTime.UtcNow;
-
- using (WriteLock.Read())
- {
- using (var connection = CreateConnection(true))
- {
- var commandText = "select ItemId,Status,Progress from SyncJobItems";
- var whereClauses = new List<string>();
-
- if (!string.IsNullOrWhiteSpace(query.TargetId))
- {
- whereClauses.Add("TargetId=@TargetId");
- }
-
- if (query.Statuses.Length > 0)
- {
- var statuses = string.Join(",", query.Statuses.Select(i => "'" + i.ToString() + "'").ToArray());
-
- whereClauses.Add(string.Format("Status in ({0})", statuses));
- }
-
- if (whereClauses.Count > 0)
- {
- commandText += " where " + string.Join(" AND ", whereClauses.ToArray());
- }
-
- var statementTexts = new List<string>
- {
- commandText
- };
-
- commandText = commandText
- .Replace("select ItemId,Status,Progress from SyncJobItems", "select ItemIds,Status,Progress from SyncJobs")
- .Replace("'Synced'", "'Completed','CompletedWithError'");
-
- statementTexts.Add(commandText);
-
- var statements = connection.PrepareAll(string.Join(";", statementTexts.ToArray()))
- .ToList();
-
- using (var statement = statements[0])
- {
- if (!string.IsNullOrWhiteSpace(query.TargetId))
- {
- statement.TryBind("@TargetId", query.TargetId);
- }
-
- foreach (var row in statement.ExecuteQuery())
- {
- AddStatusResult(row, result, false);
- }
- LogQueryTime("GetSyncedItemProgresses", commandText, now);
- }
-
- now = DateTime.UtcNow;
-
- using (var statement = statements[1])
- {
- if (!string.IsNullOrWhiteSpace(query.TargetId))
- {
- statement.TryBind("@TargetId", query.TargetId);
- }
-
- foreach (var row in statement.ExecuteQuery())
- {
- AddStatusResult(row, result, true);
- }
- LogQueryTime("GetSyncedItemProgresses", commandText, now);
- }
- }
- }
-
- return result;
- }
-
- private void LogQueryTime(string methodName, string commandText, DateTime startDate)
- {
- var elapsed = (DateTime.UtcNow - startDate).TotalMilliseconds;
-
- var slowThreshold = 1000;
-
-#if DEBUG
- slowThreshold = 50;
-#endif
-
- if (elapsed >= slowThreshold)
- {
- Logger.Debug("{2} query time (slow): {0}ms. Query: {1}",
- Convert.ToInt32(elapsed),
- commandText,
- methodName);
- }
- else
- {
- //Logger.Debug("{2} query time: {0}ms. Query: {1}",
- // Convert.ToInt32(elapsed),
- // cmd.CommandText,
- // methodName);
- }
- }
-
- private void AddStatusResult(IReadOnlyList<IResultSetValue> reader, Dictionary<string, SyncedItemProgress> result, bool multipleIds)
- {
- if (reader[0].SQLiteType == SQLiteType.Null)
- {
- return;
- }
-
- var itemIds = new List<string>();
-
- var ids = reader[0].ToString();
-
- if (multipleIds)
- {
- itemIds = ids.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
- }
- else
- {
- itemIds.Add(ids);
- }
-
- if (reader[1].SQLiteType != SQLiteType.Null)
- {
- SyncJobItemStatus status;
- var statusString = reader[1].ToString();
- if (string.Equals(statusString, "Completed", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(statusString, "CompletedWithError", StringComparison.OrdinalIgnoreCase))
- {
- status = SyncJobItemStatus.Synced;
- }
- else
- {
- status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), statusString, true);
- }
-
- if (status == SyncJobItemStatus.Synced)
- {
- foreach (var itemId in itemIds)
- {
- result[itemId] = new SyncedItemProgress
- {
- Status = SyncJobItemStatus.Synced
- };
- }
- }
- else
- {
- double progress = reader[2].SQLiteType == SQLiteType.Null ? 0.0 : reader[2].ToDouble();
-
- foreach (var itemId in itemIds)
- {
- SyncedItemProgress currentStatus;
- if (!result.TryGetValue(itemId, out currentStatus) || (currentStatus.Status != SyncJobItemStatus.Synced && progress >= currentStatus.Progress))
- {
- result[itemId] = new SyncedItemProgress
- {
- Status = status,
- Progress = progress
- };
- }
- }
- }
- }
- }
-
- public QueryResult<SyncJobItem> GetJobItems(SyncJobItemQuery query)
- {
- return GetJobItemReader(query, BaseJobItemSelectText, GetJobItem);
- }
-
- public Task Create(SyncJobItem jobItem)
- {
- return InsertOrUpdate(jobItem, true);
- }
-
- public Task Update(SyncJobItem jobItem)
- {
- return InsertOrUpdate(jobItem, false);
- }
-
- private async Task InsertOrUpdate(SyncJobItem jobItem, bool insert)
- {
- if (jobItem == null)
- {
- throw new ArgumentNullException("jobItem");
- }
-
- CheckDisposed();
-
- using (WriteLock.Write())
- {
- using (var connection = CreateConnection())
- {
- string commandText;
-
- if (insert)
- {
- commandText = "insert into SyncJobItems (Id, ItemId, ItemName, MediaSourceId, JobId, TemporaryPath, OutputPath, Status, TargetId, DateCreated, Progress, AdditionalFiles, MediaSource, IsMarkedForRemoval, JobItemIndex, ItemDateModifiedTicks) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
- }
- else
- {
- // cmd
- commandText = "update SyncJobItems set ItemId=?,ItemName=?,MediaSourceId=?,JobId=?,TemporaryPath=?,OutputPath=?,Status=?,TargetId=?,DateCreated=?,Progress=?,AdditionalFiles=?,MediaSource=?,IsMarkedForRemoval=?,JobItemIndex=?,ItemDateModifiedTicks=? where Id=?";
- }
-
- var paramList = new List<object>();
- paramList.Add(jobItem.ItemId);
- paramList.Add(jobItem.ItemName);
- paramList.Add(jobItem.MediaSourceId);
- paramList.Add(jobItem.JobId);
- paramList.Add(jobItem.TemporaryPath);
- paramList.Add(jobItem.OutputPath);
- paramList.Add(jobItem.Status.ToString());
-
- paramList.Add(jobItem.TargetId);
- paramList.Add(jobItem.DateCreated.ToDateTimeParamValue());
- paramList.Add(jobItem.Progress);
- paramList.Add(_json.SerializeToString(jobItem.AdditionalFiles));
- paramList.Add(jobItem.MediaSource == null ? null : _json.SerializeToString(jobItem.MediaSource));
- paramList.Add(jobItem.IsMarkedForRemoval);
- paramList.Add(jobItem.JobItemIndex);
- paramList.Add(jobItem.ItemDateModifiedTicks);
-
- if (insert)
- {
- paramList.Insert(0, jobItem.Id.ToGuidParamValue());
- }
- else
- {
- paramList.Add(jobItem.Id.ToGuidParamValue());
- }
-
- connection.RunInTransaction(conn =>
- {
- conn.Execute(commandText, paramList.ToArray());
- }, TransactionMode);
- }
- }
- }
-
- private SyncJobItem GetJobItem(IReadOnlyList<IResultSetValue> reader)
- {
- var info = new SyncJobItem
- {
- Id = reader[0].ReadGuid().ToString("N"),
- ItemId = reader[1].ToString()
- };
-
- if (reader[2].SQLiteType != SQLiteType.Null)
- {
- info.ItemName = reader[2].ToString();
- }
-
- if (reader[3].SQLiteType != SQLiteType.Null)
- {
- info.MediaSourceId = reader[3].ToString();
- }
-
- info.JobId = reader[4].ToString();
-
- if (reader[5].SQLiteType != SQLiteType.Null)
- {
- info.TemporaryPath = reader[5].ToString();
- }
- if (reader[6].SQLiteType != SQLiteType.Null)
- {
- info.OutputPath = reader[6].ToString();
- }
-
- if (reader[7].SQLiteType != SQLiteType.Null)
- {
- info.Status = (SyncJobItemStatus)Enum.Parse(typeof(SyncJobItemStatus), reader[7].ToString(), true);
- }
-
- info.TargetId = reader[8].ToString();
-
- info.DateCreated = reader[9].ReadDateTime();
-
- if (reader[10].SQLiteType != SQLiteType.Null)
- {
- info.Progress = reader[10].ToDouble();
- }
-
- if (reader[11].SQLiteType != SQLiteType.Null)
- {
- var json = reader[11].ToString();
-
- if (!string.IsNullOrWhiteSpace(json))
- {
- info.AdditionalFiles = _json.DeserializeFromString<List<ItemFileInfo>>(json);
- }
- }
-
- if (reader[12].SQLiteType != SQLiteType.Null)
- {
- var json = reader[12].ToString();
-
- if (!string.IsNullOrWhiteSpace(json))
- {
- info.MediaSource = _json.DeserializeFromString<MediaSourceInfo>(json);
- }
- }
-
- info.IsMarkedForRemoval = reader[13].ToBool();
- info.JobItemIndex = reader[14].ToInt();
-
- if (reader[15].SQLiteType != SQLiteType.Null)
- {
- info.ItemDateModifiedTicks = reader[15].ToInt64();
- }
-
- return info;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Sync/SyncedMediaSourceProvider.cs b/Emby.Server.Implementations/Sync/SyncedMediaSourceProvider.cs
deleted file mode 100644
index 1e54885e6..000000000
--- a/Emby.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 Emby.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 == MediaBrowser.Model.MediaInfo.MediaProtocol.File)
- {
- mediaSource.ETag = item.Id;
- }
- }
-
- public Task CloseMediaSource(string liveStreamId)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/Emby.Server.Implementations/Sync/TargetDataProvider.cs b/Emby.Server.Implementations/Sync/TargetDataProvider.cs
deleted file mode 100644
index cac8f0cd8..000000000
--- a/Emby.Server.Implementations/Sync/TargetDataProvider.cs
+++ /dev/null
@@ -1,208 +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 Emby.Server.Implementations.Sync
-{
- public class TargetDataProvider : ISyncDataProvider
- {
- private readonly SyncTarget _target;
- private readonly IServerSyncProvider _provider;
-
- private readonly SemaphoreSlim _dataLock = new SemaphoreSlim(1, 1);
- private readonly SemaphoreSlim _remoteDataLock = 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 IMemoryStreamFactory _memoryStreamProvider;
-
- public TargetDataProvider(IServerSyncProvider provider, SyncTarget target, IServerApplicationHost appHost, ILogger logger, IJsonSerializer json, IFileSystem fileSystem, IApplicationPaths appPaths, IMemoryStreamFactory 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);
-
- await _remoteDataLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- 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);
- }
- }
- }
- finally
- {
- _remoteDataLock.Release();
- }
-
- 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();
-
- await _remoteDataLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- _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);
- }
- finally
- {
- _remoteDataLock.Release();
- }
- }
- }
-
- 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/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config
index 3c82e979b..7e638e171 100644
--- a/Emby.Server.Implementations/packages.config
+++ b/Emby.Server.Implementations/packages.config
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="Emby.XmlTv" version="1.0.6" targetFramework="portable45-net45+win8" />
+ <package id="Emby.XmlTv" version="1.0.7" targetFramework="portable45-net45+win8" />
<package id="MediaBrowser.Naming" version="1.0.4" targetFramework="portable45-net45+win8" />
<package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
- <package id="SQLitePCLRaw.core" version="1.1.1" targetFramework="portable45-net45+win8" />
+ <package id="SQLitePCLRaw.core" version="1.1.2" targetFramework="portable45-net45+win8" />
<package id="UniversalDetector" version="1.0.1" targetFramework="portable45-net45+win8" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.Api/ConnectService.cs b/MediaBrowser.Api/ConnectService.cs
deleted file mode 100644
index 304dc366b..000000000
--- a/MediaBrowser.Api/ConnectService.cs
+++ /dev/null
@@ -1,177 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Connect;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Connect;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api
-{
- [Route("/Users/{Id}/Connect/Link", "POST", Summary = "Creates a Connect link for a user")]
- [Authenticated(Roles = "Admin")]
- public class CreateConnectLink : IReturn<UserLinkResult>
- {
- [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string Id { get; set; }
-
- [ApiMember(Name = "ConnectUsername", Description = "Connect username", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string ConnectUsername { get; set; }
- }
-
- [Route("/Users/{Id}/Connect/Link", "DELETE", Summary = "Removes a Connect link for a user")]
- [Authenticated(Roles = "Admin")]
- public class DeleteConnectLink : IReturnVoid
- {
- [ApiMember(Name = "Id", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
- public string Id { get; set; }
- }
-
- [Route("/Connect/Invite", "POST", Summary = "Creates a Connect link for a user")]
- [Authenticated(Roles = "Admin")]
- public class CreateConnectInvite : IReturn<UserLinkResult>
- {
- [ApiMember(Name = "ConnectUsername", Description = "Connect username", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
- public string ConnectUsername { get; set; }
-
- [ApiMember(Name = "SendingUserId", Description = "Sending User Id", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
- public string SendingUserId { get; set; }
-
- [ApiMember(Name = "EnabledLibraries", Description = "EnabledLibraries", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
- public string EnabledLibraries { get; set; }
-
- [ApiMember(Name = "EnabledChannels", Description = "EnabledChannels", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
- public string EnabledChannels { get; set; }
-
- [ApiMember(Name = "EnableLiveTv", Description = "EnableLiveTv", IsRequired = true, DataType = "string", ParameterType = "body", Verb = "POST")]
- public bool EnableLiveTv { get; set; }
- }
-
-
- [Route("/Connect/Pending", "GET", Summary = "Creates a Connect link for a user")]
- [Authenticated(Roles = "Admin")]
- public class GetPendingGuests : IReturn<List<ConnectAuthorization>>
- {
- }
-
-
- [Route("/Connect/Pending", "DELETE", Summary = "Deletes a Connect link for a user")]
- [Authenticated(Roles = "Admin")]
- public class DeleteAuthorization : IReturnVoid
- {
- [ApiMember(Name = "Id", Description = "Authorization Id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
- public string Id { get; set; }
- }
-
- [Route("/Connect/Exchange", "GET", Summary = "Gets the corresponding local user from a connect user id")]
- [Authenticated]
- public class GetLocalUser : IReturn<ConnectAuthenticationExchangeResult>
- {
- [ApiMember(Name = "ConnectUserId", Description = "ConnectUserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
- public string ConnectUserId { get; set; }
- }
-
- public class ConnectService : BaseApiService
- {
- private readonly IConnectManager _connectManager;
- private readonly ISessionManager _sessionManager;
- private readonly IAuthorizationContext _authContext;
-
- public ConnectService(IConnectManager connectManager, ISessionManager sessionManager, IAuthorizationContext authContext)
- {
- _connectManager = connectManager;
- _sessionManager = sessionManager;
- _authContext = authContext;
- }
-
- public object Post(CreateConnectLink request)
- {
- return _connectManager.LinkUser(request.Id, request.ConnectUsername);
- }
-
- public object Post(CreateConnectInvite request)
- {
- var enabledLibraries = (request.EnabledLibraries ?? string.Empty)
- .Split(',')
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .ToArray();
-
- var enabledChannels = (request.EnabledChannels ?? string.Empty)
- .Split(',')
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .ToArray();
-
- return _connectManager.InviteUser(new ConnectAuthorizationRequest
- {
- ConnectUserName = request.ConnectUsername,
- SendingUserId = request.SendingUserId,
- EnabledLibraries = enabledLibraries,
- EnabledChannels = enabledChannels,
- EnableLiveTv = request.EnableLiveTv
- });
- }
-
- public void Delete(DeleteConnectLink request)
- {
- var task = _connectManager.RemoveConnect(request.Id);
-
- Task.WaitAll(task);
- }
-
- public async Task<object> Get(GetPendingGuests request)
- {
- var result = await _connectManager.GetPendingGuests().ConfigureAwait(false);
-
- return ToOptimizedResult(result);
- }
-
- public void Delete(DeleteAuthorization request)
- {
- var task = _connectManager.CancelAuthorization(request.Id);
-
- Task.WaitAll(task);
- }
-
- public async Task<object> Get(GetLocalUser request)
- {
- var user = await _connectManager.GetLocalUser(request.ConnectUserId).ConfigureAwait(false);
-
- if (user == null)
- {
- throw new ResourceNotFoundException();
- }
-
- var auth = _authContext.GetAuthorizationInfo(Request);
-
- if (string.IsNullOrWhiteSpace(auth.Client))
- {
- return ToOptimizedResult(new ConnectAuthenticationExchangeResult
- {
- AccessToken = user.ConnectAccessKey,
- LocalUserId = user.Id.ToString("N")
- });
- }
-
- var session = await _sessionManager.CreateNewSession(new AuthenticationRequest
- {
- App = auth.Client,
- AppVersion = auth.Version,
- DeviceId = auth.DeviceId,
- DeviceName = auth.Device,
- RemoteEndPoint = Request.RemoteIp,
- Username = user.Name,
- UserId = user.Id.ToString("N")
-
- }).ConfigureAwait(false);
-
- return ToOptimizedResult(new ConnectAuthenticationExchangeResult
- {
- AccessToken = session.AccessToken,
- LocalUserId = session.User.Id
- });
- }
- }
-}
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index a3d43a3f9..cadf52be1 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -237,13 +237,6 @@ namespace MediaBrowser.Api
item.Name = request.Name;
item.ForcedSortName = request.ForcedSortName;
- var hasBudget = item as IHasBudget;
- if (hasBudget != null)
- {
- hasBudget.Budget = request.Budget;
- hasBudget.Revenue = request.Revenue;
- }
-
item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle;
item.CriticRating = request.CriticRating;
@@ -401,10 +394,21 @@ namespace MediaBrowser.Api
var series = item as Series;
if (series != null)
{
- series.Status = request.SeriesStatus;
+ series.Status = GetSeriesStatus(request);
series.AirDays = request.AirDays;
series.AirTime = request.AirTime;
}
}
+
+ private SeriesStatus? GetSeriesStatus(BaseItemDto item)
+ {
+ if (string.IsNullOrEmpty(item.Status))
+ {
+ return null;
+ }
+
+ return (SeriesStatus)Enum.Parse(typeof(SeriesStatus), item.Status, true);
+
+ }
}
}
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 909fc0623..dc4e57155 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -791,7 +791,7 @@ namespace MediaBrowser.Api.LiveTv
ProviderChannels = providerChannels.Select(i => new NameIdPair
{
Name = i.Name,
- Id = string.IsNullOrWhiteSpace(i.TunerChannelId) ? i.Id : i.TunerChannelId
+ Id = i.Id
}).ToList(),
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index 7be04d892..55ef65311 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -51,7 +51,6 @@
<Compile Include="BasePeriodicWebSocketListener.cs" />
<Compile Include="BrandingService.cs" />
<Compile Include="ChannelService.cs" />
- <Compile Include="ConnectService.cs" />
<Compile Include="Devices\DeviceService.cs" />
<Compile Include="Dlna\DlnaServerService.cs" />
<Compile Include="Dlna\DlnaService.cs" />
@@ -59,6 +58,7 @@
<Compile Include="IHasDtoOptions.cs" />
<Compile Include="Playback\MediaInfoService.cs" />
<Compile Include="Playback\TranscodingThrottler.cs" />
+ <Compile Include="Playback\UniversalAudioService.cs" />
<Compile Include="PlaylistService.cs" />
<Compile Include="Reports\Activities\ReportActivitiesBuilder.cs" />
<Compile Include="Reports\Common\HeaderActivitiesMetadata.cs" />
@@ -172,10 +172,6 @@
<Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
- <ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj">
- <Project>{0bd82fa6-eb8a-4452-8af5-74f9c3849451}</Project>
- <Name>MediaBrowser.MediaEncoding</Name>
- </ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index d1c3de427..e1559cabf 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -22,7 +22,6 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Net;
-using MediaBrowser.MediaEncoding.Encoder;
using MediaBrowser.Model.Diagnostics;
namespace MediaBrowser.Api.Playback
@@ -126,14 +125,10 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the output file path.
/// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- private string GetOutputFilePath(StreamState state)
+ private string GetOutputFilePath(StreamState state, string outputFileExtension)
{
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
- var outputFileExtension = GetOutputFileExtension(state);
-
var data = GetCommandLineArguments("dummy\\dummy", state, false);
data += "-" + (state.Request.DeviceId ?? string.Empty);
@@ -802,7 +797,7 @@ namespace MediaBrowser.Api.Playback
{
state.User = UserManager.GetUserById(auth.UserId);
}
-
+
//if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 ||
// (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 ||
// (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1)
@@ -878,9 +873,14 @@ namespace MediaBrowser.Api.Playback
if (string.IsNullOrEmpty(container))
{
+ container = request.Container;
+ }
+
+ if (string.IsNullOrEmpty(container))
+ {
container = request.Static ?
state.InputContainer :
- (Path.GetExtension(GetOutputFilePath(state)) ?? string.Empty).TrimStart('.');
+ GetOutputFileExtension(state);
}
state.OutputContainer = (container ?? string.Empty).TrimStart('.');
@@ -923,7 +923,10 @@ namespace MediaBrowser.Api.Playback
ApplyDeviceProfileSettings(state);
}
- state.OutputFilePath = GetOutputFilePath(state);
+ var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
+ ? GetOutputFileExtension(state)
+ : ("." + state.OutputContainer);
+ state.OutputFilePath = GetOutputFilePath(state, ext);
return state;
}
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index cfd7471c4..1074a8bc1 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -18,9 +18,6 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Services;
using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index fcb8c34f3..ed8449b83 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -127,7 +127,7 @@ namespace MediaBrowser.Api.Playback
SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate,
request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex,
- request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId);
+ request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId, true, true, true);
}
else
{
@@ -146,7 +146,7 @@ namespace MediaBrowser.Api.Playback
Task.WaitAll(task);
}
- public async Task<object> Post(GetPostedPlaybackInfo request)
+ public async Task<PlaybackInfoResponse> GetPlaybackInfo(GetPostedPlaybackInfo request)
{
var authInfo = _authContext.GetAuthorizationInfo(Request);
@@ -169,10 +169,17 @@ namespace MediaBrowser.Api.Playback
{
var mediaSourceId = request.MediaSourceId;
- SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId);
+ SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId, request.EnableDirectPlay, request.EnableDirectStream, request.EnableTranscoding);
}
- return ToOptimizedResult(info);
+ return info;
+ }
+
+ public async Task<object> Post(GetPostedPlaybackInfo request)
+ {
+ var result = await GetPlaybackInfo(request).ConfigureAwait(false);
+
+ return ToOptimizedResult(result);
}
private T Clone<T>(T obj)
@@ -244,13 +251,16 @@ namespace MediaBrowser.Api.Playback
int? audioStreamIndex,
int? subtitleStreamIndex,
int? maxAudioChannels,
- string userId)
+ string userId,
+ bool enableDirectPlay,
+ bool enableDirectStream,
+ bool enableTranscoding)
{
var item = _libraryManager.GetItemById(itemId);
foreach (var mediaSource in result.MediaSources)
{
- SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, maxAudioChannels, result.PlaySessionId, userId);
+ SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, maxAudioChannels, result.PlaySessionId, userId, enableDirectPlay, enableDirectStream, enableTranscoding);
}
SortMediaSources(result, maxBitrate);
@@ -267,7 +277,10 @@ namespace MediaBrowser.Api.Playback
int? subtitleStreamIndex,
int? maxAudioChannels,
string playSessionId,
- string userId)
+ string userId,
+ bool enableDirectPlay,
+ bool enableDirectStream,
+ bool enableTranscoding)
{
var streamBuilder = new StreamBuilder(_mediaEncoder, Logger);
@@ -290,6 +303,19 @@ namespace MediaBrowser.Api.Playback
var user = _userManager.GetUserById(userId);
+ if (!enableDirectPlay)
+ {
+ mediaSource.SupportsDirectPlay = false;
+ }
+ if (!enableDirectStream)
+ {
+ mediaSource.SupportsDirectStream = false;
+ }
+ if (!enableTranscoding)
+ {
+ mediaSource.SupportsTranscoding = false;
+ }
+
if (mediaSource.SupportsDirectPlay)
{
var supportsDirectStream = mediaSource.SupportsDirectStream;
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
index a0ab90664..04825c7a5 100644
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
@@ -26,8 +26,6 @@ namespace MediaBrowser.Api.Playback.Progressive
[Route("/Audio/{Id}/stream", "HEAD", Summary = "Gets an audio stream")]
public class GetAudioStream : StreamRequest
{
- [ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public string Container { get; set; }
}
/// <summary>
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 3c614567b..bc600d3ea 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -41,6 +41,7 @@ namespace MediaBrowser.Api.Playback.Progressive
[Route("/Videos/{Id}/stream.wtv", "GET")]
[Route("/Videos/{Id}/stream.mov", "GET")]
[Route("/Videos/{Id}/stream.iso", "GET")]
+ [Route("/Videos/{Id}/stream.flv", "GET")]
[Route("/Videos/{Id}/stream", "GET")]
[Route("/Videos/{Id}/stream.ts", "HEAD")]
[Route("/Videos/{Id}/stream.webm", "HEAD")]
@@ -59,6 +60,7 @@ namespace MediaBrowser.Api.Playback.Progressive
[Route("/Videos/{Id}/stream.m2ts", "HEAD")]
[Route("/Videos/{Id}/stream.mov", "HEAD")]
[Route("/Videos/{Id}/stream.iso", "HEAD")]
+ [Route("/Videos/{Id}/stream.flv", "HEAD")]
[Route("/Videos/{Id}/stream", "HEAD")]
public class GetVideoStream : VideoStreamRequest
{
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index 6bdb30890..f223c99ef 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -22,6 +22,9 @@ namespace MediaBrowser.Api.Playback
[ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string DeviceId { get; set; }
+ [ApiMember(Name = "Container", Description = "Container", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Container { get; set; }
+
/// <summary>
/// Gets or sets the audio codec.
/// </summary>
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index 4fb936340..912d60889 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -13,7 +13,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
-using MediaBrowser.MediaEncoding.Encoder;
+using MediaBrowser.Controller.MediaEncoding;
namespace MediaBrowser.Api.Playback
{
diff --git a/MediaBrowser.Api/Playback/UniversalAudioService.cs b/MediaBrowser.Api/Playback/UniversalAudioService.cs
new file mode 100644
index 000000000..e50d0f2c5
--- /dev/null
+++ b/MediaBrowser.Api/Playback/UniversalAudioService.cs
@@ -0,0 +1,248 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using MediaBrowser.Api.Playback.Hls;
+using MediaBrowser.Api.Playback.Progressive;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Devices;
+using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Services;
+
+namespace MediaBrowser.Api.Playback
+{
+ public class BaseUniversalRequest
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+
+ [ApiMember(Name = "MediaSourceId", Description = "The media version id, if playing an alternate version", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string MediaSourceId { get; set; }
+
+ [ApiMember(Name = "DeviceId", Description = "The device id of the client requesting. Used to stop encoding processes when needed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string DeviceId { get; set; }
+
+ public string Token { get; set; }
+
+ public string UserId { get; set; }
+ public string AudioCodec { get; set; }
+ public string Container { get; set; }
+
+ public int? MaxAudioChannels { get; set; }
+
+ public long? MaxStreamingBitrate { get; set; }
+
+ [ApiMember(Name = "StartTimeTicks", Description = "Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public long? StartTimeTicks { get; set; }
+ }
+
+ [Route("/Audio/{Id}/universal.{Container}", "GET", Summary = "Gets an audio stream")]
+ [Route("/Audio/{Id}/universal", "GET", Summary = "Gets an audio stream")]
+ [Route("/Audio/{Id}/universal.{Container}", "HEAD", Summary = "Gets an audio stream")]
+ [Route("/Audio/{Id}/universal", "HEAD", Summary = "Gets an audio stream")]
+ public class GetUniversalAudioStream : BaseUniversalRequest
+ {
+ }
+
+ //[Authenticated]
+ public class UniversalAudioService : BaseApiService
+ {
+ public UniversalAudioService(IServerConfigurationManager serverConfigurationManager, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, IDeviceManager deviceManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext, IImageProcessor imageProcessor, INetworkManager networkManager)
+ {
+ ServerConfigurationManager = serverConfigurationManager;
+ UserManager = userManager;
+ LibraryManager = libraryManager;
+ IsoManager = isoManager;
+ MediaEncoder = mediaEncoder;
+ FileSystem = fileSystem;
+ DlnaManager = dlnaManager;
+ DeviceManager = deviceManager;
+ SubtitleEncoder = subtitleEncoder;
+ MediaSourceManager = mediaSourceManager;
+ ZipClient = zipClient;
+ JsonSerializer = jsonSerializer;
+ AuthorizationContext = authorizationContext;
+ ImageProcessor = imageProcessor;
+ NetworkManager = networkManager;
+ }
+
+ protected IServerConfigurationManager ServerConfigurationManager { get; private set; }
+ protected IUserManager UserManager { get; private set; }
+ protected ILibraryManager LibraryManager { get; private set; }
+ protected IIsoManager IsoManager { get; private set; }
+ protected IMediaEncoder MediaEncoder { get; private set; }
+ protected IFileSystem FileSystem { get; private set; }
+ protected IDlnaManager DlnaManager { get; private set; }
+ protected IDeviceManager DeviceManager { get; private set; }
+ protected ISubtitleEncoder SubtitleEncoder { get; private set; }
+ protected IMediaSourceManager MediaSourceManager { get; private set; }
+ protected IZipClient ZipClient { get; private set; }
+ protected IJsonSerializer JsonSerializer { get; private set; }
+ protected IAuthorizationContext AuthorizationContext { get; private set; }
+ protected IImageProcessor ImageProcessor { get; private set; }
+ protected INetworkManager NetworkManager { get; private set; }
+
+ public Task<object> Get(GetUniversalAudioStream request)
+ {
+ return GetUniversalStream(request, false);
+ }
+
+ public Task<object> Head(GetUniversalAudioStream request)
+ {
+ return GetUniversalStream(request, true);
+ }
+
+ private DeviceProfile GetDeviceProfile(GetUniversalAudioStream request)
+ {
+ var deviceProfile = new DeviceProfile();
+
+ var directPlayProfiles = new List<DirectPlayProfile>();
+
+ directPlayProfiles.Add(new DirectPlayProfile
+ {
+ Type = DlnaProfileType.Audio,
+ Container = request.Container
+ });
+
+ deviceProfile.DirectPlayProfiles = directPlayProfiles.ToArray();
+
+ deviceProfile.TranscodingProfiles = new[]
+ {
+ new TranscodingProfile
+ {
+ Type = DlnaProfileType.Audio,
+ Context = EncodingContext.Streaming,
+ Container = "ts",
+ AudioCodec = "aac",
+ Protocol = "hls"
+ }
+ };
+
+ return deviceProfile;
+ }
+
+ private async Task<object> GetUniversalStream(GetUniversalAudioStream request, bool isHeadRequest)
+ {
+ var deviceProfile = GetDeviceProfile(request);
+
+ AuthorizationContext.GetAuthorizationInfo(Request).DeviceId = request.DeviceId;
+
+ var mediaInfoService = new MediaInfoService(MediaSourceManager, DeviceManager, LibraryManager, ServerConfigurationManager, NetworkManager, MediaEncoder, UserManager, JsonSerializer, AuthorizationContext)
+ {
+ Request = Request
+ };
+
+ var playbackInfoResult = await mediaInfoService.GetPlaybackInfo(new GetPostedPlaybackInfo
+ {
+ Id = request.Id,
+ MaxAudioChannels = request.MaxAudioChannels,
+ MaxStreamingBitrate = request.MaxStreamingBitrate,
+ StartTimeTicks = request.StartTimeTicks,
+ UserId = request.UserId,
+ DeviceProfile = deviceProfile,
+ MediaSourceId = request.MediaSourceId
+
+ }).ConfigureAwait(false);
+
+ var mediaSource = playbackInfoResult.MediaSources[0];
+
+ var isStatic = mediaSource.SupportsDirectStream;
+
+ if (!isStatic && string.Equals(mediaSource.TranscodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+ {
+ var service = new DynamicHlsService(ServerConfigurationManager,
+ UserManager,
+ LibraryManager,
+ IsoManager,
+ MediaEncoder,
+ FileSystem,
+ DlnaManager,
+ SubtitleEncoder,
+ DeviceManager,
+ MediaSourceManager,
+ ZipClient,
+ JsonSerializer,
+ AuthorizationContext,
+ NetworkManager)
+ {
+ Request = Request
+ };
+
+ var transcodingProfile = deviceProfile.TranscodingProfiles[0];
+
+ var newRequest = new GetMasterHlsAudioPlaylist
+ {
+ AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
+ AudioCodec = transcodingProfile.AudioCodec,
+ Container = ".m3u8",
+ DeviceId = request.DeviceId,
+ Id = request.Id,
+ MaxAudioChannels = request.MaxAudioChannels,
+ MediaSourceId = mediaSource.Id,
+ PlaySessionId = playbackInfoResult.PlaySessionId,
+ StartTimeTicks = request.StartTimeTicks,
+ Static = isStatic
+ };
+
+ if (isHeadRequest)
+ {
+ return await service.Head(newRequest).ConfigureAwait(false);
+ }
+ return await service.Get(newRequest).ConfigureAwait(false);
+ }
+ else
+ {
+ var service = new AudioService(ServerConfigurationManager,
+ UserManager,
+ LibraryManager,
+ IsoManager,
+ MediaEncoder,
+ FileSystem,
+ DlnaManager,
+ SubtitleEncoder,
+ DeviceManager,
+ MediaSourceManager,
+ ZipClient,
+ JsonSerializer,
+ AuthorizationContext,
+ ImageProcessor)
+ {
+ Request = Request
+ };
+
+ var newRequest = new GetAudioStream
+ {
+ AudioBitRate = isStatic ? (int?)null : Convert.ToInt32(Math.Min(request.MaxStreamingBitrate ?? 192000, int.MaxValue)),
+ //AudioCodec = request.AudioCodec,
+ Container = isStatic ? null : ("." + mediaSource.TranscodingContainer),
+ DeviceId = request.DeviceId,
+ Id = request.Id,
+ MaxAudioChannels = request.MaxAudioChannels,
+ MediaSourceId = mediaSource.Id,
+ PlaySessionId = playbackInfoResult.PlaySessionId,
+ StartTimeTicks = request.StartTimeTicks,
+ Static = isStatic
+ };
+
+ if (isHeadRequest)
+ {
+ return await service.Head(newRequest).ConfigureAwait(false);
+ }
+ return await service.Get(newRequest).ConfigureAwait(false);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Reports/ReportsService.cs b/MediaBrowser.Api/Reports/ReportsService.cs
index cb275fa29..5e13c1653 100644
--- a/MediaBrowser.Api/Reports/ReportsService.cs
+++ b/MediaBrowser.Api/Reports/ReportsService.cs
@@ -317,12 +317,6 @@ namespace MediaBrowser.Api.Reports
query.MaxParentalRating = _localization.GetRatingLevel(request.MaxOfficialRating);
}
- // Albums
- if (!string.IsNullOrEmpty(request.Albums))
- {
- query.AlbumNames = request.Albums.Split('|');
- }
-
return query;
}
diff --git a/MediaBrowser.Api/Sync/SyncHelper.cs b/MediaBrowser.Api/Sync/SyncHelper.cs
index 60df2bb1e..116cd8060 100644
--- a/MediaBrowser.Api/Sync/SyncHelper.cs
+++ b/MediaBrowser.Api/Sync/SyncHelper.cs
@@ -37,7 +37,7 @@ namespace MediaBrowser.Api.Sync
options.Add(SyncJobOption.ItemLimit);
break;
}
- if (item.IsFolderItem && !item.IsMusicGenre && !item.IsArtist && !item.IsType("musicalbum") && !item.IsGameGenre)
+ if ((item.IsFolder ?? false) && !item.IsMusicGenre && !item.IsArtist && !item.IsType("musicalbum") && !item.IsGameGenre)
{
options.Add(SyncJobOption.Quality);
options.Add(SyncJobOption.Profile);
@@ -57,7 +57,7 @@ namespace MediaBrowser.Api.Sync
{
if (item.SupportsSync ?? false)
{
- if (item.IsFolderItem || item.IsGameGenre || item.IsMusicGenre || item.IsGenre || item.IsArtist || item.IsStudio || item.IsPerson)
+ if ((item.IsFolder ?? false) || item.IsGameGenre || item.IsMusicGenre || item.IsGenre || item.IsArtist || item.IsStudio || item.IsPerson)
{
options.Add(SyncJobOption.SyncNewContent);
options.Add(SyncJobOption.ItemLimit);
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 5b939244e..c1cc1555d 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -245,25 +245,15 @@ namespace MediaBrowser.Api.UserLibrary
User user = null;
BaseItem parentItem;
- List<BaseItem> libraryItems = null;
if (!string.IsNullOrWhiteSpace(request.UserId))
{
user = UserManager.GetUserById(request.UserId);
parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
-
- if (RequiresLibraryItems(request, dtoOptions))
- {
- libraryItems = user.RootFolder.GetRecursiveChildren(user).ToList();
- }
}
else
{
parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId);
- if (RequiresLibraryItems(request, dtoOptions))
- {
- libraryItems = LibraryManager.RootFolder.GetRecursiveChildren().ToList();
- }
}
IEnumerable<BaseItem> items;
@@ -307,8 +297,6 @@ namespace MediaBrowser.Api.UserLibrary
var filteredItems = FilterItems(request, extractedItems, user);
- filteredItems = FilterByLibraryItems(request, filteredItems.Cast<IItemByName>(), user, libraryItems).Cast<BaseItem>();
-
filteredItems = LibraryManager.Sort(filteredItems, user, request.GetOrderBy(), request.SortOrder ?? SortOrder.Ascending);
var ibnItemsArray = filteredItems.ToList();
@@ -334,15 +322,7 @@ namespace MediaBrowser.Api.UserLibrary
}
- IEnumerable<Tuple<BaseItem, List<BaseItem>>> tuples;
- if (dtoOptions.Fields.Contains(ItemFields.ItemCounts))
- {
- tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, ((IItemByName)i).GetTaggedItems(libraryItems).ToList()));
- }
- else
- {
- tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>()));
- }
+ var tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>()));
var syncProgess = DtoService.GetSyncedItemProgress(dtoOptions);
var dtos = tuples.Select(i => DtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, syncProgess, user));
@@ -352,52 +332,6 @@ namespace MediaBrowser.Api.UserLibrary
return result;
}
- private bool RequiresLibraryItems(GetItemsByName request, DtoOptions options)
- {
- var filters = request.GetFilters().ToList();
-
- if (filters.Contains(ItemFilter.IsPlayed))
- {
- return true;
- }
-
- if (filters.Contains(ItemFilter.IsUnplayed))
- {
- return true;
- }
-
- if (request.IsPlayed.HasValue)
- {
- return true;
- }
-
- return options.Fields.Contains(ItemFields.ItemCounts);
- }
-
- private IEnumerable<IItemByName> FilterByLibraryItems(GetItemsByName request, IEnumerable<IItemByName> items, User user, IEnumerable<BaseItem> libraryItems)
- {
- var filters = request.GetFilters().ToList();
-
- if (filters.Contains(ItemFilter.IsPlayed))
- {
- items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)));
- }
-
- if (filters.Contains(ItemFilter.IsUnplayed))
- {
- items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsUnplayed(user)));
- }
-
- if (request.IsPlayed.HasValue)
- {
- var val = request.IsPlayed.Value;
-
- items = items.Where(i => i.GetTaggedItems(libraryItems).All(l => l.IsPlayed(user)) == val);
- }
-
- return items;
- }
-
/// <summary>
/// Filters the items.
/// </summary>
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
index 0594691a2..1acbce6db 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
@@ -277,6 +277,8 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "Albums", Description = "Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Albums { get; set; }
+ public string AlbumIds { get; set; }
+
/// <summary>
/// Gets or sets the item ids.
/// </summary>
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index 8300beac3..5d267d059 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Services;
@@ -359,15 +360,30 @@ namespace MediaBrowser.Api.UserLibrary
}
// ExcludeArtistIds
- if (!string.IsNullOrEmpty(request.ExcludeArtistIds))
+ if (!string.IsNullOrWhiteSpace(request.ExcludeArtistIds))
{
query.ExcludeArtistIds = request.ExcludeArtistIds.Split('|');
}
+ if (!string.IsNullOrWhiteSpace(request.AlbumIds))
+ {
+ query.AlbumIds = request.AlbumIds.Split('|');
+ }
+
// Albums
if (!string.IsNullOrEmpty(request.Albums))
{
- query.AlbumNames = request.Albums.Split('|');
+ query.AlbumIds = request.Albums.Split('|').Select(i =>
+ {
+ return _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
+ Name = i,
+ Limit = 1
+
+ }).Select(album => album.Id.ToString("N")).FirstOrDefault();
+
+ }).ToArray();
}
// Studios
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 08aba90d2..32f3a1f00 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -360,7 +360,8 @@ namespace MediaBrowser.Api.UserLibrary
var currentUser = user;
var dtos = series
- .GetRecursiveChildren(i => i is Episode && i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
+ .GetEpisodes(user)
+ .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0)
.OrderBy(i =>
{
if (i.PremiereDate.HasValue)
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 4cc6a7c7e..59b5a3869 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -46,6 +46,11 @@ namespace MediaBrowser.Controller.Entities.Audio
AlbumArtists = new List<string>();
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ return 1;
+ }
+
[IgnoreDataMember]
public override bool SupportsPlayedStatus
{
diff --git a/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs b/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
index 8c820d367..cdb6f3f61 100644
--- a/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
+++ b/MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
@@ -12,5 +12,10 @@ namespace MediaBrowser.Controller.Entities.Audio
return true;
}
}
+
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ return null;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index ffdbba6f2..6ad38033a 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -119,6 +119,11 @@ namespace MediaBrowser.Controller.Entities.Audio
return Tracks;
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ return 1;
+ }
+
public override List<string> GetUserDataKeys()
{
var list = base.GetUserDataKeys();
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 37631bbe8..20b2529c0 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -68,6 +68,11 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ return 1;
+ }
+
public override bool CanDelete()
{
return !IsAccessedByName;
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index bbe1a54a4..74679b474 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -63,6 +63,11 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ return 1;
+ }
+
public override bool CanDelete()
{
return false;
diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs
index c15cae8b1..8b1c338f1 100644
--- a/MediaBrowser.Controller/Entities/AudioBook.cs
+++ b/MediaBrowser.Controller/Entities/AudioBook.cs
@@ -47,6 +47,11 @@ namespace MediaBrowser.Controller.Entities
return SeriesPresentationUniqueKey;
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ return null;
+ }
+
[IgnoreDataMember]
public override bool EnableRefreshOnDateModifiedChange
{
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index cab7588f0..52e150aa4 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -156,7 +156,7 @@ namespace MediaBrowser.Controller.Entities
{
if (SupportsIsInMixedFolderDetection)
{
-
+
}
return IsInMixedFolder;
@@ -1260,6 +1260,11 @@ namespace MediaBrowser.Controller.Entities
get { return null; }
}
+ public virtual double? GetDefaultPrimaryImageAspectRatio()
+ {
+ return null;
+ }
+
public virtual string CreatePresentationUniqueKey()
{
return Id.ToString("N");
@@ -2073,9 +2078,31 @@ namespace MediaBrowser.Controller.Entities
/// Gets the file system path to delete when the item is to be deleted
/// </summary>
/// <returns></returns>
- public virtual IEnumerable<string> GetDeletePaths()
+ public virtual IEnumerable<FileSystemMetadata> GetDeletePaths()
{
- return new[] { Path };
+ return new[] {
+ new FileSystemMetadata
+ {
+ FullName = Path,
+ IsDirectory = IsFolder
+ }
+ }.Concat(GetLocalMetadataFilesToDelete());
+ }
+
+ protected List<FileSystemMetadata> GetLocalMetadataFilesToDelete()
+ {
+ if (IsFolder || !IsInMixedFolder)
+ {
+ return new List<FileSystemMetadata>();
+ }
+
+ var filename = System.IO.Path.GetFileNameWithoutExtension(Path);
+ var extensions = new[] { ".nfo", ".xml", ".srt" }.ToList();
+ extensions.AddRange(SupportedImageExtensionsList);
+
+ return FileSystem.GetFiles(System.IO.Path.GetDirectoryName(Path))
+ .Where(i => extensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase) && System.IO.Path.GetFileNameWithoutExtension(i.FullName).StartsWith(filename, StringComparison.OrdinalIgnoreCase))
+ .ToList();
}
public bool AllowsMultipleImages(ImageType type)
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index 8bfb8be99..d19552c07 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities
@@ -97,11 +98,17 @@ namespace MediaBrowser.Controller.Entities
return list;
}
- public override IEnumerable<string> GetDeletePaths()
+ public override IEnumerable<FileSystemMetadata> GetDeletePaths()
{
if (!DetectIsInMixedFolder())
{
- return new[] { System.IO.Path.GetDirectoryName(Path) };
+ return new[] {
+ new FileSystemMetadata
+ {
+ FullName = System.IO.Path.GetDirectoryName(Path),
+ IsDirectory = true
+ }
+ };
}
return base.GetDeletePaths();
diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs
index 888e2080d..4c033dc00 100644
--- a/MediaBrowser.Controller/Entities/IHasImages.cs
+++ b/MediaBrowser.Controller/Entities/IHasImages.cs
@@ -204,6 +204,8 @@ namespace MediaBrowser.Controller.Entities
/// <param name="image">The image.</param>
/// <param name="index">The index.</param>
void SetImage(ItemImageInfo image, int index);
+
+ double? GetDefaultPrimaryImageAspectRatio();
}
public static class HasImagesExtensions
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index f03531270..ea4d60a44 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -141,7 +141,7 @@ namespace MediaBrowser.Controller.Entities
public string ExternalSeriesId { get; set; }
public string ExternalId { get; set; }
- public string[] AlbumNames { get; set; }
+ public string[] AlbumIds { get; set; }
public string[] ArtistIds { get; set; }
public string[] ExcludeArtistIds { get; set; }
public string AncestorWithPresentationUniqueKey { get; set; }
@@ -202,7 +202,7 @@ namespace MediaBrowser.Controller.Entities
EnableTotalRecordCount = true;
DtoOptions = new DtoOptions();
- AlbumNames = new string[] { };
+ AlbumIds = new string[] { };
ArtistIds = new string[] { };
ExcludeArtistIds = new string[] { };
ExcludeProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 3e6c88a85..030831717 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -57,6 +57,14 @@ namespace MediaBrowser.Controller.Entities.Movies
return config.BlockUnratedItems.Contains(UnratedItem.Movie);
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ double value = 2;
+ value /= 3;
+
+ return value;
+ }
+
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Movie;
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index ec04879b5..de465b2f5 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -70,6 +70,14 @@ namespace MediaBrowser.Controller.Entities.Movies
set { TmdbCollectionName = value; }
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ double value = 2;
+ value /= 3;
+
+ return value;
+ }
+
[IgnoreDataMember]
protected override bool SupportsIsInMixedFolderDetection
{
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index 0c36442af..ee1aea938 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -38,6 +38,14 @@ namespace MediaBrowser.Controller.Entities
return GetItemLookupInfo<PersonLookupInfo>();
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ double value = 2;
+ value /= 3;
+
+ return value;
+ }
+
public IEnumerable<BaseItem> GetTaggedItems(InternalItemsQuery query)
{
query.PersonIds = new[] { Id.ToString("N") };
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index dbd9d1cef..b8ad691a9 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -57,6 +57,14 @@ namespace MediaBrowser.Controller.Entities
}
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ double value = 16;
+ value /= 9;
+
+ return value;
+ }
+
public override bool CanDelete()
{
return false;
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index e6ebcb7fd..31bf8d28b 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Controller.Entities.TV
@@ -114,6 +115,14 @@ namespace MediaBrowser.Controller.Entities.TV
{
return false;
}
+ }
+
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ double value = 16;
+ value /= 9;
+
+ return value;
}
public override List<string> GetUserDataKeys()
@@ -311,10 +320,16 @@ namespace MediaBrowser.Controller.Entities.TV
return list;
}
- public override IEnumerable<string> GetDeletePaths()
- {
- return new[] { Path };
- }
+ public override IEnumerable<FileSystemMetadata> GetDeletePaths()
+ {
+ return new[] {
+ new FileSystemMetadata
+ {
+ FullName = Path,
+ IsDirectory = IsFolder
+ }
+ }.Concat(GetLocalMetadataFilesToDelete());
+ }
public override UnratedItem GetBlockUnratedType()
{
@@ -330,7 +345,6 @@ namespace MediaBrowser.Controller.Entities.TV
if (series != null)
{
id.SeriesProviderIds = series.ProviderIds;
- id.AnimeSeriesIndex = series.AnimeSeriesIndex;
}
id.IsMissingEpisode = IsMissingEpisode;
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index f2a6586e2..be268782d 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -54,6 +54,14 @@ namespace MediaBrowser.Controller.Entities.TV
[IgnoreDataMember]
public string SeriesSortName { get; set; }
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ double value = 2;
+ value /= 3;
+
+ return value;
+ }
+
public string FindSeriesSortName()
{
var series = Series;
@@ -243,7 +251,6 @@ namespace MediaBrowser.Controller.Entities.TV
if (series != null)
{
id.SeriesProviderIds = series.ProviderIds;
- id.AnimeSeriesIndex = series.AnimeSeriesIndex;
}
return id;
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index eea11b167..7641c9523 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -19,8 +19,6 @@ namespace MediaBrowser.Controller.Entities.TV
/// </summary>
public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer
{
- public int? AnimeSeriesIndex { get; set; }
-
public Series()
{
AirDays = new List<DayOfWeek>();
@@ -93,6 +91,14 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ double value = 2;
+ value /= 3;
+
+ return value;
+ }
+
public override string CreatePresentationUniqueKey()
{
if (LibraryManager.GetLibraryOptions(this).EnableAutomaticSeriesGrouping)
@@ -546,8 +552,6 @@ namespace MediaBrowser.Controller.Entities.TV
{
var info = GetItemLookupInfo<SeriesInfo>();
- info.AnimeSeriesIndex = AnimeSeriesIndex;
-
return info;
}
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index dd6d8a999..b4a142a8e 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -29,6 +29,14 @@ namespace MediaBrowser.Controller.Entities
get { return TrailerTypes.Contains(TrailerType.LocalTrailer); }
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ double value = 2;
+ value /= 3;
+
+ return value;
+ }
+
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.Trailer;
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 5ac5843d7..fb00937fb 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -20,11 +20,6 @@ namespace MediaBrowser.Controller.Entities
public static ITVSeriesManager TVSeriesManager;
public static IPlaylistManager PlaylistManager;
- public bool ContainsDynamicCategories(User user)
- {
- return true;
- }
-
public override IEnumerable<Guid> GetIdsForAncestorQuery()
{
var list = new List<Guid>();
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 6e0f4ada9..f879d0fd8 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -1719,53 +1719,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- // Artists
- if (query.ArtistIds.Length > 0)
- {
- var audio = item as IHasArtist;
-
- //if (!(audio != null && query.ArtistNames.Any(audio.HasAnyArtist)))
- //{
- // return false;
- //}
- }
-
- // Albums
- if (query.AlbumNames.Length > 0)
- {
- var audio = item as Audio.Audio;
-
- if (audio != null)
- {
- if (!query.AlbumNames.Any(a => string.Equals(a, audio.Album, StringComparison.OrdinalIgnoreCase)))
- {
- return false;
- }
- }
-
- var album = item as MusicAlbum;
-
- if (album != null)
- {
- if (!query.AlbumNames.Any(a => string.Equals(a, album.Name, StringComparison.OrdinalIgnoreCase)))
- {
- return false;
- }
- }
-
- var musicVideo = item as MusicVideo;
-
- if (musicVideo != null)
- {
- if (!query.AlbumNames.Any(a => string.Equals(a, musicVideo.Album, StringComparison.OrdinalIgnoreCase)))
- {
- return false;
- }
- }
-
- return false;
- }
-
return true;
}
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index fb9c3d213..78f907d61 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -477,11 +477,17 @@ namespace MediaBrowser.Controller.Entities
}
}
- public override IEnumerable<string> GetDeletePaths()
+ public override IEnumerable<FileSystemMetadata> GetDeletePaths()
{
if (!DetectIsInMixedFolder())
{
- return new[] { ContainingFolderPath };
+ return new[] {
+ new FileSystemMetadata
+ {
+ FullName = System.IO.Path.GetDirectoryName(Path),
+ IsDirectory = true
+ }
+ };
}
return base.GetDeletePaths();
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 33cd4f3d1..ebebe71a3 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -62,8 +62,6 @@ namespace MediaBrowser.Controller.Library
/// <returns>BaseItem.</returns>
BaseItem FindByPath(string path, bool? isFolder);
- Guid? FindIdByPath(string path, bool? isFolder);
-
/// <summary>
/// Gets the artist.
/// </summary>
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
index 89d035649..5615649c2 100644
--- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs
+++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
@@ -1,5 +1,4 @@
-using System;
-using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using System.Collections.Generic;
using System.Threading;
@@ -23,7 +22,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets the channels.
/// </summary>
/// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns>
- Task<IEnumerable<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
+ Task<List<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
/// <summary>
/// Gets the tuner infos.
/// </summary>
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
index e67fc5759..8501f1580 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
@@ -91,6 +91,11 @@ namespace MediaBrowser.Controller.LiveTv
}
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ return null;
+ }
+
public override string GetClientTypeName()
{
return "Recording";
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 5a139e09d..5919d87fd 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -48,6 +48,19 @@ namespace MediaBrowser.Controller.LiveTv
return list;
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ if (IsMovie)
+ {
+ double value = 2;
+ value /= 3;
+
+ return value;
+ }
+
+ return null;
+ }
+
[IgnoreDataMember]
public override SourceType SourceType
{
diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs
index ea12db7f9..0b94c85fa 100644
--- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs
@@ -88,6 +88,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value>
public bool IsPostPaddingRequired { get; set; }
+ public bool IsManual { get; set; }
+
/// <summary>
/// Gets or sets the priority.
/// </summary>
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 28229f8a7..db5a5009c 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -184,6 +184,8 @@
<Compile Include="LiveTv\TimerInfo.cs" />
<Compile Include="LiveTv\TunerChannelMapping.cs" />
<Compile Include="MediaEncoding\ChapterImageRefreshOptions.cs" />
+ <Compile Include="MediaEncoding\EncodingHelper.cs" />
+ <Compile Include="MediaEncoding\EncodingJobInfo.cs" />
<Compile Include="MediaEncoding\EncodingJobOptions.cs" />
<Compile Include="MediaEncoding\IEncodingManager.cs" />
<Compile Include="MediaEncoding\ImageEncodingOptions.cs" />
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 83057dba4..4e1a0a8d7 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -3,22 +3,16 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Text;
using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
-namespace MediaBrowser.MediaEncoding.Encoder
+namespace MediaBrowser.Controller.MediaEncoding
{
public class EncodingHelper
{
@@ -93,10 +87,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// [vaapi @ 0x7faed8000960] No VAAPI support for codec mpeg4 profile -99.
if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase))
{
- if (videoStream.Level == -99 || videoStream.Level == 15)
- {
- return false;
- }
+ return false;
}
}
return true;
@@ -1486,33 +1477,33 @@ namespace MediaBrowser.MediaEncoding.Encoder
//inputModifier += " -noaccurate_seek";
}
- //if (!string.IsNullOrWhiteSpace(state.InputContainer))
- //{
- // var inputFormat = GetInputFormat(state.InputContainer);
- // if (!string.IsNullOrWhiteSpace(inputFormat))
- // {
- // inputModifier += " -f " + inputFormat;
- // }
- //}
-
- //if (state.RunTimeTicks.HasValue)
- //{
- // foreach (var stream in state.MediaSource.MediaStreams)
- // {
- // if (!stream.IsExternal && stream.Type != MediaStreamType.Subtitle)
- // {
- // if (!string.IsNullOrWhiteSpace(stream.Codec) && stream.Index != -1)
- // {
- // var decoder = GetDecoderFromCodec(stream.Codec);
-
- // if (!string.IsNullOrWhiteSpace(decoder))
- // {
- // inputModifier += " -codec:" + stream.Index.ToString(_usCulture) + " " + decoder;
- // }
- // }
- // }
- // }
- //}
+ if (!string.IsNullOrWhiteSpace(state.InputContainer))
+ {
+ var inputFormat = GetInputFormat(state.InputContainer);
+ if (!string.IsNullOrWhiteSpace(inputFormat))
+ {
+ inputModifier += " -f " + inputFormat;
+ }
+ }
+
+ if (state.RunTimeTicks.HasValue && string.IsNullOrWhiteSpace(encodingOptions.HardwareAccelerationType))
+ {
+ foreach (var stream in state.MediaSource.MediaStreams)
+ {
+ if (!stream.IsExternal && stream.Type != MediaStreamType.Subtitle)
+ {
+ if (!string.IsNullOrWhiteSpace(stream.Codec) && stream.Index != -1)
+ {
+ var decoder = GetDecoderFromCodec(stream.Codec);
+
+ if (!string.IsNullOrWhiteSpace(decoder))
+ {
+ inputModifier += " -codec:" + stream.Index.ToString(_usCulture) + " " + decoder;
+ }
+ }
+ }
+ }
+ }
}
return inputModifier;
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 20a9817a3..a18b86432 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -13,7 +9,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
-namespace MediaBrowser.MediaEncoding.Encoder
+namespace MediaBrowser.Controller.MediaEncoding
{
// For now, a common base class until the API and MediaEncoding classes are unified
public class EncodingJobInfo
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 9087a6e1d..0eb435375 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -58,6 +58,11 @@ namespace MediaBrowser.Controller.Playlists
}
}
+ public override double? GetDefaultPrimaryImageAspectRatio()
+ {
+ return 1;
+ }
+
public override bool IsAuthorizedToDelete(User user)
{
return true;
diff --git a/MediaBrowser.Controller/Providers/EpisodeInfo.cs b/MediaBrowser.Controller/Providers/EpisodeInfo.cs
index b879040f8..b8e88ea53 100644
--- a/MediaBrowser.Controller/Providers/EpisodeInfo.cs
+++ b/MediaBrowser.Controller/Providers/EpisodeInfo.cs
@@ -8,7 +8,6 @@ namespace MediaBrowser.Controller.Providers
public Dictionary<string, string> SeriesProviderIds { get; set; }
public int? IndexNumberEnd { get; set; }
- public int? AnimeSeriesIndex { get; set; }
public bool IsMissingEpisode { get; set; }
public bool IsVirtualUnaired { get; set; }
diff --git a/MediaBrowser.Controller/Providers/SeasonInfo.cs b/MediaBrowser.Controller/Providers/SeasonInfo.cs
index 2c785d7d7..31af268b8 100644
--- a/MediaBrowser.Controller/Providers/SeasonInfo.cs
+++ b/MediaBrowser.Controller/Providers/SeasonInfo.cs
@@ -6,7 +6,6 @@ namespace MediaBrowser.Controller.Providers
public class SeasonInfo : ItemLookupInfo
{
public Dictionary<string, string> SeriesProviderIds { get; set; }
- public int? AnimeSeriesIndex { get; set; }
public SeasonInfo()
{
diff --git a/MediaBrowser.Controller/Providers/SeriesInfo.cs b/MediaBrowser.Controller/Providers/SeriesInfo.cs
index 387865de2..0b1361757 100644
--- a/MediaBrowser.Controller/Providers/SeriesInfo.cs
+++ b/MediaBrowser.Controller/Providers/SeriesInfo.cs
@@ -2,6 +2,5 @@ namespace MediaBrowser.Controller.Providers
{
public class SeriesInfo : ItemLookupInfo
{
- public int? AnimeSeriesIndex { get; set; }
}
} \ No newline at end of file
diff --git a/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs
index 2ddd84378..f5352add7 100644
--- a/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs
@@ -69,21 +69,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
break;
}
- case "AnimeSeriesIndex":
- {
- var number = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(number))
- {
- int num;
-
- if (int.TryParse(number, out num))
- {
- item.AnimeSeriesIndex = num;
- }
- }
- break;
- }
case "Status":
{
var status = reader.ReadElementContentAsString();
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index 5eac1a16d..63e789a59 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -50,10 +50,8 @@
<Compile Include="Configuration\EncodingConfigurationFactory.cs" />
<Compile Include="Encoder\AudioEncoder.cs" />
<Compile Include="Encoder\BaseEncoder.cs" />
- <Compile Include="Encoder\EncodingHelper.cs" />
<Compile Include="Encoder\EncodingJob.cs" />
<Compile Include="Encoder\EncodingJobFactory.cs" />
- <Compile Include="Encoder\EncodingJobInfo.cs" />
<Compile Include="Encoder\EncodingUtils.cs" />
<Compile Include="Encoder\EncoderValidator.cs" />
<Compile Include="Encoder\FontConfigLoader.cs" />
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 0bbc2d916..ca22dac2d 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -19,6 +19,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Text;
using UniversalDetector;
@@ -137,18 +138,29 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw new ArgumentNullException("mediaSourceId");
}
- var subtitle = await GetSubtitleStream(itemId, mediaSourceId, subtitleStreamIndex, cancellationToken)
+ var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false);
+
+ var mediaSource = mediaSources
+ .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
+
+ var subtitleStream = mediaSource.MediaStreams
+ .First(i => i.Type == MediaStreamType.Subtitle && i.Index == subtitleStreamIndex);
+
+ var subtitle = await GetSubtitleStream(mediaSource, subtitleStream, cancellationToken)
.ConfigureAwait(false);
var inputFormat = subtitle.Item2;
var writer = TryGetWriter(outputFormat);
- if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase) && writer == null)
+ // Return the original if we don't have any way of converting it
+ if (writer == null)
{
return subtitle.Item1;
}
- if (writer == null)
+ // Return the original if the same format is being requested
+ // Character encoding was already handled in GetSubtitleStream
+ if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase))
{
return subtitle.Item1;
}
@@ -159,36 +171,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- private async Task<Tuple<Stream, string>> GetSubtitleStream(string itemId,
- string mediaSourceId,
- int subtitleStreamIndex,
+ private async Task<Tuple<Stream, string>> GetSubtitleStream(MediaSourceInfo mediaSource,
+ MediaStream subtitleStream,
CancellationToken cancellationToken)
{
- if (string.IsNullOrWhiteSpace(itemId))
- {
- throw new ArgumentNullException("itemId");
- }
- if (string.IsNullOrWhiteSpace(mediaSourceId))
- {
- throw new ArgumentNullException("mediaSourceId");
- }
-
- var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(itemId, null, false, new[] { MediaType.Audio, MediaType.Video }, cancellationToken).ConfigureAwait(false);
-
- var mediaSource = mediaSources
- .First(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
-
- var subtitleStream = mediaSource.MediaStreams
- .First(i => i.Type == MediaStreamType.Subtitle && i.Index == subtitleStreamIndex);
-
var inputFiles = new[] { mediaSource.Path };
if (mediaSource.VideoType.HasValue)
{
- if (mediaSource.VideoType.Value == VideoType.BluRay ||
- mediaSource.VideoType.Value == VideoType.Dvd)
+ if (mediaSource.VideoType.Value == VideoType.BluRay || mediaSource.VideoType.Value == VideoType.Dvd)
{
- var mediaSourceItem = (Video)_libraryManager.GetItemById(new Guid(mediaSourceId));
+ var mediaSourceItem = (Video)_libraryManager.GetItemById(new Guid(mediaSource.Id));
inputFiles = mediaSourceItem.GetPlayableStreamFiles().ToArray();
}
}
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index 24bc5dd11..7af0acc59 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -14,6 +14,7 @@
public bool EnableInternetProviders { get; set; }
public bool ImportMissingEpisodes { get; set; }
public bool EnableAutomaticSeriesGrouping { get; set; }
+ public bool EnableEmbeddedTitles { get; set; }
/// <summary>
/// Gets or sets the preferred metadata language.
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 129b49cf6..480eb23a5 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -606,9 +606,20 @@ namespace MediaBrowser.Model.Dlna
return playlistItem;
}
+ private int GetDefaultAudioBitrateIfUnknown(MediaStream audioStream)
+ {
+ if ((audioStream.Channels ?? 0) >= 6)
+ {
+ return 384000;
+ }
+
+ return 192000;
+ }
+
private int GetAudioBitrate(string subProtocol, long? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream)
{
- int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? 192000;
+ int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream);
+
// Reduce the bitrate if we're downmixing
if (targetAudioChannels.HasValue && audioStream != null && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value)
{
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index c69bbb581..c78c92967 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -80,12 +80,8 @@ namespace MediaBrowser.Model.Dto
public string PreferredMetadataCountryCode { get; set; }
public string AwardSummary { get; set; }
- public string ShareUrl { get; set; }
public float? Metascore { get; set; }
- public bool? HasDynamicCategories { get; set; }
-
- public int? AnimeSeriesIndex { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [supports synchronize].
@@ -263,7 +259,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the play access.
/// </summary>
/// <value>The play access.</value>
- public PlayAccess PlayAccess { get; set; }
+ public PlayAccess? PlayAccess { get; set; }
/// <summary>
/// Gets or sets the aspect ratio.
@@ -321,12 +317,6 @@ namespace MediaBrowser.Model.Dto
public List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
- /// Gets or sets the soundtrack ids.
- /// </summary>
- /// <value>The soundtrack ids.</value>
- public string[] SoundtrackIds { get; set; }
-
- /// <summary>
/// Gets or sets the provider ids.
/// </summary>
/// <value>The provider ids.</value>
@@ -344,15 +334,6 @@ namespace MediaBrowser.Model.Dto
/// <value><c>true</c> if this instance is folder; otherwise, <c>false</c>.</value>
public bool? IsFolder { get; set; }
- [IgnoreDataMember]
- public bool IsFolderItem
- {
- get
- {
- return IsFolder ?? false;
- }
- }
-
/// <summary>
/// Gets or sets the parent id.
/// </summary>
@@ -406,11 +387,6 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The user data.</value>
public UserItemDataDto UserData { get; set; }
- /// <summary>
- /// Gets or sets the season user data.
- /// </summary>
- /// <value>The season user data.</value>
- public UserItemDataDto SeasonUserData { get; set; }
/// <summary>
/// Gets or sets the recursive item count.
@@ -460,56 +436,6 @@ namespace MediaBrowser.Model.Dto
/// <value>The status.</value>
public string Status { get; set; }
- [IgnoreDataMember]
- public SeriesStatus? SeriesStatus
- {
- get
- {
- if (string.IsNullOrEmpty(Status))
- {
- return null;
- }
-
- return (SeriesStatus)Enum.Parse(typeof(SeriesStatus), Status, true);
- }
- set
- {
- if (value == null)
- {
- Status = null;
- }
- else
- {
- Status = value.Value.ToString();
- }
- }
- }
-
- [IgnoreDataMember]
- public RecordingStatus? RecordingStatus
- {
- get
- {
- if (string.IsNullOrEmpty(Status))
- {
- return null;
- }
-
- return (RecordingStatus)Enum.Parse(typeof(RecordingStatus), Status, true);
- }
- set
- {
- if (value == null)
- {
- Status = null;
- }
- else
- {
- Status = value.Value.ToString();
- }
- }
- }
-
/// <summary>
/// Gets or sets the air time.
/// </summary>
@@ -523,12 +449,6 @@ namespace MediaBrowser.Model.Dto
public List<DayOfWeek> AirDays { get; set; }
/// <summary>
- /// Gets or sets the index options.
- /// </summary>
- /// <value>The index options.</value>
- public string[] IndexOptions { get; set; }
-
- /// <summary>
/// Gets or sets the tags.
/// </summary>
/// <value>The tags.</value>
@@ -571,12 +491,6 @@ namespace MediaBrowser.Model.Dto
public string CollectionType { get; set; }
/// <summary>
- /// Gets or sets the type of the original collection.
- /// </summary>
- /// <value>The type of the original collection.</value>
- public string OriginalCollectionType { get; set; }
-
- /// <summary>
/// Gets or sets the display order.
/// </summary>
/// <value>The display order.</value>
@@ -653,19 +567,6 @@ namespace MediaBrowser.Model.Dto
}
/// <summary>
- /// Gets or sets a value indicating whether [supports playlists].
- /// </summary>
- /// <value><c>true</c> if [supports playlists]; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool SupportsPlaylists
- {
- get
- {
- return RunTimeTicks.HasValue || IsFolderItem || IsGenre || IsMusicGenre || IsArtist;
- }
- }
-
- /// <summary>
/// Determines whether the specified type is type.
/// </summary>
/// <param name="type">The type.</param>
@@ -759,7 +660,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the type of the location.
/// </summary>
/// <value>The type of the location.</value>
- public LocationType LocationType { get; set; }
+ public LocationType? LocationType { get; set; }
/// <summary>
/// Gets or sets the type of the iso.
@@ -786,18 +687,6 @@ namespace MediaBrowser.Model.Dto
public string HomePageUrl { get; set; }
/// <summary>
- /// Gets or sets the budget.
- /// </summary>
- /// <value>The budget.</value>
- public double? Budget { get; set; }
-
- /// <summary>
- /// Gets or sets the revenue.
- /// </summary>
- /// <value>The revenue.</value>
- public double? Revenue { get; set; }
-
- /// <summary>
/// Gets or sets the locked fields.
/// </summary>
/// <value>The locked fields.</value>
@@ -879,56 +768,6 @@ namespace MediaBrowser.Model.Dto
public string SeriesTimerId { get; set; }
/// <summary>
- /// Gets a value indicating whether this instance can resume.
- /// </summary>
- /// <value><c>true</c> if this instance can resume; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool CanResume
- {
- get { return UserData != null && UserData.PlaybackPositionTicks > 0; }
- }
-
- /// <summary>
- /// Gets the resume position ticks.
- /// </summary>
- /// <value>The resume position ticks.</value>
- [IgnoreDataMember]
- public long ResumePositionTicks
- {
- get { return UserData == null ? 0 : UserData.PlaybackPositionTicks; }
- }
-
- /// <summary>
- /// Gets the backdrop count.
- /// </summary>
- /// <value>The backdrop count.</value>
- [IgnoreDataMember]
- public int BackdropCount
- {
- get { return BackdropImageTags == null ? 0 : BackdropImageTags.Count; }
- }
-
- /// <summary>
- /// Gets the screenshot count.
- /// </summary>
- /// <value>The screenshot count.</value>
- [IgnoreDataMember]
- public int ScreenshotCount
- {
- get { return ScreenshotImageTags == null ? 0 : ScreenshotImageTags.Count; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance has banner.
- /// </summary>
- /// <value><c>true</c> if this instance has banner; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasBanner
- {
- get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Banner); }
- }
-
- /// <summary>
/// Gets a value indicating whether this instance has art.
/// </summary>
/// <value><c>true</c> if this instance has art; otherwise, <c>false</c>.</value>
@@ -979,46 +818,6 @@ namespace MediaBrowser.Model.Dto
}
/// <summary>
- /// Gets a value indicating whether this instance has disc image.
- /// </summary>
- /// <value><c>true</c> if this instance has disc image; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasDiscImage
- {
- get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Disc); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance has box image.
- /// </summary>
- /// <value><c>true</c> if this instance has box image; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasBoxImage
- {
- get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Box); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance has box image.
- /// </summary>
- /// <value><c>true</c> if this instance has box image; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasBoxRearImage
- {
- get { return ImageTags != null && ImageTags.ContainsKey(ImageType.BoxRear); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this instance has menu image.
- /// </summary>
- /// <value><c>true</c> if this instance has menu image; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool HasMenuImage
- {
- get { return ImageTags != null && ImageTags.ContainsKey(ImageType.Menu); }
- }
-
- /// <summary>
/// Gets a value indicating whether this instance is video.
/// </summary>
/// <value><c>true</c> if this instance is video; otherwise, <c>false</c>.</value>
@@ -1058,16 +857,6 @@ namespace MediaBrowser.Model.Dto
get { return StringHelper.EqualsIgnoreCase(Type, "Person"); }
}
- /// <summary>
- /// Gets a value indicating whether this instance is root.
- /// </summary>
- /// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- public bool IsRoot
- {
- get { return StringHelper.EqualsIgnoreCase(Type, "AggregateFolder"); }
- }
-
[IgnoreDataMember]
public bool IsMusicGenre
{
@@ -1104,15 +893,6 @@ namespace MediaBrowser.Model.Dto
get { return StringHelper.EqualsIgnoreCase(Type, "Studio"); }
}
- [IgnoreDataMember]
- public bool SupportsSimilarItems
- {
- get
- {
- return IsType("Movie") || IsType("Series") || IsType("MusicAlbum") || IsType("MusicArtist") || IsType("Program") || IsType("Recording") || IsType("ChannelVideoItem") || IsType("Game");
- }
- }
-
/// <summary>
/// Gets or sets the program identifier.
/// </summary>
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
index 5cf52e0ef..e2c6b0503 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
@@ -48,15 +48,6 @@ namespace MediaBrowser.Model.LiveTv
public bool ImportFavoritesOnly { get; set; }
public bool AllowHWTranscoding { get; set; }
public bool IsEnabled { get; set; }
- public string M3UUrl { get; set; }
- public string InfoUrl { get; set; }
- public string FriendlyName { get; set; }
- public int Tuners { get; set; }
- public string DiseqC { get; set; }
- public string SourceA { get; set; }
- public string SourceB { get; set; }
- public string SourceC { get; set; }
- public string SourceD { get; set; }
public bool EnableTvgId { get; set; }
public TunerHostInfo()
@@ -85,6 +76,7 @@ namespace MediaBrowser.Model.LiveTv
public string[] MovieCategories { get; set; }
public NameValuePair[] ChannelMappings { get; set; }
public string MoviePrefix { get; set; }
+ public bool EnableNewProgramIds { get; set; }
public ListingsProviderInfo()
{
diff --git a/MediaBrowser.Model/Logging/IConsoleLogger.cs b/MediaBrowser.Model/Logging/IConsoleLogger.cs
new file mode 100644
index 000000000..a8c282d65
--- /dev/null
+++ b/MediaBrowser.Model/Logging/IConsoleLogger.cs
@@ -0,0 +1,7 @@
+namespace MediaBrowser.Model.Logging
+{
+ public interface IConsoleLogger
+ {
+ void WriteLine(string message);
+ }
+}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index c50ee984e..b796effa1 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -135,6 +135,7 @@
<Compile Include="Dto\MetadataEditorInfo.cs" />
<Compile Include="Dto\NameIdPair.cs" />
<Compile Include="Dto\NameValuePair.cs" />
+ <Compile Include="Logging\IConsoleLogger.cs" />
<Compile Include="Net\IpEndPointInfo.cs" />
<Compile Include="Net\ISocket.cs" />
<Compile Include="Net\ISocketFactory.cs" />
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
index fc4714be3..57a2254b0 100644
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
@@ -23,5 +23,16 @@ namespace MediaBrowser.Model.MediaInfo
public string LiveStreamId { get; set; }
public DeviceProfile DeviceProfile { get; set; }
+
+ public bool EnableDirectPlay { get; set; }
+ public bool EnableDirectStream { get; set; }
+ public bool EnableTranscoding { get; set; }
+
+ public PlaybackInfoRequest()
+ {
+ EnableDirectPlay = true;
+ EnableDirectStream = true;
+ EnableTranscoding = true;
+ }
}
}
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index 2c059c860..902253e80 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -243,7 +243,11 @@ namespace MediaBrowser.Model.Net
if (StringHelper.EqualsIgnoreCase(ext, ".opus"))
{
return "audio/ogg";
- }
+ }
+ if (StringHelper.EqualsIgnoreCase(ext, ".ac3"))
+ {
+ return "audio/ac3";
+ }
// Playlists
if (StringHelper.EqualsIgnoreCase(ext, ".m3u8"))
diff --git a/MediaBrowser.Mono.sln b/MediaBrowser.Mono.sln
index 35c396200..66ae294a7 100644
--- a/MediaBrowser.Mono.sln
+++ b/MediaBrowser.Mono.sln
@@ -47,8 +47,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Net", "Emby.Drawing.Net\Emby.Drawing.Net.csproj", "{C97A239E-A96C-4D64-A844-CCF8CC30AECB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
EndProject
Global
@@ -386,22 +384,6 @@ Global
{C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|Any CPU.Build.0 = Release|Any CPU
{C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.ActiveCfg = Release|Any CPU
{C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.Build.0 = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.Build.0 = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.Build.0 = Signed|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|x86.ActiveCfg = Debug|Any CPU
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 1bb715866..bc68b8c98 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -195,8 +195,9 @@ namespace MediaBrowser.Providers.MediaInfo
}
await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false);
+ var libraryOptions = _libraryManager.GetLibraryOptions(video);
- FetchEmbeddedInfo(video, mediaInfo, options);
+ FetchEmbeddedInfo(video, mediaInfo, options, libraryOptions);
await FetchPeople(video, mediaInfo, options).ConfigureAwait(false);
video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1260);
@@ -222,7 +223,6 @@ namespace MediaBrowser.Providers.MediaInfo
NormalizeChapterNames(chapters);
- var libraryOptions = _libraryManager.GetLibraryOptions(video);
var extractDuringScan = false;
if (libraryOptions != null)
{
@@ -344,11 +344,11 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions options)
+ private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions refreshOptions, LibraryOptions libraryOptions)
{
- var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
+ var isFullRefresh = refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
- if (!video.LockedFields.Contains(MetadataFields.OfficialRating))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.OfficialRating))
{
if (!string.IsNullOrWhiteSpace(data.OfficialRating) || isFullRefresh)
{
@@ -361,7 +361,7 @@ namespace MediaBrowser.Providers.MediaInfo
video.OfficialRatingDescription = data.OfficialRatingDescription;
}
- if (!video.LockedFields.Contains(MetadataFields.Genres))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Genres))
{
if (video.Genres.Count == 0 || isFullRefresh)
{
@@ -374,7 +374,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
- if (!video.LockedFields.Contains(MetadataFields.Studios))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Studios))
{
if (video.Studios.Count == 0 || isFullRefresh)
{
@@ -415,9 +415,10 @@ namespace MediaBrowser.Providers.MediaInfo
video.ParentIndexNumber = data.ParentIndexNumber;
}
}
- if (!string.IsNullOrWhiteSpace(data.Name))
+
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Name))
{
- if (string.IsNullOrWhiteSpace(video.Name) || (string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase) && !video.ProviderIds.Any()))
+ if (!string.IsNullOrWhiteSpace(data.Name) && libraryOptions.EnableEmbeddedTitles)
{
// Don't use the embedded name for extras because it will often be the same name as the movie
if (!video.ExtraType.HasValue && !video.IsOwnedItem)
@@ -433,7 +434,7 @@ namespace MediaBrowser.Providers.MediaInfo
video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year;
}
- if (!video.LockedFields.Contains(MetadataFields.Overview))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Overview))
{
if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh)
{
@@ -446,7 +447,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
- if (!video.LockedFields.Contains(MetadataFields.Cast))
+ if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Cast))
{
if (isFullRefresh || _libraryManager.GetPeople(video).Count == 0)
{
@@ -477,7 +478,7 @@ namespace MediaBrowser.Providers.MediaInfo
/// </summary>
/// <param name="video">The video.</param>
/// <param name="currentStreams">The current streams.</param>
- /// <param name="options">The options.</param>
+ /// <param name="options">The refreshOptions.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task AddExternalSubtitles(Video video,
diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
index ec31824db..e6eb0951d 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
@@ -621,4 +621,4 @@ namespace MediaBrowser.Providers.Music
public int throttleMs { get; set; }
}
}
-}
+} \ No newline at end of file
diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
index 73668b73c..477543d5e 100644
--- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs
@@ -18,8 +18,6 @@ using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.IO;
namespace MediaBrowser.Providers.Omdb
{
@@ -74,7 +72,8 @@ namespace MediaBrowser.Providers.Omdb
var imdbId = searchInfo.GetProviderId(MetadataProviders.Imdb);
- var url = "https://www.omdbapi.com/?plot=full&r=json";
+ var baseUrl = await OmdbProvider.GetOmdbBaseUrl(cancellationToken).ConfigureAwait(false);
+ var url = baseUrl + "/?plot=full&r=json";
if (type == "episode" && episodeSearchInfo != null)
{
episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out imdbId);
diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
index fdd334e6e..024853778 100644
--- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs
@@ -272,6 +272,11 @@ namespace MediaBrowser.Providers.Omdb
return false;
}
+ public static async Task<string> GetOmdbBaseUrl(CancellationToken cancellationToken)
+ {
+ return "https://www.omdbapi.com";
+ }
+
private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(imdbId))
@@ -294,7 +299,8 @@ namespace MediaBrowser.Providers.Omdb
}
}
- var url = string.Format("https://www.omdbapi.com/?i={0}&plot=full&tomatoes=true&r=json", imdbParam);
+ var baseUrl = await GetOmdbBaseUrl(cancellationToken).ConfigureAwait(false);
+ var url = string.Format(baseUrl + "/?i={0}&plot=full&tomatoes=true&r=json", imdbParam);
using (var stream = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
@@ -328,7 +334,8 @@ namespace MediaBrowser.Providers.Omdb
}
}
- var url = string.Format("https://www.omdbapi.com/?i={0}&season={1}&detail=full", imdbParam, seasonId);
+ var baseUrl = await GetOmdbBaseUrl(cancellationToken).ConfigureAwait(false);
+ var url = string.Format(baseUrl + "/?i={0}&season={1}&detail=full", imdbParam, seasonId);
using (var stream = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
diff --git a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
index 884fa297f..f1fc2c9f3 100644
--- a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs
@@ -96,8 +96,7 @@ namespace MediaBrowser.Providers.TV
try
{
- int seasonNumber = AdjustForSeriesOffset(series, season.IndexNumber.Value);
- AddImages(list, seasonNumber, path, cancellationToken);
+ AddImages(list, season.IndexNumber.Value, path, cancellationToken);
}
catch (FileNotFoundException)
{
@@ -139,15 +138,6 @@ namespace MediaBrowser.Providers.TV
.ThenByDescending(i => i.VoteCount ?? 0);
}
- private int AdjustForSeriesOffset(Series series, int seasonNumber)
- {
- var offset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds);
- if (offset != null)
- return (int)(seasonNumber + offset);
-
- return seasonNumber;
- }
-
private void AddImages(List<RemoteImageInfo> list, int seasonNumber, string path, CancellationToken cancellationToken)
{
var root = _json.DeserializeFromFile<FanartSeriesProvider.RootObject>(path);
diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
index 1fd04070b..d5154b1d3 100644
--- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
@@ -203,9 +203,8 @@ namespace MediaBrowser.Providers.TV
CancellationToken cancellationToken)
{
var existingEpisodes = (from s in series
- let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
from c in s.GetRecursiveChildren().OfType<Episode>()
- select new Tuple<int, Episode>((c.ParentIndexNumber ?? 0) + seasonOffset, c))
+ select new Tuple<int, Episode>((c.ParentIndexNumber ?? 0) , c))
.ToList();
var lookup = episodeLookup as IList<Tuple<int, int>> ?? episodeLookup.ToList();
@@ -248,7 +247,6 @@ namespace MediaBrowser.Providers.TV
var now = DateTime.UtcNow;
var targetSeries = DetermineAppropriateSeries(series, tuple.Item1);
- var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(targetSeries.ProviderIds) ?? ((targetSeries.AnimeSeriesIndex ?? 1) - 1);
var unairedThresholdDays = 2;
now = now.AddDays(0 - unairedThresholdDays);
@@ -259,7 +257,7 @@ namespace MediaBrowser.Providers.TV
{
// tvdb has a lot of nearly blank episodes
_logger.Info("Creating virtual missing episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2);
- await AddEpisode(targetSeries, tuple.Item1 - seasonOffset, tuple.Item2, cancellationToken).ConfigureAwait(false);
+ await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
hasChanges = true;
}
@@ -268,7 +266,7 @@ namespace MediaBrowser.Providers.TV
{
// tvdb has a lot of nearly blank episodes
_logger.Info("Creating virtual unaired episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2);
- await AddEpisode(targetSeries, tuple.Item1 - seasonOffset, tuple.Item2, cancellationToken).ConfigureAwait(false);
+ await AddEpisode(targetSeries, tuple.Item1, tuple.Item2, cancellationToken).ConfigureAwait(false);
hasChanges = true;
}
@@ -279,13 +277,11 @@ namespace MediaBrowser.Providers.TV
private Series DetermineAppropriateSeries(IEnumerable<Series> series, int seasonNumber)
{
- var seriesAndOffsets = series.Select(s => new { Series = s, SeasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1) }).ToList();
+ var seriesAndOffsets = series.ToList();
- var bestMatch = seriesAndOffsets.FirstOrDefault(s => s.Series.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == seasonNumber)) ??
- seriesAndOffsets.FirstOrDefault(s => s.Series.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber + s.SeasonOffset) == 1)) ??
- seriesAndOffsets.OrderBy(s => s.Series.GetRecursiveChildren().OfType<Season>().Select(season => season.IndexNumber + s.SeasonOffset).Min()).First();
-
- return bestMatch.Series;
+ return seriesAndOffsets.FirstOrDefault(s => s.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber) == seasonNumber)) ??
+ seriesAndOffsets.FirstOrDefault(s => s.GetRecursiveChildren().OfType<Season>().Any(season => (season.IndexNumber) == 1)) ??
+ seriesAndOffsets.OrderBy(s => s.GetRecursiveChildren().OfType<Season>().Select(season => season.IndexNumber).Min()).First();
}
/// <summary>
@@ -296,9 +292,8 @@ namespace MediaBrowser.Providers.TV
bool allowMissingEpisodes)
{
var existingEpisodes = (from s in series
- let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
from c in s.GetRecursiveChildren().OfType<Episode>()
- select new { SeasonOffset = seasonOffset, Episode = c })
+ select new { Episode = c })
.ToList();
var physicalEpisodes = existingEpisodes
@@ -314,12 +309,12 @@ namespace MediaBrowser.Providers.TV
{
if (i.Episode.IndexNumber.HasValue && i.Episode.ParentIndexNumber.HasValue)
{
- var seasonNumber = i.Episode.ParentIndexNumber.Value + i.SeasonOffset;
+ var seasonNumber = i.Episode.ParentIndexNumber.Value;
var episodeNumber = i.Episode.IndexNumber.Value;
// If there's a physical episode with the same season and episode number, delete it
if (physicalEpisodes.Any(p =>
- p.Episode.ParentIndexNumber.HasValue && (p.Episode.ParentIndexNumber.Value + p.SeasonOffset) == seasonNumber &&
+ p.Episode.ParentIndexNumber.HasValue && (p.Episode.ParentIndexNumber.Value) == seasonNumber &&
p.Episode.ContainsEpisodeNumber(episodeNumber)))
{
return true;
@@ -371,28 +366,27 @@ namespace MediaBrowser.Providers.TV
IEnumerable<Tuple<int, int>> episodeLookup)
{
var existingSeasons = (from s in series
- let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
from c in s.Children.OfType<Season>()
- select new { SeasonOffset = seasonOffset, Season = c })
+ select c)
.ToList();
var physicalSeasons = existingSeasons
- .Where(i => i.Season.LocationType != LocationType.Virtual)
+ .Where(i => i.LocationType != LocationType.Virtual)
.ToList();
var virtualSeasons = existingSeasons
- .Where(i => i.Season.LocationType == LocationType.Virtual)
+ .Where(i => i.LocationType == LocationType.Virtual)
.ToList();
var seasonsToRemove = virtualSeasons
.Where(i =>
{
- if (i.Season.IndexNumber.HasValue)
+ if (i.IndexNumber.HasValue)
{
- var seasonNumber = i.Season.IndexNumber.Value + i.SeasonOffset;
+ var seasonNumber = i.IndexNumber.Value;
// If there's a physical season with the same number, delete it
- if (physicalSeasons.Any(p => p.Season.IndexNumber.HasValue && (p.Season.IndexNumber.Value + p.SeasonOffset) == seasonNumber && string.Equals(p.Season.Series.PresentationUniqueKey, i.Season.Series.PresentationUniqueKey, StringComparison.Ordinal)))
+ if (physicalSeasons.Any(p => p.IndexNumber.HasValue && (p.IndexNumber.Value) == seasonNumber && string.Equals(p.Series.PresentationUniqueKey, i.Series.PresentationUniqueKey, StringComparison.Ordinal)))
{
return true;
}
@@ -408,13 +402,13 @@ namespace MediaBrowser.Providers.TV
// Season does not have a number
// Remove if there are no episodes directly in series without a season number
- return i.Season.Series.GetRecursiveChildren().OfType<Episode>().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder);
+ return i.Series.GetRecursiveChildren().OfType<Episode>().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder);
})
.ToList();
var hasChanges = false;
- foreach (var seasonToRemove in seasonsToRemove.Select(s => s.Season))
+ foreach (var seasonToRemove in seasonsToRemove)
{
_logger.Info("Removing virtual season {0} {1}", seasonToRemove.Series.Name, seasonToRemove.IndexNumber);
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
index 791f56beb..989748846 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs
@@ -59,7 +59,6 @@ namespace MediaBrowser.Providers.TV
{
// Process images
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds);
- var indexOffset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds) ?? 0;
var nodes = TvdbEpisodeProvider.Current.GetEpisodeXmlNodes(seriesDataPath, episode.GetLookupInfo());
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
index 99136cd85..46be61486 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
@@ -742,7 +742,7 @@ namespace MediaBrowser.Providers.TV
{
if (!item.LockedFields.Contains(MetadataFields.Cast))
{
- AddPeople(result, val, PersonType.Writer);
+ //AddPeople(result, val, PersonType.Writer);
}
}
@@ -894,7 +894,7 @@ namespace MediaBrowser.Providers.TV
{
if (!item.LockedFields.Contains(MetadataFields.Cast))
{
- AddPeople(result, val, PersonType.Writer);
+ //AddPeople(result, val, PersonType.Writer);
}
}
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
index e189c292c..2c4ccddd7 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
@@ -99,15 +99,6 @@ namespace MediaBrowser.Providers.TV
return new RemoteImageInfo[] { };
}
- private int AdjustForSeriesOffset(Series series, int seasonNumber)
- {
- var offset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds);
- if (offset != null)
- return (seasonNumber + offset.Value);
-
- return seasonNumber;
- }
-
internal static IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, int seasonNumber, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem, CancellationToken cancellationToken)
{
var settings = xmlReaderSettingsFactory.Create(false);
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
index 2595ad585..97eedfa92 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
@@ -76,10 +76,6 @@ namespace MediaBrowser.Providers.TV
try
{
- var seriesOffset = TvdbSeriesProvider.GetSeriesOffset(item.ProviderIds);
- if (seriesOffset != null && seriesOffset.Value != 0)
- return TvdbSeasonImageProvider.GetImages(path, language, seriesOffset.Value + 1, _xmlReaderSettingsFactory, _fileSystem, cancellationToken);
-
return GetImages(path, language, cancellationToken);
}
catch (FileNotFoundException)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
index 1798299e8..8769dc801 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
@@ -27,9 +27,6 @@ namespace MediaBrowser.Providers.TV
{
public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
- private const string TvdbSeriesOffset = "TvdbSeriesOffset";
- private const string TvdbSeriesOffsetFormat = "{0}-{1}";
-
internal readonly SemaphoreSlim TvDbResourcePool = new SemaphoreSlim(2, 2);
internal static TvdbSeriesProvider Current { get; private set; }
private readonly IZipClient _zipClient;
@@ -123,23 +120,6 @@ namespace MediaBrowser.Providers.TV
return result;
}
- internal static int? GetSeriesOffset(Dictionary<string, string> seriesProviderIds)
- {
- string idString;
- if (!seriesProviderIds.TryGetValue(TvdbSeriesOffset, out idString))
- return null;
-
- var parts = idString.Split('-');
- if (parts.Length < 2)
- return null;
-
- int offset;
- if (int.TryParse(parts[1], out offset))
- return offset;
-
- return null;
- }
-
/// <summary>
/// Fetches the series data.
/// </summary>
diff --git a/MediaBrowser.Server.Mac.sln b/MediaBrowser.Server.Mac.sln
index 9b04284c3..037cd488b 100644
--- a/MediaBrowser.Server.Mac.sln
+++ b/MediaBrowser.Server.Mac.sln
@@ -49,8 +49,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RSSDP", "RSSDP\RSSDP.csproj
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AppStore|Any CPU = AppStore|Any CPU
@@ -522,26 +520,6 @@ Global
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|Any CPU.Build.0 = Release|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.ActiveCfg = Release|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Signed|x86.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.AppStore|Any CPU.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.AppStore|Any CPU.Build.0 = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.AppStore|x86.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.AppStore|x86.Build.0 = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.Build.0 = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.Build.0 = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.Build.0 = Signed|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
index f7f084917..eb5e9880e 100644
--- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
+++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
@@ -122,10 +122,16 @@
<HintPath>..\ThirdParty\taglib\TagLib.Portable.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.core">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.1\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.2\lib\net45\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3">
- <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.1\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.2\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ </Reference>
+ <Reference Include="Emby.Server.Connect">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath>
+ </Reference>
+ <Reference Include="Emby.Server.Sync">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Sync.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -246,10 +252,6 @@
<Project>{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}</Project>
<Name>SocketHttpListener.Portable</Name>
</ProjectReference>
- <ProjectReference Include="..\ServiceStack\ServiceStack.csproj">
- <Project>{680A1709-25EB-4D52-A87F-EE03FFD94BAA}</Project>
- <Name>ServiceStack</Name>
- </ProjectReference>
<ProjectReference Include="..\RSSDP\RSSDP.csproj">
<Project>{21002819-C39A-4D3E-BE83-2A276A77FB1F}</Project>
<Name>RSSDP</Name>
@@ -813,6 +815,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\thememediaplayer.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\thememediaplayer.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\touchhelper.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\touchhelper.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\visibleinviewport.js</Link>
</BundleResource>
@@ -918,6 +923,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-radio\emby-radio.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-radio\emby-radio.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-scroller\emby-scroller.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-scroller\emby-scroller.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-select\emby-select.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-select\emby-select.css</Link>
</BundleResource>
@@ -1092,6 +1100,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\images\persistentimagefetcher.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\images\persistentimagefetcher.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\images\style.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\images\style.css</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\indicators\indicators.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\indicators\indicators.css</Link>
</BundleResource>
@@ -1194,6 +1205,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\autoplaydetect.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\autoplaydetect.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\iconosd.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\iconosd.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\mediasession.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\mediasession.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\nowplayinghelper.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\nowplayinghelper.js</Link>
</BundleResource>
@@ -1212,6 +1229,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\remotecontrolautoplay.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\remotecontrolautoplay.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\volumeosd.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\volumeosd.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playlisteditor\playlisteditor.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playlisteditor\playlisteditor.js</Link>
</BundleResource>
@@ -1698,21 +1718,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\vibrant\dist\vibrant.min.js">
<Link>Resources\dashboard-ui\bower_components\vibrant\dist\vibrant.min.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\COPYING">
- <Link>Resources\dashboard-ui\bower_components\web-animations-js\COPYING</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations-next-lite.min.js">
- <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations-next-lite.min.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations-next.min.js">
- <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations-next.min.js</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations.html">
- <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations.html</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\web-animations-js\web-animations.min.js">
- <Link>Resources\dashboard-ui\bower_components\web-animations-js\web-animations.min.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\webcomponentsjs\webcomponents-lite.min.js">
<Link>Resources\dashboard-ui\bower_components\webcomponentsjs\webcomponents-lite.min.js</Link>
</BundleResource>
@@ -2151,9 +2156,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\dashboard\wizardfinishpage.js">
<Link>Resources\dashboard-ui\dashboard\wizardfinishpage.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\android\android.css">
- <Link>Resources\dashboard-ui\devices\android\android.css</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\devices\ios\ios.css">
<Link>Resources\dashboard-ui\devices\ios\ios.css</Link>
</BundleResource>
@@ -2253,9 +2255,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\homefavorites.js">
<Link>Resources\dashboard-ui\scripts\homefavorites.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\homenextup.js">
- <Link>Resources\dashboard-ui\scripts\homenextup.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\homeupcoming.js">
<Link>Resources\dashboard-ui\scripts\homeupcoming.js</Link>
</BundleResource>
diff --git a/MediaBrowser.Server.Mac/MacAppHost.cs b/MediaBrowser.Server.Mac/MacAppHost.cs
index d73a8fbbd..59e72e71e 100644
--- a/MediaBrowser.Server.Mac/MacAppHost.cs
+++ b/MediaBrowser.Server.Mac/MacAppHost.cs
@@ -9,6 +9,10 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.System;
using Emby.Server.Mac.Native;
using System.Diagnostics;
+using MediaBrowser.Controller.Connect;
+using Emby.Server.Connect;
+using Emby.Server.Sync;
+using MediaBrowser.Controller.Sync;
namespace MediaBrowser.Server.Mac
{
@@ -42,6 +46,16 @@ namespace MediaBrowser.Server.Mac
}
}
+ protected override IConnectManager CreateConnectManager()
+ {
+ return new ConnectManager();
+ }
+
+ protected override ISyncManager CreateSyncManager()
+ {
+ return new SyncManager();
+ }
+
protected override FFMpegInstallInfo GetFfmpegInstallInfo()
{
var info = new FFMpegInstallInfo();
@@ -88,6 +102,8 @@ namespace MediaBrowser.Server.Mac
var list = new List<Assembly>();
list.Add(GetType().Assembly);
+ list.Add(typeof(ConnectManager).Assembly);
+ list.Add(typeof(SyncManager).Assembly);
return list;
}
diff --git a/MediaBrowser.Server.Mac/Main.cs b/MediaBrowser.Server.Mac/Main.cs
index 8c5ca6c1f..debd5f539 100644
--- a/MediaBrowser.Server.Mac/Main.cs
+++ b/MediaBrowser.Server.Mac/Main.cs
@@ -22,6 +22,7 @@ using MonoMac.ObjCRuntime;
using Emby.Server.Core;
using Emby.Server.Implementations;
using Emby.Common.Implementations.Logging;
+using Emby.Server.Implementations.Logging;
using Emby.Common.Implementations.EnvironmentInfo;
using Emby.Server.Mac.Native;
using Emby.Server.Implementations.IO;
@@ -29,6 +30,8 @@ using Emby.Common.Implementations.Networking;
using Emby.Common.Implementations.Security;
using Mono.Unix.Native;
using MediaBrowser.Model.System;
+using MediaBrowser.Model.IO;
+using Emby.Server.Core.Logging;
namespace MediaBrowser.Server.Mac
{
@@ -37,6 +40,7 @@ namespace MediaBrowser.Server.Mac
internal static MacAppHost AppHost;
private static ILogger _logger;
+ private static IFileSystem _fileSystem;
static void Main (string[] args)
{
@@ -83,7 +87,9 @@ namespace MediaBrowser.Server.Mac
// Within the mac bundle, go uo two levels then down into Resources folder
var resourcesPath = Path.Combine(Path.GetDirectoryName(appFolderPath), "Resources");
- return new ServerApplicationPaths(programDataPath, appFolderPath, resourcesPath);
+ Action<string> createDirectoryFn = (string obj) => Directory.CreateDirectory(obj);
+
+ return new ServerApplicationPaths(programDataPath, appFolderPath, resourcesPath, createDirectoryFn);
}
/// <summary>
@@ -102,6 +108,8 @@ namespace MediaBrowser.Server.Mac
var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false, appPaths.TempDirectory);
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
+ _fileSystem = fileSystem;
+
var environmentInfo = GetEnvironmentInfo();
var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger,
@@ -294,7 +302,9 @@ namespace MediaBrowser.Server.Mac
{
var exception = (Exception)e.ExceptionObject;
- new UnhandledExceptionWriter(AppHost.ServerConfigurationManager.ApplicationPaths, _logger, AppHost.LogManager).Log(exception);
+ var consoleLogger = new ConsoleLogger();
+
+ new UnhandledExceptionWriter(AppHost.ServerConfigurationManager.ApplicationPaths, _logger, AppHost.LogManager, _fileSystem, consoleLogger).Log(exception);
if (!Debugger.IsAttached)
{
diff --git a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
index 325011adf..62c3e00a8 100644
--- a/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
+++ b/MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
@@ -61,15 +61,21 @@
<Reference Include="Emby.Common.Implementations">
<HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
</Reference>
+ <Reference Include="Emby.Server.Connect">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath>
+ </Reference>
<Reference Include="Emby.Server.Core">
<HintPath>..\ThirdParty\emby\Emby.Server.Core.dll</HintPath>
</Reference>
+ <Reference Include="Emby.Server.Sync">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Sync.dll</HintPath>
+ </Reference>
<Reference Include="Mono.Posix, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Mono.Posix.4.0.0.0\lib\net40\Mono.Posix.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
- <HintPath>..\packages\NLog.4.4.0-betaV15\lib\net45\NLog.dll</HintPath>
+ <HintPath>..\packages\NLog.4.4.3\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Text, Version=4.5.4.0, Culture=neutral, processorArchitecture=MSIL">
@@ -80,16 +86,16 @@
<HintPath>..\packages\SharpCompress.0.14.0\lib\net45\SharpCompress.dll</HintPath>
<Private>True</Private>
</Reference>
- <Reference Include="SimpleInjector, Version=3.2.4.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
- <HintPath>..\packages\SimpleInjector.3.2.4\lib\net45\SimpleInjector.dll</HintPath>
+ <Reference Include="SimpleInjector, Version=3.3.2.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
+ <HintPath>..\packages\SimpleInjector.3.3.2\lib\net45\SimpleInjector.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.1\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.2\lib\net45\SQLitePCLRaw.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62684c7b4f184e3f, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.1\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.2\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@@ -200,10 +206,6 @@
<Project>{21002819-c39a-4d3e-be83-2a276a77fb1f}</Project>
<Name>RSSDP</Name>
</ProjectReference>
- <ProjectReference Include="..\ServiceStack\ServiceStack.csproj">
- <Project>{680a1709-25eb-4d52-a87f-ee03ffd94baa}</Project>
- <Name>ServiceStack</Name>
- </ProjectReference>
<ProjectReference Include="..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj">
<Project>{4f26d5d8-a7b0-42b3-ba42-7cb7d245934e}</Project>
<Name>SocketHttpListener.Portable</Name>
diff --git a/MediaBrowser.Server.Mono/MonoAppHost.cs b/MediaBrowser.Server.Mono/MonoAppHost.cs
index 932e2d6cd..e9ded5cd5 100644
--- a/MediaBrowser.Server.Mono/MonoAppHost.cs
+++ b/MediaBrowser.Server.Mono/MonoAppHost.cs
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Reflection;
+using Emby.Server.Connect;
using Emby.Server.Core;
using Emby.Server.Implementations;
-using Emby.Server.Implementations.FFMpeg;
+using Emby.Server.Sync;
+using MediaBrowser.Controller.Connect;
+using MediaBrowser.Controller.Sync;
using MediaBrowser.IsoMounter;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
@@ -26,61 +29,14 @@ namespace MediaBrowser.Server.Mono
}
}
- public override bool CanSelfUpdate
+ protected override IConnectManager CreateConnectManager()
{
- get
- {
- return false;
- }
- }
-
- protected override FFMpegInstallInfo GetFfmpegInstallInfo()
- {
- var info = new FFMpegInstallInfo();
-
- // Windows builds: http://ffmpeg.zeranoe.com/builds/
- // Linux builds: http://johnvansickle.com/ffmpeg/
- // OS X builds: http://ffmpegmac.net/
- // OS X x64: http://www.evermeet.cx/ffmpeg/
-
- var environment = (MonoEnvironmentInfo) EnvironmentInfo;
-
- if (environment.IsBsd)
- {
-
- }
- else if (environment.OperatingSystem == Model.System.OperatingSystem.Linux)
- {
- info.FFMpegFilename = "ffmpeg";
- info.FFProbeFilename = "ffprobe";
- info.ArchiveType = "7z";
- info.Version = "20160215";
- info.DownloadUrls = GetDownloadUrls();
- }
-
- // No version available - user requirement
- info.DownloadUrls = new string[] { };
-
- return info;
+ return new ConnectManager();
}
- private string[] GetDownloadUrls()
+ protected override ISyncManager CreateSyncManager()
{
- switch (EnvironmentInfo.SystemArchitecture)
- {
- case Architecture.X64:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
- };
- case Architecture.X86:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z"
- };
- }
-
- return new string[] { };
+ return new SyncManager();
}
protected override void RestartInternal()
@@ -103,6 +59,8 @@ namespace MediaBrowser.Server.Mono
var list = new List<Assembly>();
list.Add(typeof(LinuxIsoManager).Assembly);
+ list.Add(typeof(ConnectManager).Assembly);
+ list.Add(typeof(SyncManager).Assembly);
return list;
}
@@ -137,40 +95,5 @@ namespace MediaBrowser.Server.Mono
return new Version(1, 0);
}
-
- protected override void AuthorizeServer()
- {
- throw new NotImplementedException();
- }
-
- protected override void ConfigureAutoRunInternal(bool autorun)
- {
- throw new NotImplementedException();
- }
-
- protected override void EnableLoopbackInternal(string appName)
- {
- }
-
- public override bool SupportsRunningAsService
- {
- get
- {
- return false;
- }
- }
-
- public override bool SupportsAutoRunAtStartup
- {
- get { return false; }
- }
-
- public override bool IsRunningAsService
- {
- get
- {
- return false;
- }
- }
}
}
diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs
index 4790378a9..649283410 100644
--- a/MediaBrowser.Server.Mono/Program.cs
+++ b/MediaBrowser.Server.Mono/Program.cs
@@ -16,8 +16,11 @@ using Emby.Common.Implementations.Logging;
using Emby.Common.Implementations.Networking;
using Emby.Common.Implementations.Security;
using Emby.Server.Core;
+using Emby.Server.Core.Logging;
using Emby.Server.Implementations;
using Emby.Server.Implementations.IO;
+using Emby.Server.Implementations.Logging;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.System;
using MediaBrowser.Server.Startup.Common.IO;
using Mono.Unix.Native;
@@ -32,6 +35,7 @@ namespace MediaBrowser.Server.Mono
private static ApplicationHost _appHost;
private static ILogger _logger;
+ private static IFileSystem FileSystem;
public static void Main(string[] args)
{
@@ -98,7 +102,9 @@ namespace MediaBrowser.Server.Mono
var appFolderPath = Path.GetDirectoryName(applicationPath);
- return new ServerApplicationPaths(programDataPath, appFolderPath, Path.GetDirectoryName(applicationPath));
+ Action<string> createDirectoryFn = s => Directory.CreateDirectory(s);
+
+ return new ServerApplicationPaths(programDataPath, appFolderPath, Path.GetDirectoryName(applicationPath), createDirectoryFn);
}
private static readonly TaskCompletionSource<bool> ApplicationTaskCompletionSource = new TaskCompletionSource<bool>();
@@ -111,6 +117,8 @@ namespace MediaBrowser.Server.Mono
var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false, appPaths.TempDirectory);
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
+ FileSystem = fileSystem;
+
var environmentInfo = GetEnvironmentInfo();
var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => _appHost.HttpClient, appPaths);
@@ -247,7 +255,7 @@ namespace MediaBrowser.Server.Mono
{
var exception = (Exception)e.ExceptionObject;
- new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager).Log(exception);
+ new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager, FileSystem, new ConsoleLogger()).Log(exception);
if (!Debugger.IsAttached)
{
diff --git a/MediaBrowser.Server.Mono/app.config b/MediaBrowser.Server.Mono/app.config
index a77a3a506..07c113f3e 100644
--- a/MediaBrowser.Server.Mono/app.config
+++ b/MediaBrowser.Server.Mono/app.config
@@ -20,6 +20,10 @@
<assemblyIdentity name="System.IO.FileSystem.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
+ <dependentAssembly>
+ <assemblyIdentity name="SimpleInjector" publicKeyToken="984cb50dea722e99" culture="neutral" />
+ <bindingRedirect oldVersion="0.0.0.0-3.3.2.0" newVersion="3.3.2.0" />
+ </dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /></startup></configuration>
diff --git a/MediaBrowser.Server.Mono/packages.config b/MediaBrowser.Server.Mono/packages.config
index 465a05c08..81da308f5 100644
--- a/MediaBrowser.Server.Mono/packages.config
+++ b/MediaBrowser.Server.Mono/packages.config
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
- <package id="NLog" version="4.4.0-betaV15" targetFramework="net46" />
+ <package id="NLog" version="4.4.3" targetFramework="net46" />
<package id="ServiceStack.Text" version="4.5.4" targetFramework="net46" />
<package id="SharpCompress" version="0.14.0" targetFramework="net46" />
- <package id="SimpleInjector" version="3.2.4" targetFramework="net46" />
- <package id="SQLitePCLRaw.core" version="1.1.1" targetFramework="net46" />
- <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.1" targetFramework="net46" />
+ <package id="SimpleInjector" version="3.3.2" targetFramework="net46" />
+ <package id="SQLitePCLRaw.core" version="1.1.2" targetFramework="net46" />
+ <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.2" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
index a1bca62e0..97d04cff4 100644
--- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
+++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
@@ -37,10 +37,6 @@
<Reference Include="Emby.Server.Core">
<HintPath>..\ThirdParty\emby\Emby.Server.Core.dll</HintPath>
</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="Microsoft.IO.RecyclableMemoryStream, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.1\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
<Private>True</Private>
@@ -79,28 +75,6 @@
<Compile Include="Cryptography\X520Attributes.cs" />
<Compile Include="ImageEncoderHelper.cs" />
<Compile Include="IO\MemoryStreamProvider.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\ChannelScan.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\ReportBlock.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpAppPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpByePacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpListener.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpReceiverReportPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSenderReportPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\RtcpSourceDescriptionPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionBlock.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtcp\SourceDescriptionItem.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpListener.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtp\RtpPacket.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspMethod.cs" />
- <Compile Include="LiveTv\TunerHosts\SatIp\Rtsp\RtspRequest.cs" />
- <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\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="Properties\AssemblyInfo.cs" />
<Compile Include="SystemEvents.cs" />
</ItemGroup>
@@ -136,175 +110,6 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
- <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 />
diff --git a/MediaBrowser.Server.Startup.Common/packages.config b/MediaBrowser.Server.Startup.Common/packages.config
index b12895000..d329f2cce 100644
--- a/MediaBrowser.Server.Startup.Common/packages.config
+++ b/MediaBrowser.Server.Startup.Common/packages.config
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="ini-parser" version="2.3.0" targetFramework="net46" />
<package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.1" targetFramework="net46" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/App.config b/MediaBrowser.ServerApplication/App.config
index 4bac6bb70..fae013d6e 100644
--- a/MediaBrowser.ServerApplication/App.config
+++ b/MediaBrowser.ServerApplication/App.config
@@ -49,7 +49,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SimpleInjector" publicKeyToken="984cb50dea722e99" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-2.3.6.0" newVersion="2.3.6.0" />
+ <bindingRedirect oldVersion="0.0.0.0-3.3.2.0" newVersion="3.3.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IO.FileSystem.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs
index 70b86c4a6..b41e7607c 100644
--- a/MediaBrowser.ServerApplication/MainStartup.cs
+++ b/MediaBrowser.ServerApplication/MainStartup.cs
@@ -23,11 +23,14 @@ using Emby.Common.Implementations.Logging;
using Emby.Common.Implementations.Networking;
using Emby.Common.Implementations.Security;
using Emby.Server.Core;
+using Emby.Server.Core.Logging;
using Emby.Server.Implementations;
using Emby.Server.Implementations.Browser;
using Emby.Server.Implementations.IO;
+using Emby.Server.Implementations.Logging;
using ImageMagickSharp;
using MediaBrowser.Common.Net;
+using MediaBrowser.Model.IO;
using MediaBrowser.Server.Startup.Common.IO;
namespace MediaBrowser.ServerApplication
@@ -47,6 +50,8 @@ namespace MediaBrowser.ServerApplication
public static string ApplicationPath;
+ private static IFileSystem FileSystem;
+
public static bool TryGetLocalFromUncDirectory(string local, out string unc)
{
if ((local == null) || (local == ""))
@@ -259,16 +264,18 @@ namespace MediaBrowser.ServerApplication
var resourcesPath = Path.GetDirectoryName(applicationPath);
+ Action<string> createDirectoryFn = s => Directory.CreateDirectory(s);
+
if (runAsService)
{
var systemPath = Path.GetDirectoryName(applicationPath);
var programDataPath = Path.GetDirectoryName(systemPath);
- return new ServerApplicationPaths(programDataPath, appFolderPath, resourcesPath);
+ return new ServerApplicationPaths(programDataPath, appFolderPath, resourcesPath, createDirectoryFn);
}
- return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(applicationPath), appFolderPath, resourcesPath);
+ return new ServerApplicationPaths(ApplicationPathHelper.GetProgramDataPath(applicationPath), appFolderPath, resourcesPath, createDirectoryFn);
}
/// <summary>
@@ -324,12 +331,14 @@ namespace MediaBrowser.ServerApplication
/// <param name="options">The options.</param>
private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService, StartupOptions options)
{
- var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true, appPaths.TempDirectory);
+ var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, false, appPaths.TempDirectory);
fileSystem.AddShortcutHandler(new LnkShortcutHandler());
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => _appHost.HttpClient, appPaths);
+ FileSystem = fileSystem;
+
_appHost = new WindowsAppHost(appPaths,
logManager,
options,
@@ -580,7 +589,7 @@ namespace MediaBrowser.ServerApplication
{
var exception = (Exception)e.ExceptionObject;
- new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager).Log(exception);
+ new UnhandledExceptionWriter(_appHost.ServerConfigurationManager.ApplicationPaths, _logger, _appHost.LogManager, FileSystem, new ConsoleLogger()).Log(exception);
if (!IsRunningAsService)
{
@@ -676,6 +685,7 @@ namespace MediaBrowser.ServerApplication
_appHostDisposed = true;
_appHost.Dispose();
+ _logger.Info("App host dispose complete");
}
}
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index 7badccef3..50b0aa21f 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -67,15 +67,21 @@
<Reference Include="Emby.Common.Implementations">
<HintPath>..\ThirdParty\emby\Emby.Common.Implementations.dll</HintPath>
</Reference>
+ <Reference Include="Emby.Server.Connect">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Connect.dll</HintPath>
+ </Reference>
<Reference Include="Emby.Server.Core">
<HintPath>..\ThirdParty\emby\Emby.Server.Core.dll</HintPath>
</Reference>
+ <Reference Include="Emby.Server.Sync">
+ <HintPath>..\ThirdParty\emby\Emby.Server.Sync.dll</HintPath>
+ </Reference>
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ImageMagickSharp.1.0.0.18\lib\net45\ImageMagickSharp.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
- <HintPath>..\packages\NLog.4.4.0-betaV15\lib\net45\NLog.dll</HintPath>
+ <HintPath>..\packages\NLog.4.4.3\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ServiceStack.Text, Version=4.5.4.0, Culture=neutral, processorArchitecture=MSIL">
@@ -86,16 +92,16 @@
<HintPath>..\packages\SharpCompress.0.14.0\lib\net45\SharpCompress.dll</HintPath>
<Private>True</Private>
</Reference>
- <Reference Include="SimpleInjector, Version=3.2.4.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
- <HintPath>..\packages\SimpleInjector.3.2.4\lib\net45\SimpleInjector.dll</HintPath>
+ <Reference Include="SimpleInjector, Version=3.3.2.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
+ <HintPath>..\packages\SimpleInjector.3.3.2\lib\net45\SimpleInjector.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.core.1.1.1\lib\net45\SQLitePCLRaw.core.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.core.1.1.2\lib\net45\SQLitePCLRaw.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.provider.sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=62684c7b4f184e3f, processorArchitecture=MSIL">
- <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.1\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
+ <HintPath>..\packages\SQLitePCLRaw.provider.sqlite3.net45.1.1.2\lib\net45\SQLitePCLRaw.provider.sqlite3.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@@ -1155,10 +1161,6 @@
<Project>{21002819-c39a-4d3e-be83-2a276a77fb1f}</Project>
<Name>RSSDP</Name>
</ProjectReference>
- <ProjectReference Include="..\ServiceStack\ServiceStack.csproj">
- <Project>{680a1709-25eb-4d52-a87f-ee03ffd94baa}</Project>
- <Name>ServiceStack</Name>
- </ProjectReference>
<ProjectReference Include="..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj">
<Project>{4f26d5d8-a7b0-42b3-ba42-7cb7d245934e}</Project>
<Name>SocketHttpListener.Portable</Name>
diff --git a/MediaBrowser.ServerApplication/WindowsAppHost.cs b/MediaBrowser.ServerApplication/WindowsAppHost.cs
index ec66923aa..9f11dc322 100644
--- a/MediaBrowser.ServerApplication/WindowsAppHost.cs
+++ b/MediaBrowser.ServerApplication/WindowsAppHost.cs
@@ -4,10 +4,14 @@ using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices.ComTypes;
+using Emby.Server.Connect;
using Emby.Server.Core;
using Emby.Server.Implementations;
using Emby.Server.Implementations.EntryPoints;
using Emby.Server.Implementations.FFMpeg;
+using Emby.Server.Sync;
+using MediaBrowser.Controller.Connect;
+using MediaBrowser.Controller.Sync;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.System;
@@ -27,36 +31,14 @@ namespace MediaBrowser.ServerApplication
get { return MainStartup.IsRunningAsService; }
}
- protected override FFMpegInstallInfo GetFfmpegInstallInfo()
+ protected override IConnectManager CreateConnectManager()
{
- var info = new FFMpegInstallInfo();
-
- info.FFMpegFilename = "ffmpeg.exe";
- info.FFProbeFilename = "ffprobe.exe";
- info.Version = "20160410";
- info.ArchiveType = "7z";
- info.DownloadUrls = GetDownloadUrls();
-
- return info;
+ return new ConnectManager();
}
- private string[] GetDownloadUrls()
+ protected override ISyncManager CreateSyncManager()
{
- switch (EnvironmentInfo.SystemArchitecture)
- {
- case Architecture.X64:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win64.7z"
- };
- case Architecture.X86:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win32.7z"
- };
- }
-
- return new string[] { };
+ return new SyncManager();
}
protected override void RestartInternal()
@@ -73,6 +55,8 @@ namespace MediaBrowser.ServerApplication
//list.Add(typeof(PismoIsoManager).Assembly);
}
+ list.Add(typeof(ConnectManager).Assembly);
+ list.Add(typeof(SyncManager).Assembly);
list.Add(GetType().Assembly);
return list;
@@ -94,7 +78,7 @@ namespace MediaBrowser.ServerApplication
protected override void ConfigureAutoRunInternal(bool autorun)
{
- var startupPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Startup);
+ var startupPath = Environment.GetFolderPath(System.Environment.SpecialFolder.Startup);
if (autorun && !MainStartup.IsRunningAsService)
{
@@ -121,14 +105,6 @@ namespace MediaBrowser.ServerApplication
}
}
- protected override bool SupportsDualModeSockets
- {
- get
- {
- return true;
- }
- }
-
protected override void EnableLoopbackInternal(string appName)
{
LoopUtil.Run(appName);
@@ -150,14 +126,6 @@ namespace MediaBrowser.ServerApplication
}
}
- public override bool SupportsAutoRunAtStartup
- {
- get
- {
- return true;
- }
- }
-
public override bool CanSelfUpdate
{
get
@@ -165,61 +133,5 @@ namespace MediaBrowser.ServerApplication
return MainStartup.CanSelfUpdate;
}
}
-
- public bool PortsRequireAuthorization(string applicationPath)
- {
- var appNameSrch = Path.GetFileName(applicationPath);
-
- var startInfo = new ProcessStartInfo
- {
- FileName = "netsh",
-
- Arguments = "advfirewall firewall show rule \"" + appNameSrch + "\"",
-
- CreateNoWindow = true,
- UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false,
- RedirectStandardOutput = true
- };
-
- using (var process = Process.Start(startInfo))
- {
- process.Start();
-
- try
- {
- var data = process.StandardOutput.ReadToEnd() ?? string.Empty;
-
- if (data.IndexOf("Block", StringComparison.OrdinalIgnoreCase) != -1)
- {
- Logger.Info("Found potential windows firewall rule blocking Emby Server: " + data);
- }
-
- //var parts = data.Split('\n');
-
- //return parts.Length > 4;
- //return Confirm();
- return false;
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error querying windows firewall", ex);
-
- // Hate having to do this
- try
- {
- process.Kill();
- }
- catch (Exception ex1)
- {
- Logger.ErrorException("Error killing process", ex1);
- }
-
- throw;
- }
- }
- }
-
}
}
diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config
index 2cebb9aea..68d0a7fda 100644
--- a/MediaBrowser.ServerApplication/packages.config
+++ b/MediaBrowser.ServerApplication/packages.config
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ImageMagickSharp" version="1.0.0.18" targetFramework="net45" />
- <package id="NLog" version="4.4.0-betaV15" targetFramework="net462" />
+ <package id="NLog" version="4.4.3" targetFramework="net462" />
<package id="ServiceStack.Text" version="4.5.4" targetFramework="net462" />
<package id="SharpCompress" version="0.14.0" targetFramework="net462" />
- <package id="SimpleInjector" version="3.2.4" targetFramework="net462" />
- <package id="SQLitePCLRaw.core" version="1.1.1" targetFramework="net462" />
- <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.1" targetFramework="net462" />
+ <package id="SimpleInjector" version="3.3.2" targetFramework="net462" />
+ <package id="SQLitePCLRaw.core" version="1.1.2" targetFramework="net462" />
+ <package id="SQLitePCLRaw.provider.sqlite3.net45" version="1.1.2" targetFramework="net462" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
index 57238ef87..98016f4f7 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs
@@ -65,22 +65,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
- case "animeseriesindex":
- {
- var number = reader.ReadElementContentAsString();
-
- if (!string.IsNullOrWhiteSpace(number))
- {
- int num;
-
- if (int.TryParse(number, out num))
- {
- item.AnimeSeriesIndex = num;
- }
- }
- break;
- }
-
case "status":
{
var status = reader.ReadElementContentAsString();
diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
index 9e48b0c8b..e9a5d4d60 100644
--- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs
@@ -83,11 +83,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
{
writer.WriteElementString("airs_dayofweek", series.AirDays[0].ToString());
}
-
- if (series.AnimeSeriesIndex.HasValue)
- {
- writer.WriteElementString("animeseriesindex", series.AnimeSeriesIndex.Value.ToString(CultureInfo.InvariantCulture));
- }
}
protected override List<string> GetTagsUsed(IHasMetadata item)
@@ -101,8 +96,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
"episode",
"status",
"airs_time",
- "airs_dayofweek",
- "animeseriesindex"
+ "airs_dayofweek"
});
return list;
}
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index 292d0345c..b9933969f 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -78,8 +78,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.ImageMagick",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Emby.Drawing.Net", "Emby.Drawing.Net\Emby.Drawing.Net.csproj", "{C97A239E-A96C-4D64-A844-CCF8CC30AECB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceStack", "ServiceStack\ServiceStack.csproj", "{680A1709-25EB-4D52-A87F-EE03FFD94BAA}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketHttpListener.Portable", "SocketHttpListener.Portable\SocketHttpListener.Portable.csproj", "{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}"
EndProject
Global
@@ -1061,46 +1059,6 @@ Global
{C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x64.Build.0 = Release|Any CPU
{C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.ActiveCfg = Release|Any CPU
{C97A239E-A96C-4D64-A844-CCF8CC30AECB}.Signed|x86.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Win32.ActiveCfg = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|Win32.Build.0 = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x64.ActiveCfg = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x64.Build.0 = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.ActiveCfg = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Debug|x86.Build.0 = Debug|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Any CPU.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Mixed Platforms.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Mixed Platforms.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Win32.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|Win32.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x64.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x64.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release Mono|x86.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Any CPU.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Win32.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|Win32.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x64.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x64.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.ActiveCfg = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Release|x86.Build.0 = Release|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Any CPU.Build.0 = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Mixed Platforms.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Mixed Platforms.Build.0 = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Win32.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|Win32.Build.0 = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x64.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x64.Build.0 = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.ActiveCfg = Signed|Any CPU
- {680A1709-25EB-4D52-A87F-EE03FFD94BAA}.Signed|x86.Build.0 = Signed|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F26D5D8-A7B0-42B3-BA42-7CB7D245934E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index 292c80a7c..fdc2b9f7d 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
- <version>3.0.680</version>
+ <version>3.0.681</version>
<title>Emby.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index 4bb58cd73..95cee8be1 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.694</version>
+ <version>3.0.695</version>
<title>Emby.Common</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index c475a4c91..f703690a9 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.694</version>
+ <version>3.0.695</version>
<title>Emby.Server.Core</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.694" />
+ <dependency id="MediaBrowser.Common" version="3.0.695" />
</dependencies>
</metadata>
<files>
diff --git a/ServiceStack/FilterAttributeCache.cs b/ServiceStack/FilterAttributeCache.cs
deleted file mode 100644
index 378433add..000000000
--- a/ServiceStack/FilterAttributeCache.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Threading;
-using ServiceStack;
-
-namespace ServiceStack.Support.WebHost
-{
- public static class FilterAttributeCache
- {
- public static MediaBrowser.Model.Services.IHasRequestFilter[] GetRequestFilterAttributes(Type requestDtoType)
- {
- var attributes = requestDtoType.AllAttributes().OfType<MediaBrowser.Model.Services.IHasRequestFilter>().ToList();
-
- var serviceType = ServiceStackHost.Instance.Metadata.GetServiceTypeByRequest(requestDtoType);
- if (serviceType != null)
- {
- attributes.AddRange(serviceType.AllAttributes().OfType<MediaBrowser.Model.Services.IHasRequestFilter>());
- }
-
- attributes.Sort((x,y) => x.Priority - y.Priority);
-
- return attributes.ToArray();
- }
- }
-}
diff --git a/ServiceStack/Host/ContentTypes.cs b/ServiceStack/Host/ContentTypes.cs
deleted file mode 100644
index f7734a36b..000000000
--- a/ServiceStack/Host/ContentTypes.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using MediaBrowser.Model.Services;
-
-namespace ServiceStack.Host
-{
- public class ContentTypes
- {
- public static ContentTypes Instance = new ContentTypes();
-
- public void SerializeToStream(IRequest req, object response, Stream responseStream)
- {
- var contentType = req.ResponseContentType;
- var serializer = GetStreamSerializer(contentType);
-
- serializer(response, responseStream);
- }
-
- public static Action<object, Stream> GetStreamSerializer(string contentType)
- {
- switch (GetRealContentType(contentType))
- {
- case "application/xml":
- case "text/xml":
- case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
- return (o, s) => ServiceStackHost.Instance.SerializeToXml(o, s);
-
- case "application/json":
- case "text/json":
- return (o, s) => ServiceStackHost.Instance.SerializeToJson(o, s);
- }
-
- return null;
- }
-
- public Func<Type, Stream, object> GetStreamDeserializer(string contentType)
- {
- switch (GetRealContentType(contentType))
- {
- case "application/xml":
- case "text/xml":
- case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
- return ServiceStackHost.Instance.DeserializeXml;
-
- case "application/json":
- case "text/json":
- return ServiceStackHost.Instance.DeserializeJson;
- }
-
- return null;
- }
-
- private static string GetRealContentType(string contentType)
- {
- return contentType == null
- ? null
- : contentType.Split(';')[0].ToLower().Trim();
- }
-
- }
-} \ No newline at end of file
diff --git a/ServiceStack/Host/RestHandler.cs b/ServiceStack/Host/RestHandler.cs
deleted file mode 100644
index 1eae6be38..000000000
--- a/ServiceStack/Host/RestHandler.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.Serialization;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace ServiceStack.Host
-{
- public class RestHandler
- {
- public string RequestName { get; set; }
-
- public async Task<object> HandleResponseAsync(object response)
- {
- var taskResponse = response as Task;
-
- if (taskResponse == null)
- {
- return response;
- }
-
- await taskResponse.ConfigureAwait(false);
-
- var taskResult = ServiceStackHost.Instance.GetTaskResult(taskResponse, RequestName);
-
- var subTask = taskResult as Task;
- if (subTask != null)
- {
- taskResult = ServiceStackHost.Instance.GetTaskResult(subTask, RequestName);
- }
-
- return taskResult;
- }
-
- protected static object CreateContentTypeRequest(IRequest httpReq, Type requestType, string contentType)
- {
- if (!string.IsNullOrEmpty(contentType) && httpReq.ContentLength > 0)
- {
- var deserializer = ContentTypes.Instance.GetStreamDeserializer(contentType);
- if (deserializer != null)
- {
- return deserializer(requestType, httpReq.InputStream);
- }
- }
- return ServiceStackHost.Instance.CreateInstance(requestType); //Return an empty DTO, even for empty request bodies
- }
-
- protected static object GetCustomRequestFromBinder(IRequest httpReq, Type requestType)
- {
- Func<IRequest, object> requestFactoryFn;
- ServiceStackHost.Instance.ServiceController.RequestTypeFactoryMap.TryGetValue(
- requestType, out requestFactoryFn);
-
- return requestFactoryFn != null ? requestFactoryFn(httpReq) : null;
- }
-
- public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, out string contentType)
- {
- pathInfo = GetSanitizedPathInfo(pathInfo, out contentType);
-
- return ServiceStackHost.Instance.ServiceController.GetRestPathForRequest(httpMethod, pathInfo);
- }
-
- public static string GetSanitizedPathInfo(string pathInfo, out string contentType)
- {
- contentType = null;
- var pos = pathInfo.LastIndexOf('.');
- if (pos >= 0)
- {
- var format = pathInfo.Substring(pos + 1);
- contentType = GetFormatContentType(format);
- if (contentType != null)
- {
- pathInfo = pathInfo.Substring(0, pos);
- }
- }
- return pathInfo;
- }
-
- private static string GetFormatContentType(string format)
- {
- //built-in formats
- if (format == "json")
- return "application/json";
- if (format == "xml")
- return "application/xml";
-
- return null;
- }
-
- public RestPath GetRestPath(string httpMethod, string pathInfo)
- {
- if (this.RestPath == null)
- {
- string contentType;
- this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, out contentType);
-
- if (contentType != null)
- ResponseContentType = contentType;
- }
- return this.RestPath;
- }
-
- public RestPath RestPath { get; set; }
-
- // Set from SSHHF.GetHandlerForPathInfo()
- public string ResponseContentType { get; set; }
-
- public async Task ProcessRequestAsync(IRequest httpReq, IResponse httpRes, string operationName)
- {
- var appHost = ServiceStackHost.Instance;
-
- var restPath = GetRestPath(httpReq.Verb, httpReq.PathInfo);
- if (restPath == null)
- {
- throw new NotSupportedException("No RestPath found for: " + httpReq.Verb + " " + httpReq.PathInfo);
- }
- httpReq.SetRoute(restPath);
-
- if (ResponseContentType != null)
- httpReq.ResponseContentType = ResponseContentType;
-
- var request = httpReq.Dto = CreateRequest(httpReq, restPath);
-
- appHost.ApplyRequestFilters(httpReq, httpRes, request);
-
- var rawResponse = await ServiceStackHost.Instance.ServiceController.Execute(request, httpReq).ConfigureAwait(false);
-
- var response = await HandleResponseAsync(rawResponse).ConfigureAwait(false);
-
- appHost.ApplyResponseFilters(httpReq, httpRes, response);
-
- await httpRes.WriteToResponse(httpReq, response).ConfigureAwait(false);
- }
-
- public static object CreateRequest(IRequest httpReq, RestPath restPath)
- {
- var dtoFromBinder = GetCustomRequestFromBinder(httpReq, restPath.RequestType);
- if (dtoFromBinder != null)
- return dtoFromBinder;
-
- var requestParams = httpReq.GetFlattenedRequestParams();
- return CreateRequest(httpReq, restPath, requestParams);
- }
-
- public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams)
- {
- var requestDto = CreateContentTypeRequest(httpReq, restPath.RequestType, httpReq.ContentType);
-
- return CreateRequest(httpReq, restPath, requestParams, requestDto);
- }
-
- public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto)
- {
- string contentType;
- var pathInfo = !restPath.IsWildCardPath
- ? GetSanitizedPathInfo(httpReq.PathInfo, out contentType)
- : httpReq.PathInfo;
-
- return restPath.CreateRequest(pathInfo, requestParams, requestDto);
- }
-
- /// <summary>
- /// Used in Unit tests
- /// </summary>
- /// <returns></returns>
- public object CreateRequest(IRequest httpReq, string operationName)
- {
- if (this.RestPath == null)
- throw new ArgumentNullException("No RestPath found");
-
- return CreateRequest(httpReq, this.RestPath);
- }
- }
-
-}
diff --git a/ServiceStack/Host/ServiceMetadata.cs b/ServiceStack/Host/ServiceMetadata.cs
deleted file mode 100644
index 240e6f32d..000000000
--- a/ServiceStack/Host/ServiceMetadata.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace ServiceStack.Host
-{
- public class ServiceMetadata
- {
- public ServiceMetadata()
- {
- this.OperationsMap = new Dictionary<Type, Type>();
- }
-
- public Dictionary<Type, Type> OperationsMap { get; protected set; }
-
- public void Add(Type serviceType, Type requestType, Type responseType)
- {
- this.OperationsMap[requestType] = serviceType;
- }
-
- public Type GetServiceTypeByRequest(Type requestType)
- {
- Type serviceType;
- OperationsMap.TryGetValue(requestType, out serviceType);
- return serviceType;
- }
- }
-}
diff --git a/ServiceStack/HttpHandlerFactory.cs b/ServiceStack/HttpHandlerFactory.cs
deleted file mode 100644
index 5f4892d51..000000000
--- a/ServiceStack/HttpHandlerFactory.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Services;
-using ServiceStack.Host;
-
-namespace ServiceStack
-{
- public class HttpHandlerFactory
- {
- // Entry point for HttpListener
- public static RestHandler GetHandler(IHttpRequest httpReq, ILogger logger)
- {
- var pathInfo = httpReq.PathInfo;
-
- var pathParts = pathInfo.TrimStart('/').Split('/');
- if (pathParts.Length == 0)
- {
- logger.Error("Path parts empty for PathInfo: {0}, Url: {1}", pathInfo, httpReq.RawUrl);
- return null;
- }
-
- string contentType;
- var restPath = RestHandler.FindMatchingRestPath(httpReq.HttpMethod, pathInfo, out contentType);
- if (restPath != null)
- return new RestHandler { RestPath = restPath, RequestName = restPath.RequestType.GetOperationName(), ResponseContentType = contentType };
-
- return null;
- }
- }
-} \ No newline at end of file
diff --git a/ServiceStack/HttpRequestExtensions.cs b/ServiceStack/HttpRequestExtensions.cs
deleted file mode 100644
index c34d62601..000000000
--- a/ServiceStack/HttpRequestExtensions.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Services;
-using ServiceStack.Host;
-
-namespace ServiceStack
-{
- public static class HttpRequestExtensions
- {
- /**
- *
- Input: http://localhost:96/Cambia3/Temp/Test.aspx/path/info?q=item#fragment
-
- Some HttpRequest path and URL properties:
- Request.ApplicationPath: /Cambia3
- Request.CurrentExecutionFilePath: /Cambia3/Temp/Test.aspx
- Request.FilePath: /Cambia3/Temp/Test.aspx
- Request.Path: /Cambia3/Temp/Test.aspx/path/info
- Request.PathInfo: /path/info
- Request.PhysicalApplicationPath: D:\Inetpub\wwwroot\CambiaWeb\Cambia3\
- Request.QueryString: /Cambia3/Temp/Test.aspx/path/info?query=arg
- Request.Url.AbsolutePath: /Cambia3/Temp/Test.aspx/path/info
- Request.Url.AbsoluteUri: http://localhost:96/Cambia3/Temp/Test.aspx/path/info?query=arg
- Request.Url.Fragment:
- Request.Url.Host: localhost
- Request.Url.LocalPath: /Cambia3/Temp/Test.aspx/path/info
- Request.Url.PathAndQuery: /Cambia3/Temp/Test.aspx/path/info?query=arg
- Request.Url.Port: 96
- Request.Url.Query: ?query=arg
- Request.Url.Scheme: http
- Request.Url.Segments: /
- Cambia3/
- Temp/
- Test.aspx/
- path/
- info
- * */
-
- /// <summary>
- /// Duplicate Params are given a unique key by appending a #1 suffix
- /// </summary>
- public static Dictionary<string, string> GetRequestParams(this IRequest request)
- {
- var map = new Dictionary<string, string>();
-
- foreach (var name in request.QueryString.Keys)
- {
- if (name == null) continue; //thank you ASP.NET
-
- var values = request.QueryString.GetValues(name);
- if (values.Length == 1)
- {
- map[name] = values[0];
- }
- else
- {
- for (var i = 0; i < values.Length; i++)
- {
- map[name + (i == 0 ? "" : "#" + i)] = values[i];
- }
- }
- }
-
- if ((request.Verb == HttpMethods.Post || request.Verb == HttpMethods.Put)
- && request.FormData != null)
- {
- foreach (var name in request.FormData.Keys)
- {
- if (name == null) continue; //thank you ASP.NET
-
- var values = request.FormData.GetValues(name);
- if (values.Length == 1)
- {
- map[name] = values[0];
- }
- else
- {
- for (var i = 0; i < values.Length; i++)
- {
- map[name + (i == 0 ? "" : "#" + i)] = values[i];
- }
- }
- }
- }
-
- return map;
- }
-
- /// <summary>
- /// Duplicate params have their values joined together in a comma-delimited string
- /// </summary>
- public static Dictionary<string, string> GetFlattenedRequestParams(this IRequest request)
- {
- var map = new Dictionary<string, string>();
-
- foreach (var name in request.QueryString.Keys)
- {
- if (name == null) continue; //thank you ASP.NET
- map[name] = request.QueryString[name];
- }
-
- if ((request.Verb == HttpMethods.Post || request.Verb == HttpMethods.Put)
- && request.FormData != null)
- {
- foreach (var name in request.FormData.Keys)
- {
- if (name == null) continue; //thank you ASP.NET
- map[name] = request.FormData[name];
- }
- }
-
- return map;
- }
-
- public static void SetRoute(this IRequest req, RestPath route)
- {
- req.Items["__route"] = route;
- }
-
- public static RestPath GetRoute(this IRequest req)
- {
- object route;
- req.Items.TryGetValue("__route", out route);
- return route as RestPath;
- }
- }
-} \ No newline at end of file
diff --git a/ServiceStack/HttpUtils.cs b/ServiceStack/HttpUtils.cs
deleted file mode 100644
index 41d191d61..000000000
--- a/ServiceStack/HttpUtils.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-//Copyright (c) Service Stack LLC. All Rights Reserved.
-//License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
-
-using System;
-using System.Collections.Generic;
-
-namespace ServiceStack
-{
- internal static class HttpMethods
- {
- static readonly string[] allVerbs = new[] {
- "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", // RFC 2616
- "PROPFIND", "PROPPATCH", "MKCOL", "COPY", "MOVE", "LOCK", "UNLOCK", // RFC 2518
- "VERSION-CONTROL", "REPORT", "CHECKOUT", "CHECKIN", "UNCHECKOUT",
- "MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY", // RFC 3253
- "ORDERPATCH", // RFC 3648
- "ACL", // RFC 3744
- "PATCH", // https://datatracker.ietf.org/doc/draft-dusseault-http-patch/
- "SEARCH", // https://datatracker.ietf.org/doc/draft-reschke-webdav-search/
- "BCOPY", "BDELETE", "BMOVE", "BPROPFIND", "BPROPPATCH", "NOTIFY",
- "POLL", "SUBSCRIBE", "UNSUBSCRIBE" //MS Exchange WebDav: http://msdn.microsoft.com/en-us/library/aa142917.aspx
- };
-
- public static HashSet<string> AllVerbs = new HashSet<string>(allVerbs);
-
- public const string Get = "GET";
- public const string Put = "PUT";
- public const string Post = "POST";
- public const string Delete = "DELETE";
- public const string Options = "OPTIONS";
- public const string Head = "HEAD";
- public const string Patch = "PATCH";
- }
-}
diff --git a/ServiceStack/Properties/AssemblyInfo.cs b/ServiceStack/Properties/AssemblyInfo.cs
deleted file mode 100644
index 6073dc0b4..000000000
--- a/ServiceStack/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("ServiceStack")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Service Stack LLC")]
-[assembly: AssemblyProduct("ServiceStack")]
-[assembly: AssemblyCopyright("Copyright (c) ServiceStack 2016")]
-[assembly: AssemblyTrademark("Service Stack")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("06704d66-af8e-411f-8260-8d05de5ce6ad")]
-
-[assembly: AssemblyVersion("4.0.0.0")]
-[assembly: AssemblyFileVersion("4.0.0.0")]
diff --git a/ServiceStack/ReflectionExtensions.cs b/ServiceStack/ReflectionExtensions.cs
deleted file mode 100644
index bbabd0dd7..000000000
--- a/ServiceStack/ReflectionExtensions.cs
+++ /dev/null
@@ -1,270 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Runtime.Serialization;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace ServiceStack
-{
- public static class ReflectionExtensions
- {
- public static bool IsInstanceOf(this Type type, Type thisOrBaseType)
- {
- while (type != null)
- {
- if (type == thisOrBaseType)
- return true;
-
- type = type.BaseType();
- }
- return false;
- }
-
- public static Type FirstGenericType(this Type type)
- {
- while (type != null)
- {
- if (type.IsGeneric())
- return type;
-
- type = type.BaseType();
- }
- return null;
- }
-
- public static Type GetTypeWithGenericTypeDefinitionOf(this Type type, Type genericTypeDefinition)
- {
- foreach (var t in type.GetTypeInterfaces())
- {
- if (t.IsGeneric() && t.GetGenericTypeDefinition() == genericTypeDefinition)
- {
- return t;
- }
- }
-
- var genericType = type.FirstGenericType();
- if (genericType != null && genericType.GetGenericTypeDefinition() == genericTypeDefinition)
- {
- return genericType;
- }
-
- return null;
- }
-
- public static PropertyInfo[] GetAllProperties(this Type type)
- {
- if (type.IsInterface())
- {
- var propertyInfos = new List<PropertyInfo>();
-
- var considered = new List<Type>();
- var queue = new Queue<Type>();
- considered.Add(type);
- queue.Enqueue(type);
-
- while (queue.Count > 0)
- {
- var subType = queue.Dequeue();
- foreach (var subInterface in subType.GetTypeInterfaces())
- {
- if (considered.Contains(subInterface)) continue;
-
- considered.Add(subInterface);
- queue.Enqueue(subInterface);
- }
-
- var typeProperties = subType.GetTypesProperties();
-
- var newPropertyInfos = typeProperties
- .Where(x => !propertyInfos.Contains(x));
-
- propertyInfos.InsertRange(0, newPropertyInfos);
- }
-
- return propertyInfos.ToArray();
- }
-
- return type.GetTypesProperties()
- .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties
- .ToArray();
- }
-
- public static PropertyInfo[] GetPublicProperties(this Type type)
- {
- if (type.IsInterface())
- {
- var propertyInfos = new List<PropertyInfo>();
-
- var considered = new List<Type>();
- var queue = new Queue<Type>();
- considered.Add(type);
- queue.Enqueue(type);
-
- while (queue.Count > 0)
- {
- var subType = queue.Dequeue();
- foreach (var subInterface in subType.GetTypeInterfaces())
- {
- if (considered.Contains(subInterface)) continue;
-
- considered.Add(subInterface);
- queue.Enqueue(subInterface);
- }
-
- var typeProperties = subType.GetTypesPublicProperties();
-
- var newPropertyInfos = typeProperties
- .Where(x => !propertyInfos.Contains(x));
-
- propertyInfos.InsertRange(0, newPropertyInfos);
- }
-
- return propertyInfos.ToArray();
- }
-
- return type.GetTypesPublicProperties()
- .Where(t => t.GetIndexParameters().Length == 0) // ignore indexed properties
- .ToArray();
- }
-
- public const string DataMember = "DataMemberAttribute";
-
- internal static string[] IgnoreAttributesNamed = new[] {
- "IgnoreDataMemberAttribute",
- "JsonIgnoreAttribute"
- };
-
- public static PropertyInfo[] GetSerializableProperties(this Type type)
- {
- var properties = type.IsDto()
- ? type.GetAllProperties()
- : type.GetPublicProperties();
- return properties.OnlySerializableProperties(type);
- }
-
-
- private static List<Type> _excludeTypes = new List<Type> { typeof(Stream) };
-
- public static PropertyInfo[] OnlySerializableProperties(this PropertyInfo[] properties, Type type = null)
- {
- var isDto = type.IsDto();
- var readableProperties = properties.Where(x => x.PropertyGetMethod(nonPublic: isDto) != null);
-
- if (isDto)
- {
- return readableProperties.Where(attr =>
- attr.HasAttribute<DataMemberAttribute>()).ToArray();
- }
-
- // else return those properties that are not decorated with IgnoreDataMember
- return readableProperties
- .Where(prop => prop.AllAttributes()
- .All(attr =>
- {
- var name = attr.GetType().Name;
- return !IgnoreAttributesNamed.Contains(name);
- }))
- .Where(prop => !_excludeTypes.Contains(prop.PropertyType))
- .ToArray();
- }
- }
-
- public static class PlatformExtensions //Because WinRT is a POS
- {
- public static bool IsInterface(this Type type)
- {
- return type.GetTypeInfo().IsInterface;
- }
-
- public static bool IsGeneric(this Type type)
- {
- return type.GetTypeInfo().IsGenericType;
- }
-
- public static Type BaseType(this Type type)
- {
- return type.GetTypeInfo().BaseType;
- }
-
- public static Type[] GetTypeInterfaces(this Type type)
- {
- return type.GetTypeInfo().ImplementedInterfaces.ToArray();
- }
-
- internal static PropertyInfo[] GetTypesPublicProperties(this Type subType)
- {
- var pis = new List<PropertyInfo>();
- foreach (var pi in subType.GetRuntimeProperties())
- {
- var mi = pi.GetMethod ?? pi.SetMethod;
- if (mi != null && mi.IsStatic) continue;
- pis.Add(pi);
- }
- return pis.ToArray();
- }
-
- internal static PropertyInfo[] GetTypesProperties(this Type subType)
- {
- var pis = new List<PropertyInfo>();
- foreach (var pi in subType.GetRuntimeProperties())
- {
- var mi = pi.GetMethod ?? pi.SetMethod;
- if (mi != null && mi.IsStatic) continue;
- pis.Add(pi);
- }
- return pis.ToArray();
- }
-
- public static bool HasAttribute<T>(this Type type)
- {
- return type.AllAttributes().Any(x => x.GetType() == typeof(T));
- }
-
- public static bool HasAttribute<T>(this PropertyInfo pi)
- {
- return pi.AllAttributes().Any(x => x.GetType() == typeof(T));
- }
-
- public static bool IsDto(this Type type)
- {
- if (type == null)
- return false;
-
- return type.HasAttribute<DataContractAttribute>();
- }
-
- public static MethodInfo PropertyGetMethod(this PropertyInfo pi, bool nonPublic = false)
- {
- return pi.GetMethod;
- }
-
- public static object[] AllAttributes(this PropertyInfo propertyInfo)
- {
- return propertyInfo.GetCustomAttributes(true).ToArray();
- }
-
- public static object[] AllAttributes(this PropertyInfo propertyInfo, Type attrType)
- {
- return propertyInfo.GetCustomAttributes(true).Where(x => attrType.IsInstanceOf(x.GetType())).ToArray();
- }
-
- public static object[] AllAttributes(this Type type)
- {
- return type.GetTypeInfo().GetCustomAttributes(true).ToArray();
- }
-
- public static TAttr[] AllAttributes<TAttr>(this PropertyInfo pi)
- {
- return pi.AllAttributes(typeof(TAttr)).Cast<TAttr>().ToArray();
- }
-
- public static TAttr[] AllAttributes<TAttr>(this Type type)
- where TAttr : Attribute
- {
- return type.GetTypeInfo().GetCustomAttributes<TAttr>(true).ToArray();
- }
- }
-}
diff --git a/ServiceStack/ServiceStack.csproj b/ServiceStack/ServiceStack.csproj
deleted file mode 100644
index 5413d4e55..000000000
--- a/ServiceStack/ServiceStack.csproj
+++ /dev/null
@@ -1,130 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProductVersion>9.0.30729</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{680A1709-25EB-4D52-A87F-EE03FFD94BAA}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>ServiceStack</RootNamespace>
- <AssemblyName>ServiceStack</AssemblyName>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
- <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- <FileUpgradeFlags>
- </FileUpgradeFlags>
- <OldToolsVersion>3.5</OldToolsVersion>
- <UpgradeBackupLocation />
- <PublishUrl>publish\</PublishUrl>
- <Install>true</Install>
- <InstallFrom>Disk</InstallFrom>
- <UpdateEnabled>false</UpdateEnabled>
- <UpdateMode>Foreground</UpdateMode>
- <UpdateInterval>7</UpdateInterval>
- <UpdateIntervalUnits>Days</UpdateIntervalUnits>
- <UpdatePeriodically>false</UpdatePeriodically>
- <UpdateRequired>false</UpdateRequired>
- <MapFileExtensions>true</MapFileExtensions>
- <ApplicationRevision>0</ApplicationRevision>
- <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
- <IsWebBootstrapper>false</IsWebBootstrapper>
- <UseApplicationTrust>false</UseApplicationTrust>
- <BootstrapperEnabled>true</BootstrapperEnabled>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>True</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>False</Optimize>
- <OutputPath>bin\Debug\</OutputPath>
- <DefineConstants>TRACE;DEBUG;MONO</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
- <Prefer32Bit>false</Prefer32Bit>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>pdbonly</DebugType>
- <Optimize>True</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
- <DocumentationFile>
- </DocumentationFile>
- <Prefer32Bit>false</Prefer32Bit>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Signed|AnyCPU'">
- <OutputPath>bin\Signed\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <DocumentationFile>bin\Release\ServiceStack.XML</DocumentationFile>
- <Optimize>true</Optimize>
- <DebugType>pdbonly</DebugType>
- <PlatformTarget>AnyCPU</PlatformTarget>
- <ErrorReport>prompt</ErrorReport>
- <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
- <Prefer32Bit>false</Prefer32Bit>
- </PropertyGroup>
- <ItemGroup>
- <Compile Include="HttpUtils.cs" />
- <Compile Include="Host\ContentTypes.cs" />
- <Compile Include="ReflectionExtensions.cs" />
- <Compile Include="StringMapTypeDeserializer.cs" />
- <Compile Include="HttpResult.cs" />
- <Compile Include="ServiceStackHost.cs" />
- <Compile Include="ServiceStackHost.Runtime.cs" />
- <Compile Include="Host\ServiceExec.cs" />
- <Compile Include="UrlExtensions.cs" />
- <Compile Include="Host\ActionContext.cs" />
- <Compile Include="HttpRequestExtensions.cs" />
- <Compile Include="Host\RestPath.cs" />
- <Compile Include="Host\ServiceController.cs" />
- <Compile Include="Host\ServiceMetadata.cs" />
- <Compile Include="Host\RestHandler.cs" />
- <Compile Include="HttpResponseExtensionsInternal.cs" />
- <Compile Include="HttpHandlerFactory.cs" />
- <Compile Include="FilterAttributeCache.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- </ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
- <ItemGroup>
- <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
- <Visible>False</Visible>
- <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
- <Install>false</Install>
- </BootstrapperPackage>
- <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
- <Visible>False</Visible>
- <ProductName>.NET Framework 3.5 SP1</ProductName>
- <Install>true</Install>
- </BootstrapperPackage>
- <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
- <Visible>False</Visible>
- <ProductName>Windows Installer 3.1</ProductName>
- <Install>true</Install>
- </BootstrapperPackage>
- </ItemGroup>
- <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
- Other similar extension points exist, see Microsoft.Common.targets.
- <Target Name="BeforeBuild">
- </Target>
- <Target Name="AfterBuild">
- </Target>
- -->
- <ItemGroup>
- <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
- <Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
- <Name>MediaBrowser.Common</Name>
- </ProjectReference>
- <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
- <Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
- <Name>MediaBrowser.Model</Name>
- </ProjectReference>
- </ItemGroup>
-</Project> \ No newline at end of file
diff --git a/ServiceStack/ServiceStack.nuget.targets b/ServiceStack/ServiceStack.nuget.targets
deleted file mode 100644
index e69ce0e64..000000000
--- a/ServiceStack/ServiceStack.nuget.targets
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="no"?>
-<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Target Name="EmitMSBuildWarning" BeforeTargets="Build">
- <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." />
- </Target>
-</Project> \ No newline at end of file
diff --git a/ServiceStack/ServiceStackHost.Runtime.cs b/ServiceStack/ServiceStackHost.Runtime.cs
deleted file mode 100644
index aaa50633b..000000000
--- a/ServiceStack/ServiceStackHost.Runtime.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) Service Stack LLC. All Rights Reserved.
-// License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
-
-
-using MediaBrowser.Model.Services;
-using ServiceStack.Support.WebHost;
-
-namespace ServiceStack
-{
- public abstract partial class ServiceStackHost
- {
- /// <summary>
- /// Applies the request filters. Returns whether or not the request has been handled
- /// and no more processing should be done.
- /// </summary>
- /// <returns></returns>
- public virtual void ApplyRequestFilters(IRequest req, IResponse res, object requestDto)
- {
- //Exec all RequestFilter attributes with Priority < 0
- var attributes = FilterAttributeCache.GetRequestFilterAttributes(requestDto.GetType());
- var i = 0;
- for (; i < attributes.Length && attributes[i].Priority < 0; i++)
- {
- var attribute = attributes[i];
- attribute.RequestFilter(req, res, requestDto);
- }
-
- //Exec global filters
- foreach (var requestFilter in GlobalRequestFilters)
- {
- requestFilter(req, res, requestDto);
- }
-
- //Exec remaining RequestFilter attributes with Priority >= 0
- for (; i < attributes.Length && attributes[i].Priority >= 0; i++)
- {
- var attribute = attributes[i];
- attribute.RequestFilter(req, res, requestDto);
- }
- }
-
- /// <summary>
- /// Applies the response filters. Returns whether or not the request has been handled
- /// and no more processing should be done.
- /// </summary>
- /// <returns></returns>
- public virtual void ApplyResponseFilters(IRequest req, IResponse res, object response)
- {
- //Exec global filters
- foreach (var responseFilter in GlobalResponseFilters)
- {
- responseFilter(req, res, response);
- }
- }
- }
-
-} \ No newline at end of file
diff --git a/ServiceStack/ServiceStackHost.cs b/ServiceStack/ServiceStackHost.cs
deleted file mode 100644
index 8a1db25e4..000000000
--- a/ServiceStack/ServiceStackHost.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (c) Service Stack LLC. All Rights Reserved.
-// License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
-
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Services;
-using ServiceStack.Host;
-
-namespace ServiceStack
-{
- public abstract partial class ServiceStackHost : IDisposable
- {
- public static ServiceStackHost Instance { get; protected set; }
-
- protected ServiceStackHost(string serviceName)
- {
- ServiceName = serviceName;
- ServiceController = CreateServiceController();
-
- RestPaths = new List<RestPath>();
- Metadata = new ServiceMetadata();
- GlobalRequestFilters = new List<Action<IRequest, IResponse, object>>();
- GlobalResponseFilters = new List<Action<IRequest, IResponse, object>>();
- }
-
- public abstract void Configure();
-
- public abstract object CreateInstance(Type type);
-
- protected abstract ServiceController CreateServiceController();
-
- public virtual ServiceStackHost Init()
- {
- Instance = this;
-
- ServiceController.Init();
- Configure();
-
- ServiceController.AfterInit();
-
- return this;
- }
-
- public virtual ServiceStackHost Start(string urlBase)
- {
- throw new NotImplementedException("Start(listeningAtUrlBase) is not supported by this AppHost");
- }
-
- public string ServiceName { get; set; }
-
- public ServiceMetadata Metadata { get; set; }
-
- public ServiceController ServiceController { get; set; }
-
- public List<RestPath> RestPaths = new List<RestPath>();
-
- public List<Action<IRequest, IResponse, object>> GlobalRequestFilters { get; set; }
-
- public List<Action<IRequest, IResponse, object>> GlobalResponseFilters { get; set; }
-
- public abstract T TryResolve<T>();
- public abstract T Resolve<T>();
-
- public virtual MediaBrowser.Model.Services.RouteAttribute[] GetRouteAttributes(Type requestType)
- {
- return requestType.AllAttributes<MediaBrowser.Model.Services.RouteAttribute>();
- }
-
- public abstract object GetTaskResult(Task task, string requestName);
-
- public abstract Func<string, object> GetParseFn(Type propertyType);
-
- public abstract void SerializeToJson(object o, Stream stream);
- public abstract void SerializeToXml(object o, Stream stream);
- public abstract object DeserializeXml(Type type, Stream stream);
- public abstract object DeserializeJson(Type type, Stream stream);
-
- public virtual void Dispose()
- {
- //JsConfig.Reset(); //Clears Runtime Attributes
-
- Instance = null;
- }
-
- protected abstract ILogger Logger
- {
- get;
- }
-
- public void OnLogError(Type type, string message)
- {
- Logger.Error(message);
- }
-
- public void OnLogError(Type type, string message, Exception ex)
- {
- Logger.ErrorException(message, ex);
- }
- }
-}
diff --git a/ServiceStack/packages.config b/ServiceStack/packages.config
deleted file mode 100644
index 6b8deb9c9..000000000
--- a/ServiceStack/packages.config
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-</packages> \ No newline at end of file
diff --git a/ServiceStack/project.json b/ServiceStack/project.json
deleted file mode 100644
index fbbe9eaf3..000000000
--- a/ServiceStack/project.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "frameworks":{
- "netstandard1.6":{
- "dependencies":{
- "NETStandard.Library":"1.6.0",
- }
- },
- ".NETPortable,Version=v4.5,Profile=Profile7":{
- "buildOptions": {
- "define": [ ]
- },
- "frameworkAssemblies":{
-
- }
- }
- }
-} \ No newline at end of file
diff --git a/src/Emby.Server/CoreAppHost.cs b/src/Emby.Server/CoreAppHost.cs
index 09df664fa..43142b5e8 100644
--- a/src/Emby.Server/CoreAppHost.cs
+++ b/src/Emby.Server/CoreAppHost.cs
@@ -19,11 +19,6 @@ namespace Emby.Server
{
}
- public override bool IsRunningAsService
- {
- get { return false; }
- }
-
protected override void RestartInternal()
{
Program.Restart();
@@ -34,41 +29,6 @@ namespace Emby.Server
Program.Shutdown();
}
- protected override FFMpegInstallInfo GetFfmpegInstallInfo()
- {
- var info = new FFMpegInstallInfo();
-
- if (EnvironmentInfo.OperatingSystem == OperatingSystem.Windows)
- {
- info.FFMpegFilename = "ffmpeg.exe";
- info.FFProbeFilename = "ffprobe.exe";
- info.Version = "20160410";
- info.ArchiveType = "7z";
- info.DownloadUrls = GetDownloadUrls();
- }
-
- return info;
- }
-
- private string[] GetDownloadUrls()
- {
- switch (EnvironmentInfo.SystemArchitecture)
- {
- case Architecture.X64:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win64.7z"
- };
- case Architecture.X86:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win32.7z"
- };
- }
-
- return new string[] { };
- }
-
protected override List<Assembly> GetAssembliesWithPartsInternal()
{
var list = new List<Assembly>();
@@ -78,26 +38,6 @@ namespace Emby.Server
return list;
}
- protected override void AuthorizeServer()
- {
- }
-
- protected override void ConfigureAutoRunInternal(bool autorun)
- {
- }
-
- protected override void EnableLoopbackInternal(string appName)
- {
- }
-
- public override bool SupportsRunningAsService
- {
- get
- {
- return true;
- }
- }
-
public override bool CanSelfRestart
{
get
@@ -106,14 +46,6 @@ namespace Emby.Server
}
}
- public override bool SupportsAutoRunAtStartup
- {
- get
- {
- return true;
- }
- }
-
public override bool CanSelfUpdate
{
get
@@ -121,13 +53,5 @@ namespace Emby.Server
return Program.CanSelfUpdate;
}
}
-
- protected override bool SupportsDualModeSockets
- {
- get
- {
- return true;
- }
- }
}
}
diff --git a/src/Emby.Server/Emby.Server.xproj b/src/Emby.Server/Emby.Server.xproj
index 2462c5179..78276d17d 100644
--- a/src/Emby.Server/Emby.Server.xproj
+++ b/src/Emby.Server/Emby.Server.xproj
@@ -34,7 +34,6 @@
<ProjectReference Include="..\..\MediaBrowser.XbmcMetadata\MediaBrowser.XbmcMetadata.csproj" />
<ProjectReference Include="..\..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj" />
<ProjectReference Include="..\..\RSSDP\RSSDP.csproj" />
- <ProjectReference Include="..\..\ServiceStack\ServiceStack.csproj" />
<ProjectReference Include="..\..\SocketHttpListener.Portable\SocketHttpListener.Portable.csproj" />
</ItemGroup>
<ItemGroup>
diff --git a/src/Emby.Server/Program.cs b/src/Emby.Server/Program.cs
index fde2ab7b2..5e55cfa29 100644
--- a/src/Emby.Server/Program.cs
+++ b/src/Emby.Server/Program.cs
@@ -193,7 +193,7 @@ namespace Emby.Server
/// <param name="options">The options.</param>
private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, StartupOptions options, EnvironmentInfo environmentInfo)
{
- var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, true, appPaths.TempDirectory);
+ var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, false, appPaths.TempDirectory);
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
diff --git a/src/Emby.Server/project.json b/src/Emby.Server/project.json
index 83c9ce876..e1ff8cbcc 100644
--- a/src/Emby.Server/project.json
+++ b/src/Emby.Server/project.json
@@ -113,9 +113,6 @@
"RSSDP": {
"target": "project"
},
- "ServiceStack": {
- "target": "project"
- },
"SocketHttpListener.Portable": {
"target": "project"
}