├── Discord Media Loader
├── Serraniel-64x64.ico
├── Serraniel-Logo4-ABGERUNDET.ico
├── Resources
│ └── Serraniel-Logo4-NO-BG.png
├── Properties
│ ├── Settings.settings
│ ├── Settings.Designer.cs
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── Program.cs
├── Helper
│ ├── VersionHelper.cs
│ └── TaskBarProgress.cs
├── FrmSplash.cs
├── App.config
├── FrmDownload.Designer.cs
├── FrmSplash.Designer.cs
├── FrmDownload.cs
├── packages.config
└── Discord Media Loader.csproj
├── Discord Media Loader.Application
├── Resources
│ └── Serraniel-Logo4-NO-BG.png
├── FrmInternalSplash.cs
├── Helper
│ └── IdentifiedString.cs
├── Dialogs
│ ├── LoginDialog.cs
│ └── LoginDialog.Designer.cs
├── Classes
│ ├── Settings.cs
│ ├── Job.cs
│ ├── JobScheduler.cs
│ └── Core.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── app.config
├── FrmInternalSplash.Designer.cs
├── packages.config
├── MainForm.cs
└── DML.Application.csproj
├── DML.Client
├── DML.Client.csproj
└── DMLClient.cs
├── DML.Core
├── DML.Core.Old.csproj
├── Classes
│ ├── Settings.cs
│ ├── Job.cs
│ └── JobScheduler.cs
└── Core.cs
├── DML.AppCore
└── DML.AppCore.csproj
├── README.md
├── Discord Media Loader.sln
├── .gitattributes
├── .gitignore
└── LICENSE
/Discord Media Loader/Serraniel-64x64.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TarekJor/DiscordMediaLoader/master/Discord Media Loader/Serraniel-64x64.ico
--------------------------------------------------------------------------------
/Discord Media Loader/Serraniel-Logo4-ABGERUNDET.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TarekJor/DiscordMediaLoader/master/Discord Media Loader/Serraniel-Logo4-ABGERUNDET.ico
--------------------------------------------------------------------------------
/Discord Media Loader/Resources/Serraniel-Logo4-NO-BG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TarekJor/DiscordMediaLoader/master/Discord Media Loader/Resources/Serraniel-Logo4-NO-BG.png
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Resources/Serraniel-Logo4-NO-BG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TarekJor/DiscordMediaLoader/master/Discord Media Loader.Application/Resources/Serraniel-Logo4-NO-BG.png
--------------------------------------------------------------------------------
/DML.Client/DML.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.4
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Discord Media Loader/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/FrmInternalSplash.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace DML.Application
4 | {
5 | public partial class FrmInternalSplash : Form
6 | {
7 | public FrmInternalSplash()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/DML.Core/DML.Core.Old.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp1.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/DML.AppCore/DML.AppCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard1.4
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Helper/IdentifiedString.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace DML.Application.Helper
8 | {
9 | internal class IdentifiedString
10 | {
11 | internal T Id { get; set; }
12 | internal string Caption { get; set; }
13 |
14 | internal IdentifiedString(T id, string caption)
15 | {
16 | Id = id;
17 | Caption = caption;
18 | }
19 |
20 | public override string ToString() => Caption;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Discord Media Loader/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 | using DML.Application.Classes;
4 | using Nito.AsyncEx;
5 |
6 | namespace Discord_Media_Loader
7 | {
8 | static class Program
9 | {
10 | [STAThread]
11 | static void Main(string[] paramStrings)
12 | {
13 | Application.EnableVisualStyles();
14 | Application.SetCompatibleTextRenderingDefault(false);
15 |
16 | var splashScreen = new FrmSplash();
17 | if (splashScreen.ShowDialog() == DialogResult.OK)
18 | {
19 | DoLaunch(paramStrings);
20 | }
21 | else
22 | {
23 | Application.Restart();
24 | }
25 | }
26 |
27 | private static void DoLaunch(string[] paramStrings)
28 | {
29 | AsyncContext.Run(() => Core.Run(paramStrings));
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/DML.Client/DMLClient.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Discord;
3 | using Discord.WebSocket;
4 |
5 | namespace DML.Client
6 | {
7 | public static class DMLClient
8 | {
9 | public static DiscordSocketClient Client { get; set; } = new DiscordSocketClient(new DiscordSocketConfig(){DefaultRetryMode = RetryMode.RetryRatelimit|RetryMode.RetryTimeouts});
10 |
11 | public static async Task Login(string token)
12 | {
13 | await Client.LoginAsync(TokenType.User, token);
14 | await Client.StartAsync();
15 | await Task.Delay(1000);
16 |
17 | while (Client.LoginState == LoginState.LoggingIn || Client.ConnectionState == ConnectionState.Connecting)
18 | {
19 | // wait
20 | }
21 |
22 | return Client.LoginState == LoginState.LoggedIn && Client.ConnectionState == ConnectionState.Connected;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Discord Media Loader/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Dieser Code wurde von einem Tool generiert.
4 | // Laufzeitversion:4.0.30319.42000
5 | //
6 | // Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
7 | // der Code erneut generiert wird.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Discord_Media_Loader.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Dialogs/LoginDialog.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 | using DML.Application.Classes;
4 | using static SweetLib.Utils.Logger.Logger;
5 |
6 | namespace DML.Application.Dialogs
7 | {
8 | public partial class LoginDialog : Form
9 | {
10 | public LoginDialog()
11 | {
12 | InitializeComponent();
13 | }
14 |
15 | private void LoginDialog_Shown(object sender, EventArgs e)
16 | {
17 | Trace("Login dialog shown.");
18 | edToken.Text = Core.Settings.LoginToken;
19 | }
20 |
21 | private void LoginDialog_FormClosing(object sender, FormClosingEventArgs e)
22 | {
23 | Trace($"Closing login dialog. Result: {DialogResult}");
24 | if (DialogResult != DialogResult.OK)
25 | return;
26 |
27 | Debug("Adjusting login settings...");
28 | Core.Settings.LoginToken = edToken.Text;
29 |
30 | Core.Settings.Store();
31 | }
32 |
33 | private void btnOk_Click(object sender, EventArgs e)
34 | {
35 | Trace("btnOk pressed.");
36 | DialogResult = DialogResult.OK;
37 | }
38 |
39 | private void btnAbort_Click(object sender, EventArgs e)
40 | {
41 | Trace("btnAbort pressed.");
42 | DialogResult = DialogResult.Abort;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Discord Media Loader
2 |
3 | # PROBABLY DISCONTINUED!! THIS PROJECT IS ON HOLD DUE TO DISCORD DOUBLING DOWN ON SELFBOTS (THAT DO VIOLATE TOS AFTER ALL). LATEST COMMIT SHOULD REMAIN STABLE!!
4 |
5 | *Discord Media Loader* is a small tool for downloading all attachments of *Discord* servers.
6 |
7 | **[Download](https://github.com/Serraniel/DiscordMediaLoader/releases)**
8 |
9 |
10 | ## License
11 | Apache License 2.0
12 |
13 |
14 | ## Functionality
15 | * Log into your discord account and gathering information about servers (guilds) and their channels
16 | * Downloading attachments
17 | * You can optionally specify a date. Only newer messages will be scanned
18 | * You can specify a destination path
19 | * You can specify amount of parallel workers
20 | * You can skip existing for speeding up repeated scans (messages still must be scanned)
21 |
22 |
23 | ## Requirements
24 | * [.Net Framework 4.6](https://www.microsoft.com/en-us/download/details.aspx?id=48137) by Microsoft
25 |
26 | ## How to use
27 | First things first: **Do not use if you have MFA enabled** as long as login is only supported via username and password. That might get you lost your account. A switch to a token based login will come soon™!
28 |
29 | Otherwise you may just do the following steps:
30 | 1. Login
31 | 2. Select a guild
32 | 3. Select a channel
33 | 4. Select a directory to save the files
34 | 5. Do other settings if wished
35 | 6. Press the magic button to download stuff
36 |
--------------------------------------------------------------------------------
/DML.Core/Classes/Settings.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | namespace DML.Core.Classes
4 | {
5 | internal class Settings
6 | {
7 | public int Id { get; } = 1; // using always unique ID
8 | public string Email { get; set; }
9 | public string Password { get; set; }
10 | public string LoginToken { get; set; }
11 | public bool UseUserData { get; set; } = false;
12 | public bool SavePassword { get; set; } = false;
13 | public LogLevel ApplicactionLogLevel { get; set; } = LogLevel.Info | LogLevel.Warn | LogLevel.Error;
14 | public string OperatingFolder { get; set; }
15 | public string FileNameScheme { get; set; } = @"%guild%\%channel%\%id%";
16 | public bool SkipExistingFiles { get; set; } = true;
17 | public int ThreadLimit { get; set; } = 50;
18 |
19 | internal void Store()
20 | {
21 | Trace("Getting settings collection...");
22 | var settingsDB = DML.Core.Core.Database.GetCollection("settings");
23 |
24 | Debug("Storing settings to database...");
25 |
26 | if (settingsDB.Exists(_setting => _setting.Id == Id))
27 | {
28 | Trace("Updating existing value...");
29 | settingsDB.Update(this);
30 | }
31 | else
32 | {
33 | Trace("Adding new value...");
34 | settingsDB.Insert(this);
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Classes/Settings.cs:
--------------------------------------------------------------------------------
1 | using SweetLib.Utils.Logger;
2 |
3 | namespace DML.Application.Classes
4 | {
5 | public class Settings
6 | {
7 | public int Id { get; } = 1; // using always unique ID
8 | public string Email { get; set; }
9 | public string Password { get; set; }
10 | public string LoginToken { get; set; }
11 | public bool UseUserData { get; set; } = false;
12 | public bool SavePassword { get; set; } = false;
13 | public LogLevel ApplicactionLogLevel { get; set; } = LogLevel.Info | LogLevel.Warn | LogLevel.Error;
14 | public string OperatingFolder { get; set; }
15 | public string FileNameScheme { get; set; } = @"%guild%\%channel%\%id%";
16 | public bool SkipExistingFiles { get; set; } = true;
17 | public int ThreadLimit { get; set; } = 50;
18 |
19 | public void Store()
20 | {
21 | Logger.Trace("Getting settings collection...");
22 | var settingsDB = Core.Database.GetCollection("settings");
23 |
24 | Logger.Debug("Storing settings to database...");
25 |
26 | if (settingsDB.Exists(_setting => _setting.Id == Id))
27 | {
28 | Logger.Trace("Updating existing value...");
29 | settingsDB.Update(this);
30 | }
31 | else
32 | {
33 | Logger.Trace("Adding new value...");
34 | settingsDB.Insert(this);
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Discord Media Loader/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // Allgemeine Informationen über eine Assembly werden über die folgenden
6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
7 | // die einer Assembly zugeordnet sind.
8 | [assembly: AssemblyTitle("Discord Media Loader")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Serraniel")]
12 | [assembly: AssemblyProduct("Discord Media Loader")]
13 | [assembly: AssemblyCopyright("Copyright © 2017 - 2018 by Serraniel")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
18 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
19 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
20 | [assembly: ComVisible(false)]
21 |
22 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
23 | [assembly: Guid("edc92554-dbc1-4f9c-9317-379a8bf441e8")]
24 |
25 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
26 | //
27 | // Hauptversion
28 | // Nebenversion
29 | // Buildnummer
30 | // Revision
31 | //
32 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
33 | // übernehmen, indem Sie "*" eingeben:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.3.0")]
36 | [assembly: AssemblyFileVersion("1.0.3.0")]
37 |
--------------------------------------------------------------------------------
/Discord Media Loader/Helper/VersionHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 | using System.Threading.Tasks;
5 | using Octokit;
6 |
7 | namespace Discord_Media_Loader.Helper
8 | {
9 | internal class VersionHelper
10 | {
11 | internal static Version CurrentVersion => Assembly.GetExecutingAssembly().GetName().Version;
12 |
13 | internal static Version AppVersion => AssemblyName.GetAssemblyName("Discord Media Loader.Application.dll").Version;
14 |
15 | internal static async Task GetReleaseVersion()
16 | {
17 | var github = new GitHubClient(new ProductHeaderValue("DiscordMedialLoader"));
18 |
19 | var tag =
20 | (await github.Repository.Release.GetAll("Serraniel", "DiscordMediaLoader")).OrderByDescending(x => x.CreatedAt).First().TagName.Replace("v", "") ?? "";
21 |
22 | var version = new Version(tag);
23 | return version;
24 | }
25 |
26 | internal static async Task DownloadLatestReleaseVersion()
27 | {
28 | return await DownloadVersion(await GetReleaseVersion());
29 | }
30 |
31 | internal static async Task DownloadVersion(Version version)
32 | {
33 | var github = new GitHubClient(new ProductHeaderValue("DiscordMediaLoader"));
34 | var releaseVersion = (from release in (await github.Repository.Release.GetAll("Serraniel", "DiscordMediaLoader")) where release.TagName == $"v{version.Major}.{version.Minor}.{version.Build}.{version.Revision}" select release).First();
35 |
36 | return releaseVersion.Assets.FirstOrDefault()?.BrowserDownloadUrl;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // Allgemeine Informationen über eine Assembly werden über die folgenden
6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
7 | // die einer Assembly zugeordnet sind.
8 | [assembly: AssemblyTitle("Discord Media Loader.Application")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Serraniel")]
12 | [assembly: AssemblyProduct("Discord Media Loader.Application")]
13 | [assembly: AssemblyCopyright("Copyright © 2017 - 2018 by Serraniel")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
18 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
19 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
20 | [assembly: ComVisible(false)]
21 |
22 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
23 | [assembly: Guid("c130de6a-3237-42b5-be9f-783d1cd104c6")]
24 |
25 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
26 | //
27 | // Hauptversion
28 | // Nebenversion
29 | // Buildnummer
30 | // Revision
31 | //
32 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
33 | // übernehmen, indem Sie "*" eingeben:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.1.2.0")]
36 | [assembly: AssemblyFileVersion("1.1.2.0")]
37 |
--------------------------------------------------------------------------------
/Discord Media Loader/FrmSplash.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Compression;
4 | using System.Windows.Forms;
5 | using Discord_Media_Loader.Helper;
6 |
7 | namespace Discord_Media_Loader
8 | {
9 | public partial class FrmSplash : Form
10 | {
11 | public FrmSplash()
12 | {
13 | InitializeComponent();
14 | }
15 |
16 | private async void FrmSplash_Shown(object sender, EventArgs e)
17 | {
18 | UseWaitCursor = true;
19 | try
20 | {
21 | var releaseVersion = await VersionHelper.GetReleaseVersion();
22 | if (releaseVersion > VersionHelper.AppVersion)
23 | {
24 | var tmpFile = Path.GetTempFileName();
25 | var downloadManager = new FrmDownload(tmpFile, await VersionHelper.DownloadVersion(releaseVersion));
26 |
27 | downloadManager.StartDownload();
28 | downloadManager.ShowDialog();
29 |
30 | var tmpFolder = Path.GetTempFileName();
31 | tmpFolder = Path.Combine(Path.GetFullPath(tmpFolder).Replace(Path.GetFileName(tmpFolder), ""), Path.GetFileNameWithoutExtension(tmpFolder));
32 |
33 | var di = Directory.CreateDirectory(tmpFolder);
34 |
35 | ZipFile.ExtractToDirectory(tmpFile, tmpFolder);
36 |
37 | foreach (var f in di.GetFiles())
38 | {
39 | try
40 | {
41 | var fname = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, f.Name);
42 | File.Copy(f.FullName, fname, true);
43 | }
44 | catch (Exception) { }
45 | }
46 |
47 | File.Delete(tmpFile);
48 | DialogResult = DialogResult.Cancel;
49 | }
50 | }
51 | finally
52 | {
53 | UseWaitCursor = false;
54 | }
55 |
56 | DialogResult = DialogResult.OK;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Discord Media Loader.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26730.16
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord Media Loader", "Discord Media Loader\Discord Media Loader.csproj", "{EDC92554-DBC1-4F9C-9317-379A8BF441E8}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DML.Application", "Discord Media Loader.Application\DML.Application.csproj", "{C130DE6A-3237-42B5-BE9F-783D1CD104C6}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DML.Client", "DML.Client\DML.Client.csproj", "{045EB4A1-34E7-47E0-867E-E10C40505095}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {EDC92554-DBC1-4F9C-9317-379A8BF441E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {EDC92554-DBC1-4F9C-9317-379A8BF441E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {EDC92554-DBC1-4F9C-9317-379A8BF441E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {EDC92554-DBC1-4F9C-9317-379A8BF441E8}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {C130DE6A-3237-42B5-BE9F-783D1CD104C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {C130DE6A-3237-42B5-BE9F-783D1CD104C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {C130DE6A-3237-42B5-BE9F-783D1CD104C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {C130DE6A-3237-42B5-BE9F-783D1CD104C6}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {045EB4A1-34E7-47E0-867E-E10C40505095}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {045EB4A1-34E7-47E0-867E-E10C40505095}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {045EB4A1-34E7-47E0-867E-E10C40505095}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {045EB4A1-34E7-47E0-867E-E10C40505095}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {0B742DE0-D6AF-4033-9605-863C32A7FFD8}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Discord Media Loader/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Discord Media Loader/Helper/TaskBarProgress.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Discord_Media_Loader.Helper
5 | {
6 | internal static class TaskBarProgress
7 | {
8 | internal enum TaskbarStates
9 | {
10 | NoProgress = 0,
11 | Indeterminate = 0x1,
12 | Normal = 0x2,
13 | Error = 0x4,
14 | Paused = 0x8
15 | }
16 |
17 | [ComImport]
18 | [Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
19 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
20 | private interface ITaskbarList3
21 | {
22 | // ITaskbarList
23 | [PreserveSig]
24 | void HrInit();
25 | [PreserveSig]
26 | void AddTab(IntPtr hwnd);
27 | [PreserveSig]
28 | void DeleteTab(IntPtr hwnd);
29 | [PreserveSig]
30 | void ActivateTab(IntPtr hwnd);
31 | [PreserveSig]
32 | void SetActiveAlt(IntPtr hwnd);
33 |
34 | // ITaskbarList2
35 | [PreserveSig]
36 | void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);
37 |
38 | // ITaskbarList3
39 | [PreserveSig]
40 | void SetProgressValue(IntPtr hwnd, UInt64 ullCompleted, UInt64 ullTotal);
41 | [PreserveSig]
42 | void SetProgressState(IntPtr hwnd, TaskbarStates state);
43 | }
44 |
45 | [Guid("56FDF344-FD6D-11d0-958A-006097C9A090")]
46 | [ClassInterface(ClassInterfaceType.None)]
47 | [ComImport]
48 | private class TaskbarInstance
49 | {
50 | }
51 |
52 | private static ITaskbarList3 taskbarInstance = (ITaskbarList3)new TaskbarInstance();
53 | private static bool taskbarSupported = Environment.OSVersion.Version >= new Version(6, 1);
54 |
55 | internal static void SetState(IntPtr windowHandle, TaskbarStates taskbarState)
56 | {
57 | if (taskbarSupported) taskbarInstance.SetProgressState(windowHandle, taskbarState);
58 | }
59 |
60 | internal static void SetValue(IntPtr windowHandle, double progressValue, double progressMax)
61 | {
62 | if (taskbarSupported) taskbarInstance.SetProgressValue(windowHandle, (ulong)progressValue, (ulong)progressMax);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/Discord Media Loader/FrmDownload.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace Discord_Media_Loader
2 | {
3 | partial class FrmDownload
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmDownload));
32 | this.pgbProgress = new System.Windows.Forms.ProgressBar();
33 | this.lbStatus = new System.Windows.Forms.Label();
34 | this.SuspendLayout();
35 | //
36 | // pgbProgress
37 | //
38 | this.pgbProgress.Location = new System.Drawing.Point(12, 12);
39 | this.pgbProgress.Name = "pgbProgress";
40 | this.pgbProgress.Size = new System.Drawing.Size(351, 23);
41 | this.pgbProgress.TabIndex = 0;
42 | //
43 | // lbStatus
44 | //
45 | this.lbStatus.AutoSize = true;
46 | this.lbStatus.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
47 | this.lbStatus.ForeColor = System.Drawing.SystemColors.ControlDarkDark;
48 | this.lbStatus.Location = new System.Drawing.Point(12, 47);
49 | this.lbStatus.Name = "lbStatus";
50 | this.lbStatus.Size = new System.Drawing.Size(37, 13);
51 | this.lbStatus.TabIndex = 1;
52 | this.lbStatus.Text = "Status";
53 | //
54 | // FrmDownload
55 | //
56 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
57 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
58 | this.ClientSize = new System.Drawing.Size(375, 70);
59 | this.ControlBox = false;
60 | this.Controls.Add(this.lbStatus);
61 | this.Controls.Add(this.pgbProgress);
62 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
63 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
64 | this.Name = "FrmDownload";
65 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
66 | this.Text = "Discord Media Loader";
67 | this.ResumeLayout(false);
68 | this.PerformLayout();
69 |
70 | }
71 |
72 | #endregion
73 |
74 | private System.Windows.Forms.ProgressBar pgbProgress;
75 | private System.Windows.Forms.Label lbStatus;
76 | }
77 | }
--------------------------------------------------------------------------------
/Discord Media Loader/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Dieser Code wurde von einem Tool generiert.
4 | // Laufzeitversion:4.0.30319.42000
5 | //
6 | // Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
7 | // der Code erneut generiert wird.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Discord_Media_Loader.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
17 | ///
18 | // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
19 | // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
20 | // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
21 | // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Discord_Media_Loader.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
51 | /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
65 | ///
66 | internal static System.Drawing.Bitmap Serraniel_Logo4_NO_BG {
67 | get {
68 | object obj = ResourceManager.GetObject("Serraniel_Logo4_NO_BG", resourceCulture);
69 | return ((System.Drawing.Bitmap)(obj));
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/FrmInternalSplash.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace DML.Application
2 | {
3 | partial class FrmInternalSplash
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmInternalSplash));
32 | this.lblName = new System.Windows.Forms.Label();
33 | this.pbLogo = new System.Windows.Forms.PictureBox();
34 | ((System.ComponentModel.ISupportInitialize)(this.pbLogo)).BeginInit();
35 | this.SuspendLayout();
36 | //
37 | // lblName
38 | //
39 | this.lblName.Font = new System.Drawing.Font("Microsoft Sans Serif", 21.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
40 | this.lblName.Location = new System.Drawing.Point(11, 129);
41 | this.lblName.Name = "lblName";
42 | this.lblName.Size = new System.Drawing.Size(379, 35);
43 | this.lblName.TabIndex = 3;
44 | this.lblName.Text = "Discord Media Loader";
45 | this.lblName.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
46 | //
47 | // pbLogo
48 | //
49 | this.pbLogo.Image = global::DML.Application.Properties.Resources.Serraniel_Logo4_NO_BG;
50 | this.pbLogo.Location = new System.Drawing.Point(14, 10);
51 | this.pbLogo.Name = "pbLogo";
52 | this.pbLogo.Size = new System.Drawing.Size(376, 116);
53 | this.pbLogo.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
54 | this.pbLogo.TabIndex = 2;
55 | this.pbLogo.TabStop = false;
56 | //
57 | // FrmInternalSplash
58 | //
59 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
60 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
61 | this.BackColor = System.Drawing.Color.White;
62 | this.ClientSize = new System.Drawing.Size(400, 175);
63 | this.Controls.Add(this.lblName);
64 | this.Controls.Add(this.pbLogo);
65 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
66 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
67 | this.Name = "FrmInternalSplash";
68 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
69 | this.Text = "Discord Media Loader";
70 | ((System.ComponentModel.ISupportInitialize)(this.pbLogo)).EndInit();
71 | this.ResumeLayout(false);
72 |
73 | }
74 |
75 | #endregion
76 |
77 | private System.Windows.Forms.Label lblName;
78 | private System.Windows.Forms.PictureBox pbLogo;
79 | }
80 | }
--------------------------------------------------------------------------------
/Discord Media Loader/FrmSplash.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace Discord_Media_Loader
2 | {
3 | partial class FrmSplash
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmSplash));
32 | this.lblName = new System.Windows.Forms.Label();
33 | this.pbLogo = new System.Windows.Forms.PictureBox();
34 | ((System.ComponentModel.ISupportInitialize)(this.pbLogo)).BeginInit();
35 | this.SuspendLayout();
36 | //
37 | // lblName
38 | //
39 | this.lblName.Font = new System.Drawing.Font("Microsoft Sans Serif", 21.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
40 | this.lblName.Location = new System.Drawing.Point(9, 131);
41 | this.lblName.Name = "lblName";
42 | this.lblName.Size = new System.Drawing.Size(379, 35);
43 | this.lblName.TabIndex = 1;
44 | this.lblName.Text = "Discord Media Loader";
45 | this.lblName.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
46 | //
47 | // pbLogo
48 | //
49 | this.pbLogo.Image = global::Discord_Media_Loader.Properties.Resources.Serraniel_Logo4_NO_BG;
50 | this.pbLogo.Location = new System.Drawing.Point(12, 12);
51 | this.pbLogo.Name = "pbLogo";
52 | this.pbLogo.Size = new System.Drawing.Size(376, 116);
53 | this.pbLogo.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
54 | this.pbLogo.TabIndex = 0;
55 | this.pbLogo.TabStop = false;
56 | //
57 | // FrmSplash
58 | //
59 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
60 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
61 | this.BackColor = System.Drawing.Color.White;
62 | this.ClientSize = new System.Drawing.Size(400, 175);
63 | this.Controls.Add(this.lblName);
64 | this.Controls.Add(this.pbLogo);
65 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
66 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
67 | this.Name = "FrmSplash";
68 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
69 | this.Text = "Discord Media Loader";
70 | this.Shown += new System.EventHandler(this.FrmSplash_Shown);
71 | ((System.ComponentModel.ISupportInitialize)(this.pbLogo)).EndInit();
72 | this.ResumeLayout(false);
73 |
74 | }
75 |
76 | #endregion
77 |
78 | private System.Windows.Forms.PictureBox pbLogo;
79 | private System.Windows.Forms.Label lblName;
80 | }
81 | }
--------------------------------------------------------------------------------
/Discord Media Loader/FrmDownload.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Globalization;
4 | using System.Net;
5 | using System.Threading.Tasks;
6 | using System.Windows.Forms;
7 |
8 | namespace Discord_Media_Loader
9 | {
10 | internal partial class FrmDownload : Form
11 | {
12 | private string FileName { get; }
13 | private string Source { get; }
14 | private bool Finished { get; set; } = false;
15 |
16 | internal FrmDownload(string fileName, string source)
17 | {
18 | InitializeComponent();
19 |
20 | FileName = fileName;
21 | Source = source;
22 | }
23 |
24 | internal void StartDownload(bool waitForDialog = true)
25 | {
26 | Task.Run(() =>
27 | {
28 | while (waitForDialog && !Visible) { }
29 |
30 | var wc = new WebClient
31 | {
32 | CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore)
33 | };
34 |
35 | SetStatus($"Downloading {FileName}...");
36 |
37 | SetProgress(0, 100);
38 |
39 | wc.DownloadProgressChanged += Wc_DownloadProgressChanged;
40 | wc.DownloadFileCompleted += Wc_DownloadFileCompleted;
41 |
42 | wc.DownloadFileAsync(new Uri(Source), FileName);
43 |
44 | while (!Finished)
45 | {
46 | // wait for download
47 | }
48 |
49 | RequestClose();
50 | });
51 | }
52 |
53 | private void Wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
54 | {
55 | SetStatus("Download finished");
56 | Finished = true;
57 | }
58 |
59 | private void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
60 | {
61 | var bytesIn = double.Parse(e.BytesReceived.ToString(CultureInfo.InvariantCulture));
62 | var totalBytes = double.Parse(e.TotalBytesToReceive.ToString(CultureInfo.InvariantCulture));
63 | var percentage = bytesIn / totalBytes * 100;
64 | SetProgress(percentage, 100);
65 | }
66 |
67 | delegate void SetStatusCallback(String status);
68 | private void SetStatus(String status)
69 | {
70 | if (InvokeRequired)
71 | {
72 | var callback = new SetStatusCallback(SetStatus);
73 | Invoke(callback, status);
74 | }
75 | else
76 | {
77 | lbStatus.Text = status;
78 | }
79 | }
80 |
81 | delegate void SetProgressCallback(double current, int max);
82 | private void SetProgress(double current, int max)
83 | {
84 | if (InvokeRequired)
85 | {
86 | var callback = new SetProgressCallback(SetProgress);
87 | Invoke(callback, current, max);
88 | }
89 | else
90 | {
91 | pgbProgress.Maximum = max;
92 | pgbProgress.Value = (int)current;
93 | Helper.TaskBarProgress.SetState(Handle, Helper.TaskBarProgress.TaskbarStates.Normal);
94 | Helper.TaskBarProgress.SetValue(Handle, (int)current, max);
95 | }
96 | }
97 |
98 | delegate void RequestCloseCallback();
99 | private void RequestClose()
100 | {
101 | if (InvokeRequired)
102 | {
103 | var callback = new RequestCloseCallback(RequestClose);
104 | Invoke(callback, new object[] { });
105 | }
106 | else
107 | {
108 | Close();
109 | }
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Dieser Code wurde von einem Tool generiert.
4 | // Laufzeitversion:4.0.30319.42000
5 | //
6 | // Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
7 | // der Code erneut generiert wird.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace DML.Application.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
17 | ///
18 | // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
19 | // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
20 | // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
21 | // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DML.Application.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
51 | /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Sucht eine lokalisierte Zeichenfolge, die Discord Media Loader by Serraniel - Apache 2.0 License
65 | ///https://github.com/Serraniel/DiscordMediaLoader/
66 | ///
67 | ///Made with:
68 | ///SweetLib (Copyright (c) 2017 Serraniel - GNU General Public License v3.0)
69 | ///Discord.Net (Copyright (c) 2015 RogueException - MIT License)
70 | ///Newtonsoft.Json (Copyright (c) 2007 James Newton-King - MIT License)
71 | ///Nito.AsyncEx (Copyright (c) 2014 StephenCleary - MIT License)
72 | ///RestSharp (Copyright (c) restsharp - Apache 2.0 License)
73 | ///WebSocket4Net (Copyright (c) kerryjiang - Apache 2.0 License)
74 | /// [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt.
75 | ///
76 | internal static string AboutString {
77 | get {
78 | return ResourceManager.GetString("AboutString", resourceCulture);
79 | }
80 | }
81 |
82 | ///
83 | /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
84 | ///
85 | internal static System.Drawing.Bitmap Serraniel_Logo4_NO_BG {
86 | get {
87 | object obj = ResourceManager.GetObject("Serraniel_Logo4_NO_BG", resourceCulture);
88 | return ((System.Drawing.Bitmap)(obj));
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Discord Media Loader/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/DML.Core/Classes/Job.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Discord;
6 | using Discord.WebSocket;
7 |
8 | namespace DML.Core.Classes
9 | {
10 | public class Job
11 | {
12 | public int Id { get; set; }
13 | public ulong GuildId { get; set; }
14 | public ulong ChannelId { get; set; }
15 | public double KnownTimestamp { get; set; } = 0;
16 | private double StopTimestamp { get; set; } = 0;
17 | private bool IsValid { get; set; } = true;
18 |
19 | internal void Store()
20 | {
21 | Debug("Storing job to database...");
22 | Trace("Getting jobs collection...");
23 | var jobDb = DML.Core.Core.Database.GetCollection("jobs");
24 |
25 | Trace("Adding new value...");
26 |
27 | if (jobDb.Find(x => x.ChannelId == ChannelId && x.GuildId == GuildId).Any())
28 | {
29 | jobDb.Update(this);
30 | }
31 | else
32 | {
33 | jobDb.Insert(this);
34 | }
35 | }
36 |
37 | internal void Delete()
38 | {
39 | Debug("Deleting job from database...");
40 | Trace("Getting jobs collection...");
41 | var jobDb = DML.Core.Core.Database.GetCollection("jobs");
42 |
43 | Trace("Deleting value...");
44 | jobDb.Delete(Id);
45 | }
46 |
47 | private SocketGuild FindServerById(ulong id)
48 | {
49 | Trace($"Trying to find server by Id: {id}");
50 | return (from s in DML.Core.Core.Client.Guilds where s.Id == id select s).FirstOrDefault();
51 | }
52 |
53 | private SocketTextChannel FindChannelById(SocketGuild server, ulong id)
54 | {
55 | Trace($"Trying to find channel in {server} by id: {id}");
56 | return (from c in server.TextChannels where c.Id == id select c).FirstOrDefault();
57 | }
58 |
59 | internal async Task> Scan()
60 | {
61 | Debug($"Starting scan of guild {GuildId} channel {ChannelId}...");
62 | var result = new List();
63 |
64 | var limit = 100;
65 | var lastId = ulong.MaxValue;
66 | var isFirst = true;
67 | var finished = false;
68 |
69 | var guild = FindServerById(GuildId);
70 | var channel = FindChannelById(guild, ChannelId);
71 |
72 | if (Math.Abs(StopTimestamp) < 0.4)
73 | StopTimestamp = KnownTimestamp;
74 | Trace("Initialized scanning parameters.");
75 |
76 | while (!finished)
77 | {
78 | Trace("Entering scanning loop...");
79 | SocketMessage[] messages;
80 |
81 | Trace($"Downloading next {limit} messages...");
82 | if (isFirst)
83 | {
84 | messages = await channel.GetMessagesAsync(limit).ToArray() as SocketMessage[];
85 | }
86 | else
87 | {
88 | messages = await channel.GetMessagesAsync(lastId, Direction.Before, limit).ToArray() as SocketMessage[];
89 | }
90 | Trace($"Downloaded {messages.Length} messages.");
91 |
92 | isFirst = false;
93 |
94 | foreach (var m in messages)
95 | {
96 | if (!IsValid)
97 | return null;
98 |
99 | Debug($"Processing message {m.Id}");
100 | if (m.Id < lastId)
101 | {
102 | Trace($"Updating lastId ({lastId}) to {m.Id}");
103 | lastId = m.Id;
104 | }
105 |
106 | if (SweetUtils.DateTimeToUnixTimeStamp(m.CreatedAt.UtcDateTime) <= StopTimestamp)
107 | {
108 | Debug("Found a message with a known timestamp...Stopping scan.");
109 | finished = true;
110 | continue;
111 | }
112 |
113 | Trace($"Message {m.Id} has {m.Attachments.Count} attachments.");
114 | if (m.Attachments.Count > 0)
115 | {
116 | result.Add(m);
117 | DML.Core.Core.Scheduler.TotalAttachments++;
118 | Trace($"Added message {m.Id}");
119 | }
120 | Debug($"Finished message {m.Id}");
121 |
122 | DML.Core.Core.Scheduler.MessagesScanned++;
123 | }
124 |
125 | finished = finished || messages.Length < limit;
126 | }
127 | Trace($"Downloaded all messages for guild {GuildId} channel {ChannelId}.");
128 |
129 | Trace("Sorting messages...");
130 | result.Sort((a, b) => DateTime.Compare(a.CreatedAt.UtcDateTime, b.CreatedAt.UtcDateTime));
131 |
132 | if (result.Count > 0)
133 | {
134 | Trace("Updating StopTimestamp for next scan...");
135 | StopTimestamp = SweetUtils.DateTimeToUnixTimeStamp(result[result.Count - 1].CreatedAt.UtcDateTime);
136 | }
137 |
138 | Debug($"Fisnished scan of guild {GuildId} channel {ChannelId}.");
139 |
140 | return result;
141 | }
142 |
143 | internal void Stop()
144 | {
145 | IsValid = false;
146 | }
147 |
148 | internal static IEnumerable RestoreJobs()
149 | {
150 | Debug("Restoring jobs...");
151 | Trace("Getting jobs collection...");
152 | var jobDb = DML.Core.Core.Database.GetCollection("jobs");
153 |
154 | Trace("Creating new empty job list");
155 | return jobDb.FindAll();
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Dialogs/LoginDialog.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace DML.Application.Dialogs
2 | {
3 | partial class LoginDialog
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(LoginDialog));
32 | this.pnlButtons = new System.Windows.Forms.Panel();
33 | this.btnAbort = new System.Windows.Forms.Button();
34 | this.btnOk = new System.Windows.Forms.Button();
35 | this.lbHowToToken = new System.Windows.Forms.Label();
36 | this.edToken = new System.Windows.Forms.TextBox();
37 | this.lbToken = new System.Windows.Forms.Label();
38 | this.pnlButtons.SuspendLayout();
39 | this.SuspendLayout();
40 | //
41 | // pnlButtons
42 | //
43 | this.pnlButtons.BackColor = System.Drawing.SystemColors.ButtonShadow;
44 | this.pnlButtons.Controls.Add(this.btnAbort);
45 | this.pnlButtons.Controls.Add(this.btnOk);
46 | this.pnlButtons.Dock = System.Windows.Forms.DockStyle.Bottom;
47 | this.pnlButtons.Location = new System.Drawing.Point(0, 168);
48 | this.pnlButtons.Name = "pnlButtons";
49 | this.pnlButtons.Size = new System.Drawing.Size(426, 51);
50 | this.pnlButtons.TabIndex = 0;
51 | //
52 | // btnAbort
53 | //
54 | this.btnAbort.Location = new System.Drawing.Point(348, 16);
55 | this.btnAbort.Name = "btnAbort";
56 | this.btnAbort.Size = new System.Drawing.Size(75, 23);
57 | this.btnAbort.TabIndex = 1;
58 | this.btnAbort.Text = "&Abort";
59 | this.btnAbort.UseVisualStyleBackColor = true;
60 | this.btnAbort.Click += new System.EventHandler(this.btnAbort_Click);
61 | //
62 | // btnOk
63 | //
64 | this.btnOk.Location = new System.Drawing.Point(267, 16);
65 | this.btnOk.Name = "btnOk";
66 | this.btnOk.Size = new System.Drawing.Size(75, 23);
67 | this.btnOk.TabIndex = 0;
68 | this.btnOk.Text = "&Ok";
69 | this.btnOk.UseVisualStyleBackColor = true;
70 | this.btnOk.Click += new System.EventHandler(this.btnOk_Click);
71 | //
72 | // lbHowToToken
73 | //
74 | this.lbHowToToken.Location = new System.Drawing.Point(7, 58);
75 | this.lbHowToToken.Name = "lbHowToToken";
76 | this.lbHowToToken.Size = new System.Drawing.Size(412, 87);
77 | this.lbHowToToken.TabIndex = 5;
78 | this.lbHowToToken.Text = resources.GetString("lbHowToToken.Text");
79 | //
80 | // edToken
81 | //
82 | this.edToken.Location = new System.Drawing.Point(79, 12);
83 | this.edToken.Name = "edToken";
84 | this.edToken.Size = new System.Drawing.Size(335, 20);
85 | this.edToken.TabIndex = 4;
86 | //
87 | // lbToken
88 | //
89 | this.lbToken.AutoSize = true;
90 | this.lbToken.Location = new System.Drawing.Point(7, 15);
91 | this.lbToken.Name = "lbToken";
92 | this.lbToken.Size = new System.Drawing.Size(66, 13);
93 | this.lbToken.TabIndex = 3;
94 | this.lbToken.Text = "Login token:";
95 | //
96 | // LoginDialog
97 | //
98 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
99 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
100 | this.ClientSize = new System.Drawing.Size(426, 219);
101 | this.Controls.Add(this.lbHowToToken);
102 | this.Controls.Add(this.edToken);
103 | this.Controls.Add(this.lbToken);
104 | this.Controls.Add(this.pnlButtons);
105 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
106 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
107 | this.MaximizeBox = false;
108 | this.MinimizeBox = false;
109 | this.Name = "LoginDialog";
110 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
111 | this.Text = "LoginForm";
112 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LoginDialog_FormClosing);
113 | this.Shown += new System.EventHandler(this.LoginDialog_Shown);
114 | this.pnlButtons.ResumeLayout(false);
115 | this.ResumeLayout(false);
116 | this.PerformLayout();
117 |
118 | }
119 |
120 | #endregion
121 |
122 | private System.Windows.Forms.Panel pnlButtons;
123 | private System.Windows.Forms.Button btnAbort;
124 | private System.Windows.Forms.Button btnOk;
125 | private System.Windows.Forms.Label lbHowToToken;
126 | private System.Windows.Forms.TextBox edToken;
127 | private System.Windows.Forms.Label lbToken;
128 | }
129 | }
--------------------------------------------------------------------------------
/Discord Media Loader/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\Serraniel-Logo4-NO-BG.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Classes/Job.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Discord;
6 | using Discord.WebSocket;
7 | using DML.Application.Classes;
8 | using DML.Client;
9 | using SweetLib.Utils;
10 | using SweetLib.Utils.Extensions;
11 | using static SweetLib.Utils.Logger.Logger;
12 |
13 | namespace DML.AppCore.Classes
14 | {
15 | public class Job
16 | {
17 | public int Id { get; set; }
18 | public ulong GuildId { get; set; }
19 | public ulong ChannelId { get; set; }
20 | public double KnownTimestamp { get; set; } = 0;
21 | private double StopTimestamp { get; set; } = 0;
22 | private bool IsValid { get; set; } = true;
23 |
24 | internal void Store()
25 | {
26 | Debug("Storing job to database...");
27 | Trace("Getting jobs collection...");
28 | var jobDb = Core.Database.GetCollection("jobs");
29 |
30 | Trace("Adding new value...");
31 |
32 | if (jobDb.Find(x => x.ChannelId == ChannelId && x.GuildId == GuildId).Any())
33 | {
34 | jobDb.Update(this);
35 | }
36 | else
37 | {
38 | jobDb.Insert(this);
39 | }
40 | }
41 |
42 | public void Delete()
43 | {
44 | Debug("Deleting job from database...");
45 | Trace("Getting jobs collection...");
46 | var jobDb = Core.Database.GetCollection("jobs");
47 |
48 | Trace("Deleting value...");
49 | jobDb.Delete(Id);
50 | }
51 |
52 | private SocketGuild FindServerById(ulong id)
53 | {
54 | Trace($"Trying to find server by Id: {id}");
55 | return (from s in DMLClient.Client.Guilds where s.Id == id select s).FirstOrDefault();
56 | }
57 |
58 | private SocketTextChannel FindChannelById(SocketGuild server, ulong id)
59 | {
60 | Trace($"Trying to find channel in {server} by id: {id}");
61 | return (from c in server.TextChannels where c.Id == id select c).FirstOrDefault();
62 | }
63 |
64 | internal async Task> Scan()
65 | {
66 | Debug($"Starting scan of guild {GuildId} channel {ChannelId}...");
67 | var result = new List();
68 |
69 | var limit = 100;
70 | var lastId = ulong.MaxValue;
71 | var isFirst = true;
72 | var finished = false;
73 |
74 | var guild = FindServerById(GuildId);
75 | var channel = FindChannelById(guild, ChannelId);
76 |
77 | Debug("Checking channel access");
78 | //channel.GetUser(channel.Guild.CurrentUser.Id);
79 | if (channel.GetUser(channel.Guild.CurrentUser.Id) == null)
80 | {
81 | Info("Skipping channel without access");
82 | return result;
83 | }
84 |
85 | if (Math.Abs(StopTimestamp) < 0.4)
86 | StopTimestamp = KnownTimestamp;
87 | Trace("Initialized scanning parameters.");
88 |
89 | while (!finished)
90 | {
91 | Trace("Entering scanning loop...");
92 | var messages = new List();
93 |
94 | Trace($"Downloading next {limit} messages...");
95 | if (isFirst)
96 | {
97 | //messages = await channel.GetMessagesAsync(limit).ToArray() as SocketMessage[];
98 | var realMessages = await channel.GetMessagesAsync(limit).ToArray();
99 |
100 | foreach (var realMessageArray in realMessages)
101 | {
102 | foreach (var realMessage in realMessageArray)
103 | {
104 | messages.Add(realMessage);
105 | }
106 | }
107 | }
108 | else
109 | {
110 | var realMessages = await channel.GetMessagesAsync(lastId, Direction.Before, limit).ToArray();
111 |
112 | foreach (var realMessageArray in realMessages)
113 | {
114 | foreach (var realMessage in realMessageArray)
115 | {
116 | messages.Add(realMessage);
117 | }
118 | }
119 |
120 | //messages = await channel.GetMessagesAsync(lastId, Direction.Before, limit).ToArray() as SocketMessage[];
121 | }
122 | Trace($"Downloaded {messages.Count} messages.");
123 |
124 | isFirst = false;
125 |
126 | foreach (var m in messages)
127 | {
128 | if (!IsValid)
129 | return null;
130 |
131 | Core.Scheduler.MessagesScanned++;
132 |
133 | Debug($"Processing message {m.Id}");
134 | if (m.Id < lastId)
135 | {
136 | Trace($"Updating lastId ({lastId}) to {m.Id}");
137 | lastId = m.Id;
138 | }
139 |
140 | if (m.CreatedAt.UtcDateTime.ToUnixTimeStamp() <= StopTimestamp)
141 | {
142 | Debug("Found a message with a known timestamp...Stopping scan.");
143 | finished = true;
144 | continue;
145 | }
146 |
147 | Trace($"Message {m.Id} has {m.Attachments.Count} attachments.");
148 | if (m.Attachments.Count > 0)
149 | {
150 | result.Add(m);
151 | Core.Scheduler.TotalAttachments += (ulong)m.Attachments.Count;
152 | Trace($"Added message {m.Id}");
153 | }
154 | Debug($"Finished message {m.Id}");
155 | }
156 |
157 | finished = finished || messages.Count < limit;
158 | }
159 | Trace($"Downloaded all messages for guild {GuildId} channel {ChannelId}.");
160 |
161 | Trace("Sorting messages...");
162 | result.Sort((a, b) => DateTime.Compare(a.CreatedAt.UtcDateTime, b.CreatedAt.UtcDateTime));
163 |
164 | if (result.Count > 0)
165 | {
166 | Trace("Updating StopTimestamp for next scan...");
167 | StopTimestamp = result[result.Count - 1].CreatedAt.UtcDateTime.ToUnixTimeStamp();
168 | }
169 |
170 | Debug($"Fisnished scan of guild {GuildId} channel {ChannelId}.");
171 |
172 | return result;
173 | }
174 |
175 | public void Stop()
176 | {
177 | IsValid = false;
178 | }
179 |
180 | public static IEnumerable RestoreJobs()
181 | {
182 | Debug("Restoring jobs...");
183 | Trace("Getting jobs collection...");
184 | var jobDb = Core.Database.GetCollection("jobs");
185 |
186 | Trace("Creating new empty job list");
187 | return jobDb.FindAll();
188 | }
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | Discord Media Loader by Serraniel - Apache 2.0 License
122 | https://github.com/Serraniel/DiscordMediaLoader/
123 |
124 | Made with:
125 | SweetLib (Copyright (c) 2017 Serraniel - GNU General Public License v3.0)
126 | Discord.Net (Copyright (c) 2015 RogueException - MIT License)
127 | Newtonsoft.Json (Copyright (c) 2007 James Newton-King - MIT License)
128 | Nito.AsyncEx (Copyright (c) 2014 StephenCleary - MIT License)
129 | RestSharp (Copyright (c) restsharp - Apache 2.0 License)
130 | WebSocket4Net (Copyright (c) kerryjiang - Apache 2.0 License)
131 | LiteDB (Copyright (c) 2014 - 2015 Mauricio David - MIT License)
132 | Octokit (Copyright (c) 2012 GitHub, Inc - MIT License)
133 |
134 |
135 |
136 | ..\Resources\Serraniel-Logo4-NO-BG.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
137 |
138 |
--------------------------------------------------------------------------------
/DML.Core/Core.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Discord;
7 | using Discord.Net;
8 | using Discord.WebSocket;
9 | using DML.Core.Classes;
10 |
11 | namespace DML.Core
12 | {
13 | public static class Core
14 | {
15 | internal static DiscordSocketClient Client { get; set; }
16 | internal static LiteDatabase Database { get; set; }
17 | internal static Settings Settings { get; set; }
18 | internal static JobScheduler Scheduler { get; } = new JobScheduler();
19 |
20 | internal static string DataDirectory
21 | => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Serraniel\Discord Media Loader");
22 |
23 | public static async Task Run(string[] paramStrings)
24 | {
25 | try
26 | {
27 | var splash = new FrmInternalSplash();
28 | splash.Show();
29 | System.Windows.Forms.Application.DoEvents();
30 |
31 | Info("Starting up Discord Media Loader application...");
32 | var useTrace = false;
33 | #if DEBUG
34 | //temporary add debug log level if debugging...
35 | GlobalLogLevel |= LogLevel.Debug;
36 | Debug("Running in debug configuartion. Added log level debug.");
37 | #endif
38 |
39 | Debug($"Parameters: {string.Join(", ", paramStrings)}");
40 | if (paramStrings.Contains("--trace") || paramStrings.Contains("-t"))
41 | {
42 | useTrace = true;
43 | GlobalLogLevel |= LogLevel.Trace;
44 | Trace("Trace parameter found. Added log level trace.");
45 | }
46 |
47 | Debug($"Application data folder: {DataDirectory}");
48 |
49 | Trace("Checking application data folder...");
50 | if (!Directory.Exists(DataDirectory))
51 | {
52 | Debug("Creating application data folder...");
53 | Directory.CreateDirectory(DataDirectory);
54 | Trace("Creating application data folder.");
55 | }
56 |
57 | Trace("Initializing profile optimizations...");
58 | ProfileOptimization.SetProfileRoot(System.Windows.Forms.Application.UserAppDataPath);
59 | ProfileOptimization.StartProfile("profile.opt");
60 | Trace("Finished initializing profile optimizations.");
61 |
62 | Trace("Trying to identify log memory...");
63 | var logMemory = DefaultLogMemory as ArchivableConsoleLogMemory;
64 | if (logMemory != null)
65 | {
66 | var logFolder = Path.Combine(DataDirectory, "logs");
67 | if (!Directory.Exists(logFolder))
68 | {
69 | Debug("Creating log folder...");
70 | Directory.CreateDirectory(logFolder);
71 | Trace("Created log folder.");
72 | }
73 |
74 |
75 | var logFile = Path.Combine(logFolder,
76 | SweetUtils.LegalizeFilename($"{DateTime.Now.ToString(CultureInfo.CurrentCulture.DateTimeFormat.SortableDateTimePattern)}.log.zip"));
77 |
78 | Trace($"Setting log file: {logFile}");
79 | logMemory.AutoArchiveOnDispose = true;
80 | logMemory.ArchiveFile = logFile;
81 | }
82 |
83 | Debug("Loading database...");
84 | Database = new LiteDatabase(Path.Combine(DataDirectory, "config.db"));
85 | Database.Log.Logging += (message) => Trace($"LiteDB: {message}");
86 |
87 | Debug("Loading settings collection out of database...");
88 | var settingsDB = Database.GetCollection("settings");
89 | if (settingsDB.Count() > 1)
90 | {
91 | Warn("Found more than one setting. Loading first one...");
92 | }
93 | Settings = settingsDB.FindAll().FirstOrDefault();
94 | if (Settings == null)
95 | {
96 | Warn("Settings not found. Creating new one. This is normal on first start up...");
97 | Settings = new Settings();
98 | Settings.Store();
99 | }
100 |
101 | Debug("Loading jobs collection out of database...");
102 | Scheduler.JobList = Job.RestoreJobs().ToList();
103 |
104 | Info("Loaded settings.");
105 | Debug(
106 | $"Settings: Email: {Settings.Email}, password: {(string.IsNullOrEmpty(Settings.Password) ? "not set" : "is set")}, use username: {Settings.UseUserData}, loginToken: {Settings.LoginToken}");
107 |
108 | Trace("Updating log level...");
109 | GlobalLogLevel = Settings.ApplicactionLogLevel;
110 | #if DEBUG
111 | //temporary add debug log level if debugging...
112 | GlobalLogLevel |= LogLevel.Debug;
113 | Debug("Running in debug configuartion. Added log level debug.");
114 | #endif
115 | if (useTrace)
116 | {
117 | GlobalLogLevel |= LogLevel.Trace;
118 | Trace("Creating application data folder.");
119 | }
120 |
121 | Debug("Creating discord client...");
122 |
123 | Client = new DiscordSocketClient();
124 | Client.Log += (arg) =>
125 | {
126 | var logMessage = $"DiscordClient: {arg.Message}";
127 | switch (arg.Severity)
128 | {
129 | case LogSeverity.Verbose:
130 | Trace(logMessage);
131 | break;
132 | case LogSeverity.Debug:
133 | Trace(logMessage);
134 | break;
135 | case LogSeverity.Info:
136 | Info(logMessage);
137 | break;
138 | case LogSeverity.Warning:
139 | Warn(logMessage);
140 | break;
141 | case LogSeverity.Error:
142 | Error(logMessage);
143 | break;
144 | }
145 |
146 | return Task.CompletedTask;
147 | };
148 |
149 |
150 | Info("Trying to log into discord...");
151 | var abort = false;
152 |
153 | Client.Connected += Client_Connected;
154 |
155 | while (Client.LoginState != LoginState.LoggedIn && !abort)
156 | {
157 | Debug(Client.ConnectionState.ToString());
158 | Debug(Client.LoginState.ToString());
159 |
160 | Trace("Entering login loop.");
161 |
162 | try
163 | {
164 | if (Client.ConnectionState == ConnectionState.Connecting)
165 | continue;
166 |
167 | if (!string.IsNullOrEmpty(Settings.LoginToken))
168 | {
169 | Debug("Trying to login with last known token...");
170 | await Client.LoginAsync(TokenType.User, Settings.LoginToken);
171 | await Task.Delay(1000);
172 | }
173 |
174 | }
175 | catch (HttpException ex)
176 | {
177 | Warn($"Login seems to have failed or gone wrong: {ex.GetType().Name} - {ex.Message}");
178 | }
179 |
180 | if (Client.LoginState == LoginState.LoggedOut)
181 | {
182 | Settings.Password = string.Empty;
183 | Debug("Showing dialog for username and password...");
184 | var loginDlg = new LoginDialog();
185 | loginDlg.ShowDialog();
186 | Trace("Dialog closed.");
187 | }
188 | }
189 |
190 | Debug("Start checking for invalid jobs...");
191 |
192 | //Client
193 |
194 | while (Client.Guilds.Count==0)
195 | {
196 | // wait until guilds are loaded
197 | }
198 |
199 | for (var i = Scheduler.JobList.Count - 1; i >= 0; i--)
200 | {
201 | var job = Scheduler.JobList[i];
202 | var isError = false;
203 | var guild = FindServerById(job.GuildId);
204 | if (guild == null)
205 | isError = true;
206 | else
207 | {
208 | var channel = FindChannelById(guild, job.ChannelId);
209 | if (channel == null)
210 | isError = true;
211 | }
212 |
213 | if (isError)
214 | {
215 | 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",
216 | MessageBoxButtons.OK, MessageBoxIcon.Warning);
217 |
218 | Scheduler.JobList.Remove(job);
219 | Scheduler.RunningJobs.Remove(job.Id);
220 | job.Stop();
221 | job.Delete();
222 | }
223 | }
224 |
225 | splash.Close();
226 |
227 | Info("Starting scheduler...");
228 | Scheduler.Start();
229 |
230 | System.Windows.Forms.Application.Run(new MainForm());
231 |
232 | Info("Stopping scheduler...");
233 | Scheduler.Stop();
234 | }
235 | catch (Exception ex)
236 | {
237 | Error($"{ex.Message} occured at: {ex.StackTrace}");
238 | }
239 | }
240 |
241 | private static Task Client_Connected()
242 | {
243 | Debug("Connected");
244 | return Task.CompletedTask;
245 | }
246 |
247 | //TODO: this is thrid time we implement this.....this has to be fixed!!!
248 | private static SocketGuild FindServerById(ulong id)
249 | {
250 | Trace($"Trying to find server by Id: {id}");
251 | return (from s in Core.Client.Guilds where s.Id == id select s).FirstOrDefault();
252 | }
253 |
254 | private static SocketTextChannel FindChannelById(SocketGuild server, ulong id)
255 | {
256 | Trace($"Trying to find channel in {server} by id: {id}");
257 | return (from c in server.TextChannels where c.Id == id select c).FirstOrDefault();
258 | }
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/MainForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Threading.Tasks;
7 | using System.Windows.Forms;
8 | using Discord;
9 | using Discord.WebSocket;
10 | using DML.AppCore.Classes;
11 | using DML.Application.Classes;
12 | using DML.Application.Helper;
13 | using DML.Client;
14 | using static DML.Client.DMLClient;
15 | using static SweetLib.Utils.Logger.Logger;
16 |
17 | namespace DML.Application
18 | {
19 | enum OnlineState
20 | {
21 | Online,
22 | Idle,
23 | DoNotDisturb,
24 | Invisible
25 | }
26 | public partial class MainForm : Form
27 | {
28 | private bool IsInitialized { get; set; } = false;
29 | public MainForm()
30 | {
31 | InitializeComponent();
32 | }
33 |
34 | private void MainForm_Shown(object sender, System.EventArgs e)
35 | {
36 | Trace("MainForm shown executed.");
37 | RefreshComponents();
38 |
39 | IsInitialized = true;
40 | }
41 |
42 | private void RefreshComponents()
43 | {
44 | Debug("Refreshing components...");
45 |
46 | lbVersion.Text = $"v{Assembly.GetExecutingAssembly().GetName().Version} Copyright © by Serraniel";
47 |
48 | Trace("Refreshing operating folder component...");
49 | edOperatingFolder.Text = Core.Settings.OperatingFolder;
50 |
51 | Trace("Refreshing name scheme component...");
52 | edNameScheme.Text = Core.Settings.FileNameScheme;
53 |
54 | Trace("Refreshing skip existing files component...");
55 | cbSkipExisting.Checked = Core.Settings.SkipExistingFiles;
56 |
57 | Trace("Refreshing thread limit component...");
58 | edThreadLimit.Value = Core.Settings.ThreadLimit;
59 |
60 | if (cbGuild.Items.Count == 0)
61 | {
62 | Trace("Adding guilds to component...");
63 |
64 | cbGuild.Items.AddRange(DMLClient.Client.Guilds.Where(g => g.Name != null).OrderBy(g => g.Name).Select(g => new IdentifiedString(g.Id, g.Name)).ToArray());
65 |
66 | cbGuild.SelectedIndex = 0;
67 | Trace("Guild component initialized.");
68 | }
69 |
70 | Trace("Refreshing job list component...");
71 | var oldIndex = lbxJobs.SelectedIndex;
72 | lbxJobs.Items.Clear();
73 | foreach (var job in Core.Scheduler.JobList)
74 | {
75 | lbxJobs.Items.Add(new IdentifiedString(job.Id, $"{FindServerById(job.GuildId)?.Name}:{FindChannelById(FindServerById(job.GuildId), job.ChannelId)?.Name}"));
76 | }
77 | lbxJobs.SelectedIndex = oldIndex;
78 |
79 | lbStatus.Text = DMLClient.Client.CurrentUser.Status.ToString();
80 | }
81 |
82 | private void DoSomethingChanged(object sender, System.EventArgs e)
83 | {
84 | Debug($"DoSomethingChanged excuted by {sender}.");
85 | if (!IsInitialized)
86 | {
87 | Trace("Form not initialized. Leaving DoSomethingChanged...");
88 | return;
89 | }
90 |
91 | Trace("Updating operating folder...");
92 | Core.Settings.OperatingFolder = edOperatingFolder.Text;
93 |
94 | Trace("Updating name scheme...");
95 | Core.Settings.FileNameScheme = edNameScheme.Text;
96 |
97 | Trace("Updating skip existing files...");
98 | Core.Settings.SkipExistingFiles = cbSkipExisting.Checked;
99 |
100 | Trace("Updating thread limit...");
101 | Core.Settings.ThreadLimit = (int)edThreadLimit.Value;
102 |
103 | Trace("Storing new settings...");
104 | Core.Settings.Store();
105 |
106 | Info("New settings have been saved.");
107 | }
108 |
109 | private void btnSearchFolders_Click(object sender, System.EventArgs e)
110 | {
111 | Trace("Operating folder button pressed.");
112 | using (var browserDialog = new FolderBrowserDialog())
113 | {
114 | Debug("Showing file browser dialog for operating folder...");
115 |
116 | browserDialog.SelectedPath = edOperatingFolder.Text;
117 | browserDialog.ShowNewFolderButton = true;
118 | browserDialog.Description = "Select an operating folder...";
119 |
120 | if (browserDialog.ShowDialog() == DialogResult.OK)
121 | {
122 | edOperatingFolder.Text = browserDialog.SelectedPath;
123 | Debug("Updated operating folder.");
124 | }
125 | }
126 | }
127 |
128 | private SocketGuild FindServerByName(string name)
129 | {
130 | Trace($"Trying to find server by name: {name}");
131 | return (from s in DMLClient.Client.Guilds where s.Name == name select s).FirstOrDefault();
132 | }
133 |
134 | private SocketTextChannel FindChannelByName(SocketGuild server, string name)
135 | {
136 | Trace($"Trying to find channel in {server} by name: {name}");
137 | return (from c in server.TextChannels where c.Name == name select c).FirstOrDefault();
138 | }
139 |
140 | private SocketGuild FindServerById(ulong id)
141 | {
142 | Trace($"Trying to find server by Id: {id}");
143 | return (from s in DMLClient.Client.Guilds where s.Id == id select s).FirstOrDefault();
144 | }
145 |
146 | private SocketTextChannel FindChannelById(SocketGuild server, ulong id)
147 | {
148 | Trace($"Trying to find channel in {server} by id: {id}");
149 | return (from c in server.TextChannels where c.Id == id select c).FirstOrDefault();
150 | }
151 |
152 | private void cbGuild_SelectedIndexChanged(object sender, System.EventArgs e)
153 | {
154 | Trace("Guild index changed.");
155 | Debug("Updating channel dropdown component...");
156 |
157 | UseWaitCursor = true;
158 | try
159 | {
160 | var guild = FindServerById(((IdentifiedString)cbGuild.SelectedItem).Id);
161 |
162 | if (guild != null)
163 | {
164 | Trace("Cleaning channel component from old values...");
165 | cbChannel.Items.Clear();
166 |
167 | Trace("Adding new channels...");
168 |
169 | cbChannel.Items.AddRange(guild.TextChannels.OrderBy(c => c.Position).Select(c => new IdentifiedString(c.Id, c.Name)).ToArray());
170 |
171 | Trace($"Added {cbChannel.Items.Count} channels.");
172 |
173 | cbChannel.SelectedIndex = 0;
174 | }
175 | else
176 | {
177 | Warn($"Guild {cbGuild.Text} could not be found!");
178 | }
179 | }
180 | finally
181 | {
182 | UseWaitCursor = false;
183 | }
184 |
185 | Debug("Finished updating channel dropdown component.");
186 | }
187 |
188 | private void btnAddJob_Click(object sender, System.EventArgs e)
189 | {
190 | var job = new Job
191 | {
192 | GuildId = ((IdentifiedString)cbGuild.SelectedItem).Id,
193 | ChannelId = ((IdentifiedString)cbChannel.SelectedItem).Id
194 | };
195 |
196 | if (!(from j in Core.Scheduler.JobList
197 | where j.GuildId == job.GuildId && j.ChannelId == job.ChannelId
198 | select j).Any())
199 | {
200 | job.Store();
201 | Core.Scheduler.JobList.Add(job);
202 | }
203 |
204 | RefreshComponents();
205 | }
206 |
207 | private void btnDelete_Click(object sender, System.EventArgs e)
208 | {
209 | Trace("Deleting job pressed.");
210 |
211 | if (lbxJobs.SelectedIndex < 0)
212 | {
213 | Warn("No job selected.");
214 | MessageBox.Show("No job has been seleted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
215 | }
216 |
217 | var jobId = ((IdentifiedString)lbxJobs.SelectedItem).Id;
218 |
219 | var job = Core.Scheduler.JobList.FirstOrDefault(j => j.Id == jobId);
220 | if (job != null)
221 | {
222 | Core.Scheduler.JobList.Remove(job);
223 | Core.Scheduler.RunningJobs.Remove(job.Id);
224 | job.Stop();
225 | job.Delete();
226 | }
227 |
228 | lbxJobs.SelectedIndex = -1;
229 | RefreshComponents();
230 | }
231 |
232 | private void tmrRefreshProgress_Tick(object sender, System.EventArgs e)
233 | {
234 | var scanned = Core.Scheduler.MessagesScanned;
235 | var totalAttachments = Core.Scheduler.TotalAttachments;
236 | var done = Core.Scheduler.AttachmentsDownloaded;
237 |
238 | var progress = totalAttachments > 0 ? (int)(100 * done / totalAttachments) : 0;
239 | progress = Math.Min(Math.Max(0, progress), 100);
240 | pgbProgress.Maximum = 100;
241 | pgbProgress.Value = progress;
242 |
243 | lbProgress.Text = $"Scanned: {scanned} Downloaded: {done} Open: {totalAttachments - done}";
244 | }
245 |
246 | private void aboutToolStripMenuItem_Click(object sender, System.EventArgs e)
247 | {
248 | MessageBox.Show(Properties.Resources.AboutString);
249 | }
250 |
251 | private void visitGithubToolStripMenuItem_Click(object sender, System.EventArgs e)
252 | {
253 | Process.Start("https://github.com/Serraniel/DiscordMediaLoader/");
254 | }
255 |
256 | private async void toolStripDropDownButton1_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
257 | {
258 | OnlineState state = (OnlineState)Convert.ToInt32(e.ClickedItem.Tag);
259 |
260 | lbStatus.Text = state.ToString();
261 | tmrTriggerRefresh.Start();
262 |
263 | switch (state)
264 | {
265 | case OnlineState.Online:
266 | await DMLClient.Client.SetStatusAsync(UserStatus.Online);
267 | break;
268 | case OnlineState.Idle:
269 | await DMLClient.Client.SetStatusAsync(UserStatus.Idle);
270 | break;
271 | case OnlineState.DoNotDisturb:
272 | await DMLClient.Client.SetStatusAsync(UserStatus.DoNotDisturb);
273 | break;
274 | case OnlineState.Invisible:
275 | await DMLClient.Client.SetStatusAsync(UserStatus.Invisible);
276 | break;
277 | }
278 | }
279 |
280 | private void tmrTriggerRefresh_Tick(object sender, EventArgs e)
281 | {
282 | lbStatus.Text = DMLClient.Client.CurrentUser.Status.ToString();
283 | tmrTriggerRefresh.Stop();
284 | }
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/DML.Core/Classes/JobScheduler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Discord.WebSocket;
7 |
8 | namespace DML.Core.Classes
9 | {
10 | internal class JobScheduler
11 | {
12 | private ulong messageScanned = 0;
13 | private ulong totalAttachments = 0;
14 | private ulong attachmentsDownloaded = 0;
15 |
16 | private bool Run { get; set; } = false;
17 | internal List JobList { get; set; } = new List();
18 | internal Dictionary> RunningJobs = new Dictionary>();
19 | internal int RunningThreads { get; set; } = 0;
20 |
21 | internal ulong MessagesScanned
22 | {
23 | get
24 | {
25 | lock (this)
26 | {
27 | return messageScanned;
28 | }
29 | }
30 | set
31 | {
32 | lock (this)
33 | {
34 | messageScanned = value;
35 | }
36 | }
37 | }
38 |
39 | internal ulong TotalAttachments
40 | {
41 | get
42 | {
43 | lock (this)
44 | {
45 | return totalAttachments;
46 | }
47 | }
48 | set
49 | {
50 | lock (this)
51 | {
52 | totalAttachments = value;
53 | }
54 | }
55 | }
56 |
57 | internal ulong AttachmentsDownloaded
58 | {
59 | get
60 | {
61 | lock (this)
62 | {
63 | return attachmentsDownloaded;
64 | }
65 | }
66 | set
67 | {
68 | lock (this)
69 | {
70 | attachmentsDownloaded = value;
71 | }
72 | }
73 | }
74 |
75 | internal ulong AttachmentsToDownload => TotalAttachments - AttachmentsDownloaded;
76 |
77 | internal void Stop()
78 | {
79 | Run = false;
80 | }
81 |
82 | internal void Start()
83 | {
84 | Run = true;
85 |
86 | Task.Run(async () =>
87 | {
88 | Info("Started JobScheduler...");
89 | while (Run)
90 | {
91 | Debug("Entering job list handler loop...");
92 | //foreach (var job in JobList)
93 | for (var i = JobList.Count - 1; i >= 0; i--)
94 | {
95 | var job = JobList[i];
96 | Debug($"Checking job {job}");
97 | var hasJob = false;
98 |
99 | Trace("Locking scheduler...");
100 | lock (this)
101 | {
102 | Trace("Checking if job is already performed...");
103 | hasJob = RunningJobs.ContainsKey(job.Id);
104 | }
105 | Trace("Unlocked scheduler.");
106 |
107 | if (!hasJob)
108 | {
109 | Debug("Job is not performed yet...Performing job...");
110 | var queue = new Queue();
111 |
112 | Trace("Locking scheduler...");
113 | lock (this)
114 | {
115 | Trace("Adding job to running jobs.");
116 | RunningJobs.Add(job.Id, queue);
117 | }
118 | Trace("Unlocked scheduler.");
119 |
120 | Trace("Issuing job message scan...");
121 | var messages = await job.Scan();
122 |
123 | if (messages == null)
124 | continue;
125 |
126 | Trace($"Adding {messages.Count} messages to queue...");
127 | foreach (var msg in messages)
128 | {
129 | queue.Enqueue(msg);
130 | }
131 | Trace($"Added {queue.Count} messages to queue.");
132 |
133 | if (messages.Count != queue.Count)
134 | Warn("Not all messages have been added into the queue.");
135 |
136 | var startedDownload = false;
137 |
138 | while (!startedDownload)
139 | {
140 | Debug("Entering loop to check thread availability");
141 | Trace("Locking scheduler...");
142 | lock (this)
143 | {
144 | Trace($"Checking thread limit. Running: {RunningThreads}, Max: {DML.Core.Core.Settings.ThreadLimit}");
145 | if (RunningThreads >= DML.Core.Core.Settings.ThreadLimit)
146 | continue;
147 |
148 | RunningThreads++;
149 | startedDownload = true;
150 | }
151 | Trace("Unlocked scheduler.");
152 | }
153 |
154 | Trace("Start downloading job async.");
155 | Task.Run(() => WorkQueue(job.Id)); // do not await to work parallel
156 | }
157 | }
158 | }
159 | });
160 | }
161 |
162 | private void WorkQueue(int jobId)
163 | {
164 | try
165 | {
166 | Debug("Beginning job download...");
167 | Trace("Finding job...");
168 | var job = (from j in JobList where j.Id == jobId select j).FirstOrDefault();
169 |
170 | if (job == null)
171 | {
172 | Warn($"Associating job not found! JobId: {jobId}");
173 | return;
174 | }
175 | Trace("Found job.");
176 |
177 | Queue queue;
178 | Trace("Locking scheduler...");
179 | lock (this)
180 | {
181 | Trace("Finiding queue...");
182 | if (!RunningJobs.TryGetValue(jobId, out queue))
183 | {
184 | Warn($"Queue for job {jobId} not found!");
185 | return;
186 | }
187 | Trace("Queue found.");
188 | }
189 | Trace("Unlocked scheduler.");
190 |
191 | Debug($"Messages to process for job {jobId}: {queue.Count}");
192 | while (queue.Count > 0)
193 | {
194 | Trace("Locking scheduler...");
195 | lock (this)
196 | {
197 | Trace("Checking if still a job...");
198 | if (!RunningJobs.ContainsKey(jobId))
199 | {
200 | Warn($"Queue for job {jobId} not found!");
201 | return;
202 | }
203 | Trace("Continue working...");
204 | }
205 | Trace("Unlocked scheduler.");
206 |
207 | Trace("Dequeueing message...");
208 | var message = queue.Dequeue();
209 |
210 | Debug($"Attachments for message {message.Id}: {message.Attachments.Count}");
211 | foreach (var a in message.Attachments)
212 | {
213 | try
214 | {
215 | var fileName = Path.Combine(DML.Core.Core.Settings.OperatingFolder, DML.Core.Core.Settings.FileNameScheme);
216 |
217 | Trace("Replacing filename placeholders...");
218 |
219 | var extensionRequired = !fileName.EndsWith("%name%");
220 |
221 | var serverName = "unknown";
222 |
223 | var socketTextChannel = message.Channel as SocketTextChannel;
224 | if (socketTextChannel != null)
225 | {
226 | serverName = socketTextChannel.Guild.Name.Replace(":", "").Replace("/", "")
227 | .Replace("\\", "");
228 | }
229 |
230 | fileName =
231 | fileName.Replace("%guild%", serverName)
232 | .Replace("%channel%", message.Channel.Name)
233 | .Replace("%timestamp%", SweetUtils.DateTimeToUnixTimeStamp(message.CreatedAt.UtcDateTime).ToString())
234 | .Replace("%name%", a.Filename)
235 | .Replace("%id%", a.Id.ToString());
236 |
237 | if (extensionRequired)
238 | fileName += Path.GetExtension(a.Filename);
239 |
240 | Trace($"Detemined file name: {fileName}.");
241 |
242 |
243 | if (File.Exists(fileName) && new FileInfo(fileName).Length == a.Size)
244 | {
245 | Debug($"{fileName} already existing with its estimated size. Skipping...");
246 | continue;
247 | }
248 |
249 | Trace("Determining directory...");
250 | var fileDirectory = Path.GetDirectoryName(fileName);
251 |
252 | if (!Directory.Exists(fileDirectory))
253 | {
254 | Info($"Directory {fileDirectory} does not exist. Creating directory...");
255 | Directory.CreateDirectory(fileDirectory);
256 | Debug("Created directory.");
257 | }
258 |
259 | var wc = new WebClient();
260 | Debug($"Starting downloading of attachment {a.Id}...");
261 |
262 | wc.DownloadFile(new Uri(a.Url), fileName);
263 | Debug($"Downloaded attachment {a.Id}.");
264 |
265 | Trace("Updating known timestamp for job...");
266 | job.KnownTimestamp = SweetUtils.DateTimeToUnixTimeStamp(message.CreatedAt.UtcDateTime);
267 | job.Store();
268 | }
269 | finally
270 | {
271 | AttachmentsDownloaded++;
272 | }
273 | }
274 | }
275 | }
276 | finally
277 | {
278 | Trace("Locking scheduler...");
279 | lock (this)
280 | {
281 | Trace($"Removing {jobId} from running jobs...");
282 | RunningJobs.Remove(jobId);
283 | Trace("Decreasing thread count...");
284 | RunningThreads--;
285 | }
286 | Trace("Unlocked scheduler.");
287 | }
288 | }
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Classes/JobScheduler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Threading.Tasks;
7 | using Discord;
8 | using Discord.WebSocket;
9 | using DML.Application.Classes;
10 | using SweetLib.Utils;
11 | using SweetLib.Utils.Extensions;
12 | using SweetLib.Utils.Logger;
13 |
14 | namespace DML.AppCore.Classes
15 | {
16 | public class JobScheduler
17 | {
18 | private ulong messageScanned = 0;
19 | private ulong totalAttachments = 0;
20 | private ulong attachmentsDownloaded = 0;
21 |
22 | private bool Run { get; set; } = false;
23 | public List JobList { get; set; } = new List();
24 | public Dictionary> RunningJobs = new Dictionary>();
25 | internal int RunningThreads { get; set; } = 0;
26 |
27 | internal ulong MessagesScanned
28 | {
29 | get
30 | {
31 | lock (this)
32 | {
33 | return messageScanned;
34 | }
35 | }
36 | set
37 | {
38 | lock (this)
39 | {
40 | messageScanned = value;
41 | }
42 | }
43 | }
44 |
45 | internal ulong TotalAttachments
46 | {
47 | get
48 | {
49 | lock (this)
50 | {
51 | return totalAttachments;
52 | }
53 | }
54 | set
55 | {
56 | lock (this)
57 | {
58 | totalAttachments = value;
59 | }
60 | }
61 | }
62 |
63 | internal ulong AttachmentsDownloaded
64 | {
65 | get
66 | {
67 | lock (this)
68 | {
69 | return attachmentsDownloaded;
70 | }
71 | }
72 | set
73 | {
74 | lock (this)
75 | {
76 | attachmentsDownloaded = value;
77 | }
78 | }
79 | }
80 |
81 | public void Stop()
82 | {
83 | Run = false;
84 | }
85 |
86 | public void Start()
87 | {
88 | Run = true;
89 |
90 | Task.Run(async () =>
91 | {
92 | Logger.Info("Started JobScheduler...");
93 | while (Run)
94 | {
95 | try
96 | {
97 | Logger.Debug("Entering job list handler loop...");
98 | //foreach (var job in JobList)
99 | for (var i = JobList.Count - 1; i >= 0; i--)
100 | {
101 | var job = JobList[i];
102 | Logger.Debug($"Checking job {job}");
103 | var hasJob = false;
104 |
105 | Logger.Trace("Locking scheduler...");
106 | lock (this)
107 | {
108 | Logger.Trace("Checking if job is already performed...");
109 | hasJob = RunningJobs.ContainsKey(job.Id);
110 | }
111 | Logger.Trace("Unlocked scheduler.");
112 |
113 | if (!hasJob)
114 | {
115 | Logger.Debug("Job is not performed yet...Performing job...");
116 | var queue = new Queue();
117 |
118 | Logger.Trace("Locking scheduler...");
119 | lock (this)
120 | {
121 | Logger.Trace("Adding job to running jobs.");
122 | RunningJobs.Add(job.Id, queue);
123 | }
124 | Logger.Trace("Unlocked scheduler.");
125 |
126 | Logger.Trace("Issuing job message scan...");
127 | var messages = await job.Scan();
128 |
129 | if (messages == null)
130 | continue;
131 |
132 | Logger.Trace($"Adding {messages.Count} messages to queue...");
133 | foreach (var msg in messages)
134 | {
135 | queue.Enqueue(msg);
136 | }
137 | Logger.Trace($"Added {queue.Count} messages to queue.");
138 |
139 | if (messages.Count != queue.Count)
140 | Logger.Warn("Not all messages have been added into the queue.");
141 |
142 | var startedDownload = false;
143 |
144 | while (!startedDownload)
145 | {
146 | Logger.Debug("Entering loop to check thread availability");
147 | Logger.Trace("Locking scheduler...");
148 | lock (this)
149 | {
150 | Logger.Trace(
151 | $"Checking thread limit. Running: {RunningThreads}, Max: {Core.Settings.ThreadLimit}");
152 | if (RunningThreads >= Core.Settings.ThreadLimit)
153 | continue;
154 |
155 | RunningThreads++;
156 | startedDownload = true;
157 | }
158 | Logger.Trace("Unlocked scheduler.");
159 | }
160 |
161 | Logger.Trace("Start downloading job async.");
162 | Task.Run(() => WorkQueue(job.Id)); // do not await to work parallel
163 | }
164 | }
165 | }
166 | catch (Exception ex)
167 | {
168 | Logger.Error(ex.Message);
169 | }
170 | }
171 | });
172 | }
173 |
174 | private void WorkQueue(int jobId)
175 | {
176 | try
177 | {
178 | Logger.Debug("Beginning job download...");
179 | Logger.Trace("Finding job...");
180 | var job = (from j in JobList where j.Id == jobId select j).FirstOrDefault();
181 |
182 | if (job == null)
183 | {
184 | Logger.Warn($"Associating job not found! JobId: {jobId}");
185 | return;
186 | }
187 | Logger.Trace("Found job.");
188 |
189 | Queue queue;
190 | Logger.Trace("Locking scheduler...");
191 | lock (this)
192 | {
193 | Logger.Trace("Finiding queue...");
194 | if (!RunningJobs.TryGetValue(jobId, out queue))
195 | {
196 | Logger.Warn($"Queue for job {jobId} not found!");
197 | return;
198 | }
199 | Logger.Trace("Queue found.");
200 | }
201 | Logger.Trace("Unlocked scheduler.");
202 |
203 | Logger.Debug($"Messages to process for job {jobId}: {queue.Count}");
204 | while (queue.Count > 0)
205 | {
206 | Logger.Trace("Locking scheduler...");
207 | lock (this)
208 | {
209 | Logger.Trace("Checking if still a job...");
210 | if (!RunningJobs.ContainsKey(jobId))
211 | {
212 | Logger.Warn($"Queue for job {jobId} not found!");
213 | return;
214 | }
215 | Logger.Trace("Continue working...");
216 | }
217 | Logger.Trace("Unlocked scheduler.");
218 |
219 | Logger.Trace("Dequeueing message...");
220 | var message = queue.Dequeue();
221 |
222 | Logger.Debug($"Attachments for message {message.Id}: {message.Attachments.Count}");
223 | foreach (var a in message.Attachments)
224 | {
225 | try
226 | {
227 | var fileName = Path.Combine(Core.Settings.OperatingFolder, Core.Settings.FileNameScheme);
228 |
229 | Logger.Trace("Replacing filename placeholders...");
230 |
231 | var extensionRequired = !fileName.EndsWith("%name%");
232 |
233 | var serverName = "unknown";
234 |
235 | var socketTextChannel = message.Channel as SocketTextChannel;
236 | if (socketTextChannel != null)
237 | {
238 | serverName = socketTextChannel.Guild.Name;
239 | serverName = Path.GetInvalidFileNameChars().Aggregate(serverName, (current, c) => current.Replace(c, ' '));
240 | }
241 |
242 | var channelName = message.Channel.Name;
243 | channelName = Path.GetInvalidFileNameChars().Aggregate(channelName, (current, c) => current.Replace(c, ' '));
244 |
245 | fileName =
246 | fileName.Replace("%guild%", serverName)
247 | .Replace("%channel%", channelName)
248 | .Replace("%timestamp%", message.CreatedAt.UtcDateTime.ToUnixTimeStamp().ToString())
249 | .Replace("%name%", a.Filename)
250 | .Replace("%id%", a.Id.ToString());
251 |
252 | if (extensionRequired)
253 | fileName += Path.GetExtension(a.Filename);
254 |
255 | Logger.Trace($"Detemined file name: {fileName}.");
256 |
257 |
258 | if (File.Exists(fileName) && new FileInfo(fileName).Length == a.Size)
259 | {
260 | Logger.Debug($"{fileName} already existing with its estimated size. Skipping...");
261 | continue;
262 | }
263 |
264 | Logger.Trace("Determining directory...");
265 | var fileDirectory = Path.GetDirectoryName(fileName);
266 |
267 | if (!Directory.Exists(fileDirectory))
268 | {
269 | Logger.Info($"Directory {fileDirectory} does not exist. Creating directory...");
270 | Directory.CreateDirectory(fileDirectory);
271 | Logger.Debug("Created directory.");
272 | }
273 |
274 | var wc = new WebClient();
275 | Logger.Debug($"Starting downloading of attachment {a.Id}...");
276 |
277 | wc.DownloadFile(new Uri(a.Url), fileName);
278 | Logger.Debug($"Downloaded attachment {a.Id}.");
279 |
280 | Logger.Trace("Updating known timestamp for job...");
281 | job.KnownTimestamp = message.CreatedAt.UtcDateTime.ToUnixTimeStamp();
282 | job.Store();
283 | }
284 | finally
285 | {
286 | AttachmentsDownloaded++;
287 | }
288 | }
289 | }
290 | }
291 | finally
292 | {
293 | Logger.Trace("Locking scheduler...");
294 | lock (this)
295 | {
296 | Logger.Trace($"Removing {jobId} from running jobs...");
297 | RunningJobs.Remove(jobId);
298 | Logger.Trace("Decreasing thread count...");
299 | RunningThreads--;
300 | }
301 | Logger.Trace("Unlocked scheduler.");
302 | }
303 | }
304 | }
305 | }
306 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/Classes/Core.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Runtime;
6 | using System.Threading.Tasks;
7 | using System.Windows.Forms;
8 | using Discord;
9 | using Discord.WebSocket;
10 | using DML.AppCore.Classes;
11 | using DML.Application.Dialogs;
12 | using DML.Client;
13 | using LiteDB;
14 | using SharpRaven;
15 | using SharpRaven.Data;
16 | using SweetLib.Utils;
17 | using SweetLib.Utils.Logger;
18 | using SweetLib.Utils.Logger.Memory;
19 | using Logger = SweetLib.Utils.Logger.Logger;
20 |
21 | namespace DML.Application.Classes
22 | {
23 | public static class Core
24 | {
25 | //internal static DiscordSocketClient Client { get; set; }
26 | internal static LiteDatabase Database { get; set; }
27 | internal static Settings Settings { get; set; }
28 | internal static JobScheduler Scheduler { get; } = new JobScheduler();
29 | internal static RavenClient Raven = new RavenClient("https://0de964231669473e9098b9f6cc1d6278:79d9f2eb24034de199b2a37cc058e0f2@sentry.io/257114");
30 |
31 | internal static string DataDirectory
32 | => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Serraniel\Discord Media Loader");
33 |
34 | public static async Task Run(string[] paramStrings)
35 | {
36 | try
37 | {
38 | var splash = new FrmInternalSplash();
39 | splash.Show();
40 | System.Windows.Forms.Application.DoEvents();
41 |
42 | Logger.Info("Starting up Discord Media Loader application...");
43 | var useTrace = false;
44 | #if DEBUG
45 | //temporary add debug log level if debugging...
46 | Logger.GlobalLogLevel |= LogLevel.Debug;
47 | Logger.Debug("Running in debug configuartion. Added log level debug.");
48 | #endif
49 |
50 | Logger.Debug($"Parameters: {string.Join(", ", paramStrings)}");
51 | if (paramStrings.Contains("--trace") || paramStrings.Contains("-t"))
52 | {
53 | useTrace = true;
54 | Logger.GlobalLogLevel |= LogLevel.Trace;
55 | Logger.Trace("Trace parameter found. Added log level trace.");
56 | }
57 |
58 | Logger.Debug($"Application data folder: {DataDirectory}");
59 |
60 | Logger.Trace("Checking application data folder...");
61 | if (!Directory.Exists(DataDirectory))
62 | {
63 | Logger.Debug("Creating application data folder...");
64 | Directory.CreateDirectory(DataDirectory);
65 | Logger.Trace("Creating application data folder.");
66 | }
67 |
68 | Logger.Trace("Initializing profile optimizations...");
69 | ProfileOptimization.SetProfileRoot(System.Windows.Forms.Application.UserAppDataPath);
70 | ProfileOptimization.StartProfile("profile.opt");
71 | Logger.Trace("Finished initializing profile optimizations.");
72 |
73 | Logger.Trace("Trying to identify log memory...");
74 | var logMemory = Logger.DefaultLogMemory as ArchivableConsoleLogMemory;
75 | if (logMemory != null)
76 | {
77 | var logFolder = Path.Combine(DataDirectory, "logs");
78 | if (!Directory.Exists(logFolder))
79 | {
80 | Logger.Debug("Creating log folder...");
81 | Directory.CreateDirectory(logFolder);
82 | Logger.Trace("Created log folder.");
83 | }
84 |
85 |
86 | var logFile = Path.Combine(logFolder,
87 | SweetUtils.LegalizeFilename($"{DateTime.Now.ToString(CultureInfo.CurrentCulture.DateTimeFormat.SortableDateTimePattern)}.log.zip"));
88 |
89 | Logger.Trace($"Setting log file: {logFile}");
90 | logMemory.AutoArchiveOnDispose = true;
91 | logMemory.ArchiveFile = logFile;
92 | }
93 |
94 | Logger.Debug("Loading database...");
95 | Database = new LiteDatabase(Path.Combine(DataDirectory, "config.db"));
96 | Database.Log.Logging += (message) => Logger.Trace($"LiteDB: {message}");
97 |
98 | Logger.Debug("Loading settings collection out of database...");
99 | var settingsDB = Database.GetCollection("settings");
100 | if (settingsDB.Count() > 1)
101 | {
102 | Logger.Warn("Found more than one setting. Loading first one...");
103 | }
104 | Settings = settingsDB.FindAll().FirstOrDefault();
105 | if (Settings == null)
106 | {
107 | Logger.Warn("Settings not found. Creating new one. This is normal on first start up...");
108 | Settings = new Settings();
109 | Settings.Store();
110 | }
111 |
112 | Logger.Debug("Loading jobs collection out of database...");
113 | Scheduler.JobList = Job.RestoreJobs().ToList();
114 |
115 | Logger.Info("Loaded settings.");
116 | Logger.Debug(
117 | $"Settings: Email: {Settings.Email}, password: {(string.IsNullOrEmpty(Settings.Password) ? "not set" : "is set")}, use username: {Settings.UseUserData}, loginToken: {Settings.LoginToken}");
118 |
119 | Logger.Trace("Updating log level...");
120 | Logger.GlobalLogLevel = Settings.ApplicactionLogLevel;
121 | #if DEBUG
122 | //temporary add debug log level if debugging...
123 | Logger.GlobalLogLevel |= LogLevel.Debug;
124 | Logger.Debug("Running in debug configuartion. Added log level debug.");
125 | #endif
126 | if (useTrace)
127 | {
128 | Logger.GlobalLogLevel |= LogLevel.Trace;
129 | Logger.Trace("Creating application data folder.");
130 | }
131 |
132 | Logger.Debug("Creating discord client...");
133 |
134 | var config = new DiscordSocketConfig()
135 | {
136 | DefaultRetryMode = RetryMode.AlwaysRetry,
137 | };
138 |
139 | //Client = new DiscordSocketClient(config);
140 | DMLClient.Client.Log += (arg) =>
141 | {
142 | var logMessage = $"DiscordClient: {arg.Message}";
143 | switch (arg.Severity)
144 | {
145 | case LogSeverity.Verbose:
146 | Logger.Trace(logMessage);
147 | break;
148 | case LogSeverity.Debug:
149 | Logger.Trace(logMessage);
150 | break;
151 | case LogSeverity.Info:
152 | Logger.Info(logMessage);
153 | break;
154 | case LogSeverity.Warning:
155 | Logger.Warn(logMessage);
156 | break;
157 | case LogSeverity.Error:
158 | Logger.Error(logMessage);
159 | break;
160 | }
161 |
162 | return Task.CompletedTask;
163 | };
164 |
165 |
166 | Logger.Info("Trying to log into discord...");
167 | var abort = false;
168 |
169 | DMLClient.Client.Connected += Client_Connected;
170 |
171 | var loggedIn = false;
172 |
173 | while (!loggedIn)
174 | {
175 | if (!string.IsNullOrEmpty(Settings.LoginToken))
176 | {
177 | Logger.Debug("Trying to login with last known token...");
178 | loggedIn= await DMLClient.Login(Settings.LoginToken);
179 | }
180 |
181 | if (!loggedIn)
182 | {
183 | Logger.Debug("Showing dialog for username and password...");
184 | var loginDlg = new LoginDialog();
185 | loginDlg.ShowDialog();
186 | }
187 | }
188 |
189 | /*while ((Client.LoginState != LoginState.LoggedIn || Client.ConnectionState!=ConnectionState.Connected) && !abort)
190 | {
191 | Logger.Debug(Client.ConnectionState.ToString());
192 | Logger.Debug(Client.LoginState.ToString());
193 |
194 | Logger.Trace("Entering login loop.");
195 |
196 | try
197 | {
198 | if (Client.ConnectionState == ConnectionState.Connecting)
199 | continue;
200 |
201 | if (!string.IsNullOrEmpty(Settings.LoginToken))
202 | {
203 | Logger.Debug("Trying to login with last known token...");
204 | await Client.LoginAsync(TokenType.User, Settings.LoginToken);
205 | await Client.StartAsync();
206 | await Task.Delay(1000);
207 | }
208 |
209 | }
210 | catch (HttpException ex)
211 | {
212 | Logger.Warn($"Login seems to have failed or gone wrong: {ex.GetType().Name} - {ex.Message}");
213 | }
214 |
215 | if (Client.LoginState == LoginState.LoggedOut)
216 | {
217 | Settings.Password = string.Empty;
218 | Logger.Debug("Showing dialog for username and password...");
219 | var loginDlg = new LoginDialog();
220 | loginDlg.ShowDialog();
221 | Logger.Trace("Dialog closed.");
222 | }
223 | }*/
224 |
225 | Logger.Debug("Start checking for invalid jobs...");
226 |
227 | //Client
228 |
229 | while (DMLClient.Client.Guilds.Count == 0)
230 | {
231 | // wait until guilds are loaded
232 | }
233 |
234 | for (var i = Scheduler.JobList.Count - 1; i >= 0; i--)
235 | {
236 | var job = Scheduler.JobList[i];
237 | var isError = false;
238 | var guild = FindServerById(job.GuildId);
239 | if (guild == null)
240 | isError = true;
241 | else
242 | {
243 | var channel = FindChannelById(guild, job.ChannelId);
244 | if (channel == null)
245 | isError = true;
246 | }
247 |
248 | if (isError)
249 | {
250 | 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",
251 | MessageBoxButtons.OK, MessageBoxIcon.Warning);
252 |
253 | Scheduler.JobList.Remove(job);
254 | Scheduler.RunningJobs.Remove(job.Id);
255 | job.Stop();
256 | job.Delete();
257 | }
258 | }
259 |
260 | splash.Close();
261 |
262 | Logger.Info("Starting scheduler...");
263 | Scheduler.Start();
264 |
265 | System.Windows.Forms.Application.Run(new MainForm());
266 |
267 | Logger.Info("Stopping scheduler...");
268 | Scheduler.Stop();
269 | }
270 | catch (Exception ex)
271 | {
272 | Logger.Error($"{ex.Message} occured at: {ex.StackTrace}");
273 | if (MessageBox.Show($"An error occured while running Discord Media Loader:\n{ex.GetType().Name}: {ex.Message}\n\nDo you aggree to sending the error report to the creator of the tool?", "Discord Media Loader", MessageBoxButtons.YesNo) == DialogResult.Yes)
274 | Raven.Capture(new SentryEvent(ex));
275 | }
276 | }
277 |
278 | private static Task Client_Connected()
279 | {
280 | Logger.Debug("Connected");
281 | return Task.CompletedTask;
282 | }
283 |
284 | //TODO: this is thrid time we implement this.....this has to be fixed!!!
285 | private static SocketGuild FindServerById(ulong id)
286 | {
287 | Logger.Trace($"Trying to find server by Id: {id}");
288 | return (from s in DMLClient.Client.Guilds where s.Id == id select s).FirstOrDefault();
289 | }
290 |
291 | private static SocketTextChannel FindChannelById(SocketGuild server, ulong id)
292 | {
293 | Logger.Trace($"Trying to find channel in {server} by id: {id}");
294 | return (from c in server.TextChannels where c.Id == id select c).FirstOrDefault();
295 | }
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/Discord Media Loader/Discord Media Loader.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {EDC92554-DBC1-4F9C-9317-379A8BF441E8}
8 | WinExe
9 | Properties
10 | Discord_Media_Loader
11 | Discord Media Loader
12 | v4.6.1
13 | 512
14 | true
15 |
16 |
17 |
18 | AnyCPU
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | AnyCPU
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 | Serraniel-64x64.ico
38 |
39 |
40 |
41 |
42 |
43 |
44 | ..\packages\Discord.Net.Commands.1.0.2\lib\netstandard1.1\Discord.Net.Commands.dll
45 |
46 |
47 | ..\packages\Discord.Net.Core.1.0.2\lib\net45\Discord.Net.Core.dll
48 |
49 |
50 | ..\packages\Discord.Net.Rest.1.0.2\lib\net45\Discord.Net.Rest.dll
51 |
52 |
53 | ..\packages\Discord.Net.Rpc.1.0.2\lib\net45\Discord.Net.Rpc.dll
54 |
55 |
56 | ..\packages\Discord.Net.Webhook.1.0.2\lib\netstandard1.1\Discord.Net.Webhook.dll
57 |
58 |
59 | ..\packages\Discord.Net.WebSocket.1.0.2\lib\net45\Discord.Net.WebSocket.dll
60 |
61 |
62 | ..\packages\Microsoft.Extensions.DependencyInjection.1.1.1\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll
63 |
64 |
65 | ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.1\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll
66 |
67 |
68 | ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll
69 |
70 |
71 | ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
72 |
73 |
74 | ..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll
75 | True
76 |
77 |
78 | ..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll
79 | True
80 |
81 |
82 | ..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll
83 | True
84 |
85 |
86 | ..\packages\Octokit.0.24.1-alpha0001\lib\net45\Octokit.dll
87 | True
88 |
89 |
90 |
91 | ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll
92 |
93 |
94 | ..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll
95 | True
96 |
97 |
98 |
99 | ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll
100 |
101 |
102 |
103 | ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll
104 |
105 |
106 | ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll
107 |
108 |
109 | ..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll
110 |
111 |
112 | ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll
113 | True
114 |
115 |
116 |
117 | ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll
118 |
119 |
120 | ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll
121 |
122 |
123 | ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll
124 |
125 |
126 | ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll
127 |
128 |
129 | ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll
130 |
131 |
132 |
133 | ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
134 | True
135 |
136 |
137 | ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll
138 |
139 |
140 | ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
141 |
142 |
143 | ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
144 |
145 |
146 | ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll
158 |
159 |
160 |
161 |
162 | Form
163 |
164 |
165 | FrmDownload.cs
166 |
167 |
168 | Form
169 |
170 |
171 | FrmSplash.cs
172 |
173 |
174 |
175 |
176 |
177 |
178 | FrmDownload.cs
179 |
180 |
181 | FrmSplash.cs
182 |
183 |
184 | ResXFileCodeGenerator
185 | Resources.Designer.cs
186 | Designer
187 |
188 |
189 | True
190 | Resources.resx
191 | True
192 |
193 |
194 |
195 | SettingsSingleFileGenerator
196 | Settings.Designer.cs
197 |
198 |
199 | True
200 | Settings.settings
201 | True
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 | {c130de6a-3237-42b5-be9f-783d1cd104c6}
215 | DML.Application
216 |
217 |
218 |
219 |
226 |
--------------------------------------------------------------------------------
/Discord Media Loader.Application/DML.Application.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {C130DE6A-3237-42B5-BE9F-783D1CD104C6}
8 | Library
9 | Properties
10 | DML.Application
11 | Discord Media Loader.Application
12 | v4.6.1
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | ..\packages\Discord.Net.Commands.1.0.2\lib\netstandard1.1\Discord.Net.Commands.dll
35 |
36 |
37 | ..\packages\Discord.Net.Core.1.0.2\lib\net45\Discord.Net.Core.dll
38 |
39 |
40 | ..\packages\Discord.Net.Rest.1.0.2\lib\net45\Discord.Net.Rest.dll
41 |
42 |
43 | ..\packages\Discord.Net.Rpc.1.0.2\lib\net45\Discord.Net.Rpc.dll
44 |
45 |
46 | ..\packages\Discord.Net.Webhook.1.0.2\lib\netstandard1.1\Discord.Net.Webhook.dll
47 |
48 |
49 | ..\packages\Discord.Net.WebSocket.1.0.2\lib\net45\Discord.Net.WebSocket.dll
50 |
51 |
52 | ..\packages\LiteDB.3.1.0\lib\net35\LiteDB.dll
53 | True
54 |
55 |
56 | ..\packages\Microsoft.Extensions.DependencyInjection.1.1.1\lib\netstandard1.1\Microsoft.Extensions.DependencyInjection.dll
57 |
58 |
59 | ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.1.1\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll
60 |
61 |
62 | ..\packages\Microsoft.Win32.Primitives.4.3.0\lib\net46\Microsoft.Win32.Primitives.dll
63 |
64 |
65 | ..\packages\Microsoft.Win32.Registry.4.3.0\lib\net46\Microsoft.Win32.Registry.dll
66 |
67 |
68 | ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
69 |
70 |
71 | ..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.dll
72 | True
73 |
74 |
75 | ..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll
76 | True
77 |
78 |
79 | ..\packages\Nito.AsyncEx.3.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll
80 | True
81 |
82 |
83 | ..\packages\RestSharp.105.2.3\lib\net46\RestSharp.dll
84 | True
85 |
86 |
87 | ..\packages\SharpRaven.2.2.0\lib\net45\SharpRaven.dll
88 |
89 |
90 | ..\packages\SweetLib.0.2.1-alpha\lib\netstandard1.3\SweetLib.dll
91 |
92 |
93 |
94 | ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll
95 |
96 |
97 | ..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll
98 | True
99 |
100 |
101 |
102 | ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll
103 |
104 |
105 |
106 | ..\packages\System.Diagnostics.DiagnosticSource.4.3.0\lib\net46\System.Diagnostics.DiagnosticSource.dll
107 |
108 |
109 |
110 | ..\packages\System.Globalization.Calendars.4.3.0\lib\net46\System.Globalization.Calendars.dll
111 |
112 |
113 | ..\packages\System.Interactive.Async.3.1.1\lib\net46\System.Interactive.Async.dll
114 |
115 |
116 | ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll
117 | True
118 |
119 |
120 |
121 | ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll
122 |
123 |
124 | ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll
125 |
126 |
127 | ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll
128 |
129 |
130 | ..\packages\System.Net.Http.4.3.0\lib\net46\System.Net.Http.dll
131 |
132 |
133 | ..\packages\System.Net.Sockets.4.3.0\lib\net46\System.Net.Sockets.dll
134 |
135 |
136 |
137 | ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
138 | True
139 |
140 |
141 | ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll
142 |
143 |
144 | ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
145 |
146 |
147 | ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
148 |
149 |
150 | ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll
160 |
161 |
162 | ..\packages\WebSocket4Net.0.14.1\lib\net45\WebSocket4Net.dll
163 | True
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | Form
172 |
173 |
174 | LoginDialog.cs
175 |
176 |
177 | Form
178 |
179 |
180 | FrmInternalSplash.cs
181 |
182 |
183 |
184 | Form
185 |
186 |
187 | MainForm.cs
188 |
189 |
190 |
191 | True
192 | True
193 | Resources.resx
194 |
195 |
196 |
197 |
198 |
199 | LoginDialog.cs
200 |
201 |
202 | FrmInternalSplash.cs
203 |
204 |
205 | MainForm.cs
206 |
207 |
208 | ResXFileCodeGenerator
209 | Resources.Designer.cs
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | {045eb4a1-34e7-47e0-867e-e10c40505095}
219 | DML.Client
220 |
221 |
222 |
223 |
224 |
225 |
226 |
233 |
--------------------------------------------------------------------------------