mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-04 03:27:21 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			310 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			310 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using MediaBrowser.Controller;
 | 
						|
using MediaBrowser.Controller.Activity;
 | 
						|
using MediaBrowser.Model.Activity;
 | 
						|
using MediaBrowser.Model.Logging;
 | 
						|
using MediaBrowser.Model.Querying;
 | 
						|
using MediaBrowser.Server.Implementations.Persistence;
 | 
						|
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Data;
 | 
						|
using System.Globalization;
 | 
						|
using System.IO;
 | 
						|
using System.Threading;
 | 
						|
using System.Threading.Tasks;
 | 
						|
 | 
						|
namespace MediaBrowser.Server.Implementations.Activity
 | 
						|
{
 | 
						|
    public class ActivityRepository : IActivityRepository, IDisposable
 | 
						|
    {
 | 
						|
        private IDbConnection _connection;
 | 
						|
        private readonly ILogger _logger;
 | 
						|
        private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1);
 | 
						|
        private readonly IServerApplicationPaths _appPaths;
 | 
						|
        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 | 
						|
 | 
						|
        private IDbCommand _saveActivityCommand;
 | 
						|
 | 
						|
        public ActivityRepository(ILogger logger, IServerApplicationPaths appPaths)
 | 
						|
        {
 | 
						|
            _logger = logger;
 | 
						|
            _appPaths = appPaths;
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task Initialize()
 | 
						|
        {
 | 
						|
            var dbFile = Path.Combine(_appPaths.DataPath, "activitylog.db");
 | 
						|
 | 
						|
            _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false);
 | 
						|
 | 
						|
            string[] queries = {
 | 
						|
 | 
						|
                                "create table if not exists ActivityLogEntries (Id GUID PRIMARY KEY, Name TEXT, Overview TEXT, ShortOverview TEXT, Type TEXT, ItemId TEXT, UserId TEXT, DateCreated DATETIME, LogSeverity TEXT)",
 | 
						|
                                "create index if not exists idx_ActivityLogEntries on ActivityLogEntries(Id)",
 | 
						|
 | 
						|
                                //pragmas
 | 
						|
                                "pragma temp_store = memory",
 | 
						|
 | 
						|
                                "pragma shrink_memory"
 | 
						|
                               };
 | 
						|
 | 
						|
            _connection.RunQueries(queries, _logger);
 | 
						|
 | 
						|
            PrepareStatements();
 | 
						|
        }
 | 
						|
 | 
						|
        private void PrepareStatements()
 | 
						|
        {
 | 
						|
            _saveActivityCommand = _connection.CreateCommand();
 | 
						|
            _saveActivityCommand.CommandText = "replace into ActivityLogEntries (Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Id, @Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)";
 | 
						|
 | 
						|
            _saveActivityCommand.Parameters.Add(_saveActivityCommand, "@Id");
 | 
						|
            _saveActivityCommand.Parameters.Add(_saveActivityCommand, "@Name");
 | 
						|
            _saveActivityCommand.Parameters.Add(_saveActivityCommand, "@Overview");
 | 
						|
            _saveActivityCommand.Parameters.Add(_saveActivityCommand, "@ShortOverview");
 | 
						|
            _saveActivityCommand.Parameters.Add(_saveActivityCommand, "@Type");
 | 
						|
            _saveActivityCommand.Parameters.Add(_saveActivityCommand, "@ItemId");
 | 
						|
            _saveActivityCommand.Parameters.Add(_saveActivityCommand, "@UserId");
 | 
						|
            _saveActivityCommand.Parameters.Add(_saveActivityCommand, "@DateCreated");
 | 
						|
            _saveActivityCommand.Parameters.Add(_saveActivityCommand, "@LogSeverity");
 | 
						|
        }
 | 
						|
 | 
						|
        private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLogEntries";
 | 
						|
 | 
						|
        public Task Create(ActivityLogEntry entry)
 | 
						|
        {
 | 
						|
            return Update(entry);
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task Update(ActivityLogEntry entry)
 | 
						|
        {
 | 
						|
            if (entry == null)
 | 
						|
            {
 | 
						|
                throw new ArgumentNullException("entry");
 | 
						|
            }
 | 
						|
 | 
						|
            await _writeLock.WaitAsync().ConfigureAwait(false);
 | 
						|
 | 
						|
            IDbTransaction transaction = null;
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                transaction = _connection.BeginTransaction();
 | 
						|
 | 
						|
                var index = 0;
 | 
						|
 | 
						|
                _saveActivityCommand.GetParameter(index++).Value = new Guid(entry.Id);
 | 
						|
                _saveActivityCommand.GetParameter(index++).Value = entry.Name;
 | 
						|
                _saveActivityCommand.GetParameter(index++).Value = entry.Overview;
 | 
						|
                _saveActivityCommand.GetParameter(index++).Value = entry.ShortOverview;
 | 
						|
                _saveActivityCommand.GetParameter(index++).Value = entry.Type;
 | 
						|
                _saveActivityCommand.GetParameter(index++).Value = entry.ItemId;
 | 
						|
                _saveActivityCommand.GetParameter(index++).Value = entry.UserId;
 | 
						|
                _saveActivityCommand.GetParameter(index++).Value = entry.Date;
 | 
						|
                _saveActivityCommand.GetParameter(index++).Value = entry.Severity.ToString();
 | 
						|
 | 
						|
                _saveActivityCommand.Transaction = transaction;
 | 
						|
 | 
						|
                _saveActivityCommand.ExecuteNonQuery();
 | 
						|
 | 
						|
                transaction.Commit();
 | 
						|
            }
 | 
						|
            catch (OperationCanceledException)
 | 
						|
            {
 | 
						|
                if (transaction != null)
 | 
						|
                {
 | 
						|
                    transaction.Rollback();
 | 
						|
                }
 | 
						|
 | 
						|
                throw;
 | 
						|
            }
 | 
						|
            catch (Exception e)
 | 
						|
            {
 | 
						|
                _logger.ErrorException("Failed to save record:", e);
 | 
						|
 | 
						|
                if (transaction != null)
 | 
						|
                {
 | 
						|
                    transaction.Rollback();
 | 
						|
                }
 | 
						|
 | 
						|
                throw;
 | 
						|
            }
 | 
						|
            finally
 | 
						|
            {
 | 
						|
                if (transaction != null)
 | 
						|
                {
 | 
						|
                    transaction.Dispose();
 | 
						|
                }
 | 
						|
 | 
						|
                _writeLock.Release();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
 | 
						|
        {
 | 
						|
            using (var cmd = _connection.CreateCommand())
 | 
						|
            {
 | 
						|
                cmd.CommandText = BaseActivitySelectText;
 | 
						|
 | 
						|
                var whereClauses = new List<string>();
 | 
						|
 | 
						|
                if (minDate.HasValue)
 | 
						|
                {
 | 
						|
                    whereClauses.Add("DateCreated>=@DateCreated");
 | 
						|
                    cmd.Parameters.Add(cmd, "@DateCreated", DbType.Date).Value = minDate.Value;
 | 
						|
                }
 | 
						|
 | 
						|
                var whereTextWithoutPaging = whereClauses.Count == 0 ?
 | 
						|
                    string.Empty :
 | 
						|
                    " where " + string.Join(" AND ", whereClauses.ToArray());
 | 
						|
 | 
						|
                if (startIndex.HasValue && startIndex.Value > 0)
 | 
						|
                {
 | 
						|
                    var pagingWhereText = whereClauses.Count == 0 ?
 | 
						|
                        string.Empty :
 | 
						|
                        " where " + string.Join(" AND ", whereClauses.ToArray());
 | 
						|
                    
 | 
						|
                    whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLogEntries {0} ORDER BY DateCreated DESC LIMIT {1})",
 | 
						|
                        pagingWhereText,
 | 
						|
                        startIndex.Value.ToString(_usCulture)));
 | 
						|
                }
 | 
						|
 | 
						|
                var whereText = whereClauses.Count == 0 ? 
 | 
						|
                    string.Empty :
 | 
						|
                    " where " + string.Join(" AND ", whereClauses.ToArray());
 | 
						|
 | 
						|
                cmd.CommandText += whereText;
 | 
						|
 | 
						|
                cmd.CommandText += " ORDER BY DateCreated DESC";
 | 
						|
 | 
						|
                if (limit.HasValue)
 | 
						|
                {
 | 
						|
                    cmd.CommandText += " LIMIT " + limit.Value.ToString(_usCulture);
 | 
						|
                }
 | 
						|
 | 
						|
                cmd.CommandText += "; select count (Id) from ActivityLogEntries" + whereTextWithoutPaging;
 | 
						|
 | 
						|
                var list = new List<ActivityLogEntry>();
 | 
						|
                var count = 0;
 | 
						|
 | 
						|
                using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
 | 
						|
                {
 | 
						|
                    while (reader.Read())
 | 
						|
                    {
 | 
						|
                        list.Add(GetEntry(reader));
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (reader.NextResult() && reader.Read())
 | 
						|
                    {
 | 
						|
                        count = reader.GetInt32(0);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                return new QueryResult<ActivityLogEntry>()
 | 
						|
                {
 | 
						|
                    Items = list.ToArray(),
 | 
						|
                    TotalRecordCount = count
 | 
						|
                };
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private ActivityLogEntry GetEntry(IDataReader reader)
 | 
						|
        {
 | 
						|
            var index = 0;
 | 
						|
 | 
						|
            var info = new ActivityLogEntry
 | 
						|
            {
 | 
						|
                Id = reader.GetGuid(index).ToString("N")
 | 
						|
            };
 | 
						|
 | 
						|
            index++;
 | 
						|
            if (!reader.IsDBNull(index))
 | 
						|
            {
 | 
						|
                info.Name = reader.GetString(index);
 | 
						|
            }
 | 
						|
 | 
						|
            index++;
 | 
						|
            if (!reader.IsDBNull(index))
 | 
						|
            {
 | 
						|
                info.Overview = reader.GetString(index);
 | 
						|
            }
 | 
						|
 | 
						|
            index++;
 | 
						|
            if (!reader.IsDBNull(index))
 | 
						|
            {
 | 
						|
                info.ShortOverview = reader.GetString(index);
 | 
						|
            }
 | 
						|
 | 
						|
            index++;
 | 
						|
            if (!reader.IsDBNull(index))
 | 
						|
            {
 | 
						|
                info.Type = reader.GetString(index);
 | 
						|
            }
 | 
						|
 | 
						|
            index++;
 | 
						|
            if (!reader.IsDBNull(index))
 | 
						|
            {
 | 
						|
                info.ItemId = reader.GetString(index);
 | 
						|
            }
 | 
						|
 | 
						|
            index++;
 | 
						|
            if (!reader.IsDBNull(index))
 | 
						|
            {
 | 
						|
                info.UserId = reader.GetString(index);
 | 
						|
            }
 | 
						|
 | 
						|
            index++;
 | 
						|
            info.Date = reader.GetDateTime(index).ToUniversalTime();
 | 
						|
 | 
						|
            index++;
 | 
						|
            if (!reader.IsDBNull(index))
 | 
						|
            {
 | 
						|
                info.Severity = (LogSeverity)Enum.Parse(typeof(LogSeverity), reader.GetString(index), true);
 | 
						|
            }
 | 
						|
 | 
						|
            return info;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <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);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |