diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 07424403c..526e87b0d 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -253,7 +253,6 @@ - diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs deleted file mode 100644 index 075ef4239..000000000 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs +++ /dev/null @@ -1,304 +0,0 @@ -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Data; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Persistence -{ - public class SqliteChapterRepository - { - private IDbConnection _connection; - - private readonly ILogger _logger; - - private IDbCommand _deleteChaptersCommand; - private IDbCommand _saveChapterCommand; - - /// - /// Initializes a new instance of the class. - /// - /// The connection. - /// The log manager. - /// appPaths - /// or - /// jsonSerializer - public SqliteChapterRepository(IDbConnection connection, ILogManager logManager) - { - _connection = connection; - - _logger = logManager.GetLogger(GetType().Name); - } - - /// - /// Opens the connection to the database - /// - /// Task. - public void Initialize() - { - string[] queries = { - - "create table if not exists chapters (ItemId GUID, ChapterIndex INT, StartPositionTicks BIGINT, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))", - "create index if not exists idx_chapters on chapters(ItemId, ChapterIndex)", - - //pragmas - "pragma temp_store = memory", - - "pragma shrink_memory" - }; - - _connection.RunQueries(queries, _logger); - - PrepareStatements(); - } - - /// - /// The _write lock - /// - private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); - - /// - /// Prepares the statements. - /// - private void PrepareStatements() - { - _deleteChaptersCommand = _connection.CreateCommand(); - _deleteChaptersCommand.CommandText = "delete from chapters where ItemId=@ItemId"; - _deleteChaptersCommand.Parameters.Add(_deleteChaptersCommand, "@ItemId"); - - _saveChapterCommand = _connection.CreateCommand(); - _saveChapterCommand.CommandText = "replace into chapters (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath)"; - - _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ItemId"); - _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ChapterIndex"); - _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@StartPositionTicks"); - _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@Name"); - _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ImagePath"); - } - - /// - /// Gets chapters for an item - /// - /// The id. - /// IEnumerable{ChapterInfo}. - /// id - public IEnumerable GetChapters(Guid id) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - - using (var cmd = _connection.CreateCommand()) - { - cmd.CommandText = "select StartPositionTicks,Name,ImagePath from Chapters where ItemId = @ItemId order by ChapterIndex asc"; - - cmd.Parameters.Add(cmd, "@ItemId", DbType.Guid).Value = id; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) - { - while (reader.Read()) - { - yield return GetChapter(reader); - } - } - } - } - - /// - /// Gets a single chapter for an item - /// - /// The id. - /// The index. - /// ChapterInfo. - /// id - public ChapterInfo GetChapter(Guid id, int index) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - - using (var cmd = _connection.CreateCommand()) - { - cmd.CommandText = "select StartPositionTicks,Name,ImagePath from Chapters where ItemId = @ItemId and ChapterIndex=@ChapterIndex"; - - cmd.Parameters.Add(cmd, "@ItemId", DbType.Guid).Value = id; - cmd.Parameters.Add(cmd, "@ChapterIndex", DbType.Int32).Value = index; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) - { - if (reader.Read()) - { - return GetChapter(reader); - } - } - return null; - } - } - - /// - /// Gets the chapter. - /// - /// The reader. - /// ChapterInfo. - private ChapterInfo GetChapter(IDataReader reader) - { - var chapter = new ChapterInfo - { - StartPositionTicks = reader.GetInt64(0) - }; - - if (!reader.IsDBNull(1)) - { - chapter.Name = reader.GetString(1); - } - - if (!reader.IsDBNull(2)) - { - chapter.ImagePath = reader.GetString(2); - } - - return chapter; - } - - /// - /// Saves the chapters. - /// - /// The id. - /// The chapters. - /// The cancellation token. - /// Task. - /// - /// id - /// or - /// chapters - /// or - /// cancellationToken - /// - public async Task SaveChapters(Guid id, IEnumerable chapters, CancellationToken cancellationToken) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - - if (chapters == null) - { - throw new ArgumentNullException("chapters"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); - - IDbTransaction transaction = null; - - try - { - transaction = _connection.BeginTransaction(); - - // First delete chapters - _deleteChaptersCommand.GetParameter(0).Value = id; - - _deleteChaptersCommand.Transaction = transaction; - - _deleteChaptersCommand.ExecuteNonQuery(); - - var index = 0; - - foreach (var chapter in chapters) - { - cancellationToken.ThrowIfCancellationRequested(); - - _saveChapterCommand.GetParameter(0).Value = id; - _saveChapterCommand.GetParameter(1).Value = index; - _saveChapterCommand.GetParameter(2).Value = chapter.StartPositionTicks; - _saveChapterCommand.GetParameter(3).Value = chapter.Name; - _saveChapterCommand.GetParameter(4).Value = chapter.ImagePath; - - _saveChapterCommand.Transaction = transaction; - - _saveChapterCommand.ExecuteNonQuery(); - - index++; - } - - transaction.Commit(); - } - catch (OperationCanceledException) - { - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - catch (Exception e) - { - _logger.ErrorException("Failed to save chapters:", e); - - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - finally - { - if (transaction != null) - { - transaction.Dispose(); - } - - _writeLock.Release(); - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private readonly object _disposeLock = new object(); - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - try - { - lock (_disposeLock) - { - if (_connection != null) - { - if (_connection.IsOpen()) - { - _connection.Close(); - } - - _connection.Dispose(); - _connection = null; - } - } - } - catch (Exception ex) - { - _logger.ErrorException("Error disposing database", ex); - } - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs index f8cb6c9f4..011cbce1c 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteExtensions.cs @@ -154,6 +154,15 @@ namespace MediaBrowser.Server.Implementations.Persistence return connection; } + public static void Attach(IDbConnection db, string path, string alias) + { + using (var cmd = db.CreateCommand()) + { + cmd.CommandText = string.Format("attach '{0}' as {1};", path, alias); + cmd.ExecuteNonQuery(); + } + } + /// /// Serializes to bytes. /// diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index 36af71967..f9b69f70b 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -63,7 +63,6 @@ namespace MediaBrowser.Server.Implementations.Persistence private readonly string _criticReviewsPath; - private SqliteChapterRepository _chapterRepository; private SqliteMediaStreamsRepository _mediaStreamsRepository; private IDbCommand _deleteChildrenCommand; @@ -73,6 +72,9 @@ namespace MediaBrowser.Server.Implementations.Persistence private IDbCommand _deletePeopleCommand; private IDbCommand _savePersonCommand; + private IDbCommand _deleteChaptersCommand; + private IDbCommand _saveChapterCommand; + private const int LatestSchemaVersion = 13; /// @@ -104,15 +106,13 @@ namespace MediaBrowser.Server.Implementations.Persistence _logger = logManager.GetLogger(GetType().Name); - var chapterDbFile = Path.Combine(_appPaths.DataPath, "chapters.db"); - var chapterConnection = SqliteExtensions.ConnectToDb(chapterDbFile, _logger).Result; - _chapterRepository = new SqliteChapterRepository(chapterConnection, logManager); - var mediaStreamsDbFile = Path.Combine(_appPaths.DataPath, "mediainfo.db"); var mediaStreamsConnection = SqliteExtensions.ConnectToDb(mediaStreamsDbFile, _logger).Result; _mediaStreamsRepository = new SqliteMediaStreamsRepository(mediaStreamsConnection, logManager); } + private const string ChaptersTableName = "Chapters2"; + /// /// Opens the connection to the database /// @@ -133,6 +133,9 @@ namespace MediaBrowser.Server.Implementations.Persistence "create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)", + "create table if not exists "+ChaptersTableName+" (ItemId GUID, ChapterIndex INT, StartPositionTicks BIGINT, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))", + "create index if not exists idx_"+ChaptersTableName+" on "+ChaptersTableName+"(ItemId, ChapterIndex)", + //pragmas "pragma temp_store = memory", @@ -195,7 +198,35 @@ namespace MediaBrowser.Server.Implementations.Persistence PrepareStatements(); _mediaStreamsRepository.Initialize(); - _chapterRepository.Initialize(); + + var chapterDbFile = Path.Combine(_appPaths.DataPath, "chapters.db"); + + if (File.Exists(chapterDbFile)) + { + MigrateChapters(chapterDbFile); + } + } + + private void MigrateChapters(string file) + { + var backupFile = file + ".bak"; + File.Copy(file, backupFile, true); + SqliteExtensions.Attach(_connection, backupFile, "ChaptersOld"); + + string[] queries = { + "INSERT INTO "+ChaptersTableName+"(ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath) SELECT * FROM ChaptersOld.Chapters;" + }; + + try + { + _connection.RunQueries(queries, _logger); + } + catch (Exception ex) + { + throw ex; + } + + File.Delete(file); } /// @@ -326,6 +357,19 @@ namespace MediaBrowser.Server.Implementations.Persistence _savePersonCommand.Parameters.Add(_savePersonCommand, "@PersonType"); _savePersonCommand.Parameters.Add(_savePersonCommand, "@SortOrder"); _savePersonCommand.Parameters.Add(_savePersonCommand, "@ListOrder"); + + _deleteChaptersCommand = _connection.CreateCommand(); + _deleteChaptersCommand.CommandText = "delete from " + ChaptersTableName + " where ItemId=@ItemId"; + _deleteChaptersCommand.Parameters.Add(_deleteChaptersCommand, "@ItemId"); + + _saveChapterCommand = _connection.CreateCommand(); + _saveChapterCommand.CommandText = "replace into " + ChaptersTableName + " (ItemId, ChapterIndex, StartPositionTicks, Name, ImagePath) values (@ItemId, @ChapterIndex, @StartPositionTicks, @Name, @ImagePath)"; + + _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ItemId"); + _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ChapterIndex"); + _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@StartPositionTicks"); + _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@Name"); + _saveChapterCommand.Parameters.Add(_saveChapterCommand, "@ImagePath"); } /// @@ -748,7 +792,25 @@ namespace MediaBrowser.Server.Implementations.Persistence public IEnumerable GetChapters(Guid id) { CheckDisposed(); - return _chapterRepository.GetChapters(id); + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select StartPositionTicks,Name,ImagePath from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc"; + + cmd.Parameters.Add(cmd, "@ItemId", DbType.Guid).Value = id; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + yield return GetChapter(reader); + } + } + } } /// @@ -761,7 +823,52 @@ namespace MediaBrowser.Server.Implementations.Persistence public ChapterInfo GetChapter(Guid id, int index) { CheckDisposed(); - return _chapterRepository.GetChapter(id, index); + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select StartPositionTicks,Name,ImagePath from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex"; + + cmd.Parameters.Add(cmd, "@ItemId", DbType.Guid).Value = id; + cmd.Parameters.Add(cmd, "@ChapterIndex", DbType.Int32).Value = index; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) + { + if (reader.Read()) + { + return GetChapter(reader); + } + } + return null; + } + } + + /// + /// Gets the chapter. + /// + /// The reader. + /// ChapterInfo. + private ChapterInfo GetChapter(IDataReader reader) + { + var chapter = new ChapterInfo + { + StartPositionTicks = reader.GetInt64(0) + }; + + if (!reader.IsDBNull(1)) + { + chapter.Name = reader.GetString(1); + } + + if (!reader.IsDBNull(2)) + { + chapter.ImagePath = reader.GetString(2); + } + + return chapter; } /// @@ -778,10 +885,87 @@ namespace MediaBrowser.Server.Implementations.Persistence /// or /// cancellationToken /// - public Task SaveChapters(Guid id, IEnumerable chapters, CancellationToken cancellationToken) + public async Task SaveChapters(Guid id, IEnumerable chapters, CancellationToken cancellationToken) { CheckDisposed(); - return _chapterRepository.SaveChapters(id, chapters, cancellationToken); + + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + if (chapters == null) + { + throw new ArgumentNullException("chapters"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + IDbTransaction transaction = null; + + try + { + transaction = _connection.BeginTransaction(); + + // First delete chapters + _deleteChaptersCommand.GetParameter(0).Value = id; + + _deleteChaptersCommand.Transaction = transaction; + + _deleteChaptersCommand.ExecuteNonQuery(); + + var index = 0; + + foreach (var chapter in chapters) + { + cancellationToken.ThrowIfCancellationRequested(); + + _saveChapterCommand.GetParameter(0).Value = id; + _saveChapterCommand.GetParameter(1).Value = index; + _saveChapterCommand.GetParameter(2).Value = chapter.StartPositionTicks; + _saveChapterCommand.GetParameter(3).Value = chapter.Name; + _saveChapterCommand.GetParameter(4).Value = chapter.ImagePath; + + _saveChapterCommand.Transaction = transaction; + + _saveChapterCommand.ExecuteNonQuery(); + + index++; + } + + transaction.Commit(); + } + catch (OperationCanceledException) + { + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + catch (Exception e) + { + _logger.ErrorException("Failed to save chapters:", e); + + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + finally + { + if (transaction != null) + { + transaction.Dispose(); + } + + _writeLock.Release(); + } } /// @@ -831,12 +1015,6 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection = null; } - if (_chapterRepository != null) - { - _chapterRepository.Dispose(); - _chapterRepository = null; - } - if (_mediaStreamsRepository != null) { _mediaStreamsRepository.Dispose(); diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index f24a3541b..0f01e8aea 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -351,11 +351,7 @@ namespace MediaBrowser.Server.Startup.Common { var migrations = new List { - new MigrateUserFolders(ApplicationPaths, FileSystemManager), - new RenameXbmcOptions(ServerConfigurationManager), - new RenameXmlOptions(ServerConfigurationManager), - new DeprecatePlugins(ApplicationPaths, FileSystemManager), - new DeleteDlnaProfiles(ApplicationPaths, FileSystemManager) + new RenameXmlOptions(ServerConfigurationManager) }; foreach (var task in migrations) diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index e1eccdc32..9def89073 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -71,11 +71,7 @@ - - - - diff --git a/MediaBrowser.Server.Startup.Common/Migrations/DeleteDlnaProfiles.cs b/MediaBrowser.Server.Startup.Common/Migrations/DeleteDlnaProfiles.cs deleted file mode 100644 index 7b6152220..000000000 --- a/MediaBrowser.Server.Startup.Common/Migrations/DeleteDlnaProfiles.cs +++ /dev/null @@ -1,46 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Controller; -using System.IO; -using CommonIO; - -namespace MediaBrowser.Server.Startup.Common.Migrations -{ - public class DeleteDlnaProfiles : IVersionMigration - { - private readonly IServerApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; - - public DeleteDlnaProfiles(IServerApplicationPaths appPaths, IFileSystem fileSystem) - { - _appPaths = appPaths; - _fileSystem = fileSystem; - } - - public void Run() - { - RemoveProfile("Android"); - RemoveProfile("Windows Phone"); - RemoveProfile("Windows 8 RT"); - } - - private void RemoveProfile(string filename) - { - try - { - _fileSystem.DeleteFile(Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system", filename + ".xml")); - } - catch - { - - } - try - { - _fileSystem.DeleteFile(Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user", filename + ".xml")); - } - catch - { - - } - } - } -} diff --git a/MediaBrowser.Server.Startup.Common/Migrations/DeprecatePlugins.cs b/MediaBrowser.Server.Startup.Common/Migrations/DeprecatePlugins.cs deleted file mode 100644 index a7f1d332e..000000000 --- a/MediaBrowser.Server.Startup.Common/Migrations/DeprecatePlugins.cs +++ /dev/null @@ -1,36 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Controller; -using System.IO; -using CommonIO; - -namespace MediaBrowser.Server.Startup.Common.Migrations -{ - public class DeprecatePlugins : IVersionMigration - { - private readonly IServerApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; - - public DeprecatePlugins(IServerApplicationPaths appPaths, IFileSystem fileSystem) - { - _appPaths = appPaths; - _fileSystem = fileSystem; - } - - public void Run() - { - RemovePlugin("MediaBrowser.Plugins.LocalTrailers.dll"); - } - - private void RemovePlugin(string filename) - { - try - { - _fileSystem.DeleteFile(Path.Combine(_appPaths.PluginsPath, filename)); - } - catch - { - - } - } - } -} diff --git a/MediaBrowser.Server.Startup.Common/Migrations/MigrateUserFolders.cs b/MediaBrowser.Server.Startup.Common/Migrations/MigrateUserFolders.cs deleted file mode 100644 index 4a40090f0..000000000 --- a/MediaBrowser.Server.Startup.Common/Migrations/MigrateUserFolders.cs +++ /dev/null @@ -1,41 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Controller; -using System; -using System.IO; -using System.Linq; -using CommonIO; - -namespace MediaBrowser.Server.Startup.Common.Migrations -{ - public class MigrateUserFolders : IVersionMigration - { - private readonly IServerApplicationPaths _appPaths; - private readonly IFileSystem _fileSystem; - - public MigrateUserFolders(IServerApplicationPaths appPaths, IFileSystem fileSystem) - { - _appPaths = appPaths; - _fileSystem = fileSystem; - } - - public void Run() - { - try - { - var rootPath = _appPaths.RootFolderPath; - - var folders = _fileSystem.GetDirectories(rootPath) - .Where(i => !string.Equals(i.Name, "default", StringComparison.OrdinalIgnoreCase)) - .ToList(); - - foreach (var folder in folders) - { - _fileSystem.DeleteDirectory(folder.FullName, true); - } - } - catch (IOException) - { - } - } - } -} diff --git a/MediaBrowser.Server.Startup.Common/Migrations/RenameXbmcOptions.cs b/MediaBrowser.Server.Startup.Common/Migrations/RenameXbmcOptions.cs deleted file mode 100644 index 02b0f9a0a..000000000 --- a/MediaBrowser.Server.Startup.Common/Migrations/RenameXbmcOptions.cs +++ /dev/null @@ -1,56 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using System; - -namespace MediaBrowser.Server.Startup.Common.Migrations -{ - public class RenameXbmcOptions : IVersionMigration - { - private readonly IServerConfigurationManager _config; - - public RenameXbmcOptions(IServerConfigurationManager config) - { - _config = config; - } - - public void Run() - { - var changed = false; - - foreach (var option in _config.Configuration.MetadataOptions) - { - if (Migrate(option.DisabledMetadataSavers)) - { - changed = true; - } - if (Migrate(option.LocalMetadataReaderOrder)) - { - changed = true; - } - } - - if (changed) - { - _config.SaveConfiguration(); - } - } - - private bool Migrate(string[] options) - { - var changed = false; - - if (options != null) - { - for (var i = 0; i < options.Length; i++) - { - if (string.Equals(options[i], "Xbmc Nfo", StringComparison.OrdinalIgnoreCase)) - { - options[i] = "Nfo"; - changed = true; - } - } - } - - return changed; - } - } -}