update chapters
This commit is contained in:
parent
61e6dd9b2f
commit
21c9f6e75e
|
@ -253,7 +253,6 @@
|
||||||
<Compile Include="Notifications\InternalNotificationService.cs" />
|
<Compile Include="Notifications\InternalNotificationService.cs" />
|
||||||
<Compile Include="Notifications\NotificationConfigurationFactory.cs" />
|
<Compile Include="Notifications\NotificationConfigurationFactory.cs" />
|
||||||
<Compile Include="Notifications\NotificationManager.cs" />
|
<Compile Include="Notifications\NotificationManager.cs" />
|
||||||
<Compile Include="Persistence\SqliteChapterRepository.cs" />
|
|
||||||
<Compile Include="Persistence\SqliteExtensions.cs" />
|
<Compile Include="Persistence\SqliteExtensions.cs" />
|
||||||
<Compile Include="Persistence\SqliteFileOrganizationRepository.cs" />
|
<Compile Include="Persistence\SqliteFileOrganizationRepository.cs" />
|
||||||
<Compile Include="Persistence\SqliteMediaStreamsRepository.cs" />
|
<Compile Include="Persistence\SqliteMediaStreamsRepository.cs" />
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="SqliteItemRepository" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="connection">The connection.</param>
|
|
||||||
/// <param name="logManager">The log manager.</param>
|
|
||||||
/// <exception cref="System.ArgumentNullException">appPaths
|
|
||||||
/// or
|
|
||||||
/// jsonSerializer</exception>
|
|
||||||
public SqliteChapterRepository(IDbConnection connection, ILogManager logManager)
|
|
||||||
{
|
|
||||||
_connection = connection;
|
|
||||||
|
|
||||||
_logger = logManager.GetLogger(GetType().Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens the connection to the database
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The _write lock
|
|
||||||
/// </summary>
|
|
||||||
private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prepares the statements.
|
|
||||||
/// </summary>
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets chapters for an item
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The id.</param>
|
|
||||||
/// <returns>IEnumerable{ChapterInfo}.</returns>
|
|
||||||
/// <exception cref="System.ArgumentNullException">id</exception>
|
|
||||||
public IEnumerable<ChapterInfo> 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a single chapter for an item
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The id.</param>
|
|
||||||
/// <param name="index">The index.</param>
|
|
||||||
/// <returns>ChapterInfo.</returns>
|
|
||||||
/// <exception cref="System.ArgumentNullException">id</exception>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the chapter.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reader">The reader.</param>
|
|
||||||
/// <returns>ChapterInfo.</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Saves the chapters.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The id.</param>
|
|
||||||
/// <param name="chapters">The chapters.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
/// <exception cref="System.ArgumentNullException">
|
|
||||||
/// id
|
|
||||||
/// or
|
|
||||||
/// chapters
|
|
||||||
/// or
|
|
||||||
/// cancellationToken
|
|
||||||
/// </exception>
|
|
||||||
public async Task SaveChapters(Guid id, IEnumerable<ChapterInfo> 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly object _disposeLock = new object();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases unmanaged and - optionally - managed resources.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
|
||||||
protected virtual void Dispose(bool dispose)
|
|
||||||
{
|
|
||||||
if (dispose)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (_disposeLock)
|
|
||||||
{
|
|
||||||
if (_connection != null)
|
|
||||||
{
|
|
||||||
if (_connection.IsOpen())
|
|
||||||
{
|
|
||||||
_connection.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
_connection.Dispose();
|
|
||||||
_connection = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error disposing database", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -154,6 +154,15 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
return connection;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializes to bytes.
|
/// Serializes to bytes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -63,7 +63,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
|
|
||||||
private readonly string _criticReviewsPath;
|
private readonly string _criticReviewsPath;
|
||||||
|
|
||||||
private SqliteChapterRepository _chapterRepository;
|
|
||||||
private SqliteMediaStreamsRepository _mediaStreamsRepository;
|
private SqliteMediaStreamsRepository _mediaStreamsRepository;
|
||||||
|
|
||||||
private IDbCommand _deleteChildrenCommand;
|
private IDbCommand _deleteChildrenCommand;
|
||||||
|
@ -73,6 +72,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
private IDbCommand _deletePeopleCommand;
|
private IDbCommand _deletePeopleCommand;
|
||||||
private IDbCommand _savePersonCommand;
|
private IDbCommand _savePersonCommand;
|
||||||
|
|
||||||
|
private IDbCommand _deleteChaptersCommand;
|
||||||
|
private IDbCommand _saveChapterCommand;
|
||||||
|
|
||||||
private const int LatestSchemaVersion = 13;
|
private const int LatestSchemaVersion = 13;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -104,15 +106,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
|
|
||||||
_logger = logManager.GetLogger(GetType().Name);
|
_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 mediaStreamsDbFile = Path.Combine(_appPaths.DataPath, "mediainfo.db");
|
||||||
var mediaStreamsConnection = SqliteExtensions.ConnectToDb(mediaStreamsDbFile, _logger).Result;
|
var mediaStreamsConnection = SqliteExtensions.ConnectToDb(mediaStreamsDbFile, _logger).Result;
|
||||||
_mediaStreamsRepository = new SqliteMediaStreamsRepository(mediaStreamsConnection, logManager);
|
_mediaStreamsRepository = new SqliteMediaStreamsRepository(mediaStreamsConnection, logManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const string ChaptersTableName = "Chapters2";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens the connection to the database
|
/// Opens the connection to the database
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -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 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
|
//pragmas
|
||||||
"pragma temp_store = memory",
|
"pragma temp_store = memory",
|
||||||
|
|
||||||
|
@ -195,7 +198,35 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
PrepareStatements();
|
PrepareStatements();
|
||||||
|
|
||||||
_mediaStreamsRepository.Initialize();
|
_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -326,6 +357,19 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
_savePersonCommand.Parameters.Add(_savePersonCommand, "@PersonType");
|
_savePersonCommand.Parameters.Add(_savePersonCommand, "@PersonType");
|
||||||
_savePersonCommand.Parameters.Add(_savePersonCommand, "@SortOrder");
|
_savePersonCommand.Parameters.Add(_savePersonCommand, "@SortOrder");
|
||||||
_savePersonCommand.Parameters.Add(_savePersonCommand, "@ListOrder");
|
_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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -748,7 +792,25 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
public IEnumerable<ChapterInfo> GetChapters(Guid id)
|
public IEnumerable<ChapterInfo> GetChapters(Guid id)
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -761,7 +823,52 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
public ChapterInfo GetChapter(Guid id, int index)
|
public ChapterInfo GetChapter(Guid id, int index)
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the chapter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The reader.</param>
|
||||||
|
/// <returns>ChapterInfo.</returns>
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -778,10 +885,87 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
/// or
|
/// or
|
||||||
/// cancellationToken
|
/// cancellationToken
|
||||||
/// </exception>
|
/// </exception>
|
||||||
public Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken)
|
public async Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -831,12 +1015,6 @@ namespace MediaBrowser.Server.Implementations.Persistence
|
||||||
_connection = null;
|
_connection = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_chapterRepository != null)
|
|
||||||
{
|
|
||||||
_chapterRepository.Dispose();
|
|
||||||
_chapterRepository = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_mediaStreamsRepository != null)
|
if (_mediaStreamsRepository != null)
|
||||||
{
|
{
|
||||||
_mediaStreamsRepository.Dispose();
|
_mediaStreamsRepository.Dispose();
|
||||||
|
|
|
@ -351,11 +351,7 @@ namespace MediaBrowser.Server.Startup.Common
|
||||||
{
|
{
|
||||||
var migrations = new List<IVersionMigration>
|
var migrations = new List<IVersionMigration>
|
||||||
{
|
{
|
||||||
new MigrateUserFolders(ApplicationPaths, FileSystemManager),
|
new RenameXmlOptions(ServerConfigurationManager)
|
||||||
new RenameXbmcOptions(ServerConfigurationManager),
|
|
||||||
new RenameXmlOptions(ServerConfigurationManager),
|
|
||||||
new DeprecatePlugins(ApplicationPaths, FileSystemManager),
|
|
||||||
new DeleteDlnaProfiles(ApplicationPaths, FileSystemManager)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var task in migrations)
|
foreach (var task in migrations)
|
||||||
|
|
|
@ -71,11 +71,7 @@
|
||||||
<Compile Include="FFMpeg\FFmpegValidator.cs" />
|
<Compile Include="FFMpeg\FFmpegValidator.cs" />
|
||||||
<Compile Include="INativeApp.cs" />
|
<Compile Include="INativeApp.cs" />
|
||||||
<Compile Include="MbLinkShortcutHandler.cs" />
|
<Compile Include="MbLinkShortcutHandler.cs" />
|
||||||
<Compile Include="Migrations\DeleteDlnaProfiles.cs" />
|
|
||||||
<Compile Include="Migrations\DeprecatePlugins.cs" />
|
|
||||||
<Compile Include="Migrations\IVersionMigration.cs" />
|
<Compile Include="Migrations\IVersionMigration.cs" />
|
||||||
<Compile Include="Migrations\MigrateUserFolders.cs" />
|
|
||||||
<Compile Include="Migrations\RenameXbmcOptions.cs" />
|
|
||||||
<Compile Include="Migrations\RenameXmlOptions.cs" />
|
<Compile Include="Migrations\RenameXmlOptions.cs" />
|
||||||
<Compile Include="NativeEnvironment.cs" />
|
<Compile Include="NativeEnvironment.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user