diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index 167592910..b1e0339fc 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -147,9 +147,7 @@
-
-
-
+
diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
index e020b066c..bfbbc06ee 100644
--- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
+++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
@@ -16,6 +16,7 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
+using ServiceStack;
using SimpleInjector;
using System;
using System.Collections.Generic;
@@ -181,6 +182,9 @@ namespace MediaBrowser.Common.Implementations
/// Task.
public virtual async Task Init()
{
+ // https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4
+ Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0=");
+
JsonSerializer = CreateJsonSerializer();
IsFirstRun = !ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted;
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index a8972a2bb..ba0e71d7d 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -273,7 +273,7 @@ namespace MediaBrowser.Controller.Entities
return base.GetDeletePaths();
}
- public IEnumerable GetMediaStreams()
+ public virtual IEnumerable GetMediaStreams()
{
return ItemRepository.GetMediaStreams(new MediaStreamQuery
{
@@ -281,7 +281,7 @@ namespace MediaBrowser.Controller.Entities
});
}
- public MediaStream GetDefaultVideoStream()
+ public virtual MediaStream GetDefaultVideoStream()
{
if (!DefaultVideoStreamIndex.HasValue)
{
diff --git a/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs b/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
index 08e62afc5..37d39f3d9 100644
--- a/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
+++ b/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
@@ -23,7 +23,7 @@ namespace MediaBrowser.Providers
public class ImageFromMediaLocationProvider : BaseMetadataProvider
{
protected readonly IFileSystem FileSystem;
-
+
public ImageFromMediaLocationProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
: base(logManager, configurationManager)
{
@@ -54,6 +54,7 @@ namespace MediaBrowser.Providers
return item.IsInMixedFolder && item.Parent != null && !(item is Episode);
}
+
return false;
}
@@ -153,6 +154,13 @@ namespace MediaBrowser.Providers
.FirstOrDefault(i => i != null);
}
+ protected virtual FileSystemInfo GetImage(List files, string filenameWithoutExtension)
+ {
+ return BaseItem.SupportedImageExtensions
+ .Select(i => files.FirstOrDefault(f => string.Equals(f.Extension, i, StringComparison.OrdinalIgnoreCase) && string.Equals(filenameWithoutExtension, Path.GetFileNameWithoutExtension(f.Name), StringComparison.OrdinalIgnoreCase)))
+ .FirstOrDefault(i => i != null);
+ }
+
protected virtual string GetFullImagePath(BaseItem item, ItemResolveArgs args, string filenameWithoutExtension, string extension)
{
var path = item.MetaLocation;
@@ -258,24 +266,13 @@ namespace MediaBrowser.Providers
var isFileSystemItem = item.LocationType == LocationType.FileSystem;
// Support plex/xbmc convention
- if (image == null && item is Season && item.IndexNumber.HasValue && isFileSystemItem)
+ if (image == null)
{
- var seasonMarker = item.IndexNumber.Value == 0
- ? "-specials"
- : item.IndexNumber.Value.ToString("00", _usCulture);
-
- // Get this one directly from the file system since we have to go up a level
- var filename = "season" + seasonMarker + "-poster";
-
- var path = Path.GetDirectoryName(item.Path);
-
- path = Path.Combine(path, filename);
-
- image = new FileInfo(path);
-
- if (!image.Exists)
+ // Supprt xbmc conventions
+ var season = item as Season;
+ if (season != null && item.IndexNumber.HasValue && isFileSystemItem)
{
- image = null;
+ image = GetSeasonImageFromSeriesFolder(season, "-poster");
}
}
@@ -315,26 +312,13 @@ namespace MediaBrowser.Providers
if (image == null)
{
+ var isFileSystemItem = item.LocationType == LocationType.FileSystem;
+
// Supprt xbmc conventions
- if (item is Season && item.IndexNumber.HasValue && item.LocationType == LocationType.FileSystem)
+ var season = item as Season;
+ if (season != null && item.IndexNumber.HasValue && isFileSystemItem)
{
- var seasonMarker = item.IndexNumber.Value == 0
- ? "-specials"
- : item.IndexNumber.Value.ToString("00", _usCulture);
-
- // Get this one directly from the file system since we have to go up a level
- var filename = "season" + seasonMarker + "-banner";
-
- var path = Path.GetDirectoryName(item.Path);
-
- path = Path.Combine(path, filename);
-
- image = new FileInfo(path);
-
- if (!image.Exists)
- {
- image = null;
- }
+ image = GetSeasonImageFromSeriesFolder(season, "-banner");
}
}
@@ -356,26 +340,13 @@ namespace MediaBrowser.Providers
if (image == null)
{
+ var isFileSystemItem = item.LocationType == LocationType.FileSystem;
+
// Supprt xbmc conventions
- if (item is Season && item.IndexNumber.HasValue && item.LocationType == LocationType.FileSystem)
+ var season = item as Season;
+ if (season != null && item.IndexNumber.HasValue && isFileSystemItem)
{
- var seasonMarker = item.IndexNumber.Value == 0
- ? "-specials"
- : item.IndexNumber.Value.ToString("00", _usCulture);
-
- // Get this one directly from the file system since we have to go up a level
- var filename = "season" + seasonMarker + "-landscape";
-
- var path = Path.GetDirectoryName(item.Path);
-
- path = Path.Combine(path, filename);
-
- image = new FileInfo(path);
-
- if (!image.Exists)
- {
- image = null;
- }
+ image = GetSeasonImageFromSeriesFolder(season, "-landscape");
}
}
@@ -420,22 +391,12 @@ namespace MediaBrowser.Providers
PopulateBackdrops(item, args, backdropFiles, "background", "background-");
PopulateBackdrops(item, args, backdropFiles, "art", "art-");
- if (item is Season && item.IndexNumber.HasValue && isFileSystemItem)
+ var season = item as Season;
+ if (season != null && item.IndexNumber.HasValue && isFileSystemItem)
{
- var seasonMarker = item.IndexNumber.Value == 0
- ? "-specials"
- : item.IndexNumber.Value.ToString("00", _usCulture);
+ var image = GetSeasonImageFromSeriesFolder(season, "-fanart");
- // Get this one directly from the file system since we have to go up a level
- var filename = "season" + seasonMarker + "-fanart";
-
- var path = Path.GetDirectoryName(item.Path);
-
- path = Path.Combine(path, filename);
-
- var image = new FileInfo(path);
-
- if (image.Exists)
+ if (image != null)
{
backdropFiles.Add(image.FullName);
}
@@ -452,6 +413,51 @@ namespace MediaBrowser.Providers
}
}
+ private FileSystemInfo GetSeasonImageFromSeriesFolder(Season season, string imageSuffix)
+ {
+ var series = season.Series;
+ var seriesFolderArgs = series.ResolveArgs;
+
+ var seasonNumber = season.IndexNumber;
+
+ string filename = null;
+ FileSystemInfo image;
+
+ if (seasonNumber.HasValue)
+ {
+ var seasonMarker = seasonNumber.Value == 0
+ ? "-specials"
+ : seasonNumber.Value.ToString("00", _usCulture);
+
+ // Get this one directly from the file system since we have to go up a level
+ filename = "season" + seasonMarker + imageSuffix;
+
+ image = GetImage(series, seriesFolderArgs, filename);
+
+ if (image != null && image.Exists)
+ {
+ return image;
+ }
+ }
+
+ var previousFilename = filename;
+
+ // Try using the season name
+ filename = season.Name.ToLower().Replace(" ", string.Empty) + imageSuffix;
+
+ if (!string.Equals(previousFilename, filename))
+ {
+ image = GetImage(series, seriesFolderArgs, filename);
+
+ if (image != null && image.Exists)
+ {
+ return image;
+ }
+ }
+
+ return null;
+ }
+
///
/// Populates the backdrops from extra fanart.
///
diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
index ee10fb934..27ce90787 100644
--- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
+++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
@@ -74,7 +74,8 @@ namespace MediaBrowser.Server.Implementations.Drawing
try
{
- sizeDictionary = jsonSerializer.DeserializeFromFile>(ImageSizeFile);
+ sizeDictionary = jsonSerializer.DeserializeFromFile>(ImageSizeFile) ??
+ new Dictionary();
}
catch (FileNotFoundException)
{
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
index c50588f95..29dce6747 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -5,7 +5,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
using ServiceStack;
-using ServiceStack.Configuration;
using ServiceStack.Host;
using ServiceStack.Host.Handlers;
using ServiceStack.Host.HttpListener;
@@ -58,9 +57,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
public HttpListenerHost(IApplicationHost applicationHost, ILogManager logManager, string serviceName, string handlerPath, string defaultRedirectPath, params Assembly[] assembliesWithServices)
: base(serviceName, assembliesWithServices)
{
- // https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4
- Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0=");
-
DefaultRedirectPath = defaultRedirectPath;
ServerName = serviceName;
HandlerPath = handlerPath;
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index f22a96455..39966f0d7 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -37,18 +37,23 @@
..\packages\Alchemy.2.2.1\lib\net40\Alchemy.dll
-
+
False
..\ThirdParty\ServiceStack\ServiceStack.dll
-
+
+ False
..\ThirdParty\ServiceStack\ServiceStack.Client.dll
-
+
+ False
+ ..\ThirdParty\ServiceStack\ServiceStack.Common.dll
+
+
False
..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll
-
+
False
..\ThirdParty\ServiceStack\ServiceStack.Text.dll
@@ -143,6 +148,7 @@
+
@@ -237,9 +243,6 @@
-
- PreserveNewest
-
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs
index 9121ae87b..075ef4239 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs
@@ -44,7 +44,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_chapters on chapters(ItemId, ChapterIndex)",
//pragmas
- "pragma temp_store = memory"
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs
index 09f438aef..9f6ec0f24 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteDisplayPreferencesRepository.cs
@@ -86,8 +86,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
"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)",
+
//pragmas
- "pragma temp_store = memory"
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
index 893d6ea62..200898a62 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
@@ -124,18 +124,24 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_ChildrenIds on ChildrenIds(ParentId,ItemId)",
//pragmas
- "pragma temp_store = memory"
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
PrepareStatements();
-
+
_mediaStreamsRepository.Initialize();
_providerInfoRepository.Initialize();
_chapterRepository.Initialize();
+
+ _shrinkMemoryTimer = new SqliteShrinkMemoryTimer(_connection, _writeLock, _logger);
}
+ private SqliteShrinkMemoryTimer _shrinkMemoryTimer;
+
///
/// The _write lock
///
@@ -402,6 +408,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
lock (_disposeLock)
{
+ if (_shrinkMemoryTimer != null)
+ {
+ _shrinkMemoryTimer.Dispose();
+ _shrinkMemoryTimer = null;
+ }
+
if (_connection != null)
{
if (_connection.IsOpen())
@@ -412,30 +424,30 @@ namespace MediaBrowser.Server.Implementations.Persistence
_connection.Dispose();
_connection = null;
}
+
+ if (_chapterRepository != null)
+ {
+ _chapterRepository.Dispose();
+ _chapterRepository = null;
+ }
+
+ if (_mediaStreamsRepository != null)
+ {
+ _mediaStreamsRepository.Dispose();
+ _mediaStreamsRepository = null;
+ }
+
+ if (_providerInfoRepository != null)
+ {
+ _providerInfoRepository.Dispose();
+ _providerInfoRepository = null;
+ }
}
}
catch (Exception ex)
{
_logger.ErrorException("Error disposing database", ex);
}
-
- if (_chapterRepository != null)
- {
- _chapterRepository.Dispose();
- _chapterRepository = null;
- }
-
- if (_mediaStreamsRepository != null)
- {
- _mediaStreamsRepository.Dispose();
- _mediaStreamsRepository = null;
- }
-
- if (_providerInfoRepository != null)
- {
- _providerInfoRepository.Dispose();
- _providerInfoRepository = null;
- }
}
}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs
index ba189396a..b898398d8 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteMediaStreamsRepository.cs
@@ -43,7 +43,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_mediastreams on mediastreams(ItemId, StreamIndex)",
//pragmas
- "pragma temp_store = memory"
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs
index c5f391765..0f9b4295e 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteNotificationsRepository.cs
@@ -45,7 +45,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_Notifications on Notifications(Id, UserId)",
//pragmas
- "pragma temp_store = memory"
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteProviderInfoRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteProviderInfoRepository.cs
index 8972beb05..5d836b090 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteProviderInfoRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteProviderInfoRepository.cs
@@ -42,7 +42,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create index if not exists idx_providerinfos on providerinfos(ItemId, ProviderId)",
//pragmas
- "pragma temp_store = memory"
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteShrinkMemoryTimer.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteShrinkMemoryTimer.cs
new file mode 100644
index 000000000..01784d540
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteShrinkMemoryTimer.cs
@@ -0,0 +1,84 @@
+using MediaBrowser.Model.Logging;
+using System;
+using System.Data;
+using System.Threading;
+
+namespace MediaBrowser.Server.Implementations.Persistence
+{
+ class SqliteShrinkMemoryTimer : IDisposable
+ {
+ private Timer _shrinkMemoryTimer;
+
+ private readonly SemaphoreSlim _writeLock;
+ private readonly ILogger _logger;
+ private readonly IDbConnection _connection;
+
+ public SqliteShrinkMemoryTimer(IDbConnection connection, SemaphoreSlim writeLock, ILogger logger)
+ {
+ _connection = connection;
+ _writeLock = writeLock;
+ _logger = logger;
+
+ _shrinkMemoryTimer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(30), TimeSpan.FromMinutes(30));
+ }
+
+ private async void TimerCallback(object state)
+ {
+ await _writeLock.WaitAsync(CancellationToken.None).ConfigureAwait(false);
+
+ IDbTransaction transaction = null;
+
+ try
+ {
+ transaction = _connection.BeginTransaction();
+
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.Transaction = transaction;
+ cmd.CommandText = "pragma shrink_memory";
+ cmd.ExecuteNonQuery();
+ }
+
+ transaction.Commit();
+ }
+ catch (OperationCanceledException)
+ {
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ catch (Exception e)
+ {
+ _logger.ErrorException("Failed to save items:", e);
+
+ if (transaction != null)
+ {
+ transaction.Rollback();
+ }
+
+ throw;
+ }
+ finally
+ {
+ if (transaction != null)
+ {
+ transaction.Dispose();
+ }
+
+ _writeLock.Release();
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_shrinkMemoryTimer != null)
+ {
+ _shrinkMemoryTimer.Dispose();
+ _shrinkMemoryTimer = null;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs
index fa195859b..d3f9100b1 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserDataRepository.cs
@@ -72,7 +72,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create unique index if not exists userdataindex on userdata (key, userId)",
//pragmas
- "pragma temp_store = memory"
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs
index 222cc9422..d97a55ae6 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteUserRepository.cs
@@ -77,8 +77,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create table if not exists users (guid GUID primary key, data BLOB)",
"create index if not exists idx_users on users(guid)",
"create table if not exists schema_version (table_name primary key, version)",
+
//pragmas
- "pragma temp_store = memory"
+ "pragma temp_store = memory",
+
+ "pragma shrink_memory"
};
_connection.RunQueries(queries, _logger);
diff --git a/MediaBrowser.Server.Implementations/swagger-ui/index.html b/MediaBrowser.Server.Implementations/swagger-ui/index.html
deleted file mode 100644
index 49f983a72..000000000
--- a/MediaBrowser.Server.Implementations/swagger-ui/index.html
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
- Swagger UI
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/MediaBrowser.ServerApplication/EntryPoints/ResourceEntryPoint.cs b/MediaBrowser.ServerApplication/EntryPoints/ResourceEntryPoint.cs
new file mode 100644
index 000000000..e7a33d864
--- /dev/null
+++ b/MediaBrowser.ServerApplication/EntryPoints/ResourceEntryPoint.cs
@@ -0,0 +1,38 @@
+using MediaBrowser.Controller.Plugins;
+using System;
+using System.Threading;
+
+namespace MediaBrowser.ServerApplication.EntryPoints
+{
+ public class ResourceEntryPoint : IServerEntryPoint
+ {
+ private Timer _timer;
+
+ public void Run()
+ {
+ _timer = new Timer(TimerCallback, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(30));
+ }
+
+ private void TimerCallback(object state)
+ {
+ try
+ {
+ // Bad practice, i know. But we keep a lot in memory, unfortunately.
+ GC.Collect(2, GCCollectionMode.Forced, true);
+ GC.Collect(2, GCCollectionMode.Forced, true);
+ }
+ catch
+ {
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_timer != null)
+ {
+ _timer.Dispose();
+ _timer = null;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index 9482351fb..e02bb1d69 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -131,8 +131,7 @@
False
..\packages\MediaBrowser.IsoMounting.3.0.65\lib\net45\pfmclrapi.dll
-
- False
+
..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll
@@ -161,6 +160,7 @@
Component
+