diff --git a/Emby.Common.Implementations/IO/ManagedFileSystem.cs b/Emby.Common.Implementations/IO/ManagedFileSystem.cs index 4fb70d4e2..b5943e17b 100644 --- a/Emby.Common.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Common.Implementations/IO/ManagedFileSystem.cs @@ -397,16 +397,34 @@ namespace Emby.Common.Implementations.IO private FileAccess GetFileAccess(FileAccessMode mode) { - var val = (int)mode; - - return (FileAccess)val; + switch (mode) + { + case FileAccessMode.ReadWrite: + return FileAccess.ReadWrite; + case FileAccessMode.Write: + return FileAccess.Write; + case FileAccessMode.Read: + return FileAccess.Read; + default: + throw new Exception("Unrecognized FileAccessMode"); + } } private FileShare GetFileShare(FileShareMode mode) { - var val = (int)mode; - - return (FileShare)val; + switch (mode) + { + case FileShareMode.ReadWrite: + return FileShare.ReadWrite; + case FileShareMode.Write: + return FileShare.Write; + case FileShareMode.Read: + return FileShare.Read; + case FileShareMode.None: + return FileShare.None; + default: + throw new Exception("Unrecognized FileShareMode"); + } } public void SetHidden(string path, bool isHidden) diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs index 7e5d6e31c..90848d930 100644 --- a/Emby.Server.Core/ApplicationHost.cs +++ b/Emby.Server.Core/ApplicationHost.cs @@ -551,7 +551,7 @@ namespace Emby.Server.Core DisplayPreferencesRepository = displayPreferencesRepo; RegisterSingleInstance(DisplayPreferencesRepository); - var itemRepo = new SqliteItemRepository(ServerConfigurationManager, JsonSerializer, LogManager.GetLogger("SqliteItemRepository"), MemoryStreamFactory, assemblyInfo, FileSystemManager, EnvironmentInfo); + var itemRepo = new SqliteItemRepository(ServerConfigurationManager, JsonSerializer, LogManager.GetLogger("SqliteItemRepository"), MemoryStreamFactory, assemblyInfo, FileSystemManager, EnvironmentInfo, TimerFactory); ItemRepository = itemRepo; RegisterSingleInstance(ItemRepository); diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 5c60a6f86..308b8356f 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -88,11 +88,14 @@ namespace Emby.Server.Implementations.Data var queries = new List { - "PRAGMA temp_store = memory", - //"PRAGMA journal_mode=WAL" //"PRAGMA cache size=-10000" }; + if (EnableTempStoreMemory) + { + queries.Add("PRAGMA temp_store = memory"); + } + //var cacheSize = CacheSize; //if (cacheSize.HasValue) //{ @@ -116,7 +119,7 @@ namespace Emby.Server.Implementations.Data db.ExecuteAll(string.Join(";", queries.ToArray())); } } - else + else if (queries.Count > 0) { db.ExecuteAll(string.Join(";", queries.ToArray())); } @@ -124,6 +127,14 @@ namespace Emby.Server.Implementations.Data return db; } + protected virtual bool EnableTempStoreMemory + { + get + { + return false; + } + } + protected virtual int? CacheSize { get diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs index 17afbcfa9..1bd64b21d 100644 --- a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -63,8 +63,8 @@ namespace Emby.Server.Implementations.Data string[] queries = { - "create table if not exists userdisplaypreferences (id GUID, userId GUID, client text, data BLOB)", - "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)" + "create table if not exists userdisplaypreferences (id GUID, userId GUID, client text, data BLOB)", + "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)" }; connection.RunQueries(queries); @@ -107,10 +107,10 @@ namespace Emby.Server.Implementations.Data private void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, IDatabaseConnection connection) { - using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userid, @client, @data)")) - { - var serialized = _jsonSerializer.SerializeToBytes(displayPreferences, _memoryStreamProvider); + var serialized = _jsonSerializer.SerializeToBytes(displayPreferences, _memoryStreamProvider); + using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)")) + { statement.TryBind("@id", displayPreferences.Id.ToGuidParamValue()); statement.TryBind("@userId", userId.ToGuidParamValue()); statement.TryBind("@client", client); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index c6e5a6dcf..29aacc059 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -30,6 +30,7 @@ using MediaBrowser.Server.Implementations.Playlists; using MediaBrowser.Model.Reflection; using SQLitePCL.pretty; using MediaBrowser.Model.System; +using MediaBrowser.Model.Threading; namespace Emby.Server.Implementations.Data { @@ -68,11 +69,13 @@ namespace Emby.Server.Implementations.Data private readonly IMemoryStreamFactory _memoryStreamProvider; private readonly IFileSystem _fileSystem; private readonly IEnvironmentInfo _environmentInfo; + private readonly ITimerFactory _timerFactory; + private ITimer _shrinkMemoryTimer; /// /// Initializes a new instance of the class. /// - public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogger logger, IMemoryStreamFactory memoryStreamProvider, IAssemblyInfo assemblyInfo, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) + public SqliteItemRepository(IServerConfigurationManager config, IJsonSerializer jsonSerializer, ILogger logger, IMemoryStreamFactory memoryStreamProvider, IAssemblyInfo assemblyInfo, IFileSystem fileSystem, IEnvironmentInfo environmentInfo, ITimerFactory timerFactory) : base(logger) { if (config == null) @@ -89,6 +92,7 @@ namespace Emby.Server.Implementations.Data _memoryStreamProvider = memoryStreamProvider; _fileSystem = fileSystem; _environmentInfo = environmentInfo; + _timerFactory = timerFactory; _typeMapper = new TypeMapper(assemblyInfo); _criticReviewsPath = Path.Combine(_config.ApplicationPaths.DataPath, "critic-reviews"); @@ -119,6 +123,14 @@ namespace Emby.Server.Implementations.Data } } + protected override bool EnableTempStoreMemory + { + get + { + return true; + } + } + private SQLiteDatabaseConnection _backgroundConnection; protected override void CloseConnection() { @@ -129,6 +141,12 @@ namespace Emby.Server.Implementations.Data _backgroundConnection.Dispose(); _backgroundConnection = null; } + + if (_shrinkMemoryTimer != null) + { + _shrinkMemoryTimer.Dispose(); + _shrinkMemoryTimer = null; + } } /// @@ -364,13 +382,35 @@ namespace Emby.Server.Implementations.Data connection.RunQueries(postQueries); - //SqliteExtensions.Attach(_connection, Path.Combine(_config.ApplicationPaths.DataPath, "userdata_v2.db"), "UserDataDb"); //await Vacuum(_connection).ConfigureAwait(false); } userDataRepo.Initialize(WriteLock); _backgroundConnection = CreateConnection(true); + + _shrinkMemoryTimer = _timerFactory.Create(OnShrinkMemoryTimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(30)); + } + + private void OnShrinkMemoryTimerCallback(object state) + { + try + { + using (WriteLock.Write()) + { + using (var connection = CreateConnection()) + { + connection.RunQueries(new string[] + { + "pragma shrink_memory" + }); + } + } + } + catch (Exception ex) + { + Logger.ErrorException("Error running shrink memory", ex); + } } private readonly string[] _retriveItemColumns = @@ -666,7 +706,7 @@ namespace Emby.Server.Implementations.Data { var requiresReset = false; - var statements = db.PrepareAll(string.Join(";", new string[] + var statements = db.PrepareAll(string.Join(";", new string[] { GetSaveItemCommandText(), "delete from AncestorIds where ItemId=@ItemId", diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 4c1b8fcd9..b01f215e0 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -84,6 +84,14 @@ namespace Emby.Server.Implementations.Data } } + protected override bool EnableTempStoreMemory + { + get + { + return true; + } + } + private void ImportUserDataIfNeeded(IDatabaseConnection connection) { if (!_fileSystem.FileExists(_importFile)) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 313db6a75..995dc7b7b 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -735,7 +735,7 @@ namespace Emby.Server.Implementations.HttpServer /// true if [is not modified] [the specified cache key]; otherwise, false. private bool IsNotModified(IRequest requestContext, Guid? cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration) { - var isNotModified = true; + //var isNotModified = true; var ifModifiedSinceHeader = requestContext.Headers.Get("If-Modified-Since"); @@ -745,18 +745,23 @@ namespace Emby.Server.Implementations.HttpServer if (DateTime.TryParse(ifModifiedSinceHeader, out ifModifiedSince)) { - isNotModified = IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified); + if (IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified)) + { + return true; + } } } var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match"); // Validate If-None-Match - if (isNotModified && (cacheKey.HasValue || !string.IsNullOrEmpty(ifNoneMatchHeader))) + if ((cacheKey.HasValue || !string.IsNullOrEmpty(ifNoneMatchHeader))) { Guid ifNoneMatch; - if (Guid.TryParse(ifNoneMatchHeader ?? string.Empty, out ifNoneMatch)) + ifNoneMatchHeader = (ifNoneMatchHeader ?? string.Empty).Trim('\"'); + + if (Guid.TryParse(ifNoneMatchHeader, out ifNoneMatch)) { if (cacheKey.HasValue && cacheKey.Value == ifNoneMatch) { diff --git a/Emby.Server.Implementations/Sync/SyncRepository.cs b/Emby.Server.Implementations/Sync/SyncRepository.cs index 8cce7a8bf..b2d9fbcc9 100644 --- a/Emby.Server.Implementations/Sync/SyncRepository.cs +++ b/Emby.Server.Implementations/Sync/SyncRepository.cs @@ -83,6 +83,14 @@ namespace Emby.Server.Implementations.Sync } } + 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"; diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs index 22e1e7758..62bb66ea8 100644 --- a/MediaBrowser.Model/IO/IFileSystem.cs +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -369,7 +369,6 @@ namespace MediaBrowser.Model.IO Append = 6 } - [Flags] public enum FileAccessMode { // @@ -388,7 +387,6 @@ namespace MediaBrowser.Model.IO ReadWrite = 3 } - [Flags] public enum FileShareMode { // @@ -417,16 +415,7 @@ namespace MediaBrowser.Model.IO // or another process) will fail until the file is closed. However, even if this // flag is specified, additional permissions might still be needed to access the // file. - ReadWrite = 3, - // - // Summary: - // Allows subsequent deleting of a file. - Delete = 4, - // - // Summary: - // Makes the file handle inheritable by child processes. This is not directly supported - // by Win32. - Inheritable = 16 + ReadWrite = 3 } } diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 8f7e94cf0..c5e140032 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -32,7 +32,6 @@ namespace MediaBrowser.Model.LiveTv public LiveTvOptions() { EnableMovieProviders = true; - EnableRecordingSubfolders = true; TunerHosts = new List(); ListingProviders = new List(); MediaLocationsCreated = new string[] { }; diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 9f8dca434..9c6d6a482 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -78,16 +78,15 @@ namespace MediaBrowser.Providers.Manager bool hasRefreshedMetadata = true; bool hasRefreshedImages = true; + var isFirstRefresh = item.DateLastRefreshed == default(DateTime); // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { - var providers = GetProviders(item, refreshOptions, requiresRefresh) + var providers = GetProviders(item, refreshOptions, isFirstRefresh, requiresRefresh) .ToList(); - var dateLastRefresh = item.DateLastRefreshed; - - if (providers.Count > 0 || dateLastRefresh == default(DateTime)) + if (providers.Count > 0 || isFirstRefresh) { if (item.BeforeMetadataRefresh()) { @@ -110,11 +109,7 @@ namespace MediaBrowser.Providers.Manager var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false); updateType = updateType | result.UpdateType; - if (result.Failures == 0) - { - hasRefreshedMetadata = true; - } - else + if (result.Failures > 0) { hasRefreshedMetadata = false; } @@ -138,19 +133,13 @@ namespace MediaBrowser.Providers.Manager var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false); updateType = updateType | result.UpdateType; - if (result.Failures == 0) - { - hasRefreshedImages = true; - } - else + if (result.Failures > 0) { hasRefreshedImages = false; } } } - var isFirstRefresh = item.DateLastRefreshed == default(DateTime); - var beforeSaveResult = await BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh, updateType).ConfigureAwait(false); updateType = updateType | beforeSaveResult; @@ -373,15 +362,18 @@ namespace MediaBrowser.Providers.Manager /// Gets the providers. /// /// IEnumerable{`0}. - protected IEnumerable GetProviders(IHasMetadata item, MetadataRefreshOptions options, bool requiresRefresh) + protected IEnumerable GetProviders(IHasMetadata item, MetadataRefreshOptions options, bool isFirstRefresh, bool requiresRefresh) { // Get providers to refresh var providers = ((ProviderManager)ProviderManager).GetMetadataProviders(item).ToList(); - var dateLastRefresh = item.DateLastRefreshed; + var metadataRefreshMode = options.MetadataRefreshMode; // Run all if either of these flags are true - var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || dateLastRefresh == default(DateTime) || requiresRefresh; + var runAllProviders = options.ReplaceAllMetadata || + metadataRefreshMode == MetadataRefreshMode.FullRefresh || + (isFirstRefresh && metadataRefreshMode >= MetadataRefreshMode.Default) || + (requiresRefresh && metadataRefreshMode >= MetadataRefreshMode.Default); if (!runAllProviders) { @@ -404,6 +396,9 @@ namespace MediaBrowser.Providers.Manager } else { + var anyRemoteProvidersChanged = providersWithChanges.OfType() + .Any(); + providers = providers.Where(i => { // If any provider reports a change, always run local ones as well @@ -412,9 +407,6 @@ namespace MediaBrowser.Providers.Manager return true; } - var anyRemoteProvidersChanged = providersWithChanges.OfType() - .Any(); - // If any remote providers changed, run them all so that priorities can be honored if (i is IRemoteMetadataProvider) { diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 84dd095cd..125ac5291 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -911,17 +911,14 @@ namespace MediaBrowser.XbmcMetadata.Savers var image = item.GetImageInfo(ImageType.Primary, 0); - if (image != null && image.IsLocalFile) + if (image != null) { - writer.WriteElementString("poster", GetPathToSave(image.Path, libraryManager, config)); + writer.WriteElementString("poster", GetImagePathToSave(image, libraryManager, config)); } foreach (var backdrop in item.GetImages(ImageType.Backdrop)) { - if (backdrop.IsLocalFile) - { - writer.WriteElementString("fanart", GetPathToSave(backdrop.Path, libraryManager, config)); - } + writer.WriteElementString("fanart", GetImagePathToSave(backdrop, libraryManager, config)); } writer.WriteEndElement(); @@ -1012,9 +1009,9 @@ namespace MediaBrowser.XbmcMetadata.Savers var personEntity = libraryManager.GetPerson(person.Name); var image = personEntity.GetImageInfo(ImageType.Primary, 0); - if (image != null && image.IsLocalFile) + if (image != null) { - writer.WriteElementString("thumb", GetPathToSave(image.Path, libraryManager, config)); + writer.WriteElementString("thumb", GetImagePathToSave(image, libraryManager, config)); } } catch (Exception) @@ -1026,9 +1023,14 @@ namespace MediaBrowser.XbmcMetadata.Savers } } - private static string GetPathToSave(string path, ILibraryManager libraryManager, IServerConfigurationManager config) + private static string GetImagePathToSave(ItemImageInfo image, ILibraryManager libraryManager, IServerConfigurationManager config) { - return libraryManager.GetPathAfterNetworkSubstitution(path); + if (!image.IsLocalFile) + { + return image.Path; + } + + return libraryManager.GetPathAfterNetworkSubstitution(image.Path); } private static bool IsPersonType(PersonInfo person, string type)