From e5d4a6793813de9df8c04a63caebae8cce936dec Mon Sep 17 00:00:00 2001 From: Splamy Date: Sat, 21 Oct 2017 20:52:54 +0200 Subject: [PATCH] Extracted db for injection later. + Deprecated old history upgrader --- .travis.yml | 4 +- TS3ABotUnitTests/UnitTests.cs | 39 +- TS3AudioBot/DbStore.cs | 71 +++ .../History/Deprecated/AudioLogEntry.cs | 103 ---- TS3AudioBot/History/Deprecated/HistoryFile.cs | 454 ------------------ TS3AudioBot/History/HistoryManager.cs | 86 +--- TS3AudioBot/MainBot.cs | 14 +- TS3AudioBot/TS3AudioBot.csproj | 3 +- 8 files changed, 115 insertions(+), 659 deletions(-) create mode 100644 TS3AudioBot/DbStore.cs delete mode 100644 TS3AudioBot/History/Deprecated/AudioLogEntry.cs delete mode 100644 TS3AudioBot/History/Deprecated/HistoryFile.cs diff --git a/.travis.yml b/.travis.yml index 806347ab..8fc0c8f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,8 +41,8 @@ after_success: - cd ./TS3AudioBot/bin/Release - ls - zip TS3AudioBot.zip TS3AudioBot.exe TS3Client.dll Nett.dll LiteDB.dll Heijden.Dns.dll BouncyCastle.Crypto.dll x64/* x86/* - - 'export version=`./TS3AudioBot.exe --version | grep "Version: "`' - - "curl -I -H \"Content-Type: application/zip\" -X PUT \"http://splamy.de/api/nightly/ts3ab/${TRAVIS_BRANCH}?token=${uploadkey}&filename=TS3AudioBot.zip&commit=${TRAVIS_COMMIT}&version=${version}\" --upload-file ./TS3AudioBot.zip" + - 'export version=`mono TS3AudioBot.exe --version | grep "Version: "`' + - "curl -I -H \"Content-Type: application/zip\" -X PUT \"http://splamy.de/api/nightly/ts3ab/${TRAVIS_BRANCH}?token=${uploadkey}&filename=TS3AudioBot.zip&commit=${TRAVIS_COMMIT}&version=${version:9}\" --upload-file ./TS3AudioBot.zip" - cd "$MAIN_DIR" after_script: diff --git a/TS3ABotUnitTests/UnitTests.cs b/TS3ABotUnitTests/UnitTests.cs index 14c41bff..b8c452e1 100644 --- a/TS3ABotUnitTests/UnitTests.cs +++ b/TS3ABotUnitTests/UnitTests.cs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +using TS3AudioBot; + namespace TS3ABotUnitTests { using System; @@ -56,8 +58,9 @@ public void HistoryFileIntergrityTest() var data2 = new HistorySaveData(ar2, inv2.DatabaseId); var data3 = new HistorySaveData(ar3, 103); - - var hf = new HistoryManager(new HistoryManagerData { HistoryFile = testFile, FillDeletedIds = false }); + var hmf = new HistoryManagerData {HistoryFile = testFile, FillDeletedIds = false}; + var db = new DbStore(hmf); + var hf = new HistoryManager(hmf, db); hf.LogAudioResource(data1); @@ -66,9 +69,10 @@ public void HistoryFileIntergrityTest() var lastEntry = lastXEntries.First(); Assert.AreEqual(ar1, lastEntry.AudioResource); - hf.Dispose(); + db.Dispose(); - hf = new HistoryManager(new HistoryManagerData { HistoryFile = testFile, FillDeletedIds = false }); + db = new DbStore(hmf); + hf = new HistoryManager(hmf, db); lastXEntries = hf.GetLastXEntrys(1); Assert.True(lastXEntries.Any()); lastEntry = lastXEntries.First(); @@ -82,10 +86,11 @@ public void HistoryFileIntergrityTest() lastEntry = lastXEntries.First(); Assert.AreEqual(ar2, lastEntry.AudioResource); - hf.Dispose(); + db.Dispose(); // store and order check - hf = new HistoryManager(new HistoryManagerData { HistoryFile = testFile, FillDeletedIds = false }); + db = new DbStore(hmf); + hf = new HistoryManager(hmf, db); var lastXEntriesArray = hf.GetLastXEntrys(2).ToArray(); Assert.AreEqual(2, lastXEntriesArray.Length); Assert.AreEqual(ar2, lastXEntriesArray[0].AudioResource); @@ -96,10 +101,11 @@ public void HistoryFileIntergrityTest() hf.LogAudioResource(new HistorySaveData(ale1.AudioResource, 42)); - hf.Dispose(); + db.Dispose(); // check entry renaming - hf = new HistoryManager(new HistoryManagerData { HistoryFile = testFile, FillDeletedIds = false }); + db = new DbStore(hmf); + hf = new HistoryManager(hmf, db); lastXEntriesArray = hf.GetLastXEntrys(2).ToArray(); Assert.AreEqual(2, lastXEntriesArray.Length); Assert.AreEqual(ar1, lastXEntriesArray[0].AudioResource); @@ -116,18 +122,20 @@ public void HistoryFileIntergrityTest() hf.RenameEntry(ale2, "me_ar2_exxxxxtra_loong1"); hf.LogAudioResource(new HistorySaveData(ale2.AudioResource, 42)); - hf.Dispose(); + db.Dispose(); // recheck order - hf = new HistoryManager(new HistoryManagerData { HistoryFile = testFile, FillDeletedIds = false }); + db = new DbStore(hmf); + hf = new HistoryManager(hmf, db); lastXEntriesArray = hf.GetLastXEntrys(2).ToArray(); Assert.AreEqual(2, lastXEntriesArray.Length); Assert.AreEqual(ar2, lastXEntriesArray[0].AudioResource); Assert.AreEqual(ar1, lastXEntriesArray[1].AudioResource); - hf.Dispose(); + db.Dispose(); // delete entry 1 - hf = new HistoryManager(new HistoryManagerData { HistoryFile = testFile, FillDeletedIds = false }); + db = new DbStore(hmf); + hf = new HistoryManager(hmf, db); hf.RemoveEntry(hf.FindEntryByResource(ar1)); lastXEntriesArray = hf.GetLastXEntrys(3).ToArray(); @@ -138,10 +146,11 @@ public void HistoryFileIntergrityTest() lastXEntriesArray = hf.GetLastXEntrys(3).ToArray(); Assert.AreEqual(2, lastXEntriesArray.Length); - hf.Dispose(); + db.Dispose(); // delete entry 2 - hf = new HistoryManager(new HistoryManagerData { HistoryFile = testFile, FillDeletedIds = false }); + db = new DbStore(hmf); + hf = new HistoryManager(hmf, db); // .. check integrity from previous store lastXEntriesArray = hf.GetLastXEntrys(3).ToArray(); Assert.AreEqual(2, lastXEntriesArray.Length); @@ -152,7 +161,7 @@ public void HistoryFileIntergrityTest() lastXEntriesArray = hf.GetLastXEntrys(3).ToArray(); Assert.AreEqual(1, lastXEntriesArray.Length); Assert.AreEqual(ar3, lastXEntriesArray[0].AudioResource); - hf.Dispose(); + db.Dispose(); File.Delete(testFile); diff --git a/TS3AudioBot/DbStore.cs b/TS3AudioBot/DbStore.cs new file mode 100644 index 00000000..c8545d08 --- /dev/null +++ b/TS3AudioBot/DbStore.cs @@ -0,0 +1,71 @@ +// TS3AudioBot - An advanced Musicbot for Teamspeak 3 +// Copyright (C) 2017 TS3AudioBot contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Open Software License v. 3.0 +// +// You should have received a copy of the Open Software License along with this +// program. If not, see . + +namespace TS3AudioBot +{ + using History; + using LiteDB; + using System; + using System.IO; + + public class DbStore : IDisposable + { + private const string DbMetaInformationTable = "dbmeta"; + + private readonly LiteDatabase database; + private readonly LiteCollection metaTable; + + // TODO rework config class with config rewok + public DbStore(HistoryManagerData hmd) + { + var historyFile = new FileInfo(hmd.HistoryFile); + database = new LiteDatabase(historyFile.FullName); + + metaTable = database.GetCollection(DbMetaInformationTable); + } + + public DbMetaData GetMetaData(string table) + { + var meta = metaTable.FindById(table); + if (meta == null) + { + meta = new DbMetaData { Id = table, Version = 0, CustomData = null }; + metaTable.Insert(meta); + } + return meta; + } + + public void UpdateMetaData(DbMetaData metaData) + { + metaTable.Update(metaData); + } + + public LiteCollection GetCollection(string name) + { + return database.GetCollection(name); + } + + public void CleanFile() + { + database.Shrink(); + } + + public void Dispose() + { + database.Dispose(); + } + } + + public class DbMetaData + { + public string Id { get; set; } + public int Version { get; set; } + public object CustomData { get; set; } + } +} diff --git a/TS3AudioBot/History/Deprecated/AudioLogEntry.cs b/TS3AudioBot/History/Deprecated/AudioLogEntry.cs deleted file mode 100644 index 08d47556..00000000 --- a/TS3AudioBot/History/Deprecated/AudioLogEntry.cs +++ /dev/null @@ -1,103 +0,0 @@ -// TS3AudioBot - An advanced Musicbot for Teamspeak 3 -// Copyright (C) 2017 TS3AudioBot contributors -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the Open Software License v. 3.0 -// -// You should have received a copy of the Open Software License along with this -// program. If not, see . - -namespace TS3AudioBot.History.Deprecated -{ - using ResourceFactories; - using System; - using System.Globalization; - using System.Text; - - public class AudioLogEntry - { - /// A unique id for each , given by the history system. - public uint Id { get; } - /// The dbid of the teamspeak user, who played this song first. - public uint UserInvokeId { get; set; } - /// How often the song has been played. - public uint PlayCount { get; set; } - /// The last time this song has been played. - public DateTime Timestamp { get; set; } - /// Zero based offset this entry is stored in the history file. - public long FilePosIndex { get; set; } - - public AudioResource AudioResource { get; private set; } - - public AudioLogEntry(uint id, AudioResource resource) - { - Id = id; - PlayCount = 0; - AudioResource = resource; - } - - public AudioLogEntry(uint id, string resourceId, string resourceTitle, string audioType) - : this(id, new AudioResource(resourceId, resourceTitle, audioType)) { } - - public void SetName(string newName) - { - AudioResource = AudioResource.WithName(newName); - } - - public string ToFileString() - { - var strb = new StringBuilder(); - // HEX STRINGS - strb.Append(AsHex(Id)); - strb.Append(","); - strb.Append(AsHex(UserInvokeId)); - strb.Append(","); - strb.Append(AsHex(PlayCount)); - strb.Append(","); - strb.Append(AsHex(Timestamp.ToFileTime())); - strb.Append(","); - - // OTHER STRINGS - strb.Append(AudioResource.AudioType.ToString()); - strb.Append(","); - strb.Append(Uri.EscapeDataString(AudioResource.ResourceId)); - strb.Append(","); - strb.Append(Uri.EscapeDataString(AudioResource.ResourceTitle)); - - return strb.ToString(); - } - - public static AudioLogEntry Parse(string line, long readIndex) - { - string[] strParts = line.TrimEnd(' ').Split(','); - if (strParts.Length != 7) - return null; - // Array.ForEach(strParts) // check if spacetrims are needed - int index = 0; - uint id = uint.Parse(strParts[index++], NumberStyles.HexNumber, CultureInfo.InvariantCulture); - uint userInvId = uint.Parse(strParts[index++], NumberStyles.HexNumber, CultureInfo.InvariantCulture); - uint playCount = uint.Parse(strParts[index++], NumberStyles.HexNumber, CultureInfo.InvariantCulture); - long dtStamp = long.Parse(strParts[index++], NumberStyles.HexNumber, CultureInfo.InvariantCulture); - var dateTime = DateTime.FromFileTime(dtStamp); - string audioType = strParts[index++]; - string resId = Uri.UnescapeDataString(strParts[index++]); - string title = Uri.UnescapeDataString(strParts[index++]); - return new AudioLogEntry(id, resId, title, audioType) - { - PlayCount = playCount, - Timestamp = dateTime, - UserInvokeId = userInvId, - FilePosIndex = readIndex, - }; - } - - private static string AsHex(uint num) => num.ToString("X8", CultureInfo.InvariantCulture); - private static string AsHex(long num) => num.ToString("X16", CultureInfo.InvariantCulture); - - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "[{0}] @ {1} by {2}: {3}, ({4})", Id, Timestamp, UserInvokeId, AudioResource.ResourceTitle, AudioResource); - } - } - -} diff --git a/TS3AudioBot/History/Deprecated/HistoryFile.cs b/TS3AudioBot/History/Deprecated/HistoryFile.cs deleted file mode 100644 index 3687650f..00000000 --- a/TS3AudioBot/History/Deprecated/HistoryFile.cs +++ /dev/null @@ -1,454 +0,0 @@ -// TS3AudioBot - An advanced Musicbot for Teamspeak 3 -// Copyright (C) 2017 TS3AudioBot contributors -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the Open Software License v. 3.0 -// -// You should have received a copy of the Open Software License along with this -// program. If not, see . - -namespace TS3AudioBot.History.Deprecated -{ - using Helper; - using ResourceFactories; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Text; - - public sealed class HistoryFile : IDisposable - { - private IDictionary resIdToId; - private IDictionary idFilter; - private IDictionary> userIdFilter; - private SortedList timeFilter; - private LinkedList unusedIds; - - public uint CurrentID { get; private set; } = 0; - public bool ReuseUnusedIds { get; set; } - - private readonly IList noResult = new List().AsReadOnly(); - - private static readonly Encoding FileEncoding = Encoding.ASCII; - private static readonly byte[] NewLineArray = new byte[] { (byte)'\n' }; - public FileInfo historyFile; - private FileStream fileStream; - private PositionedStreamReader fileReader; - - const string VersionHeader = "VERSION-"; - private const int HistoryManagerVersion = 1; - - - public HistoryFile() - { - resIdToId = new Dictionary(); - idFilter = new SortedList(); - userIdFilter = new SortedList>(); - timeFilter = new SortedList(); - unusedIds = new LinkedList(); - } - - public void OpenFile(string path) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - historyFile = new FileInfo(path); - - CloseFile(); - Clear(); - - fileStream = historyFile.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); - fileReader = new PositionedStreamReader(fileStream, FileEncoding); - VersionCheckAndUpgrade(); - RestoreFromFile(); - } - - public void CloseFile() - { - if (fileReader != null) - { - fileReader.Dispose(); - fileReader = null; - } - if (fileStream != null) - { - fileStream.Dispose(); - fileStream = null; - } - } - - private void VersionCheckAndUpgrade() - { - string line = fileReader.ReadLine(); - if (line == null) - { - // fresh file - WriteHeader(); - return; - } - - int fileVersion = -1; - if (!line.StartsWith(VersionHeader, StringComparison.Ordinal) - || !int.TryParse(line.Substring(VersionHeader.Length), out fileVersion)) - throw new FormatException("The history file has an invalid header."); - - if (fileVersion < HistoryManagerVersion) - BackupFile(); - - switch (fileVersion) - { - case 0: /*do upgrade stuff*/ goto case 1; - case 1: break; // lastest version - - default: - throw new FormatException("Not recognized header version"); - } - } - - public void CleanFile() - { - BackupFile(); - - fileStream.SetLength(0); - WriteHeader(); - - for (uint i = 0; i < CurrentID; i++) - { - var ale = GetEntryById(i); - if (ale != null) - AppendToFile(ale, false); - } - fileStream.Flush(true); - fileReader.InvalidateBuffer(); - } - - private void RestoreFromFile() - { - string line; - long readIndex = fileReader.ReadPosition; - while ((line = fileReader.ReadLine()) != null) - { - if (!string.IsNullOrEmpty(line) && line[0] != ' ') - { - var ale = AudioLogEntry.Parse(line, readIndex); - if (ale != null) - { - AddToMemoryIndex(ale); - if (ale.Id >= CurrentID) - CurrentID = ale.Id + 1; - } - } - readIndex = fileReader.ReadPosition; - } - - // fill up unused-id list - for(uint i = 0; i < CurrentID; i++) - { - if (GetEntryById(i) == null) - unusedIds.AddLast(i); - } - } - - private void WriteHeader() - { - byte[] versionHeader = FileEncoding.GetBytes(VersionHeader + HistoryManagerVersion); - fileStream.Write(versionHeader, 0, versionHeader.Length); - fileStream.Write(NewLineArray, 0, NewLineArray.Length); - } - - public void BackupFile() - { - int backUpNum = 0; - string fileName; - do - { - fileName = Path.Combine(historyFile.DirectoryName, - historyFile.Name.Substring(0, historyFile.Name.Length - historyFile.Extension.Length) - + "_old_" + backUpNum + historyFile.Extension); - backUpNum++; - } while (File.Exists(fileName)); - historyFile.CopyTo(fileName); - } - - - public AudioLogEntry Store(HistorySaveData saveData) - { - if (saveData == null) - throw new ArgumentNullException(nameof(saveData)); - - AudioLogEntry ale; - uint? index = Contains(saveData.Resource); - if (!index.HasValue) - { - ale = CreateLogEntry(saveData); - if (ale != null) - { - AddToMemoryIndex(ale); - AppendToFile(ale); - } - else - Log.Write(Log.Level.Error, "AudioLogEntry could not be created!"); - } - else - { - ale = GetEntryById(index.Value); - if (ale == null) - throw new InvalidOperationException("Entry removed while read"); - LogEntryPlay(ale); - } - return ale; - } - - public uint? Contains(AudioResource resource) - { - if (resource == null) - throw new ArgumentNullException(nameof(resource)); - - if (resIdToId.TryGetValue(resource.UniqueId, out uint rId)) - return rId; - return null; - } - - /// Gets an AudioLogEntry by its unique id or null if not exising. - /// The id of the AudioLogEntry - public AudioLogEntry GetEntryById(uint id) - { - if (idFilter.TryGetValue(id, out var ale)) - return ale; - return null; - } - - /// Gets all Entrys last called from a user. - /// Sort: By id ascending. - /// TeamSpeak 3 Database UID of the user. - /// A list of all found entries. - public IList SeachByUser(uint userId) - { - if (userIdFilter.TryGetValue(userId, out var result)) - return result; - else - return noResult; - } - - /// Gets all Entries until a certain datetime. - /// Sort: By call time ascending. - /// Included last time of an entry called. - /// A list of all found entries. - public IList SeachTillTime(DateTime time) - { - int index = timeFilter.Keys.ToList().BinarySearch(time); - - if (index > 0) - { - return timeFilter.Values.Skip(index - 1).ToList(); - } - else - { - index = ~index; - if (index == 0) - return timeFilter.Values; - else if (index == timeFilter.Values.Count) - return noResult; - else - return timeFilter.Values.Skip(index).ToList(); - } - } - - /// Gets the last played entries. - /// Sort: By call time ascending. - /// The maximal amount of entries. - /// A list of all found entries. - public IList GetLastXEntrys(int idAmount) - { - if (idAmount <= 0) - return noResult; - - var aleArray = timeFilter.Values.ToArray(); - var result = new AudioLogEntry[Math.Min(aleArray.Length, idAmount)]; - Array.Copy(aleArray, Math.Max(0, aleArray.Length - idAmount), result, 0, Math.Min(aleArray.Length, result.Length)); - return result; - } - - public IList GetAll() => idFilter.Values.ToList(); - - // User features - - /// Increases the playcount and updates the last playtime. - /// The AudioLogEntry to update. - /// True when the changes should be applied directly to the file. - /// False to write it manually later with - public void LogEntryPlay(AudioLogEntry ale, bool flush = true) - { - if (ale == null) - throw new ArgumentNullException(nameof(ale)); - - // update the playtime - timeFilter.Remove(ale.Timestamp); - ale.Timestamp = Util.GetNow(); - timeFilter.Add(ale.Timestamp, ale); - - // update the playcount - ale.PlayCount++; - - if (flush) ReWriteToFile(ale); - } - - /// Sets the name of a AudioLogEntry. - /// The AudioLogEntry to rename. - /// The new name for the AudioLogEntry. - /// True when the changes should be applied directly to the file. - /// False to write it manually later with - /// When the name is null, empty or only whitspaces - public void LogEntryRename(AudioLogEntry ale, string newName, bool flush = true) - { - if (string.IsNullOrWhiteSpace(newName)) - throw new ArgumentNullException(nameof(newName)); - - // update the name - ale.SetName(newName); - - if (flush) ReWriteToFile(ale); - } - - /// Removes the AudioLogEntry from the memory index list and file. - /// The AudioLogEntry to delete. - public void LogEntryRemove(AudioLogEntry ale) - { - if (ale == null) - throw new ArgumentNullException(nameof(ale)); - if (!Contains(ale.AudioResource).HasValue) - throw new ArgumentException("The requested entry was not found."); - - RemoveFromMemoryIndex(ale); - RemoveFromFile(ale); - } - - // Internal features - - private AudioLogEntry CreateLogEntry(HistorySaveData saveData) - { - if (string.IsNullOrWhiteSpace(saveData.Resource.ResourceTitle)) - return null; - - uint nextHid; - if (ReuseUnusedIds && unusedIds.Count > 0) - { - nextHid = unusedIds.First.Value; - unusedIds.RemoveFirst(); - } - else - { - nextHid = CurrentID; - CurrentID++; - } - - var ale = new AudioLogEntry(nextHid, saveData.Resource) - { - UserInvokeId = (uint)(saveData.OwnerDbId ?? 0), - Timestamp = Util.GetNow(), - PlayCount = 1, - }; - - return ale; - } - - private void AppendToFile(AudioLogEntry logEntry, bool flush = true) - { - fileStream.Seek(0, SeekOrigin.End); - logEntry.FilePosIndex = fileStream.Position; - - var fileString = logEntry.ToFileString(); - var strBytes = FileEncoding.GetBytes(fileString); - fileStream.Write(strBytes, 0, strBytes.Length); - fileStream.Write(NewLineArray, 0, NewLineArray.Length); - if (flush) fileStream.Flush(true); - } - - private void ReWriteToFile(AudioLogEntry logEntry) - { - fileStream.Seek(logEntry.FilePosIndex, SeekOrigin.Begin); - fileReader.InvalidateBuffer(); - byte[] curLine = FileEncoding.GetBytes(fileReader.ReadLine()); - byte[] newLine = FileEncoding.GetBytes(logEntry.ToFileString()); - - if (Enumerable.SequenceEqual(curLine, newLine)) - return; - - fileStream.Seek(logEntry.FilePosIndex, SeekOrigin.Begin); - if (newLine.Length <= curLine.Length) - { - fileStream.Write(newLine, 0, newLine.Length); - if (newLine.Length < curLine.Length) - CleanLine(curLine.Length - newLine.Length); - } - else - { - byte[] filler = Enumerable.Repeat((byte)' ', curLine.Length).ToArray(); - fileStream.Write(filler, 0, filler.Length); - AppendToFile(logEntry, false); - } - fileStream.Flush(true); - } - - private void AddToMemoryIndex(AudioLogEntry logEntry) - { - resIdToId.Add(logEntry.AudioResource.UniqueId, logEntry.Id); - idFilter.Add(logEntry.Id, logEntry); - AutoAdd(userIdFilter, logEntry); - timeFilter.Add(logEntry.Timestamp, logEntry); - } - - private void RemoveFromFile(AudioLogEntry logEntry) - { - fileStream.Seek(logEntry.FilePosIndex, SeekOrigin.Begin); - fileReader.InvalidateBuffer(); - byte[] curLine = FileEncoding.GetBytes(fileReader.ReadLine()); - fileStream.Seek(logEntry.FilePosIndex, SeekOrigin.Begin); - CleanLine(curLine.Length); - fileStream.Flush(true); - } - - private void CleanLine(int length) - { - byte[] filler = Enumerable.Repeat((byte)' ', length).ToArray(); - fileStream.Write(filler, 0, length); - } - - private void RemoveFromMemoryIndex(AudioLogEntry logEntry) - { - resIdToId.Remove(logEntry.AudioResource.UniqueId); - idFilter.Remove(logEntry.Id); - userIdFilter[logEntry.UserInvokeId].Remove(logEntry); - timeFilter.Remove(logEntry.Timestamp); - unusedIds.AddLast(logEntry.Id); - } - - private static void AutoAdd(IDictionary> dict, AudioLogEntry value) - { - if (!dict.TryGetValue(value.UserInvokeId, out var uidList)) - { - uidList = new List(); - dict.Add(value.UserInvokeId, uidList); - } - uidList.Add(value); - } - - private void Clear() - { - resIdToId.Clear(); - idFilter.Clear(); - userIdFilter.Clear(); - timeFilter.Clear(); - unusedIds.Clear(); - - CurrentID = 0; - } - - public void Dispose() - { - CloseFile(); - Clear(); - } - } -} diff --git a/TS3AudioBot/History/HistoryManager.cs b/TS3AudioBot/History/HistoryManager.cs index 15c8ea92..1e424a88 100644 --- a/TS3AudioBot/History/HistoryManager.cs +++ b/TS3AudioBot/History/HistoryManager.cs @@ -14,17 +14,14 @@ namespace TS3AudioBot.History using ResourceFactories; using System; using System.Collections.Generic; - using System.IO; using System.Linq; - public sealed class HistoryManager : IDisposable + public sealed class HistoryManager { private const int CurrentHistoryVersion = 1; private const string AudioLogEntriesTable = "audioLogEntries"; - private const string DbMetaInformationTable = "dbmeta"; private const string ResourceTitleQueryColumn = "lowTitle"; - private readonly LiteDatabase database; private readonly LiteCollection audioLogEntries; private readonly HistoryManagerData historyManagerData; private readonly LinkedList unusedIds; @@ -38,77 +35,28 @@ static HistoryManager() .Id(x => x.Id); } - public HistoryManager(HistoryManagerData hmd) + public HistoryManager(HistoryManagerData hmd, DbStore database) { Formatter = new SmartHistoryFormatter(); historyManagerData = hmd; - #region CheckUpgrade - AudioLogEntry[] moveData = null; - - var upgrader = new Deprecated.HistoryFile(); - // check if the old history database system can open it - try - { - upgrader.OpenFile(historyManagerData.HistoryFile); - Log.Write(Log.Level.Info, "Found old history database vesion, upgrading now."); - - moveData = upgrader - .GetAll() - .Select(x => new AudioLogEntry((int)x.Id, x.AudioResource) - { - PlayCount = x.PlayCount, - Timestamp = x.Timestamp, - UserInvokeId = x.UserInvokeId, - }) - .ToArray(); - // the old database allowed 0-id, while the new one kinda doesn't - var nullIdEntity = moveData.FirstOrDefault(x => x.Id == 0); - if (nullIdEntity != null) - nullIdEntity.Id = moveData.Select(x => x.Id).Max() + 1; - - upgrader.CloseFile(); - upgrader.BackupFile(); - upgrader.historyFile.Delete(); - } - // if not it is already the new one or corrupted - catch (FormatException) { } - finally - { - upgrader.Dispose(); - } - #endregion - Util.Init(ref unusedIds); - var historyFile = new FileInfo(hmd.HistoryFile); - database = new LiteDatabase(historyFile.FullName); audioLogEntries = database.GetCollection(AudioLogEntriesTable); audioLogEntries.EnsureIndex(x => x.AudioResource.UniqueId, true); audioLogEntries.EnsureIndex(x => x.Timestamp); audioLogEntries.EnsureIndex(ResourceTitleQueryColumn, $"LOWER($.{nameof(AudioLogEntry.AudioResource)}.{nameof(AudioResource.ResourceTitle)})"); - - RestoreFromFile(); - #region CheckUpgrade - if (moveData != null) - audioLogEntries.Insert(moveData); - #endregion + RestoreFromFile(); // Content upgrade - var metaTable = database.GetCollection(DbMetaInformationTable); - var metaInfo = metaTable.FindById(0); - if (metaInfo == null) - { - metaInfo = new DbMetaData { Id = 0, DbVersion = 0, HistoryVersion = 0 }; - metaTable.Insert(metaInfo); - } - if (metaInfo.HistoryVersion >= CurrentHistoryVersion) + var meta = database.GetMetaData(AudioLogEntriesTable); + if (meta.Version >= CurrentHistoryVersion) return; - switch (metaInfo.HistoryVersion) + switch (meta.Version) { case 0: var all = audioLogEntries.FindAll().ToArray(); @@ -123,11 +71,12 @@ public HistoryManager(HistoryManagerData hmd) } } audioLogEntries.Update(all); - metaInfo.HistoryVersion = 1; + meta.Version = 1; + database.UpdateMetaData(meta); goto default; default: - metaTable.Update(metaInfo); + Log.Write(Log.Level.Info, "Database table \"{0}\" upgraded to {1}", AudioLogEntriesTable, meta.Version); break; } } @@ -284,11 +233,6 @@ public void RenameEntry(AudioLogEntry ale, string newName) audioLogEntries.Update(ale); } - public void CleanHistoryFile() - { - database.Shrink(); - } - public void RemoveBrokenLinks(CommandSystem.ExecutionInformation info) { const int iterations = 3; @@ -333,18 +277,6 @@ private static List FilterList(CommandSystem.ExecutionInformation } return nextIter; } - - public void Dispose() - { - database.Dispose(); - } - } - - internal class DbMetaData - { - public int Id { get; set; } - public int DbVersion { get; set; } - public int HistoryVersion { get; set; } } public class HistoryManagerData : ConfigData diff --git a/TS3AudioBot/MainBot.cs b/TS3AudioBot/MainBot.cs index 833d0c81..69cef76a 100644 --- a/TS3AudioBot/MainBot.cs +++ b/TS3AudioBot/MainBot.cs @@ -66,8 +66,9 @@ internal static void Main(string[] args) private StreamWriter logStream; + internal DbStore Database { get; set; } private TargetScript TargetScript { get; set; } - internal PluginManager PluginManager { get; private set; } + private PluginManager PluginManager { get; set; } /// Mangement for the bot command system. public CommandManager CommandManager { get; private set; } /// Mangement for playlists. @@ -222,13 +223,14 @@ void ColorLog(string msg, Log.Level lvl) Log.Write(Log.Level.Info, "[============ Initializing Modules ============]"); AudioValues.audioFrameworkData = afd; + Database = new DbStore(hmd); var teamspeakClient = new Ts3Full(tfcd); QueryConnection = teamspeakClient; PlayerConnection = teamspeakClient; PlaylistManager = new PlaylistManager(pld); SessionManager = new SessionManager(); if (hmd.EnableHistory) - historyManager = new HistoryManager(hmd); + historyManager = new HistoryManager(hmd, Database); PluginManager = new PluginManager(this, pmd); PlayManager = new PlayManager(this); WebManager = new WebManager(this, webd); @@ -614,7 +616,7 @@ public string CommandHistoryClean(ExecutionInformation info) { if (info.ApiCall) { - HistoryManager.CleanHistoryFile(); + Database.CleanFile(); return null; } info.Session.SetResponse(ResponseHistoryClean, null); @@ -1663,7 +1665,7 @@ private string ResponseHistoryClean(ExecutionInformation info) string param = info.Session.ResponseData as string; if (string.IsNullOrEmpty(param)) { - HistoryManager.CleanHistoryFile(); + Database.CleanFile(); return "Cleanup done!"; } else if (param == "removedefective") @@ -1791,8 +1793,8 @@ public void Dispose() QueryConnection?.Dispose(); // before: logStream, QueryConnection = null; - historyManager?.Dispose(); // before: logStream, - historyManager = null; + Database?.Dispose(); // before: logStream, + Database = null; TickPool.Close(); // before: diff --git a/TS3AudioBot/TS3AudioBot.csproj b/TS3AudioBot/TS3AudioBot.csproj index 94da1ff0..7a69fd8d 100644 --- a/TS3AudioBot/TS3AudioBot.csproj +++ b/TS3AudioBot/TS3AudioBot.csproj @@ -91,6 +91,7 @@ + @@ -154,8 +155,6 @@ - -