diff --git a/DML.AppCore/DML.AppCore.csproj b/DML.AppCore/DML.AppCore.csproj new file mode 100644 index 0000000..6d9fa23 --- /dev/null +++ b/DML.AppCore/DML.AppCore.csproj @@ -0,0 +1,17 @@ + + + + netstandard1.4 + + + + + + + + + + + + + diff --git a/DML.Core/DML.Core.csproj b/DML.Core/DML.Core.Old.csproj similarity index 100% rename from DML.Core/DML.Core.csproj rename to DML.Core/DML.Core.Old.csproj diff --git a/Discord Media Loader.Application/Classes/Core.cs b/Discord Media Loader.Application/Classes/Core.cs new file mode 100644 index 0000000..f96a1d2 --- /dev/null +++ b/Discord Media Loader.Application/Classes/Core.cs @@ -0,0 +1,268 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime; +using System.Threading.Tasks; +using System.Windows.Forms; +using Discord; +using Discord.Net; +using Discord.WebSocket; +using DML.AppCore.Classes; +using DML.Application.Dialogs; +using LiteDB; +using SweetLib.Utils; +using SweetLib.Utils.Logger; +using SweetLib.Utils.Logger.Memory; +using Logger = SweetLib.Utils.Logger.Logger; + +namespace DML.Application.Classes +{ + public static class Core + { + internal static DiscordSocketClient Client { get; set; } + internal static LiteDatabase Database { get; set; } + internal static Settings Settings { get; set; } + internal static JobScheduler Scheduler { get; } = new JobScheduler(); + + internal static string DataDirectory + => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Serraniel\Discord Media Loader"); + + public static async Task Run(string[] paramStrings) + { + try + { + var splash = new FrmInternalSplash(); + splash.Show(); + System.Windows.Forms.Application.DoEvents(); + + Logger.Info("Starting up Discord Media Loader application..."); + var useTrace = false; +#if DEBUG + //temporary add debug log level if debugging... + Logger.GlobalLogLevel |= LogLevel.Debug; + Logger.Debug("Running in debug configuartion. Added log level debug."); +#endif + + Logger.Debug($"Parameters: {string.Join(", ", paramStrings)}"); + if (paramStrings.Contains("--trace") || paramStrings.Contains("-t")) + { + useTrace = true; + Logger.GlobalLogLevel |= LogLevel.Trace; + Logger.Trace("Trace parameter found. Added log level trace."); + } + + Logger.Debug($"Application data folder: {DataDirectory}"); + + Logger.Trace("Checking application data folder..."); + if (!Directory.Exists(DataDirectory)) + { + Logger.Debug("Creating application data folder..."); + Directory.CreateDirectory(DataDirectory); + Logger.Trace("Creating application data folder."); + } + + Logger.Trace("Initializing profile optimizations..."); + ProfileOptimization.SetProfileRoot(System.Windows.Forms.Application.UserAppDataPath); + ProfileOptimization.StartProfile("profile.opt"); + Logger.Trace("Finished initializing profile optimizations."); + + Logger.Trace("Trying to identify log memory..."); + var logMemory = Logger.DefaultLogMemory as ArchivableConsoleLogMemory; + if (logMemory != null) + { + var logFolder = Path.Combine(DataDirectory, "logs"); + if (!Directory.Exists(logFolder)) + { + Logger.Debug("Creating log folder..."); + Directory.CreateDirectory(logFolder); + Logger.Trace("Created log folder."); + } + + + var logFile = Path.Combine(logFolder, + SweetUtils.LegalizeFilename($"{DateTime.Now.ToString(CultureInfo.CurrentCulture.DateTimeFormat.SortableDateTimePattern)}.log.zip")); + + Logger.Trace($"Setting log file: {logFile}"); + logMemory.AutoArchiveOnDispose = true; + logMemory.ArchiveFile = logFile; + } + + Logger.Debug("Loading database..."); + Database = new LiteDatabase(Path.Combine(DataDirectory, "config.db")); + Database.Log.Logging += (message) => Logger.Trace($"LiteDB: {message}"); + + Logger.Debug("Loading settings collection out of database..."); + var settingsDB = Database.GetCollection("settings"); + if (settingsDB.Count() > 1) + { + Logger.Warn("Found more than one setting. Loading first one..."); + } + Settings = settingsDB.FindAll().FirstOrDefault(); + if (Settings == null) + { + Logger.Warn("Settings not found. Creating new one. This is normal on first start up..."); + Settings = new Settings(); + Settings.Store(); + } + + Logger.Debug("Loading jobs collection out of database..."); + Scheduler.JobList = Job.RestoreJobs().ToList(); + + Logger.Info("Loaded settings."); + Logger.Debug( + $"Settings: Email: {Settings.Email}, password: {(string.IsNullOrEmpty(Settings.Password) ? "not set" : "is set")}, use username: {Settings.UseUserData}, loginToken: {Settings.LoginToken}"); + + Logger.Trace("Updating log level..."); + Logger.GlobalLogLevel = Settings.ApplicactionLogLevel; +#if DEBUG + //temporary add debug log level if debugging... + Logger.GlobalLogLevel |= LogLevel.Debug; + Logger.Debug("Running in debug configuartion. Added log level debug."); +#endif + if (useTrace) + { + Logger.GlobalLogLevel |= LogLevel.Trace; + Logger.Trace("Creating application data folder."); + } + + Logger.Debug("Creating discord client..."); + + Client = new DiscordSocketClient(); + Client.Log += (arg) => + { + var logMessage = $"DiscordClient: {arg.Message}"; + switch (arg.Severity) + { + case LogSeverity.Verbose: + Logger.Trace(logMessage); + break; + case LogSeverity.Debug: + Logger.Trace(logMessage); + break; + case LogSeverity.Info: + Logger.Info(logMessage); + break; + case LogSeverity.Warning: + Logger.Warn(logMessage); + break; + case LogSeverity.Error: + Logger.Error(logMessage); + break; + } + + return Task.CompletedTask; + }; + + + Logger.Info("Trying to log into discord..."); + var abort = false; + + Client.Connected += Client_Connected; + + while (Client.LoginState != LoginState.LoggedIn && !abort) + { + Logger.Debug(Client.ConnectionState.ToString()); + Logger.Debug(Client.LoginState.ToString()); + + Logger.Trace("Entering login loop."); + + try + { + if (Client.ConnectionState == ConnectionState.Connecting) + continue; + + if (!string.IsNullOrEmpty(Settings.LoginToken)) + { + Logger.Debug("Trying to login with last known token..."); + await Client.LoginAsync(TokenType.User, Settings.LoginToken); + await Task.Delay(1000); + } + + } + catch (HttpException ex) + { + Logger.Warn($"Login seems to have failed or gone wrong: {ex.GetType().Name} - {ex.Message}"); + } + + if (Client.LoginState == LoginState.LoggedOut) + { + Settings.Password = string.Empty; + Logger.Debug("Showing dialog for username and password..."); + var loginDlg = new LoginDialog(); + loginDlg.ShowDialog(); + Logger.Trace("Dialog closed."); + } + } + + Logger.Debug("Start checking for invalid jobs..."); + + //Client + + while (Client.Guilds.Count == 0) + { + // wait until guilds are loaded + } + + for (var i = Scheduler.JobList.Count - 1; i >= 0; i--) + { + var job = Scheduler.JobList[i]; + var isError = false; + var guild = FindServerById(job.GuildId); + if (guild == null) + isError = true; + else + { + var channel = FindChannelById(guild, job.ChannelId); + if (channel == null) + isError = true; + } + + if (isError) + { + MessageBox.Show($"Invalid job for guild {job.GuildId}, channel {job.ChannelId} found. Guild or channel may not exist any more. This job will be deleted...", "Invalid job", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + + Scheduler.JobList.Remove(job); + Scheduler.RunningJobs.Remove(job.Id); + job.Stop(); + job.Delete(); + } + } + + splash.Close(); + + Logger.Info("Starting scheduler..."); + Scheduler.Start(); + + System.Windows.Forms.Application.Run(new MainForm()); + + Logger.Info("Stopping scheduler..."); + Scheduler.Stop(); + } + catch (Exception ex) + { + Logger.Error($"{ex.Message} occured at: {ex.StackTrace}"); + } + } + + private static Task Client_Connected() + { + Logger.Debug("Connected"); + return Task.CompletedTask; + } + + //TODO: this is thrid time we implement this.....this has to be fixed!!! + private static SocketGuild FindServerById(ulong id) + { + Logger.Trace($"Trying to find server by Id: {id}"); + return (from s in Core.Client.Guilds where s.Id == id select s).FirstOrDefault(); + } + + private static SocketTextChannel FindChannelById(SocketGuild server, ulong id) + { + Logger.Trace($"Trying to find channel in {server} by id: {id}"); + return (from c in server.TextChannels where c.Id == id select c).FirstOrDefault(); + } + } +} diff --git a/Discord Media Loader.Application/Classes/Job.cs b/Discord Media Loader.Application/Classes/Job.cs new file mode 100644 index 0000000..5a60bae --- /dev/null +++ b/Discord Media Loader.Application/Classes/Job.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Discord; +using Discord.WebSocket; +using DML.Application.Classes; +using SweetLib.Utils; +using static SweetLib.Utils.Logger.Logger; + +namespace DML.AppCore.Classes +{ + public class Job + { + public int Id { get; set; } + public ulong GuildId { get; set; } + public ulong ChannelId { get; set; } + public double KnownTimestamp { get; set; } = 0; + private double StopTimestamp { get; set; } = 0; + private bool IsValid { get; set; } = true; + + internal void Store() + { + Debug("Storing job to database..."); + Trace("Getting jobs collection..."); + var jobDb = Core.Database.GetCollection("jobs"); + + Trace("Adding new value..."); + + if (jobDb.Find(x => x.ChannelId == ChannelId && x.GuildId == GuildId).Any()) + { + jobDb.Update(this); + } + else + { + jobDb.Insert(this); + } + } + + public void Delete() + { + Debug("Deleting job from database..."); + Trace("Getting jobs collection..."); + var jobDb = Core.Database.GetCollection("jobs"); + + Trace("Deleting value..."); + jobDb.Delete(Id); + } + + private SocketGuild FindServerById(ulong id) + { + Trace($"Trying to find server by Id: {id}"); + return (from s in Core.Client.Guilds where s.Id == id select s).FirstOrDefault(); + } + + private SocketTextChannel FindChannelById(SocketGuild server, ulong id) + { + Trace($"Trying to find channel in {server} by id: {id}"); + return (from c in server.TextChannels where c.Id == id select c).FirstOrDefault(); + } + + internal async Task> Scan() + { + Debug($"Starting scan of guild {GuildId} channel {ChannelId}..."); + var result = new List(); + + var limit = 100; + var lastId = ulong.MaxValue; + var isFirst = true; + var finished = false; + + var guild = FindServerById(GuildId); + var channel = FindChannelById(guild, ChannelId); + + if (Math.Abs(StopTimestamp) < 0.4) + StopTimestamp = KnownTimestamp; + Trace("Initialized scanning parameters."); + + while (!finished) + { + Trace("Entering scanning loop..."); + SocketMessage[] messages; + + Trace($"Downloading next {limit} messages..."); + if (isFirst) + { + messages = await channel.GetMessagesAsync(limit).ToArray() as SocketMessage[]; + } + else + { + messages = await channel.GetMessagesAsync(lastId, Direction.Before, limit).ToArray() as SocketMessage[]; + } + Trace($"Downloaded {messages.Length} messages."); + + isFirst = false; + + foreach (var m in messages) + { + if (!IsValid) + return null; + + Debug($"Processing message {m.Id}"); + if (m.Id < lastId) + { + Trace($"Updating lastId ({lastId}) to {m.Id}"); + lastId = m.Id; + } + + if (SweetUtils.DateTimeToUnixTimeStamp(m.CreatedAt.UtcDateTime) <= StopTimestamp) + { + Debug("Found a message with a known timestamp...Stopping scan."); + finished = true; + continue; + } + + Trace($"Message {m.Id} has {m.Attachments.Count} attachments."); + if (m.Attachments.Count > 0) + { + result.Add(m); + Core.Scheduler.TotalAttachments++; + Trace($"Added message {m.Id}"); + } + Debug($"Finished message {m.Id}"); + + Core.Scheduler.MessagesScanned++; + } + + finished = finished || messages.Length < limit; + } + Trace($"Downloaded all messages for guild {GuildId} channel {ChannelId}."); + + Trace("Sorting messages..."); + result.Sort((a, b) => DateTime.Compare(a.CreatedAt.UtcDateTime, b.CreatedAt.UtcDateTime)); + + if (result.Count > 0) + { + Trace("Updating StopTimestamp for next scan..."); + StopTimestamp = SweetUtils.DateTimeToUnixTimeStamp(result[result.Count - 1].CreatedAt.UtcDateTime); + } + + Debug($"Fisnished scan of guild {GuildId} channel {ChannelId}."); + + return result; + } + + public void Stop() + { + IsValid = false; + } + + public static IEnumerable RestoreJobs() + { + Debug("Restoring jobs..."); + Trace("Getting jobs collection..."); + var jobDb = Core.Database.GetCollection("jobs"); + + Trace("Creating new empty job list"); + return jobDb.FindAll(); + } + } +} diff --git a/Discord Media Loader.Application/Classes/JobScheduler.cs b/Discord Media Loader.Application/Classes/JobScheduler.cs new file mode 100644 index 0000000..b81ea96 --- /dev/null +++ b/Discord Media Loader.Application/Classes/JobScheduler.cs @@ -0,0 +1,295 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Discord.WebSocket; +using DML.Application.Classes; +using SweetLib.Utils; +using SweetLib.Utils.Logger; + +namespace DML.AppCore.Classes +{ + public class JobScheduler + { + private ulong messageScanned = 0; + private ulong totalAttachments = 0; + private ulong attachmentsDownloaded = 0; + + private bool Run { get; set; } = false; + public List JobList { get; set; } = new List(); + public Dictionary> RunningJobs = new Dictionary>(); + internal int RunningThreads { get; set; } = 0; + + internal ulong MessagesScanned + { + get + { + lock (this) + { + return messageScanned; + } + } + set + { + lock (this) + { + messageScanned = value; + } + } + } + + internal ulong TotalAttachments + { + get + { + lock (this) + { + return totalAttachments; + } + } + set + { + lock (this) + { + totalAttachments = value; + } + } + } + + internal ulong AttachmentsDownloaded + { + get + { + lock (this) + { + return attachmentsDownloaded; + } + } + set + { + lock (this) + { + attachmentsDownloaded = value; + } + } + } + + internal ulong AttachmentsToDownload => TotalAttachments - AttachmentsDownloaded; + + public void Stop() + { + Run = false; + } + + public void Start() + { + Run = true; + + Task.Run(async () => + { + Logger.Info("Started JobScheduler..."); + while (Run) + { + Logger.Debug("Entering job list handler loop..."); + //foreach (var job in JobList) + for (var i = JobList.Count - 1; i >= 0; i--) + { + var job = JobList[i]; + Logger.Debug($"Checking job {job}"); + var hasJob = false; + + Logger.Trace("Locking scheduler..."); + lock (this) + { + Logger.Trace("Checking if job is already performed..."); + hasJob = RunningJobs.ContainsKey(job.Id); + } + Logger.Trace("Unlocked scheduler."); + + if (!hasJob) + { + Logger.Debug("Job is not performed yet...Performing job..."); + var queue = new Queue(); + + Logger.Trace("Locking scheduler..."); + lock (this) + { + Logger.Trace("Adding job to running jobs."); + RunningJobs.Add(job.Id, queue); + } + Logger.Trace("Unlocked scheduler."); + + Logger.Trace("Issuing job message scan..."); + var messages = await job.Scan(); + + if (messages == null) + continue; + + Logger.Trace($"Adding {messages.Count} messages to queue..."); + foreach (var msg in messages) + { + queue.Enqueue(msg); + } + Logger.Trace($"Added {queue.Count} messages to queue."); + + if (messages.Count != queue.Count) + Logger.Warn("Not all messages have been added into the queue."); + + var startedDownload = false; + + while (!startedDownload) + { + Logger.Debug("Entering loop to check thread availability"); + Logger.Trace("Locking scheduler..."); + lock (this) + { + Logger.Trace($"Checking thread limit. Running: {RunningThreads}, Max: {Core.Settings.ThreadLimit}"); + if (RunningThreads >= Core.Settings.ThreadLimit) + continue; + + RunningThreads++; + startedDownload = true; + } + Logger.Trace("Unlocked scheduler."); + } + + Logger.Trace("Start downloading job async."); + Task.Run(() => WorkQueue(job.Id)); // do not await to work parallel + } + } + } + }); + } + + private void WorkQueue(int jobId) + { + try + { + Logger.Debug("Beginning job download..."); + Logger.Trace("Finding job..."); + var job = (from j in JobList where j.Id == jobId select j).FirstOrDefault(); + + if (job == null) + { + Logger.Warn($"Associating job not found! JobId: {jobId}"); + return; + } + Logger.Trace("Found job."); + + Queue queue; + Logger.Trace("Locking scheduler..."); + lock (this) + { + Logger.Trace("Finiding queue..."); + if (!RunningJobs.TryGetValue(jobId, out queue)) + { + Logger.Warn($"Queue for job {jobId} not found!"); + return; + } + Logger.Trace("Queue found."); + } + Logger.Trace("Unlocked scheduler."); + + Logger.Debug($"Messages to process for job {jobId}: {queue.Count}"); + while (queue.Count > 0) + { + Logger.Trace("Locking scheduler..."); + lock (this) + { + Logger.Trace("Checking if still a job..."); + if (!RunningJobs.ContainsKey(jobId)) + { + Logger.Warn($"Queue for job {jobId} not found!"); + return; + } + Logger.Trace("Continue working..."); + } + Logger.Trace("Unlocked scheduler."); + + Logger.Trace("Dequeueing message..."); + var message = queue.Dequeue(); + + Logger.Debug($"Attachments for message {message.Id}: {message.Attachments.Count}"); + foreach (var a in message.Attachments) + { + try + { + var fileName = Path.Combine(Core.Settings.OperatingFolder, Core.Settings.FileNameScheme); + + Logger.Trace("Replacing filename placeholders..."); + + var extensionRequired = !fileName.EndsWith("%name%"); + + var serverName = "unknown"; + + var socketTextChannel = message.Channel as SocketTextChannel; + if (socketTextChannel != null) + { + serverName = socketTextChannel.Guild.Name.Replace(":", "").Replace("/", "") + .Replace("\\", ""); + } + + fileName = + fileName.Replace("%guild%", serverName) + .Replace("%channel%", message.Channel.Name) + .Replace("%timestamp%", SweetUtils.DateTimeToUnixTimeStamp(message.CreatedAt.UtcDateTime).ToString()) + .Replace("%name%", a.Filename) + .Replace("%id%", a.Id.ToString()); + + if (extensionRequired) + fileName += Path.GetExtension(a.Filename); + + Logger.Trace($"Detemined file name: {fileName}."); + + + if (File.Exists(fileName) && new FileInfo(fileName).Length == a.Size) + { + Logger.Debug($"{fileName} already existing with its estimated size. Skipping..."); + continue; + } + + Logger.Trace("Determining directory..."); + var fileDirectory = Path.GetDirectoryName(fileName); + + if (!Directory.Exists(fileDirectory)) + { + Logger.Info($"Directory {fileDirectory} does not exist. Creating directory..."); + Directory.CreateDirectory(fileDirectory); + Logger.Debug("Created directory."); + } + + var wc = new WebClient(); + Logger.Debug($"Starting downloading of attachment {a.Id}..."); + + wc.DownloadFile(new Uri(a.Url), fileName); + Logger.Debug($"Downloaded attachment {a.Id}."); + + Logger.Trace("Updating known timestamp for job..."); + job.KnownTimestamp = SweetUtils.DateTimeToUnixTimeStamp(message.CreatedAt.UtcDateTime); + job.Store(); + } + finally + { + AttachmentsDownloaded++; + } + } + } + } + finally + { + Logger.Trace("Locking scheduler..."); + lock (this) + { + Logger.Trace($"Removing {jobId} from running jobs..."); + RunningJobs.Remove(jobId); + Logger.Trace("Decreasing thread count..."); + RunningThreads--; + } + Logger.Trace("Unlocked scheduler."); + } + } + } +} diff --git a/Discord Media Loader.Application/Classes/Settings.cs b/Discord Media Loader.Application/Classes/Settings.cs new file mode 100644 index 0000000..4cc9067 --- /dev/null +++ b/Discord Media Loader.Application/Classes/Settings.cs @@ -0,0 +1,39 @@ +using System.Diagnostics; +using SweetLib.Utils.Logger; + +namespace DML.Application.Classes +{ + public class Settings + { + public int Id { get; } = 1; // using always unique ID + public string Email { get; set; } + public string Password { get; set; } + public string LoginToken { get; set; } + public bool UseUserData { get; set; } = false; + public bool SavePassword { get; set; } = false; + public LogLevel ApplicactionLogLevel { get; set; } = LogLevel.Info | LogLevel.Warn | LogLevel.Error; + public string OperatingFolder { get; set; } + public string FileNameScheme { get; set; } = @"%guild%\%channel%\%id%"; + public bool SkipExistingFiles { get; set; } = true; + public int ThreadLimit { get; set; } = 50; + + public void Store() + { + Logger.Trace("Getting settings collection..."); + var settingsDB = Core.Database.GetCollection("settings"); + + Logger.Debug("Storing settings to database..."); + + if (settingsDB.Exists(_setting => _setting.Id == Id)) + { + Logger.Trace("Updating existing value..."); + settingsDB.Update(this); + } + else + { + Logger.Trace("Adding new value..."); + settingsDB.Insert(this); + } + } + } +} diff --git a/Discord Media Loader.Application/DML.Application.csproj b/Discord Media Loader.Application/DML.Application.csproj index f2e385b..1662b67 100644 --- a/Discord Media Loader.Application/DML.Application.csproj +++ b/Discord Media Loader.Application/DML.Application.csproj @@ -155,6 +155,9 @@ + + + Form @@ -179,6 +182,7 @@ True Resources.resx + @@ -204,6 +208,10 @@ {02c1f8ef-32f2-4e77-a36d-79129402af37} SweetLib + + {4db264a7-2352-4057-b3de-61fcfec01ae5} + DML.AppCore + diff --git a/Discord Media Loader.Application/Dialogs/LoginDialog.cs b/Discord Media Loader.Application/Dialogs/LoginDialog.cs index 6bc566a..16ca42f 100644 --- a/Discord Media Loader.Application/Dialogs/LoginDialog.cs +++ b/Discord Media Loader.Application/Dialogs/LoginDialog.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using DML.Application.Classes; using static SweetLib.Utils.Logger.Logger; namespace DML.Application.Dialogs diff --git a/Discord Media Loader.Application/MainForm.cs b/Discord Media Loader.Application/MainForm.cs index 72adc27..362e675 100644 --- a/Discord Media Loader.Application/MainForm.cs +++ b/Discord Media Loader.Application/MainForm.cs @@ -5,6 +5,8 @@ using System.Reflection; using System.Windows.Forms; using Discord; using Discord.WebSocket; +using DML.AppCore; +using DML.AppCore.Classes; using DML.Application.Classes; using static SweetLib.Utils.Logger.Logger; diff --git a/Discord Media Loader.Application/packages.config b/Discord Media Loader.Application/packages.config index 1b50a5f..d3b12fc 100644 --- a/Discord Media Loader.Application/packages.config +++ b/Discord Media Loader.Application/packages.config @@ -1,6 +1,5 @@  - diff --git a/Discord Media Loader.sln b/Discord Media Loader.sln index 33d02e8..f3279eb 100644 --- a/Discord Media Loader.sln +++ b/Discord Media Loader.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.10 +VisualStudioVersion = 15.0.26730.16 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord Media Loader", "Discord Media Loader\Discord Media Loader.csproj", "{EDC92554-DBC1-4F9C-9317-379A8BF441E8}" EndProject @@ -9,7 +9,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DML.Application", "Discord EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SweetLib", "..\SweetLib\SweetLib\SweetLib.csproj", "{02C1F8EF-32F2-4E77-A36D-79129402AF37}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DML.Core", "DML.Core\DML.Core.csproj", "{BADDA2C1-BF8E-40DC-A2B2-80B5D609C4ED}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DML.AppCore", "DML.AppCore\DML.AppCore.csproj", "{4DB264A7-2352-4057-B3DE-61FCFEC01AE5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -29,10 +29,10 @@ Global {02C1F8EF-32F2-4E77-A36D-79129402AF37}.Debug|Any CPU.Build.0 = Debug|Any CPU {02C1F8EF-32F2-4E77-A36D-79129402AF37}.Release|Any CPU.ActiveCfg = Release|Any CPU {02C1F8EF-32F2-4E77-A36D-79129402AF37}.Release|Any CPU.Build.0 = Release|Any CPU - {BADDA2C1-BF8E-40DC-A2B2-80B5D609C4ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BADDA2C1-BF8E-40DC-A2B2-80B5D609C4ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BADDA2C1-BF8E-40DC-A2B2-80B5D609C4ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BADDA2C1-BF8E-40DC-A2B2-80B5D609C4ED}.Release|Any CPU.Build.0 = Release|Any CPU + {4DB264A7-2352-4057-B3DE-61FCFEC01AE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DB264A7-2352-4057-B3DE-61FCFEC01AE5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DB264A7-2352-4057-B3DE-61FCFEC01AE5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DB264A7-2352-4057-B3DE-61FCFEC01AE5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Discord Media Loader/Discord Media Loader.csproj b/Discord Media Loader/Discord Media Loader.csproj index 0977fa7..336e17d 100644 --- a/Discord Media Loader/Discord Media Loader.csproj +++ b/Discord Media Loader/Discord Media Loader.csproj @@ -214,6 +214,10 @@ {c130de6a-3237-42b5-be9f-783d1cd104c6} DML.Application + + {4db264a7-2352-4057-b3de-61fcfec01ae5} + DML.AppCore +