├── -assets ├── QBDLX1.png ├── QBDLX2.png ├── QBDLX3.png ├── New-QBDLX.png ├── QBDLX-Icon.ico ├── New-QBDLX-Icon.ico └── New-QBDLX-Icon.png ├── QobuzDownloaderX ├── Resources │ ├── alert.png │ ├── email.png │ ├── info.png │ ├── lock.png │ ├── logout.png │ ├── search.png │ ├── download.png │ ├── log_out.png │ ├── new-post.png │ ├── password.png │ ├── settings.png │ ├── QBDLX-Icon.ico │ ├── QBDLX-Icon.png │ ├── qbdlx_icon.ico │ ├── qbdlx_new.png │ ├── download-from-cloud--v1.png │ ├── themes.json │ └── Languages │ │ ├── zh-cn.json │ │ ├── en.json │ │ ├── ru.json │ │ └── de.json ├── packages.config ├── Program.cs ├── Settings.cs ├── Helpers │ ├── FixMD5.cs │ ├── PaddingNumbers.cs │ ├── GetInfo.cs │ ├── TagFile.cs │ ├── Updating.cs │ ├── Download │ │ ├── DownloadFile.cs │ │ ├── DownloadTrack.cs │ │ └── DownloadAlbum.cs │ ├── RenameTemplates.cs │ └── SearchPanelHelper.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Settings.settings │ └── Resources.resx ├── Old │ ├── AboutForm.cs │ ├── searchForm.Designer.cs │ ├── searchForm.cs │ ├── AboutForm.Designer.cs │ └── LoginForm-v2.Designer.cs ├── App.config └── QobuzDownloaderX.csproj ├── QobuzDownloaderX.sln ├── README.md ├── .gitattributes └── .gitignore /-assets/QBDLX1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/-assets/QBDLX1.png -------------------------------------------------------------------------------- /-assets/QBDLX2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/-assets/QBDLX2.png -------------------------------------------------------------------------------- /-assets/QBDLX3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/-assets/QBDLX3.png -------------------------------------------------------------------------------- /-assets/New-QBDLX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/-assets/New-QBDLX.png -------------------------------------------------------------------------------- /-assets/QBDLX-Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/-assets/QBDLX-Icon.ico -------------------------------------------------------------------------------- /-assets/New-QBDLX-Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/-assets/New-QBDLX-Icon.ico -------------------------------------------------------------------------------- /-assets/New-QBDLX-Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/-assets/New-QBDLX-Icon.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/alert.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/email.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/info.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/lock.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/logout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/logout.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/search.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/download.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/log_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/log_out.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/new-post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/new-post.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/password.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/password.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/settings.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/QBDLX-Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/QBDLX-Icon.ico -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/QBDLX-Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/QBDLX-Icon.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/qbdlx_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/qbdlx_icon.ico -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/qbdlx_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/qbdlx_new.png -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/download-from-cloud--v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImAiiR/QobuzDownloaderX/HEAD/QobuzDownloaderX/Resources/download-from-cloud--v1.png -------------------------------------------------------------------------------- /QobuzDownloaderX/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace QobuzDownloaderX 5 | { 6 | static class Program 7 | { 8 | /// 9 | /// The main entry point for the application. 10 | /// 11 | [STAThread] 12 | static void Main() 13 | { 14 | Application.EnableVisualStyles(); 15 | Application.SetCompatibleTextRenderingDefault(false); 16 | Application.Run(new LoginForm()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /QobuzDownloaderX.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QobuzDownloaderX", "QobuzDownloaderX\QobuzDownloaderX.csproj", "{4CEB979A-035A-4D36-9607-D554BBECABE0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {4CEB979A-035A-4D36-9607-D554BBECABE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {4CEB979A-035A-4D36-9607-D554BBECABE0}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {4CEB979A-035A-4D36-9607-D554BBECABE0}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {4CEB979A-035A-4D36-9607-D554BBECABE0}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Settings.cs: -------------------------------------------------------------------------------- 1 | namespace QobuzDownloaderX.Properties { 2 | 3 | 4 | // This class allows you to handle specific events on the settings class: 5 | // The SettingChanging event is raised before a setting's value is changed. 6 | // The PropertyChanged event is raised after a setting's value is changed. 7 | // The SettingsLoaded event is raised after the setting values are loaded. 8 | // The SettingsSaving event is raised before the setting values are saved. 9 | internal sealed partial class Settings { 10 | 11 | public Settings() { 12 | // // To add event handlers for saving and changing settings, uncomment the lines below: 13 | // 14 | // this.SettingChanging += this.SettingChangingEventHandler; 15 | // 16 | // this.SettingsSaving += this.SettingsSavingEventHandler; 17 | // 18 | } 19 | 20 | private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { 21 | // Add code to handle the SettingChangingEvent event here. 22 | } 23 | 24 | private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { 25 | // Add code to handle the SettingsSaving event here. 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/FixMD5.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace QobuzDownloaderX 5 | { 6 | class FixMD5 7 | { 8 | GetInfo getInfo = new GetInfo(); 9 | 10 | public string outputResult { get; set; } 11 | 12 | public void fixMD5(string filePath, string flacEXEPath) 13 | { 14 | qbdlxForm._qbdlxForm.logger.Debug("Attempting to fix unset MD5..."); 15 | string driveLetter = filePath.Substring(0, 2); 16 | string cmdText = "/C echo Fixing unset MD5s... & \"" + flacEXEPath + "\" -f8 \"" + filePath + "\""; 17 | qbdlxForm._qbdlxForm.logger.Debug("Commands - " + cmdText); 18 | 19 | try 20 | { 21 | qbdlxForm._qbdlxForm.logger.Debug("Running cmd to run ffmpeg command to fix MD5"); 22 | Process cmd = new Process(); 23 | cmd.StartInfo.FileName = "cmd.exe"; 24 | cmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 25 | cmd.StartInfo.Arguments = cmdText; 26 | cmd.Start(); 27 | cmd.WaitForExit(); 28 | outputResult = "COMPLETE"; 29 | qbdlxForm._qbdlxForm.logger.Debug("MD5 has been fixed for file!"); 30 | } 31 | catch (Exception fixMD5ex) 32 | { 33 | outputResult = "FAILED"; 34 | qbdlxForm._qbdlxForm.logger.Error("Failed to fix MD5s, error below:\r\n" + fixMD5ex); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("QobuzDownloaderX")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("AiiR")] 12 | [assembly: AssemblyProduct("QobuzDownloaderX")] 13 | [assembly: AssemblyCopyright("Copyright © 2019")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4ceb979a-035a-4d36-9607-d554bbecabe0")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | 36 | [assembly: AssemblyVersion("1.0.1.2")] 37 | [assembly: AssemblyFileVersion("1.0.1.2")] 38 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/PaddingNumbers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using QopenAPI; 3 | 4 | namespace QobuzDownloaderX 5 | { 6 | class PaddingNumbers 7 | { 8 | public Album QoAlbum = new Album(); 9 | 10 | public int padTracks(Album QoAlbum) 11 | { 12 | var paddingLength = 2; 13 | 14 | // Prepare track number padding in filename. 15 | string paddingLog = Math.Floor(Math.Log10(QoAlbum.TracksCount) + 1).ToString(); 16 | 17 | switch (paddingLog) 18 | { 19 | case "1": 20 | return paddingLength = 2; 21 | default: 22 | return paddingLength = (int)Math.Floor(Math.Log10(QoAlbum.TracksCount) + 1); 23 | } 24 | } 25 | 26 | public int padPlaylistTracks(Playlist QoPlaylist) 27 | { 28 | var paddingLength = 2; 29 | 30 | // Prepare track number padding in filename. 31 | string paddingLog = Math.Floor(Math.Log10(QoPlaylist.TracksCount) + 1).ToString(); 32 | 33 | switch (paddingLog) 34 | { 35 | case "1": 36 | return paddingLength = 2; 37 | default: 38 | return paddingLength = (int)Math.Floor(Math.Log10(QoPlaylist.TracksCount) + 1); 39 | } 40 | } 41 | 42 | public int padDiscs(Album QoAlbum) 43 | { 44 | var paddingLength = 2; 45 | 46 | // Prepare track number padding in filename. 47 | string paddingLog = Math.Floor(Math.Log10(QoAlbum.MediaCount) + 1).ToString(); 48 | 49 | switch (paddingLog) 50 | { 51 | case "1": 52 | return paddingLength = 2; 53 | default: 54 | return paddingLength = (int)Math.Floor(Math.Log10(QoAlbum.MediaCount) + 1); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 |
Buy Me A Coffee
7 | 8 | 9 | Inspired by Qo-DL by Sorrow & Dash 10 | 11 |

12 | 13 | 14 | 15 |

16 | 17 | Telegram Group Chat - @QbdLX
18 | Telegram Updates Channel - @QobuzDownloaderX
19 | I'm never really active on Telegram, but these are the official channels. 20 | 21 | # About 22 | QobuzDownloaderX is a program for downloading music streams from the streaming platform Qobuz. 23 | 24 | You can not download anything with a free account. 25 | 26 | # Disclaimer & Legal 27 | I will not be responsible for how you use QBDLX (QobuzDownloaderX). 28 | 29 | This program DOES NOT include... 30 | - Code to bypass Qobuz's region restrictions. 31 | - Qobuz app IDs or secrets. 32 | 33 | QBDLX does not publish any of Qobuz's private secrets or app IDs. It contains regular expressions and other code to dynamically grab them from Qobuz's web player's *publicly available* JavaScript, which is not rehosted, but grabbed client side. Scraping public data is not a violation of the Computer Fraud and Abuse Act (USA) according to the Ninth Court of Appeals, [case # 17-16783](http://cdn.ca9.uscourts.gov/datastore/opinions/2019/09/09/17-16783.pdf) (see page 29). 34 | 35 | QBDLX uses the Qobuz API, but is not endorsed, certified or otherwise approved in any way by Qobuz. 36 | 37 | Qobuz brand and name is the registered trademark of its respective owner. 38 | 39 | QBDLX has no partnership, sponsorship or endorsement with Qobuz. 40 | 41 | By using QBDLX, you agree to the following: http://static.qobuz.com/apps/api/QobuzAPI-TermsofUse.pdf 42 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/themes.json: -------------------------------------------------------------------------------- 1 | { 2 | "themes": { 3 | "Dark": { 4 | "FormBackground": "#121212", 5 | "MainPanelBackground": "#121212", 6 | "HighlightedButtonBackground": "#121212", 7 | "SidePanelBackground": "#0d0d0d", 8 | "ButtonBackground": "#0d0d0d", 9 | "ClickedButtonBackground": "#191919", 10 | "TextBoxBackground": "#1e1e1e", 11 | "PlaceholderTextBoxText": "#3c3c3c", 12 | "TextBoxText": "#939393", 13 | "LabelText": "#939393", 14 | "ButtonText": "#939393", 15 | "NavBarIconColor": "#939393", 16 | "HiResLabelText": "#fac819", 17 | "LogoReplaceURL": null, 18 | "InvertLogo": false 19 | }, 20 | "Light": { 21 | "FormBackground": "#ededed", 22 | "MainPanelBackground": "#ededed", 23 | "HighlightedButtonBackground": "#ededed", 24 | "SidePanelBackground": "#f2f2f2", 25 | "ButtonBackground": "#f2f2f2", 26 | "ClickedButtonBackground": "#e6e6e6", 27 | "TextBoxBackground": "#e1e1e1", 28 | "PlaceholderTextBoxText": "#c3c3c3", 29 | "TextBoxText": "#6c6c6c", 30 | "LabelText": "#6c6c6c", 31 | "ButtonText": "#6c6c6c", 32 | "NavBarIconColor": "#6c6c6c", 33 | "HiResLabelText": "#cd9b00", 34 | "LogoReplaceURL": null, 35 | "InvertLogo": true 36 | }, 37 | "JAM.S": { 38 | "FormBackground": "#2b2f34", 39 | "MainPanelBackground": "#2b2f34", 40 | "HighlightedButtonBackground": "#bc243c", 41 | "SidePanelBackground": "#56343e", 42 | "ButtonBackground": "#56343e", 43 | "ClickedButtonBackground": "#734141", 44 | "TextBoxBackground": "#1e1e1e", 45 | "PlaceholderTextBoxText": "#3c3c3c", 46 | "TextBoxText": "#8c8c8c", 47 | "LabelText": "#8c8c8c", 48 | "ButtonText": "#FFFFFF", 49 | "NavBarIconColor": "#FFFFFF", 50 | "HiResLabelText": "#fac819", 51 | "LogoReplaceURL": "https://justanothermusic.site/styles/pach/logo.png", 52 | "InvertLogo": false 53 | }, 54 | "Qotify": { 55 | "FormBackground": "#121212", 56 | "MainPanelBackground": "#121212", 57 | "HighlightedButtonBackground": "#4ac776", 58 | "SidePanelBackground": "#1db954", 59 | "ButtonBackground": "#1db954", 60 | "ClickedButtonBackground": "#62d089", 61 | "TextBoxBackground": "#1e1e1e", 62 | "PlaceholderTextBoxText": "#3c3c3c", 63 | "TextBoxText": "#939393", 64 | "LabelText": "#FFFFFF", 65 | "ButtonText": "#FFFFFF", 66 | "NavBarIconColor": "#FFFFFF", 67 | "HiResLabelText": "#1db954", 68 | "LogoReplaceURL": null, 69 | "InvertLogo": false 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /QobuzDownloaderX/Old/AboutForm.cs: -------------------------------------------------------------------------------- 1 | using QobuzDownloaderX.Properties; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Data; 7 | using System.Drawing; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Forms; 12 | using System.Reflection; 13 | 14 | namespace QobuzDownloaderX 15 | { 16 | public partial class AboutForm : Form 17 | { 18 | public AboutForm() 19 | { 20 | InitializeComponent(); 21 | } 22 | 23 | public const int WM_NCLBUTTONDOWN = 0xA1; 24 | public const int HT_CAPTION = 0x2; 25 | 26 | [DllImportAttribute("user32.dll")] 27 | public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); 28 | [DllImportAttribute("user32.dll")] 29 | public static extern bool ReleaseCapture(); 30 | 31 | private void AboutForm_Load(object sender, EventArgs e) 32 | { 33 | // Get and display version number. 34 | verNumLabel2.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString(); 35 | } 36 | 37 | private void exitLabel_Click(object sender, EventArgs e) 38 | { 39 | this.Close(); 40 | } 41 | 42 | private void panel1_MouseMove(object sender, MouseEventArgs e) 43 | { 44 | if (e.Button == MouseButtons.Left) 45 | { 46 | ReleaseCapture(); 47 | SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); 48 | } 49 | } 50 | 51 | private void pictureBox1_MouseMove(object sender, MouseEventArgs e) 52 | { 53 | if (e.Button == MouseButtons.Left) 54 | { 55 | ReleaseCapture(); 56 | SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); 57 | } 58 | } 59 | 60 | private void verNumLabel2_MouseMove(object sender, MouseEventArgs e) 61 | { 62 | if (e.Button == MouseButtons.Left) 63 | { 64 | ReleaseCapture(); 65 | SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); 66 | } 67 | } 68 | 69 | private void AboutForm_MouseMove(object sender, MouseEventArgs e) 70 | { 71 | if (e.Button == MouseButtons.Left) 72 | { 73 | ReleaseCapture(); 74 | SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Old/searchForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace QobuzDownloaderX 2 | { 3 | partial class searchForm 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(searchForm)); 32 | this.searchInput = new System.Windows.Forms.TextBox(); 33 | this.searchButton = new System.Windows.Forms.Button(); 34 | this.resultTextbox = new System.Windows.Forms.TextBox(); 35 | this.SuspendLayout(); 36 | // 37 | // searchInput 38 | // 39 | this.searchInput.Location = new System.Drawing.Point(12, 14); 40 | this.searchInput.Name = "searchInput"; 41 | this.searchInput.Size = new System.Drawing.Size(612, 20); 42 | this.searchInput.TabIndex = 0; 43 | this.searchInput.KeyDown += new System.Windows.Forms.KeyEventHandler(this.searchInput_KeyDown); 44 | // 45 | // searchButton 46 | // 47 | this.searchButton.Location = new System.Drawing.Point(630, 12); 48 | this.searchButton.Name = "searchButton"; 49 | this.searchButton.Size = new System.Drawing.Size(98, 23); 50 | this.searchButton.TabIndex = 1; 51 | this.searchButton.Text = "Search"; 52 | this.searchButton.UseVisualStyleBackColor = true; 53 | this.searchButton.Click += new System.EventHandler(this.searchButton_Click); 54 | // 55 | // resultTextbox 56 | // 57 | this.resultTextbox.Location = new System.Drawing.Point(12, 40); 58 | this.resultTextbox.Multiline = true; 59 | this.resultTextbox.Name = "resultTextbox"; 60 | this.resultTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both; 61 | this.resultTextbox.Size = new System.Drawing.Size(716, 513); 62 | this.resultTextbox.TabIndex = 2; 63 | this.resultTextbox.WordWrap = false; 64 | // 65 | // searchForm 66 | // 67 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 68 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 69 | this.ClientSize = new System.Drawing.Size(740, 565); 70 | this.Controls.Add(this.resultTextbox); 71 | this.Controls.Add(this.searchButton); 72 | this.Controls.Add(this.searchInput); 73 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 74 | this.Name = "searchForm"; 75 | this.Text = "QobuzDLX | Search"; 76 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.searchForm_FormClosed); 77 | this.Load += new System.EventHandler(this.searchForm_Load); 78 | this.ResumeLayout(false); 79 | this.PerformLayout(); 80 | 81 | } 82 | 83 | #endregion 84 | 85 | private System.Windows.Forms.TextBox searchInput; 86 | private System.Windows.Forms.Button searchButton; 87 | private System.Windows.Forms.TextBox resultTextbox; 88 | } 89 | } -------------------------------------------------------------------------------- /QobuzDownloaderX/Old/searchForm.cs: -------------------------------------------------------------------------------- 1 | using QobuzDownloaderX.Properties; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Data; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Text; 10 | using System.IO; 11 | using System.Diagnostics; 12 | using System.Runtime.InteropServices; 13 | using System.Threading.Tasks; 14 | using System.Windows.Forms; 15 | using System.Security.Cryptography; 16 | using System.Reflection; 17 | using System.Text.RegularExpressions; 18 | using System.Net.Http; 19 | using System.Net.Http.Headers; 20 | using System.Drawing.Imaging; 21 | using TagLib.Flac; 22 | using System.Globalization; 23 | using System.Threading; 24 | 25 | namespace QobuzDownloaderX 26 | { 27 | public partial class searchForm : Form 28 | { 29 | public searchForm() 30 | { 31 | InitializeComponent(); 32 | } 33 | 34 | public String appid { get; set; } 35 | public String userAuth { get; set; } 36 | 37 | static string DecodeEncodedNonAsciiCharacters(string value) 38 | { 39 | return Regex.Replace( 40 | value, 41 | @"\\u(?[a-zA-Z0-9]{4})", 42 | m => { 43 | return ((char)int.Parse(m.Groups["Value"].Value, NumberStyles.HexNumber)).ToString(); 44 | }); 45 | } 46 | 47 | private void searchForm_Load(object sender, EventArgs e) 48 | { 49 | 50 | } 51 | 52 | private void searchButton_Click(object sender, EventArgs e) 53 | { 54 | #region Do Search 55 | WebRequest searchwr = WebRequest.Create("http://www.qobuz.com/api.json/0.2/album/search?app_id=" + appid + "&query=" + searchInput.Text + "&limit=15&user_auth_token=" + userAuth); 56 | 57 | WebResponse searchws = searchwr.GetResponse(); 58 | StreamReader searchsr = new StreamReader(searchws.GetResponseStream()); 59 | 60 | string searchRequest = searchsr.ReadToEnd(); 61 | 62 | // Remove backslashes from the stream URL to have a proper URL. 63 | string resultpattern = "\"maximum_bit_depth\":(?.*?),(?:.*?),\"artist\":(?:.*?)\"name\":\"(?.*?)\",(?:.*?)\"title\":\"(?.*?)\"(?:.*?),\"maximum_channel_count\":(?:.*?),\"id\":\"(?.*?)\",\"maximum_sampling_rate\":(?.*?),\""; 64 | string resultinput = searchRequest; 65 | RegexOptions resultoptions = RegexOptions.Multiline; 66 | 67 | resultTextbox.Invoke(new Action(() => resultTextbox.Text = String.Empty)); 68 | 69 | foreach (Match mResult in Regex.Matches(resultinput, resultpattern, resultoptions)) 70 | { 71 | resultTextbox.Invoke(new Action(() => resultTextbox.AppendText(string.Format("{0} - {1} [{2}bit/{3}kHz]\r\nhttps://play.qobuz.com/album/{4}\r\n\r\n", mResult.Groups["albumArtist"].Value, mResult.Groups["albumTitle"].Value, mResult.Groups["bitDepth"].Value, mResult.Groups["sampleRate"].Value, mResult.Groups["albumID"].Value)))); 72 | 73 | // For converting unicode characters to ASCII 74 | string unicodeResult = resultTextbox.Text; 75 | string decodedResult = DecodeEncodedNonAsciiCharacters(unicodeResult); 76 | 77 | var fixedText = decodedResult.Replace(@"\/", "/"); 78 | resultTextbox.Invoke(new Action(() => resultTextbox.Text = fixedText)); 79 | } 80 | 81 | #endregion 82 | } 83 | 84 | private void searchForm_FormClosed(object sender, FormClosedEventArgs e) 85 | { 86 | 87 | } 88 | 89 | private void searchInput_KeyDown(object sender, KeyEventArgs e) 90 | { 91 | if (e.KeyCode == Keys.Enter) 92 | { 93 | searchButton_Click(sender, e); 94 | e.Handled = true; 95 | e.SuppressKeyPress = true; 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /.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 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 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 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | Old/ 184 | ClientBin/ 185 | [Ss]tyle[Cc]op.* 186 | ~$* 187 | *~ 188 | *.dbmdl 189 | *.dbproj.schemaview 190 | *.pfx 191 | *.publishsettings 192 | node_modules/ 193 | orleans.codegen.cs 194 | 195 | # RIA/Silverlight projects 196 | Generated_Code/ 197 | 198 | # Backup & report files from converting an old project file 199 | # to a newer Visual Studio version. Backup files are not needed, 200 | # because we have git ;-) 201 | _UpgradeReport_Files/ 202 | Backup*/ 203 | UpgradeLog*.XML 204 | UpgradeLog*.htm 205 | 206 | # SQL Server files 207 | *.mdf 208 | *.ldf 209 | 210 | # Business Intelligence projects 211 | *.rdl.data 212 | *.bim.layout 213 | *.bim_*.settings 214 | 215 | # Microsoft Fakes 216 | FakesAssemblies/ 217 | 218 | # GhostDoc plugin setting file 219 | *.GhostDoc.xml 220 | 221 | # Node.js Tools for Visual Studio 222 | .ntvs_analysis.dat 223 | 224 | # Visual Studio 6 build log 225 | *.plg 226 | 227 | # Visual Studio 6 workspace options file 228 | *.opt 229 | 230 | # Visual Studio LightSwitch build output 231 | **/*.HTMLClient/GeneratedArtifacts 232 | **/*.DesktopClient/GeneratedArtifacts 233 | **/*.DesktopClient/ModelManifest.xml 234 | **/*.Server/GeneratedArtifacts 235 | **/*.Server/ModelManifest.xml 236 | _Pvt_Extensions 237 | 238 | # LightSwitch generated files 239 | GeneratedArtifacts/ 240 | ModelManifest.xml 241 | 242 | # Paket dependency manager 243 | .paket/paket.exe 244 | 245 | # FAKE - F# Make 246 | .fake/ 247 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/Languages/zh-cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "TranslationCredit": "Mxqing", 3 | "TranslationUpdatedOn": "November 21, 2024, 01:15AM GMT+8", 4 | "TranslationFont": "Microsoft YaHei UI", 5 | "_SECTION1_": "=================== 主表单按钮 ===================", 6 | "additionalSettingsButton": "附加设置", 7 | "aboutButton": "关于", 8 | "closeAdditionalButton": "返回设置", 9 | "downloadButton": "获取", 10 | "downloaderButton": "下载器", 11 | "logoutButton": "退出登录", 12 | "openFolderButton": "打开文件夹", 13 | "qualitySelectButton": "音质选择器", 14 | "saveTemplatesButton": "保存", 15 | "searchButton": "搜索", 16 | "searchAlbumsButton": "搜专辑", 17 | "searchTracksButton": "搜曲目", 18 | "selectFolderButton": "选择文件夹", 19 | "settingsButton": "设置", 20 | "_SECTION2_": "=================== 主表单标签 ===================", 21 | "advancedOptionsLabel": "高级选项", 22 | "albumTemplateLabel": "专辑模板", 23 | "artistTemplateLabel": "艺术家模板", 24 | "commentLabel": "自定义备注", 25 | "downloadFolderLabel": "下载文件夹", 26 | "downloadOptionsLabel": "下载选项", 27 | "embeddedArtLabel": "嵌入的封面尺寸", 28 | "extraSettingsLabel": "附加设置", 29 | "languageLabel": "当前语言", 30 | "playlistTemplateLabel": "播放列表模板", 31 | "favoritesTemplateLabel": "收藏模板", 32 | "savedArtLabel": "保存的封面尺寸", 33 | "searchingLabel": "搜索中...", 34 | "taggingOptionsLabel": "标签选项", 35 | "templatesLabel": "模板", 36 | "templatesListLabel": "模板列表", 37 | "themeLabel": "当前主题", 38 | "themeSectionLabel": "主题选项", 39 | "trackTemplateLabel": "曲目模板", 40 | "userInfoLabel": "用户信息", 41 | "welcomeLabel": "欢迎\r\n{username}", 42 | "_SECTION3_": "=================== 主表单复选框 ===================", 43 | "albumArtistCheckbox": "专辑艺术家", 44 | "albumTitleCheckbox": "专辑标题", 45 | "trackArtistCheckbox": "曲目艺术家", 46 | "trackTitleCheckbox": "曲目标题", 47 | "releaseDateCheckbox": "发行日期", 48 | "releaseTypeCheckbox": "发行类型", 49 | "genreCheckbox": "流派", 50 | "trackNumberCheckbox": "曲目编号", 51 | "trackTotalCheckbox": "总曲目数", 52 | "discNumberCheckbox": "光盘编号", 53 | "discTotalCheckbox": "总光盘数", 54 | "composerCheckbox": "作曲家", 55 | "explicitCheckbox": "Explicit 提示", 56 | "coverArtCheckbox": "专辑封面", 57 | "copyrightCheckbox": "版权", 58 | "labelCheckbox": "唱片公司", 59 | "upcCheckbox": "UPC/条形码", 60 | "isrcCheckbox": "ISRC", 61 | "streamableCheckbox": "歌曲流媒体可播性检查", 62 | "fixMD5sCheckbox": "自动修复生成 MD5 校验和(必须在 PATH 变量中包含 FLAC)", 63 | "downloadSpeedCheckbox": "显示下载速度", 64 | "_SECTION4_": "=================== 主表单占位符 ===================", 65 | "albumLabelPlaceholder": "欢迎使用 QBDLX!", 66 | "artistLabelPlaceholder": "输入你的 Qobuz URL 并点击获取!", 67 | "infoLabelPlaceholder": "发行", 68 | "inputTextboxPlaceholder": "粘贴一个 Qobuz URL...", 69 | "searchTextboxPlaceholder": "输入你的搜索内容...", 70 | "downloadFolderPlaceholder": "未选择文件夹", 71 | "userInfoTextboxPlaceholder": "用户 ID = {user_id}\r\n邮箱 = {user_email}\r\n国家 = {user_country}\r\n订阅 = {user_subscription}\r\n到期 = {user_subscription_expiration}", 72 | "downloadOutputWelcome": "欢迎 {user_display_name}!", 73 | "downloadOutputExpired": "你的订阅已过期,下载将仅限于 30 秒的片段!", 74 | "downloadOutputPath": "下载路径", 75 | "downloadOutputNoPath": "未设置路径! 记得选择一个文件夹!", 76 | "downloadOutputAPIError": "Qobuz API 错误。可能是该发行版在当前账户地区不可用?", 77 | "downloadOutputNotImplemented": "尚未实现或 URL 无法理解。是否有拼写错误?", 78 | "downloadOutputCheckLink": "正在检查链接...", 79 | "downloadOutputTrNotStream": "曲目{TrackNumber}不支持流媒体播放。跳过。", 80 | "downloadOutputAlNotStream": "发行版不支持流媒体播放。", 81 | "downloadOutputGoodyFound": "找到歌曲,正在下载...", 82 | "downloadOutputGoodyExists": "歌曲文件已存在", 83 | "downloadOutputGoodyNoURL": "未找到歌曲的下载 URL,跳过", 84 | "downloadOutputFileExists": "曲目{TrackNumber}的文件已存在,跳过。", 85 | "downloadOutputDownloading": "正在下载", 86 | "downloadOutputDone": "完成", 87 | "downloadOutputCompleted": "下载完成", 88 | "progressLabelInactive": "无下载任务", 89 | "progressLabelActive": "下载进度", 90 | "_SECTION5_": "=================== 登录表单按钮 ===================", 91 | "closeAboutButton": "关闭", 92 | "customSaveButton": "保存", 93 | "exitButton": "退出", 94 | "loginButton": "登录", 95 | "_SECTION6_": "=================== 登录表单标签 ===================", 96 | "appidLabel": "App ID", 97 | "appSecretLabel": "App Secret", 98 | "customLabel": "使用自定义 App ID + Secret", 99 | "_SECTION7_": "=================== 登录表单文本框 ===================", 100 | "customInfoTextbox": "如果想自动获取值,请留空!", 101 | "aboutTextbox": "版本 - {version}\r\n由 AiiR 创建\r\n\r\n受 Qo-DL 启发\r\n(由 Sorrow 和 DashLt 创建)\r\n\r\n感谢 Github 和 Telegram 上的用户提供的错误报告和想法! 还要特别感谢 DJDoubleD,在我忙碌的时候保持了原版的运行!", 102 | "_SECTION8_": "=================== 登录表单占位符 ===================", 103 | "emailPlaceholder": "请输入邮箱", 104 | "passwordPlaceholder": "请输入密码", 105 | "tokenPlaceholder": "Token", 106 | "altLoginLabelToken": "使用 Token 登录", 107 | "altLoginLabelEmail": "使用邮箱和密码登录", 108 | "loginTextWaiting": "等待登录...", 109 | "loginTextStart": "正在登录...", 110 | "loginTextError": "登录失败,已保存错误日志", 111 | "loginTextNoEmail": "输入中没有邮箱", 112 | "loginTextNoPassword": "输入中没有密码/Token", 113 | "updateNotification": "QBDLX 的新版本可用!\r\n\r\n已安装版本 - {currentVersion}\r\n最新版本 - {newVersion}\r\n\r\n更新日志如下\r\n==============\r\n{changelog}\r\n==============\r\n\r\n你是否要更新?", 114 | "updateNotificationTitle": "QBDLX | 有可用更新" 115 | } 116 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/Languages/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "TranslationCredit": "AiiR", 3 | "TranslationUpdatedOn": "November 17, 2024, 12:38PM EST", 4 | "TranslationFont": "Nirmala UI", 5 | "_SECTION1_": "=================== MAIN FORM BUTTONS ===================", 6 | "additionalSettingsButton": "Additional Settings", 7 | "aboutButton": "ABOUT", 8 | "closeAdditionalButton": "Back to Settings", 9 | "downloadButton": "GET", 10 | "downloaderButton": "DOWNLOADER", 11 | "logoutButton": "LOGOUT", 12 | "openFolderButton": "Open Folder", 13 | "qualitySelectButton": "Quality Selector", 14 | "saveTemplatesButton": "Save", 15 | "searchButton": "SEARCH", 16 | "searchAlbumsButton": "RELEASES", 17 | "searchTracksButton": "TRACKS", 18 | "selectFolderButton": "Select Folder", 19 | "settingsButton": "SETTINGS", 20 | "_SECTION2_": "=================== MAIN FORM LABELS ===================", 21 | "advancedOptionsLabel": "ADVANCED OPTIONS", 22 | "albumTemplateLabel": "ALBUM TEMPLATE", 23 | "artistTemplateLabel": "ARTIST TEMPLATE", 24 | "commentLabel": "Custom Comment", 25 | "downloadFolderLabel": "DOWNLOAD FOLDER", 26 | "downloadOptionsLabel": "DOWNLOAD OPTIONS", 27 | "embeddedArtLabel": "Embedded Artwork Size", 28 | "extraSettingsLabel": "ADDITIONAL SETTINGS", 29 | "languageLabel": "Current Language", 30 | "playlistTemplateLabel": "PLAYLIST TEMPLATE", 31 | "favoritesTemplateLabel": "FAVORITES TEMPLATE", 32 | "savedArtLabel": "Saved Artwork Size", 33 | "searchingLabel": "Searching...", 34 | "taggingOptionsLabel": "TAGGING OPTIONS", 35 | "templatesLabel": "TEMPLATES", 36 | "templatesListLabel": "TEMPLATES LIST", 37 | "themeLabel": "Current Theme", 38 | "themeSectionLabel": "THEMING OPTIONS", 39 | "trackTemplateLabel": "TRACK TEMPLATE", 40 | "userInfoLabel": "USER INFO", 41 | "welcomeLabel": "Welcome\r\n{username}", 42 | "_SECTION3_": "=================== MAIN FORM CHECKBOXES ===================", 43 | "albumArtistCheckbox": "Album Artist", 44 | "albumTitleCheckbox": "Album Title", 45 | "trackArtistCheckbox": "Track Artist", 46 | "trackTitleCheckbox": "Track Title", 47 | "releaseDateCheckbox": "Release Date", 48 | "releaseTypeCheckbox": "Release Type", 49 | "genreCheckbox": "Genre", 50 | "trackNumberCheckbox": "Track Number", 51 | "trackTotalCheckbox": "Total Tracks", 52 | "discNumberCheckbox": "Disc Number", 53 | "discTotalCheckbox": "Total Discs", 54 | "composerCheckbox": "Composer", 55 | "explicitCheckbox": "Explicit Advisory", 56 | "coverArtCheckbox": "Cover Art", 57 | "copyrightCheckbox": "Copyright", 58 | "labelCheckbox": "Label", 59 | "upcCheckbox": "UPC / Barcode", 60 | "isrcCheckbox": "ISRC", 61 | "streamableCheckbox": "Streamable Check", 62 | "fixMD5sCheckbox": "Auto-Fix Unset MD5s (must have FLAC in PATH variables)", 63 | "downloadSpeedCheckbox": "Print Download Speed", 64 | "_SECTION4_": "=================== MAIN FORM PLACEHOLDERS ===================", 65 | "albumLabelPlaceholder": "Welcome to QBDLX!", 66 | "artistLabelPlaceholder": "Input your Qobuz link and hit GET!", 67 | "infoLabelPlaceholder": "Released", 68 | "inputTextboxPlaceholder": "Paste a Qobuz URL...", 69 | "searchTextboxPlaceholder": "Input your search...", 70 | "downloadFolderPlaceholder": "No folder selected", 71 | "userInfoTextboxPlaceholder": "User ID = {user_id}\r\nE-mail = {user_email}\r\nCountry = {user_country}\r\nSubscription = {user_subscription}\r\nExpires = {user_subscription_expiration}", 72 | "downloadOutputWelcome": "Welcome {user_display_name}!", 73 | "downloadOutputExpired": "YOUR SUBSCRIPTION HAS EXPIRED, DOWNLOADS WILL BE LIMITED TO 30 SECOND SNIPPETS!", 74 | "downloadOutputPath": "Download Path", 75 | "downloadOutputNoPath": "No path has been set! Remember to Choose a Folder!", 76 | "downloadOutputAPIError": "Qobuz API error. Maybe release isn't available in this account region?", 77 | "downloadOutputNotImplemented": "Not implemented yet or the URL is not understood. Is there a typo?", 78 | "downloadOutputCheckLink": "Checking Link...", 79 | "downloadOutputTrNotStream": "Track {TrackNumber} is not available for streaming. Skipping.", 80 | "downloadOutputAlNotStream": "Release is not available for streaming.", 81 | "downloadOutputGoodyFound": "Goody found, downloading...", 82 | "downloadOutputGoodyExists": "File for goody already exists", 83 | "downloadOutputGoodyNoURL": "No download URL found for goody, skipping", 84 | "downloadOutputFileExists": "File for track {TrackNumber} already exists, skipping.", 85 | "downloadOutputDownloading": "Downloading", 86 | "downloadOutputDone": "DONE", 87 | "downloadOutputCompleted": "DOWNLOAD COMPLETE", 88 | "progressLabelInactive": "No download active", 89 | "progressLabelActive": "Download progress", 90 | "_SECTION5_": "=================== LOGIN FORM BUTTONS ===================", 91 | "closeAboutButton": "CLOSE", 92 | "customSaveButton": "SAVE", 93 | "exitButton": "EXIT", 94 | "loginButton": "LOGIN", 95 | "_SECTION6_": "=================== LOGIN FORM LABELS ===================", 96 | "appidLabel": "App ID", 97 | "appSecretLabel": "App Secret", 98 | "customLabel": "USE CUSTOM APP ID + SECRET", 99 | "_SECTION7_": "=================== LOGIN FORM TEXTBOXES ===================", 100 | "customInfoTextbox": "Leave values blank if you would like to automatically grab the values!", 101 | "aboutTextbox": "Version - {version}\r\nCreated by AiiR\r\n\r\nInspired By Qo-DL\r\n(Created by Sorrow and DashLt)\r\n\r\nThanks to the users on Github and Telegram for offering bug reports and ideas! And huge shoutout to DJDoubleD for keeping the original running since I've been busy!", 102 | "_SECTION8_": "=================== LOGIN FORM PLACEHOLDERS ===================", 103 | "emailPlaceholder": "e-mail", 104 | "passwordPlaceholder": "password", 105 | "tokenPlaceholder": "token", 106 | "altLoginLabelToken": "LOGIN WITH TOKEN", 107 | "altLoginLabelEmail": "LOGIN WITH E-MAIL AND PASSWORD", 108 | "loginTextWaiting": "waiting for login...", 109 | "loginTextStart": "logging in...", 110 | "loginTextError": "login failed, error log saved", 111 | "loginTextNoEmail": "no e-mail in input", 112 | "loginTextNoPassword": "no password/token in input", 113 | "updateNotification": "New version of QBDLX is available!\r\n\r\nInstalled version - {currentVersion}\r\nLatest version - {newVersion}\r\n\r\nChangelog Below\r\n==============\r\n{changelog}\r\n==============\r\n\r\nWould you like to update?", 114 | "updateNotificationTitle": "QBDLX | Update Available" 115 | } 116 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/GetInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using QopenAPI; 3 | 4 | namespace QobuzDownloaderX 5 | { 6 | class GetInfo 7 | { 8 | public Service QoService = new Service(); 9 | public User QoUser = new User(); 10 | public Artist QoArtist = new Artist(); 11 | public Album QoAlbum = new Album(); 12 | public Item QoItem = new Item(); 13 | public Favorites QoFavorites = new Favorites(); 14 | public Playlist QoPlaylist = new Playlist(); 15 | public QopenAPI.Label QoLabel = new QopenAPI.Label(); 16 | 17 | public string outputText { get; set; } 18 | 19 | public void updateDownloadOutput(string text) 20 | { 21 | if (outputText == "Test String" | outputText == null) 22 | { 23 | qbdlxForm._qbdlxForm.update(null); 24 | outputText = qbdlxForm._qbdlxForm.downloadOutput.Text; 25 | qbdlxForm._qbdlxForm.update(text); 26 | outputText = qbdlxForm._qbdlxForm.downloadOutput.Text; 27 | } 28 | else if (text == null) 29 | { 30 | qbdlxForm._qbdlxForm.update(null); 31 | outputText = qbdlxForm._qbdlxForm.downloadOutput.Text; 32 | } 33 | else 34 | { 35 | qbdlxForm._qbdlxForm.update(outputText + text); 36 | outputText = qbdlxForm._qbdlxForm.downloadOutput.Text; 37 | } 38 | 39 | } 40 | 41 | public Artist getArtistInfo(string app_id, string artist_id, string user_auth_token) 42 | { 43 | try 44 | { 45 | // Grab artist info with auth 46 | outputText = null; 47 | qbdlxForm._qbdlxForm.logger.Debug("Getting artist Info..."); 48 | QoArtist = QoService.ArtistGetWithAuth(app_id, artist_id, user_auth_token); 49 | return QoArtist; 50 | } 51 | catch (Exception getArtistInfoEx) 52 | { 53 | updateDownloadOutput("\r\n" + getArtistInfoEx.ToString()); 54 | qbdlxForm._qbdlxForm.logger.Error("Failed to get artist info, error below:\r\n" + getArtistInfoEx); 55 | return null; 56 | } 57 | } 58 | 59 | public QopenAPI.Label getLabelInfo(string app_id, string label_id, string user_auth_token) 60 | { 61 | try 62 | { 63 | // Grab label info with auth 64 | outputText = null; 65 | qbdlxForm._qbdlxForm.logger.Debug("Getting label Info..."); 66 | QoLabel = QoService.LabelGetWithAuth(app_id, label_id, "albums", 500, 0, user_auth_token); 67 | return QoLabel; 68 | } 69 | catch (Exception getLabelInfoEx) 70 | { 71 | updateDownloadOutput("\r\n" + getLabelInfoEx.ToString()); 72 | qbdlxForm._qbdlxForm.logger.Error("Failed to get label info, error below:\r\n" + getLabelInfoEx); 73 | return null; 74 | } 75 | } 76 | 77 | public Favorites getFavoritesInfo(string app_id, string user_id, string type, string user_auth_token) 78 | { 79 | try 80 | { 81 | // Grab favorites info with auth 82 | outputText = null; 83 | qbdlxForm._qbdlxForm.logger.Debug("Getting favorites Info..."); 84 | QoFavorites = QoService.FavoriteGetUserFavoritesWithAuth(app_id, user_id, type, 500, 0, user_auth_token); 85 | return QoFavorites; 86 | } 87 | catch (Exception getFavoritesInfoEx) 88 | { 89 | updateDownloadOutput("\r\n" + getFavoritesInfoEx.ToString()); 90 | qbdlxForm._qbdlxForm.logger.Error("Failed to get favorites info, error below:\r\n" + getFavoritesInfoEx); 91 | return null; 92 | } 93 | } 94 | 95 | public Item getTrackInfoLabels(string app_id, string track_id, string user_auth_token) 96 | { 97 | try 98 | { 99 | // Grab track info with auth 100 | outputText = null; 101 | qbdlxForm._qbdlxForm.logger.Debug("Getting track Info..."); 102 | QoItem = QoService.TrackGetWithAuth(app_id, track_id, user_auth_token); 103 | string album_id = QoItem.Album.Id; 104 | QoAlbum = QoService.AlbumGetWithAuth(app_id, album_id, user_auth_token); 105 | return QoItem; 106 | } 107 | catch (Exception getTrackInfoLabelsEx) 108 | { 109 | updateDownloadOutput("\r\n" + getTrackInfoLabelsEx.ToString()); 110 | qbdlxForm._qbdlxForm.logger.Error("Failed to get track info, error below:\r\n" + getTrackInfoLabelsEx); 111 | return null; 112 | } 113 | } 114 | 115 | public Album getAlbumInfoLabels(string app_id, string album_id, string user_auth_token) 116 | { 117 | try 118 | { 119 | // Grab album info with auth 120 | outputText = null; 121 | qbdlxForm._qbdlxForm.logger.Debug("Getting album Info..."); 122 | QoAlbum = QoService.AlbumGetWithAuth(app_id, album_id, user_auth_token); 123 | return QoAlbum; 124 | } 125 | catch (Exception getAlbumInfoLabelsEx) 126 | { 127 | updateDownloadOutput("\r\n" + getAlbumInfoLabelsEx.ToString()); 128 | qbdlxForm._qbdlxForm.logger.Error("Failed to get album info, error below:\r\n" + getAlbumInfoLabelsEx); 129 | return null; 130 | } 131 | } 132 | 133 | public Playlist getPlaylistInfoLabels(string app_id, string playlist_id, string user_auth_token) 134 | { 135 | try 136 | { 137 | // Grab playlist info with auth 138 | outputText = null; 139 | qbdlxForm._qbdlxForm.logger.Debug("Getting playlist Info..."); 140 | QoPlaylist = QoService.PlaylistGetWithAuth(app_id, playlist_id, "tracks", 500, 0, user_auth_token); 141 | return QoPlaylist; 142 | } 143 | catch (Exception getPlaylistInfoLabelsEx) 144 | { 145 | updateDownloadOutput("\r\n" + getPlaylistInfoLabelsEx.ToString()); 146 | qbdlxForm._qbdlxForm.logger.Error("Failed to get playlist info, error below:\r\n" + getPlaylistInfoLabelsEx); 147 | return null; 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/Languages/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "TranslationCredit": "Kuzmich55", 3 | "TranslationUpdatedOn": "December 03, 2024, 11:01AM MSK", 4 | "TranslationFont": "Segoe UI", 5 | "_SECTION1_": "=================== КНОПКИ ГЛАВНОГО ОКНА ===================", 6 | "additionalSettingsButton": "Дополнительно", 7 | "aboutButton": "О ПРОГРАММЕ", 8 | "closeAdditionalButton": "Назад", 9 | "downloadButton": "ЗАГРУЗИТЬ", 10 | "downloaderButton": "ЗАГРУЗЧИК", 11 | "logoutButton": "ВЫХОД", 12 | "openFolderButton": "Открыть папку", 13 | "qualitySelectButton": "Выбор качества", 14 | "saveTemplatesButton": "Сохранить", 15 | "searchButton": "ПОИСК", 16 | "searchAlbumsButton": "РЕЛИЗЫ", 17 | "searchTracksButton": "ТРЕКИ", 18 | "selectFolderButton": "Выбрать папку", 19 | "settingsButton": "НАСТРОЙКИ", 20 | "_SECTION2_": "=================== ПОДПИСИ ГЛАВНОГО ОКНА ===================", 21 | "advancedOptionsLabel": "РАСШИРЕННЫЕ НАСТРОЙКИ", 22 | "albumTemplateLabel": "ШАБЛОН АЛЬБОМА", 23 | "artistTemplateLabel": "ШАБЛОН ИСПОЛНИТЕЛЯ", 24 | "commentLabel": "Комментарий пользователя", 25 | "downloadFolderLabel": "ПАПКА ЗАГРУЗКИ", 26 | "downloadOptionsLabel": "ПАРАМЕТРЫ ЗАГРУЗКИ", 27 | "embeddedArtLabel": "Размер встраиваемого изображения", 28 | "extraSettingsLabel": "ДОПОЛНИТЕЛЬНО", 29 | "languageLabel": "Язык интерфейса", 30 | "playlistTemplateLabel": "ШАБЛОН ПЛЕЙЛИСТА", 31 | "favoritesTemplateLabel": "ШАБЛОН ИЗБРАННОГО", 32 | "savedArtLabel": "Размер сохраняемого изображения", 33 | "searchingLabel": "Поиск...", 34 | "taggingOptionsLabel": "ПАРАМЕТРЫ ТЕГИРОВАНИЯ", 35 | "templatesLabel": "ШАБЛОНЫ", 36 | "templatesListLabel": "СПИСОК ШАБЛОНОВ", 37 | "themeLabel": "Тема оформления", 38 | "themeSectionLabel": "ПАРАМЕТРЫ ТЕМ", 39 | "trackTemplateLabel": "ШАБЛОН ТРЕКА", 40 | "userInfoLabel": "ИНФОРМАЦИЯ О ПОЛЬЗОВАТЕЛЕ", 41 | "welcomeLabel": "Добро пожаловать\r\n{username}", 42 | "_SECTION3_": "=================== ЧЕКБОКСЫ ОСНОВНОЙ ФОРМЫ ===================", 43 | "albumArtistCheckbox": "Исполнитель альбома", 44 | "albumTitleCheckbox": "Название альбома", 45 | "trackArtistCheckbox": "Исполнитель трека", 46 | "trackTitleCheckbox": "Название трека", 47 | "releaseDateCheckbox": "Дата релиза", 48 | "releaseTypeCheckbox": "Тип релиза", 49 | "genreCheckbox": "Жанр", 50 | "trackNumberCheckbox": "Номер трека", 51 | "trackTotalCheckbox": "Общее количество треков", 52 | "discNumberCheckbox": "Номер диска", 53 | "discTotalCheckbox": "Общее количество дисков", 54 | "composerCheckbox": "Композитор", 55 | "explicitCheckbox": "Предупреждение о содержании", 56 | "coverArtCheckbox": "Обложка альбома", 57 | "copyrightCheckbox": "Авторские права", 58 | "labelCheckbox": "Лейбл", 59 | "upcCheckbox": "UPC / Штрих-код", 60 | "isrcCheckbox": "ISRC", 61 | "streamableCheckbox": "Проверка возможности трансляции", 62 | "fixMD5sCheckbox": "Автоматическое исправление отсутствующих MD5 (FLAC должен быть в переменной PATH)", 63 | "downloadSpeedCheckbox": "Показывать скорость загрузки", 64 | "_SECTION4_": "=================== ПЛЕЙСХОЛДЕРЫ ОСНОВНОЙ ФОРМЫ ===================", 65 | "albumLabelPlaceholder": "Добро пожаловать в QBDLX!", 66 | "artistLabelPlaceholder": "Вставьте Qobuz URL и нажмите загрузить!", 67 | "infoLabelPlaceholder": "Дата релиза", 68 | "inputTextboxPlaceholder": "Вставьте Qobuz URL...", 69 | "searchTextboxPlaceholder": "Введите запрос...", 70 | "downloadFolderPlaceholder": "Папка не выбрана", 71 | "userInfoTextboxPlaceholder": "ID пользователя = {user_id}\r\nE-mail = {user_email}\r\nСтрана = {user_country}\r\nПодписка = {user_subscription}\r\nИстекает = {user_subscription_expiration}", 72 | "downloadOutputWelcome": "Добро пожаловать {user_display_name}!", 73 | "downloadOutputExpired": "ВАША ПОДПИСКА ИСТЕКЛА, ЗАГРУЗКИ БУДУТ ОГРАНИЧЕНЫ 30-СЕКУНДНЫМИ ФРАГМЕНТАМИ!", 74 | "downloadOutputPath": "Путь загрузки", 75 | "downloadOutputNoPath": "Путь не установлен! Не забудьте выбрать папку!", 76 | "downloadOutputAPIError": "Ошибка API Qobuz. Возможно, релиз недоступен в этом регионе аккаунта?", 77 | "downloadOutputNotImplemented": "Ещё не реализовано или URL невозможно разобрать. Возможно, есть опечатка?", 78 | "downloadOutputCheckLink": "Проверка ссылки...", 79 | "downloadOutputTrNotStream": "Трек {TrackNumber} недоступен для трансляции, пропускаем его.", 80 | "downloadOutputAlNotStream": "Релиз недоступен для трансляции.", 81 | "downloadOutputGoodyFound": "Хорошая находка, загружаем...", 82 | "downloadOutputGoodyExists": "Файл уже существует", 83 | "downloadOutputGoodyNoURL": "Не найден URL для загрузки, пропускаем его.", 84 | "downloadOutputFileExists": "Файл {TrackNumber} трека уже существует, пропускаем его.", 85 | "downloadOutputDownloading": "Загрузка", 86 | "downloadOutputDone": "ГОТОВО", 87 | "downloadOutputCompleted": "ЗАГРУЗКА ЗАВЕРШЕНА", 88 | "progressLabelInactive": "Активных загрузок нет", 89 | "progressLabelActive": "Прогресс загрузки", 90 | "_SECTION5_": "=================== КНОПКИ ФОРМЫ АВТОРИЗАЦИИ ===================", 91 | "closeAboutButton": "ЗАКРЫТЬ", 92 | "customSaveButton": "СОХРАНИТЬ", 93 | "exitButton": "ВЫХОД", 94 | "loginButton": "ВХОД", 95 | "_SECTION6_": "=================== ПОДПИСИ ФОРМЫ АВТОРИЗАЦИИ ===================", 96 | "appidLabel": "App ID", 97 | "appSecretLabel": "App Secret", 98 | "customLabel": "ИСПОЛЬЗОВАТЬ СВОЙ APP ID + SECRET", 99 | "_SECTION7_": "=================== ТЕКСТОВЫЕ ПОЛЯ ФОРМЫ АВТОРИЗАЦИИ ===================", 100 | "customInfoTextbox": "Оставьте поля пустыми, чтобы заполнить значения автоматически!", 101 | "aboutTextbox": "Версия - {version}\r\nСоздано AiiR\r\n\r\nВдохновлено Qo-DL\r\n(Создано Sorrow и DashLt)\r\n\r\nСпасибо пользователям на Github и в Telegram за отчеты об ошибках и идеи! И огромное спасибо DJDoubleD за поддержку оригинала, пока я был занят!", 102 | "_SECTION8_": "=================== ПЛЕЙСХОЛДЕРЫ ФОРМЫ АВТОРИЗАЦИИ ===================", 103 | "emailPlaceholder": "e-mail", 104 | "passwordPlaceholder": "пароль", 105 | "tokenPlaceholder": "токен", 106 | "altLoginLabelToken": "ВХОД ПО ТОКЕНУ", 107 | "altLoginLabelEmail": "ВХОД ПО E-MAIL И ПАРОЛЮ", 108 | "loginTextWaiting": "ожидание входа...", 109 | "loginTextStart": "вход в систему...", 110 | "loginTextError": "вход не выполнен, ошибка сохранена в журнал", 111 | "loginTextNoEmail": "в поле ввода отсутствует e-mail", 112 | "loginTextNoPassword": "в поле ввода отсутствует пароль/токен", 113 | "updateNotification": "Доступна новая версия QBDLX!\r\n\r\nУстановлена версия - {currentVersion}\r\nПоследняя версия - {newVersion}\r\n\r\nЖурнал изменений\r\n==============\r\n{changelog}\r\n==============\r\n\r\nВы хотите обновить?", 114 | "updateNotificationTitle": "QBDLX | Доступно обновление" 115 | } 116 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Resources/Languages/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "TranslationCredit": "TheParaTiger", 3 | "TranslationUpdatedOn": "November 24, 2024, 09:37AM MET", 4 | "TranslationFont": "Nirmala UI", 5 | "_SECTION1_": "=================== MAIN FORM BUTTONS ===================", 6 | "additionalSettingsButton": "Weitere Optionen", 7 | "aboutButton": "ÜBER", 8 | "closeAdditionalButton": "Optionen", 9 | "downloadButton": "HOLEN!", 10 | "downloaderButton": "DOWNLOADER", 11 | "logoutButton": "AUSLOGGEN", 12 | "openFolderButton": "Ordner Öffnen", 13 | "qualitySelectButton": "Qualität", 14 | "saveTemplatesButton": "Speichern", 15 | "searchButton": "SUCHE", 16 | "searchAlbumsButton": "ALBUM", 17 | "searchTracksButton": "TRACK", 18 | "selectFolderButton": "Ordner Auswählen", 19 | "settingsButton": "OPTIONEN", 20 | "_SECTION2_": "=================== MAIN FORM LABELS ===================", 21 | "advancedOptionsLabel": "Weitere Optionen", 22 | "albumTemplateLabel": "ALBUM VORLAGE", 23 | "artistTemplateLabel": "ARTIST VORLAGE", 24 | "commentLabel": "Eigener Kommentar", 25 | "downloadFolderLabel": "DOWNLOAD ORDNER", 26 | "downloadOptionsLabel": "DOWNLOAD OPTIONEN", 27 | "embeddedArtLabel": "Eingebettete Artwork Größe", 28 | "extraSettingsLabel": "WEITERE OPTIONEN", 29 | "languageLabel": "Aktuelle Sprache", 30 | "playlistTemplateLabel": "PLAYLIST VORLAGE", 31 | "favoritesTemplateLabel": "FAVORITEN VORLAGE", 32 | "savedArtLabel": "Gespeicherte Artwork Größe", 33 | "searchingLabel": "Suche...", 34 | "taggingOptionsLabel": "TAG OPTIONEN", 35 | "templatesLabel": "VORLAGEN", 36 | "templatesListLabel": "VORLAGEN LISTE", 37 | "themeLabel": "Aktuelles Theme", 38 | "themeSectionLabel": "THEME OPTIONEN", 39 | "trackTemplateLabel": "TRACK VORLAGE", 40 | "userInfoLabel": "BENUTZERINFO", 41 | "welcomeLabel": "Willkommen\r\n{username}", 42 | "_SECTION3_": "=================== MAIN FORM CHECKBOXES ===================", 43 | "albumArtistCheckbox": "Album Artist", 44 | "albumTitleCheckbox": "Album Titel", 45 | "trackArtistCheckbox": "Track Artist", 46 | "trackTitleCheckbox": "Track Titel", 47 | "releaseDateCheckbox": "Veröffentlichungsdatum", 48 | "releaseTypeCheckbox": "Veröffentlichungstyp", 49 | "genreCheckbox": "Genre", 50 | "trackNumberCheckbox": "Track Nummer", 51 | "trackTotalCheckbox": "Totale Anzahl Tracks", 52 | "discNumberCheckbox": "CD-Nummer", 53 | "discTotalCheckbox": "Totale Anzahl CDs", 54 | "composerCheckbox": "Komponist", 55 | "explicitCheckbox": "Explizit", 56 | "coverArtCheckbox": "Cover Art", 57 | "copyrightCheckbox": "Copyright", 58 | "labelCheckbox": "Label", 59 | "upcCheckbox": "UPC / Barcode", 60 | "isrcCheckbox": "ISRC", 61 | "streamableCheckbox": "Ist Streamfähig Check", 62 | "fixMD5sCheckbox": "Nicht gesetzte MD5s setzen (muss FLAC in der PATH Variable haben)", 63 | "downloadSpeedCheckbox": "Downloadgeschwindigkeit Anzeigen", 64 | "_SECTION4_": "=================== MAIN FORM PLACEHOLDERS ===================", 65 | "albumLabelPlaceholder": "Willkommen in QBDLX!", 66 | "artistLabelPlaceholder": "Füge einen Qobuz-Link ein und Klicke HOLEN!", 67 | "infoLabelPlaceholder": "Veröffentlicht:", 68 | "inputTextboxPlaceholder": "Qobuz URL Eingeben...", 69 | "searchTextboxPlaceholder": "Suchbegriff eingeben...", 70 | "downloadFolderPlaceholder": "Kein Ordner Ausgewählt", 71 | "userInfoTextboxPlaceholder": "Nutzer ID = {user_id}\r\nE-Mail = {user_email}\r\nLand = {user_country}\r\nAbonnement = {user_subscription}\r\nLäuft ab = {user_subscription_expiration}", 72 | "downloadOutputWelcome": "Willkommen {user_display_name}!", 73 | "downloadOutputExpired": "DEIN ABONNEMENT IST ABGELAUFEN!, ES WERDEN NUR 30-SEKUNDEN AUSSCHNITTE HERUNTERGELADEN!", 74 | "downloadOutputPath": "Download Pfad", 75 | "downloadOutputNoPath": "Es wurde kein Pfad gesetzt! Denke daran einen Ordner auszuwählen!", 76 | "downloadOutputAPIError": "Qobuz-API Fehler, ist dieses Album in deiner Region verfügbar?", 77 | "downloadOutputNotImplemented": "Noch nicht Implementiert oder nicht verstanden, hast du dich vielleicht vertippt?", 78 | "downloadOutputCheckLink": "Überprüfe Link...", 79 | "downloadOutputTrNotStream": "Track {TrackNumber} ist nicht fürs Streamen verfügbar und wird Übersprungen", 80 | "downloadOutputAlNotStream": "Album ist nicht fürs Streaming verfügbar.", 81 | "downloadOutputGoodyFound": "Zusatzinhalte gefunden. Es wird versucht, sie Herunterzuladen...", 82 | "downloadOutputGoodyExists": "Die Datei für den Zusatzinhalt existiert bereits.", 83 | "downloadOutputGoodyNoURL": "Keine Download URL für den Zusatzinhalt gefunden. Er wird Übersprungen", 84 | "downloadOutputFileExists": "Die Datei für den Track {TrackNumber} existiert bereits und wird Übersprungen", 85 | "downloadOutputDownloading": "Lädt Herunter", 86 | "downloadOutputDone": "FERTIG", 87 | "downloadOutputCompleted": "DOWNLOAD FERTIG!", 88 | "progressLabelInactive": "Kein Download aktiv", 89 | "progressLabelActive": "Download Fortschritt", 90 | "_SECTION5_": "=================== LOGIN FORM BUTTONS ===================", 91 | "closeAboutButton": "SCHLIEßEN", 92 | "customSaveButton": "SPEICHERN", 93 | "exitButton": "VERLASSEN", 94 | "loginButton": "EINLOGGEN", 95 | "_SECTION6_": "=================== LOGIN FORM LABELS ===================", 96 | "appidLabel": "App ID", 97 | "appSecretLabel": "App Secret", 98 | "customLabel": "EIGENE APP ID + SECRET NUTZEN", 99 | "_SECTION7_": "=================== LOGIN FORM TEXTBOXES ===================", 100 | "customInfoTextbox": "Felder leerlassen wenn du willst, dass das Programm die Werte selber holt.", 101 | "aboutTextbox": "Version - {version}\r\nErstellt von AiiR\r\n\r\nInspiriert von Qo-DL\r\n(Erstellt von Sorrow und DashLt)\r\n\r\nDanke an die Benutzer von GitHub und Telegramm für die Fehlerberichte und Ideen! Und ein großes Dankeschön an DJDoubleD für die Weiterentwicklung des ursprünglichen Programms während ich Beschäftigt war!", 102 | "_SECTION8_": "=================== LOGIN FORM PLACEHOLDERS ===================", 103 | "emailPlaceholder": "E-Mail", 104 | "passwordPlaceholder": "Passwort", 105 | "tokenPlaceholder": "Token", 106 | "altLoginLabelToken": "MIT TOKEN ANMELDEN", 107 | "altLoginLabelEmail": "MIT E-MAIL UND PASSWORT ANMELDEN", 108 | "loginTextWaiting": "Wartet auf das Einloggen...", 109 | "loginTextStart": "Loggt sich ein...", 110 | "loginTextError": "Login fehlgeschlagen. Der Fehler wurde im Fehler-log gespeichert.", 111 | "loginTextNoEmail": "Keine E-Mail in dem Feld", 112 | "loginTextNoPassword": "Kein Passwort/Token in dem Feld", 113 | "updateNotification": "Eine neue Version von QBDLX ist verfügbar!\r\n\r\nInstallierte Version - {currentVersion}\r\nNeueste Version - {newVersion}\r\n\r\nÄnderungsliste\r\n==============\r\n{changelog}\r\n==============\r\n\r\nMöchtest du aktualisieren?", 114 | "updateNotificationTitle": "QBDLX | Update Verfügbar" 115 | } 116 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/TagFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using TagLib; 4 | using File = TagLib.File; 5 | using QobuzDownloaderX.Properties; 6 | using QopenAPI; 7 | using TagLib.Id3v2; 8 | using System.Text.RegularExpressions; 9 | 10 | namespace QobuzDownloaderX 11 | { 12 | class TagFile 13 | { 14 | public static void WriteToFile(string tempPath, string artworkPath, Album QoAlbum, Item QoItem) 15 | { 16 | using (var file = File.Create(tempPath)) 17 | { 18 | bool isFlac = tempPath.Contains(".flac"); 19 | qbdlxForm._qbdlxForm.logger.Debug(isFlac ? "FLAC detected, setting FLAC specific tags" : "Non-FLAC detected, setting MP3 specific tags"); 20 | 21 | if (isFlac) 22 | { 23 | var customTagsFLAC = (TagLib.Ogg.XiphComment)file.GetTag(TagLib.TagTypes.Xiph); 24 | SetFlacTags(customTagsFLAC, QoAlbum, QoItem); 25 | } 26 | else 27 | { 28 | var mp3Tag = (TagLib.Id3v2.Tag)file.GetTag(TagTypes.Id3v2, true); 29 | SetMp3Tags(mp3Tag, QoAlbum, QoItem); 30 | } 31 | 32 | SetCommonTags(file, QoAlbum, QoItem); 33 | SetAlbumArtists(file, QoAlbum); 34 | EmbedArtwork(file, artworkPath); 35 | 36 | // Save All Tags 37 | file.Save(); 38 | qbdlxForm._qbdlxForm.logger.Debug("File tagging completed!"); 39 | } 40 | } 41 | 42 | private static void SetFlacTags(TagLib.Ogg.XiphComment customTags, Album QoAlbum, Item QoItem) 43 | { 44 | if (Settings.Default.yearTag) customTags.SetField("DATE", QoAlbum.ReleaseDateOriginal); 45 | if (Settings.Default.isrcTag) customTags.SetField("ISRC", QoItem.ISRC); 46 | if (Settings.Default.typeTag) customTags.SetField("MEDIATYPE", QoAlbum.ProductType.ToUpper()); 47 | if (Settings.Default.upcTag) customTags.SetField("BARCODE", QoAlbum.UPC); 48 | if (Settings.Default.labelTag) customTags.SetField("LABEL", Regex.Replace(QoAlbum.Label.Name, @"\s+", " ")); 49 | if (Settings.Default.explicitTag) customTags.SetField("ITUNESADVISORY", QoItem.ParentalWarning ? "1" : "0"); 50 | if (Settings.Default.commentTag && !string.IsNullOrEmpty(Settings.Default.commentText)) 51 | { 52 | customTags.SetField("COMMENT", new string[] 53 | { 54 | Regex.Replace(Settings.Default.commentText, @"%(.*?)%", match => match.Value.ToLower()) 55 | .Replace("%description%", QoAlbum.Description) 56 | .Replace("
", Environment.NewLine) 57 | .Replace("
", Environment.NewLine) 58 | }); 59 | } 60 | } 61 | 62 | private static void SetMp3Tags(TagLib.Id3v2.Tag mp3Tag, Album QoAlbum, Item QoItem) 63 | { 64 | if (Settings.Default.upcTag) UserTextInformationFrame.Get(mp3Tag, "BARCODE", true).Text = new[] { QoAlbum.UPC }; 65 | if (Settings.Default.explicitTag) UserTextInformationFrame.Get(mp3Tag, "ITUNESADVISORY", true).Text = new[] { QoItem.ParentalWarning ? "1" : "0" }; 66 | if (Settings.Default.yearTag) mp3Tag.Year = uint.Parse(QoAlbum.ReleaseDateOriginal.Substring(0, 4)); 67 | if (Settings.Default.isrcTag) mp3Tag.SetTextFrame("TSRC", QoItem.ISRC); 68 | if (Settings.Default.labelTag) mp3Tag.SetTextFrame("TPUB", Regex.Replace(QoAlbum.Label.Name, @"\s+", " ")); 69 | if (Settings.Default.typeTag) mp3Tag.SetTextFrame("TMED", QoAlbum.ProductType.ToUpper()); 70 | if (Settings.Default.commentTag && !string.IsNullOrEmpty(Settings.Default.commentText)) 71 | { 72 | mp3Tag.Comment = Regex.Replace(Settings.Default.commentText, @"%(.*?)%", match => match.Value.ToLower()) 73 | .Replace("%description%", QoAlbum.Description) 74 | .Replace("
", Environment.NewLine) 75 | .Replace("
", Environment.NewLine); 76 | } 77 | } 78 | 79 | private static void SetCommonTags(TagLib.File file, Album QoAlbum, Item QoItem) 80 | { 81 | if (Settings.Default.trackTitleTag) file.Tag.Title = QoItem.Version == null ? QoItem.Title : $"{QoItem.Title.TrimEnd()} ({QoItem.Version})"; 82 | if (Settings.Default.artistTag) file.Tag.Performers = new[] { QoItem.Performer?.Name }; 83 | if (Settings.Default.genreTag) file.Tag.Genres = new[] { QoAlbum.Genre.Name }; 84 | if (Settings.Default.albumTag) file.Tag.Album = QoAlbum.Version == null ? QoAlbum.Title : $"{QoAlbum.Title.TrimEnd()} ({QoAlbum.Version})"; 85 | if (Settings.Default.composerTag) file.Tag.Composers = new[] { QoItem.Composer?.Name }; 86 | if (Settings.Default.trackTag) file.Tag.Track = (uint)QoItem.TrackNumber; 87 | if (Settings.Default.totalTracksTag) file.Tag.TrackCount = (uint)QoAlbum.TracksCount; 88 | if (Settings.Default.discTag) file.Tag.Disc = (uint)QoItem.MediaNumber; 89 | if (Settings.Default.totalDiscsTag) file.Tag.DiscCount = (uint)QoAlbum.MediaCount; 90 | if (Settings.Default.copyrightTag) file.Tag.Copyright = QoAlbum.Copyright; 91 | } 92 | 93 | private static void SetAlbumArtists(TagLib.File file, Album QoAlbum) 94 | { 95 | if (!Settings.Default.albumArtistTag) return; 96 | 97 | var mainArtists = QoAlbum.Artists.Where(a => a.Roles.Contains("main-artist")).ToList(); 98 | if (mainArtists.Count > 1) 99 | { 100 | var allButLastArtist = string.Join(", ", mainArtists.Take(mainArtists.Count - 1).Select(a => a.Name)); 101 | var lastArtist = mainArtists.Last().Name; 102 | file.Tag.AlbumArtists = new[] { $"{allButLastArtist} & {lastArtist}" }; 103 | return; 104 | } 105 | 106 | file.Tag.AlbumArtists = new[] { QoAlbum.Artist.Name }; 107 | } 108 | 109 | private static void EmbedArtwork(TagLib.File file, string artworkPath) 110 | { 111 | if (!Settings.Default.imageTag) return; 112 | try 113 | { 114 | qbdlxForm._qbdlxForm.logger.Debug("Attempting to embed artwork"); 115 | var pic = new AttachedPictureFrame 116 | { 117 | TextEncoding = TagLib.StringType.Latin1, 118 | MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg, 119 | Type = PictureType.FrontCover, 120 | Data = ByteVector.FromPath(artworkPath) 121 | }; 122 | file.Tag.Pictures = new IPicture[] { pic }; 123 | qbdlxForm._qbdlxForm.logger.Debug("Artwork embed complete"); 124 | } 125 | catch 126 | { 127 | qbdlxForm._qbdlxForm.logger.Error("Unable to write embedded artwork"); 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace QobuzDownloaderX.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.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 | /// Returns the cached ResourceManager instance used by this class. 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("QobuzDownloaderX.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 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 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap alert { 67 | get { 68 | object obj = ResourceManager.GetObject("alert", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap email { 77 | get { 78 | object obj = ResourceManager.GetObject("email", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Drawing.Bitmap. 85 | /// 86 | internal static System.Drawing.Bitmap info { 87 | get { 88 | object obj = ResourceManager.GetObject("info", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Drawing.Bitmap. 95 | /// 96 | internal static System.Drawing.Bitmap log_out { 97 | get { 98 | object obj = ResourceManager.GetObject("log_out", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | 103 | /// 104 | /// Looks up a localized resource of type System.Drawing.Bitmap. 105 | /// 106 | internal static System.Drawing.Bitmap logout { 107 | get { 108 | object obj = ResourceManager.GetObject("logout", resourceCulture); 109 | return ((System.Drawing.Bitmap)(obj)); 110 | } 111 | } 112 | 113 | /// 114 | /// Looks up a localized resource of type System.Drawing.Bitmap. 115 | /// 116 | internal static System.Drawing.Bitmap password { 117 | get { 118 | object obj = ResourceManager.GetObject("password", resourceCulture); 119 | return ((System.Drawing.Bitmap)(obj)); 120 | } 121 | } 122 | 123 | /// 124 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). 125 | /// 126 | internal static System.Drawing.Icon QBDLX_Icon1 { 127 | get { 128 | object obj = ResourceManager.GetObject("QBDLX_Icon1", resourceCulture); 129 | return ((System.Drawing.Icon)(obj)); 130 | } 131 | } 132 | 133 | /// 134 | /// Looks up a localized resource of type System.Drawing.Bitmap. 135 | /// 136 | internal static System.Drawing.Bitmap qbdlx_new { 137 | get { 138 | object obj = ResourceManager.GetObject("qbdlx_new", resourceCulture); 139 | return ((System.Drawing.Bitmap)(obj)); 140 | } 141 | } 142 | 143 | /// 144 | /// Looks up a localized resource of type System.Drawing.Bitmap. 145 | /// 146 | internal static System.Drawing.Bitmap search { 147 | get { 148 | object obj = ResourceManager.GetObject("search", resourceCulture); 149 | return ((System.Drawing.Bitmap)(obj)); 150 | } 151 | } 152 | 153 | /// 154 | /// Looks up a localized resource of type System.Drawing.Bitmap. 155 | /// 156 | internal static System.Drawing.Bitmap settings { 157 | get { 158 | object obj = ResourceManager.GetObject("settings", resourceCulture); 159 | return ((System.Drawing.Bitmap)(obj)); 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | True 22 | 23 | 24 | True 25 | 26 | 27 | True 28 | 29 | 30 | True 31 | 32 | 33 | True 34 | 35 | 36 | True 37 | 38 | 39 | True 40 | 41 | 42 | True 43 | 44 | 45 | True 46 | 47 | 48 | True 49 | 50 | 51 | True 52 | 53 | 54 | True 55 | 56 | 57 | True 58 | 59 | 60 | True 61 | 62 | 63 | True 64 | 65 | 66 | False 67 | 68 | 69 | True 70 | 71 | 72 | True 73 | 74 | 75 | 76 | 77 | 78 | False 79 | 80 | 81 | False 82 | 83 | 84 | False 85 | 86 | 87 | True 88 | 89 | 90 | 27 91 | 92 | 93 | .flac 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | False 103 | 104 | 105 | 2 106 | 107 | 108 | True 109 | 110 | 111 | 36 112 | 113 | 114 | 1 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | %TrackNumber% %TrackTitle% 124 | 125 | 126 | %AlbumTitle% 127 | 128 | 129 | %ArtistName% 130 | 131 | 132 | %PlaylistTitle% 133 | 134 | 135 | - Favorites 136 | 137 | 138 | 0 139 | 140 | 141 | True 142 | 143 | 144 | True 145 | 146 | 147 | True 148 | 149 | 150 | False 151 | 152 | 153 | Dark 154 | 155 | 156 | EN 157 | 158 | 159 | AiiR 160 | 161 | 162 | -------------------------------------------------------------------------------- /QobuzDownloaderX/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 | True 30 | 31 | 32 | True 33 | 34 | 35 | True 36 | 37 | 38 | True 39 | 40 | 41 | True 42 | 43 | 44 | True 45 | 46 | 47 | True 48 | 49 | 50 | True 51 | 52 | 53 | True 54 | 55 | 56 | True 57 | 58 | 59 | True 60 | 61 | 62 | True 63 | 64 | 65 | True 66 | 67 | 68 | True 69 | 70 | 71 | True 72 | 73 | 74 | False 75 | 76 | 77 | True 78 | 79 | 80 | True 81 | 82 | 83 | 84 | 85 | 86 | False 87 | 88 | 89 | False 90 | 91 | 92 | False 93 | 94 | 95 | True 96 | 97 | 98 | 27 99 | 100 | 101 | .flac 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | False 111 | 112 | 113 | 2 114 | 115 | 116 | True 117 | 118 | 119 | 36 120 | 121 | 122 | 1 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | %TrackNumber% %TrackTitle% 132 | 133 | 134 | %AlbumTitle% 135 | 136 | 137 | %ArtistName% 138 | 139 | 140 | %PlaylistTitle% 141 | 142 | 143 | - Favorites 144 | 145 | 146 | 0 147 | 148 | 149 | True 150 | 151 | 152 | True 153 | 154 | 155 | True 156 | 157 | 158 | False 159 | 160 | 161 | Dark 162 | 163 | 164 | EN 165 | 166 | 167 | AiiR 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/Updating.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | using Newtonsoft.Json.Linq; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using System.Reflection; 7 | using System.Net; 8 | using ZetaLongPaths; 9 | 10 | namespace QobuzDownloaderX.Helpers 11 | { 12 | public class TranslationUpdater 13 | { 14 | // List of language files 15 | private static readonly Dictionary LanguageFiles = new Dictionary 16 | { 17 | { "en.json", "Languages/en.json" }, 18 | { "ru.json", "Languages/ru.json" }, 19 | { "zh-cn.json", "Languages/zh-cn.json" } 20 | }; 21 | 22 | public static async Task CheckAndUpdateLanguageFiles() 23 | { 24 | using (HttpClient client = new HttpClient()) 25 | { 26 | client.DefaultRequestHeaders.Add("User-Agent", "TranslationUpdater"); 27 | 28 | foreach (var languageFile in LanguageFiles) 29 | { 30 | string fileName = languageFile.Key; 31 | string localFilePath = languageFile.Value; 32 | 33 | string apiUrl = $"https://api.github.com/repos/ImAiiR/QobuzDownloaderX/contents/QobuzDownloaderX/Resources/{localFilePath}"; 34 | 35 | try 36 | { 37 | HttpResponseMessage response = await client.GetAsync(apiUrl); 38 | if (response.IsSuccessStatusCode) 39 | { 40 | string jsonResponse = await response.Content.ReadAsStringAsync(); 41 | JObject fileMetadata = JObject.Parse(jsonResponse); 42 | 43 | // Retrieve the download URL for the remote file 44 | string downloadUrl = fileMetadata["download_url"]?.ToString(); 45 | if (!string.IsNullOrEmpty(downloadUrl)) 46 | { 47 | // Fetch the remote file content 48 | string remoteContent = await client.GetStringAsync(downloadUrl); 49 | 50 | // Parse the "TranslationUpdatedOn" field from the remote file 51 | JObject remoteJson = JObject.Parse(remoteContent); 52 | string remoteUpdatedOnString = remoteJson["TranslationUpdatedOn"]?.ToString(); 53 | 54 | // Parse the local file's "TranslationUpdatedOn" field 55 | if (ZlpIOHelper.FileExists(localFilePath.ToLower())) 56 | { 57 | string localContent = ZlpIOHelper.ReadAllText(localFilePath.ToLower()); 58 | JObject localJson = JObject.Parse(localContent); 59 | string localUpdatedOnString = localJson["TranslationUpdatedOn"]?.ToString(); 60 | 61 | // Compare updated date 62 | if (remoteUpdatedOnString != localUpdatedOnString) 63 | { 64 | ZlpIOHelper.WriteAllText(localFilePath.ToLower(), remoteContent); 65 | qbdlxForm._qbdlxForm.logger.Debug($"File {fileName} updated successfully."); 66 | } 67 | else 68 | { 69 | qbdlxForm._qbdlxForm.logger.Debug($"File {fileName} is already up-to-date."); 70 | } 71 | } 72 | else 73 | { 74 | // Local file does not exist, download it 75 | ZlpIOHelper.WriteAllText(localFilePath.ToLower(), remoteContent); 76 | qbdlxForm._qbdlxForm.logger.Debug($"File {fileName} downloaded successfully."); 77 | } 78 | } 79 | else 80 | { 81 | qbdlxForm._qbdlxForm.logger.Error($"Failed to retrieve the download URL for {fileName}."); 82 | } 83 | } 84 | else 85 | { 86 | qbdlxForm._qbdlxForm.logger.Error($"Failed to fetch metadata for {fileName}: {response.StatusCode}"); 87 | } 88 | } 89 | catch (Exception ex) 90 | { 91 | qbdlxForm._qbdlxForm.logger.Error($"Error updating {fileName}: {ex.Message}"); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | public static class VersionChecker 99 | { 100 | public static async Task<(bool isUpdateAvailable, string newVersion, string currentVersion, string changes)> CheckForUpdate() 101 | { 102 | string changes = string.Empty; 103 | string currentVersion = string.Empty; 104 | string newVersion = string.Empty; 105 | bool isUpdateAvailable = false; 106 | 107 | try 108 | { 109 | // Initialize HttpClient to grab version number from GitHub 110 | using (var versionURLClient = new HttpClient()) 111 | { 112 | qbdlxForm._qbdlxForm.logger.Debug("versionURLClient initialized"); 113 | 114 | // Configure TLS for secure connection 115 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; 116 | 117 | // Set user-agent to Firefox 118 | versionURLClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0"); 119 | 120 | // Request the latest release from GitHub 121 | qbdlxForm._qbdlxForm.logger.Debug("Starting request for latest GitHub version"); 122 | var versionURL = "https://api.github.com/repos/ImAiiR/QobuzDownloaderX/releases/latest"; 123 | var versionURLResponse = await versionURLClient.GetAsync(versionURL); 124 | string versionURLResponseString = await versionURLResponse.Content.ReadAsStringAsync(); 125 | 126 | // Parse the JSON response 127 | JObject joVersionResponse = JObject.Parse(versionURLResponseString); 128 | 129 | // Extract version number and changelog 130 | newVersion = (string)joVersionResponse["tag_name"]; 131 | qbdlxForm._qbdlxForm.logger.Debug("Received version from GitHub: " + newVersion); 132 | changes = (string)joVersionResponse["body"]; 133 | 134 | // Get the current version from the assembly 135 | currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); 136 | 137 | // Compare versions 138 | if (!currentVersion.Contains(newVersion)) 139 | { 140 | isUpdateAvailable = true; 141 | qbdlxForm._qbdlxForm.logger.Debug("New version available: " + newVersion); 142 | } 143 | else 144 | { 145 | qbdlxForm._qbdlxForm.logger.Debug("Current version matches the latest version."); 146 | } 147 | } 148 | } 149 | catch (Exception ex) 150 | { 151 | qbdlxForm._qbdlxForm.logger.Error("Connection to GitHub failed: " + ex.Message); 152 | } 153 | 154 | return (isUpdateAvailable, newVersion, currentVersion, changes); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /QobuzDownloaderX/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\info.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\qbdlx_icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\Resources\email.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\Resources\qbdlx_new.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | 134 | ..\Resources\password.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 135 | 136 | 137 | ..\Resources\log_out.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 138 | 139 | 140 | ..\Resources\logout.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 141 | 142 | 143 | ..\Resources\settings.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 144 | 145 | 146 | ..\Resources\QBDLX-Icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 147 | 148 | 149 | ..\Resources\QBDLX-Icon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 150 | 151 | 152 | ..\Resources\alert.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 153 | 154 | 155 | ..\Resources\search.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 156 | 157 | -------------------------------------------------------------------------------- /QobuzDownloaderX/QobuzDownloaderX.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4CEB979A-035A-4D36-9607-D554BBECABE0} 8 | WinExe 9 | Properties 10 | QobuzDownloaderX 11 | QobuzDownloaderX 12 | v4.8 13 | 512 14 | true 15 | 16 | publish\ 17 | true 18 | Disk 19 | false 20 | Foreground 21 | 7 22 | Days 23 | false 24 | false 25 | true 26 | 0 27 | 1.0.0.%2a 28 | false 29 | false 30 | true 31 | 32 | 33 | AnyCPU 34 | true 35 | full 36 | false 37 | bin\Debug\ 38 | DEBUG;TRACE 39 | prompt 40 | 4 41 | 42 | 43 | AnyCPU 44 | pdbonly 45 | true 46 | bin\Release\ 47 | TRACE 48 | prompt 49 | 4 50 | 51 | 52 | Resources\QBDLX-Icon.ico 53 | 54 | 55 | 56 | 57 | 58 | 59 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 60 | True 61 | 62 | 63 | ..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll 64 | True 65 | 66 | 67 | ..\packages\QopenAPI.0.0.3.4\lib\net48\Qo(penAPI).dll 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | ..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll 82 | True 83 | 84 | 85 | ..\packages\ZetaLongPaths.1.0.17\lib\net452-full\ZetaLongPaths.dll 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | Form 101 | 102 | 103 | LoginForm.cs 104 | 105 | 106 | 107 | 108 | Form 109 | 110 | 111 | qbdlxForm.cs 112 | 113 | 114 | 115 | 116 | LoginForm.cs 117 | 118 | 119 | ResXFileCodeGenerator 120 | Resources.Designer.cs 121 | Designer 122 | 123 | 124 | True 125 | Resources.resx 126 | True 127 | 128 | 129 | qbdlxForm.cs 130 | 131 | 132 | 133 | SettingsSingleFileGenerator 134 | Settings.Designer.cs 135 | 136 | 137 | True 138 | Settings.settings 139 | True 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | Designer 151 | 152 | 153 | 154 | 155 | False 156 | Microsoft .NET Framework 4.6 %28x86 and x64%29 157 | true 158 | 159 | 160 | False 161 | .NET Framework 3.5 SP1 162 | false 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | xcopy /y /i "$(ProjectDir)Resources\Languages\*.json" "$(TargetDir)languages\" 183 | xcopy /y /i "$(ProjectDir)Resources\themes.json" "$(TargetDir)" 184 | 185 | 186 | 193 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Old/AboutForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace QobuzDownloaderX 2 | { 3 | partial class AboutForm 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(AboutForm)); 32 | this.panel1 = new System.Windows.Forms.Panel(); 33 | this.verNumLabel2 = new System.Windows.Forms.Label(); 34 | this.exitLabel = new System.Windows.Forms.Label(); 35 | this.pictureBox1 = new System.Windows.Forms.PictureBox(); 36 | this.devLabel = new System.Windows.Forms.Label(); 37 | this.label1 = new System.Windows.Forms.Label(); 38 | this.label2 = new System.Windows.Forms.Label(); 39 | this.label3 = new System.Windows.Forms.Label(); 40 | this.panel1.SuspendLayout(); 41 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); 42 | this.SuspendLayout(); 43 | // 44 | // panel1 45 | // 46 | this.panel1.BackgroundImage = null; 47 | this.panel1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; 48 | this.panel1.Controls.Add(this.exitLabel); 49 | this.panel1.Controls.Add(this.pictureBox1); 50 | this.panel1.Dock = System.Windows.Forms.DockStyle.Top; 51 | this.panel1.Location = new System.Drawing.Point(0, 0); 52 | this.panel1.Name = "panel1"; 53 | this.panel1.Size = new System.Drawing.Size(365, 95); 54 | this.panel1.TabIndex = 1; 55 | this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseMove); 56 | // 57 | // verNumLabel2 58 | // 59 | this.verNumLabel2.BackColor = System.Drawing.Color.Transparent; 60 | this.verNumLabel2.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 61 | this.verNumLabel2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 62 | this.verNumLabel2.Location = new System.Drawing.Point(12, 98); 63 | this.verNumLabel2.Name = "verNumLabel2"; 64 | this.verNumLabel2.Size = new System.Drawing.Size(341, 18); 65 | this.verNumLabel2.TabIndex = 32; 66 | this.verNumLabel2.Text = "#.#.#.#"; 67 | this.verNumLabel2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 68 | this.verNumLabel2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.verNumLabel2_MouseMove); 69 | // 70 | // exitLabel 71 | // 72 | this.exitLabel.AutoSize = true; 73 | this.exitLabel.BackColor = System.Drawing.Color.Transparent; 74 | this.exitLabel.Font = new System.Drawing.Font("Calibri", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 75 | this.exitLabel.ForeColor = System.Drawing.Color.Black; 76 | this.exitLabel.Location = new System.Drawing.Point(345, 0); 77 | this.exitLabel.Name = "exitLabel"; 78 | this.exitLabel.Size = new System.Drawing.Size(20, 23); 79 | this.exitLabel.TabIndex = 9; 80 | this.exitLabel.Text = "X"; 81 | this.exitLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; 82 | this.exitLabel.Click += new System.EventHandler(this.exitLabel_Click); 83 | // 84 | // pictureBox1 85 | // 86 | this.pictureBox1.BackColor = System.Drawing.Color.Transparent; 87 | this.pictureBox1.Image = null; 88 | this.pictureBox1.Location = new System.Drawing.Point(53, 12); 89 | this.pictureBox1.Name = "pictureBox1"; 90 | this.pictureBox1.Size = new System.Drawing.Size(258, 64); 91 | this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; 92 | this.pictureBox1.TabIndex = 29; 93 | this.pictureBox1.TabStop = false; 94 | this.pictureBox1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseMove); 95 | // 96 | // devLabel 97 | // 98 | this.devLabel.AutoSize = true; 99 | this.devLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 100 | this.devLabel.Location = new System.Drawing.Point(140, 131); 101 | this.devLabel.Name = "devLabel"; 102 | this.devLabel.Size = new System.Drawing.Size(84, 13); 103 | this.devLabel.TabIndex = 34; 104 | this.devLabel.Text = "Developer - AiiR"; 105 | // 106 | // label1 107 | // 108 | this.label1.AutoSize = true; 109 | this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 110 | this.label1.Location = new System.Drawing.Point(79, 154); 111 | this.label1.Name = "label1"; 112 | this.label1.Size = new System.Drawing.Size(207, 13); 113 | this.label1.TabIndex = 35; 114 | this.label1.Text = "Inspired By - Qo-DL by Sorrow and DashLt"; 115 | this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 116 | // 117 | // label2 118 | // 119 | this.label2.AutoSize = true; 120 | this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 121 | this.label2.Location = new System.Drawing.Point(47, 177); 122 | this.label2.Name = "label2"; 123 | this.label2.Size = new System.Drawing.Size(271, 26); 124 | this.label2.TabIndex = 36; 125 | this.label2.Text = "Thanks to the users on Github and Telegram for offering\r\nbug reports and ideas!"; 126 | this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 127 | // 128 | // label3 129 | // 130 | this.label3.AutoSize = true; 131 | this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 132 | this.label3.Location = new System.Drawing.Point(15, 237); 133 | this.label3.Name = "label3"; 134 | this.label3.Size = new System.Drawing.Size(334, 26); 135 | this.label3.TabIndex = 37; 136 | this.label3.Text = "IF YOU PAID FOR THIS PROGRAM, YOU HAVE BEEN SCAMMED!\r\nTHIS SOFTWARE IS COMPLETELY" + 137 | " FREE AND OPEN-SOURCE."; 138 | this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 139 | // 140 | // AboutForm 141 | // 142 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 143 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 144 | this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20))))); 145 | this.ClientSize = new System.Drawing.Size(365, 289); 146 | this.Controls.Add(this.label3); 147 | this.Controls.Add(this.label2); 148 | this.Controls.Add(this.label1); 149 | this.Controls.Add(this.devLabel); 150 | this.Controls.Add(this.verNumLabel2); 151 | this.Controls.Add(this.panel1); 152 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; 153 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 154 | this.Name = "AboutForm"; 155 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 156 | this.Text = "QobuzDLX | About"; 157 | this.Load += new System.EventHandler(this.AboutForm_Load); 158 | this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.AboutForm_MouseMove); 159 | this.panel1.ResumeLayout(false); 160 | this.panel1.PerformLayout(); 161 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); 162 | this.ResumeLayout(false); 163 | this.PerformLayout(); 164 | 165 | } 166 | 167 | #endregion 168 | 169 | private System.Windows.Forms.Panel panel1; 170 | private System.Windows.Forms.Label verNumLabel2; 171 | private System.Windows.Forms.Label exitLabel; 172 | private System.Windows.Forms.PictureBox pictureBox1; 173 | private System.Windows.Forms.Label devLabel; 174 | private System.Windows.Forms.Label label1; 175 | private System.Windows.Forms.Label label2; 176 | private System.Windows.Forms.Label label3; 177 | } 178 | } -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/Download/DownloadFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using QopenAPI; 5 | using QobuzDownloaderX.Properties; 6 | using System.Text.RegularExpressions; 7 | using ZetaLongPaths; 8 | 9 | namespace QobuzDownloaderX 10 | { 11 | class DownloadFile 12 | { 13 | TagFile tagFile = new TagFile(); 14 | RenameTemplates renameTemplates = new RenameTemplates(); 15 | PaddingNumbers paddingNumbers = new PaddingNumbers(); 16 | GetInfo getInfo = new GetInfo(); 17 | FixMD5 fixMD5 = new FixMD5(); 18 | 19 | public string artworkPath { get; set; } 20 | 21 | public async Task createPath(string downloadLocation, string artistTemplate, string albumTemplate, string trackTemplate, string playlistTemplate, string favoritesTemplate, int paddedTrackLength, int paddedDiscLength, Album QoAlbum, Item QoItem, Playlist QoPlaylist) 22 | { 23 | return await Task.Run(() => 24 | { 25 | string downloadPath; 26 | if (QoPlaylist == null) 27 | { 28 | qbdlxForm._qbdlxForm.logger.Debug("Using non-playlist path"); 29 | string artistTemplateConverted = renameTemplates.renameTemplates(artistTemplate, paddedTrackLength, paddedDiscLength, qbdlxForm._qbdlxForm.audio_format, QoAlbum, null, null); 30 | string albumTemplateConverted = renameTemplates.renameTemplates(albumTemplate, paddedTrackLength, paddedDiscLength, qbdlxForm._qbdlxForm.audio_format, QoAlbum, null, null); 31 | downloadPath = ZlpPathHelper.Combine(downloadLocation, artistTemplateConverted, albumTemplateConverted.TrimEnd(ZlpPathHelper.DirectorySeparatorChar) + ZlpPathHelper.DirectorySeparatorChar); 32 | downloadPath = Regex.Replace(downloadPath, @"\s+", " "); // Remove double spaces 33 | 34 | } 35 | else 36 | { 37 | qbdlxForm._qbdlxForm.logger.Debug("Using playlist path"); 38 | string playlistTemplateConverted = renameTemplates.renameTemplates(playlistTemplate, paddedTrackLength, paddedDiscLength, qbdlxForm._qbdlxForm.audio_format, null, null, QoPlaylist); 39 | downloadPath = ZlpPathHelper.Combine(downloadLocation, playlistTemplateConverted.TrimEnd(ZlpPathHelper.DirectorySeparatorChar) + ZlpPathHelper.DirectorySeparatorChar); 40 | downloadPath = Regex.Replace(downloadPath, @"\s+", " "); // Remove double spaces 41 | } 42 | return downloadPath; 43 | }); 44 | } 45 | 46 | public async Task DownloadStream(string streamUrl, string downloadPath, string filePath, string audio_format, Album QoAlbum, Item QoItem) 47 | { 48 | qbdlxForm._qbdlxForm.logger.Debug("Writing temp file to qbdlx-temp/qbdlx_downloading-" + QoItem.Id.ToString() + audio_format); 49 | 50 | // Create a temp directory inside the exe location, to download files to. 51 | string tempFile = ZlpPathHelper.Combine(@"qbdlx-temp", "qbdlx_downloading-" + QoItem.Id.ToString() + audio_format); 52 | ZlpIOHelper.CreateDirectory(@"qbdlx-temp"); 53 | 54 | using (var client = new WebClient()) 55 | { 56 | // Set path for downloaded artwork. 57 | artworkPath = downloadPath + qbdlxForm._qbdlxForm.embeddedArtSize + @".jpg"; 58 | qbdlxForm._qbdlxForm.logger.Debug("Artwork path: " + artworkPath); 59 | 60 | // Use secure connection 61 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; 62 | 63 | // Create a TaskCompletionSource to handle asynchronous waiting 64 | var tcs = new TaskCompletionSource(); 65 | 66 | // Fields to track previous progress and time for speed calculation 67 | long previousBytesReceived = 0; 68 | DateTime lastUpdateTime = DateTime.Now; 69 | 70 | // Subscribe to progress changed event 71 | client.DownloadProgressChanged += (sender, e) => 72 | { 73 | int progressPercentage = e.ProgressPercentage; 74 | long bytesReceived = e.BytesReceived; 75 | long totalBytesToReceive = e.TotalBytesToReceive; 76 | 77 | if (qbdlxForm._qbdlxForm.downloadSpeedCheckbox.Checked) 78 | { 79 | // Calculate download speed in bytes per second 80 | DateTime currentTime = DateTime.Now; 81 | double timeDiff = (currentTime - lastUpdateTime).TotalSeconds; 82 | 83 | if (timeDiff > 0) 84 | { 85 | long bytesDiff = bytesReceived - previousBytesReceived; 86 | double speed = bytesDiff / timeDiff; // bytes per second 87 | string speedText = speed > 1024 * 1024 88 | ? $"{speed / (1024 * 1024):F2} MB/s" 89 | : $"{speed / 1024:F2} KB/s"; 90 | 91 | qbdlxForm._qbdlxForm.BeginInvoke(new Action(() => { qbdlxForm._qbdlxForm.progressLabel.Text = $"{qbdlxForm._qbdlxForm.progressLabelActive} - {progressPercentage}% [{speedText}]"; })); 92 | } 93 | } 94 | else 95 | { 96 | qbdlxForm._qbdlxForm.BeginInvoke(new Action(() => { qbdlxForm._qbdlxForm.progressLabel.Text = $"{qbdlxForm._qbdlxForm.progressLabelActive} - {progressPercentage}%"; })); 97 | } 98 | }; 99 | 100 | // Handle completion of the download 101 | client.DownloadFileCompleted += (sender, e) => 102 | { 103 | if (e.Error != null) 104 | { 105 | qbdlxForm._qbdlxForm.logger.Error("Download failed: " + e.Error.Message); 106 | tcs.SetException(e.Error); 107 | return; 108 | } 109 | 110 | qbdlxForm._qbdlxForm.logger.Debug("Download complete."); 111 | 112 | if (Settings.Default.fixMD5s && audio_format.Contains("flac")) 113 | { 114 | qbdlxForm._qbdlxForm.logger.Debug("Attempting to fix unset MD5s..."); 115 | fixMD5.fixMD5(tempFile, "flac"); 116 | } 117 | 118 | qbdlxForm._qbdlxForm.logger.Debug("Starting file metadata tagging"); 119 | TagFile.WriteToFile(tempFile, artworkPath, QoAlbum, QoItem); 120 | 121 | // Move the file with the full name (Zeta Long Paths to avoid MAX_PATH error) 122 | qbdlxForm._qbdlxForm.logger.Debug("Moving temp file to - " + filePath); 123 | ZlpIOHelper.MoveFile(tempFile, filePath); 124 | 125 | // Signal the TaskCompletionSource that the task is complete 126 | tcs.SetResult(true); 127 | }; 128 | 129 | // Start the asynchronous download 130 | qbdlxForm._qbdlxForm.logger.Debug("Downloading to temp file..."); 131 | if (QoAlbum.MediaCount > 1) 132 | { 133 | qbdlxForm._qbdlxForm.logger.Debug("More than 1 volume, using subfolders for each volume"); 134 | ZlpIOHelper.CreateDirectory(ZlpPathHelper.GetDirectoryPathNameFromFilePath(downloadPath + "CD " + QoItem.MediaNumber.ToString().PadLeft(paddingNumbers.padDiscs(QoAlbum), '0') + ZlpPathHelper.DirectorySeparatorChar)); 135 | } 136 | else 137 | { 138 | ZlpIOHelper.CreateDirectory(ZlpPathHelper.GetDirectoryPathNameFromFilePath(downloadPath)); 139 | } 140 | 141 | client.DownloadFileAsync(new Uri(streamUrl), tempFile); 142 | 143 | // Await the TaskCompletionSource to wait until download completes 144 | await tcs.Task; 145 | } 146 | } 147 | 148 | public async Task DownloadArtwork(string downloadPath, Album QoAlbum) 149 | { 150 | using (var client = new WebClient()) 151 | { 152 | // Use secure connection 153 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; 154 | 155 | // Download cover art (600x600) to the download path 156 | qbdlxForm._qbdlxForm.logger.Debug("Downloading Cover Art"); 157 | ZlpIOHelper.CreateDirectory(ZlpPathHelper.GetDirectoryPathNameFromFilePath(downloadPath)); 158 | 159 | if (!ZlpIOHelper.FileExists(downloadPath + @"Cover.jpg")) 160 | { 161 | qbdlxForm._qbdlxForm.logger.Debug("Saved artwork Cover.jpg not found, downloading"); 162 | try { await client.DownloadFileTaskAsync(QoAlbum.Image.Large.Replace("_600", "_" + qbdlxForm._qbdlxForm.savedArtSize), ZlpPathHelper.GetFullPath(downloadPath + @"Cover.jpg")); } catch (Exception ex) { qbdlxForm._qbdlxForm.logger.Error($"Failed to download cover art. Error below:\r\n{ex}"); } 163 | } 164 | if (!ZlpIOHelper.FileExists(downloadPath + qbdlxForm._qbdlxForm.embeddedArtSize + @".jpg")) 165 | { 166 | qbdlxForm._qbdlxForm.logger.Debug("Saved artwork for embedding not found, downloading"); 167 | try { await client.DownloadFileTaskAsync(QoAlbum.Image.Large.Replace("_600", "_" + qbdlxForm._qbdlxForm.embeddedArtSize), ZlpPathHelper.GetFullPath(downloadPath + qbdlxForm._qbdlxForm.embeddedArtSize + @".jpg")); } catch (Exception ex) { qbdlxForm._qbdlxForm.logger.Error($"Failed to download cover art. Error below:\r\n{ex}"); } 168 | } 169 | } 170 | } 171 | 172 | public async Task DownloadGoody(string downloadPath, Album QoAlbum, Goody QoGoody) 173 | { 174 | using (var client = new WebClient()) 175 | { 176 | // Use secure connection 177 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; 178 | 179 | // Download goody to the download path 180 | ZlpIOHelper.CreateDirectory(ZlpPathHelper.GetDirectoryPathNameFromFilePath(downloadPath)); 181 | await client.DownloadFileTaskAsync(QoGoody.Url, downloadPath + renameTemplates.GetSafeFilename(QoAlbum.Title) + " (" + QoGoody.Id + @").pdf"); 182 | } 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/Download/DownloadTrack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using QopenAPI; 4 | using QobuzDownloaderX.Properties; 5 | using ZetaLongPaths; 6 | 7 | namespace QobuzDownloaderX 8 | { 9 | class DownloadTrack 10 | { 11 | public Service QoService = new Service(); 12 | public User QoUser = new User(); 13 | public Item QoItem = new Item(); 14 | public QopenAPI.Stream QoStream = new QopenAPI.Stream(); 15 | 16 | public int paddedTrackLength { get; set; } 17 | public int paddedDiscLength { get; set; } 18 | 19 | public string downloadPath { get; set; } 20 | public string filePath { get; set; } 21 | 22 | PaddingNumbers padNumber = new PaddingNumbers(); 23 | DownloadFile downloadFile = new DownloadFile(); 24 | RenameTemplates renameTemplates = new RenameTemplates(); 25 | GetInfo getInfo = new GetInfo(); 26 | FixMD5 fixMD5 = new FixMD5(); 27 | 28 | public void clearOutputText() => getInfo.outputText = null; 29 | 30 | private bool VerifyStreamable(Item QoItem, int paddedTrackLength) 31 | { 32 | if (QoItem.Streamable || !Settings.Default.streamableCheck) return true; 33 | 34 | getInfo.updateDownloadOutput($"{qbdlxForm._qbdlxForm.downloadOutputTrNotStream.Replace("{TrackNumber}", QoItem.TrackNumber.ToString().PadLeft(paddedTrackLength, '0'))}\r\n"); 35 | return false; 36 | } 37 | 38 | private bool CheckForExistingFile(string filePath, int paddedTrackLength, Item QoItem) 39 | { 40 | if (ZlpIOHelper.FileExists(filePath)) 41 | { 42 | getInfo.updateDownloadOutput($"{qbdlxForm._qbdlxForm.downloadOutputFileExists.Replace("{TrackNumber}", QoItem.TrackNumber.ToString().PadLeft(paddedTrackLength, '0'))}\r\n"); 43 | return true; 44 | } 45 | return false; 46 | } 47 | 48 | private void CleanupArtwork(string downloadPath) 49 | { 50 | string artworkPath = downloadFile.artworkPath; 51 | if (ZlpIOHelper.FileExists(artworkPath)) 52 | { 53 | ZlpIOHelper.DeleteFile(artworkPath); 54 | } 55 | } 56 | 57 | private void CreatePadding(Album QoAlbum, Playlist QoPlaylist) 58 | { 59 | if (QoPlaylist != null) 60 | { 61 | paddedTrackLength = padNumber.padPlaylistTracks(QoPlaylist); 62 | paddedDiscLength = 2; 63 | } 64 | else 65 | { 66 | paddedTrackLength = padNumber.padTracks(QoAlbum); 67 | paddedDiscLength = padNumber.padDiscs(QoAlbum); 68 | } 69 | } 70 | 71 | private async Task DownloadAndSaveTrack(string app_id, string format_id, string user_auth_token, string app_secret, Album QoAlbum, Item QoItem, Playlist QoPlaylist, string downloadPath, string filePath, string audio_format, int paddedTrackLength) 72 | { 73 | var QoStream = QoService.TrackGetFileUrl(QoItem.Id.ToString(), format_id, app_id, user_auth_token, app_secret); 74 | string streamURL = QoStream.StreamURL; 75 | 76 | // Display download status (depending on track number or playlist position number) 77 | var trackName = QoItem.Version == null ? QoItem.Title : $"{QoItem.Title.Trim()} ({QoItem.Version})"; 78 | if (QoPlaylist == null) { getInfo.updateDownloadOutput($"{qbdlxForm._qbdlxForm.downloadOutputDownloading} - {QoItem.TrackNumber.ToString().PadLeft(paddedTrackLength, '0')} {trackName}..."); } 79 | else { getInfo.updateDownloadOutput($"{qbdlxForm._qbdlxForm.downloadOutputDownloading} - {QoItem.Position.ToString().PadLeft(paddedTrackLength, '0')} {trackName}..."); } 80 | 81 | // Download stream 82 | await downloadFile.DownloadStream(streamURL, downloadPath, filePath, audio_format, QoAlbum, QoItem); 83 | getInfo.updateDownloadOutput($" {qbdlxForm._qbdlxForm.downloadOutputDone}\r\n"); 84 | } 85 | 86 | public async Task DownloadTrackAsync(string downloadType, string app_id, string album_id, string format_id, string audio_format, string user_auth_token, string app_secret, string downloadLocation, string artistTemplate, string albumTemplate, string trackTemplate, Album QoAlbum, Item QoItem) 87 | { 88 | // Empty output on main form if individual track download 89 | if (downloadType == "track") { getInfo.outputText = null; } 90 | 91 | try 92 | { 93 | // Create padding for tracks and possible multi-volume releases 94 | CreatePadding(QoAlbum, null); 95 | 96 | try 97 | { 98 | // Get track info with auth if not there already 99 | if (QoItem == null) { QoItem = QoService.TrackGetWithAuth(app_id, album_id, user_auth_token); } 100 | 101 | // Check if QoItem is still null. If so, skip. 102 | if (QoItem == null) 103 | { 104 | getInfo.updateDownloadOutput($"{qbdlxForm._qbdlxForm.downloadOutputAPIError}\r\n"); 105 | return; 106 | } 107 | 108 | // Verify Streamable 109 | try { if (!VerifyStreamable(QoItem, paddedTrackLength)) return; } catch (Exception ex) { qbdlxForm._qbdlxForm.logger.Error($"Unable to verify if track is streamable. Error below:\r\n{ex}"); } 110 | 111 | // Setting up download and file path 112 | downloadPath = await downloadFile.createPath(downloadLocation, artistTemplate, albumTemplate, trackTemplate, null, null, paddedTrackLength, paddedDiscLength, QoAlbum, QoItem, null); 113 | string trackTemplateConverted = renameTemplates.renameTemplates(trackTemplate, paddedTrackLength, paddedDiscLength, audio_format, QoAlbum, QoItem, null); 114 | 115 | if (trackTemplateConverted.Contains(@"\")) 116 | { 117 | downloadPath = downloadPath + trackTemplateConverted.Substring(0, trackTemplateConverted.LastIndexOf(@"\")) + @"\"; 118 | trackTemplateConverted = trackTemplateConverted.Substring(trackTemplateConverted.LastIndexOf(@"\") + 1); 119 | Console.WriteLine(downloadPath); 120 | } 121 | 122 | // Create subfolders for multi-volume releases 123 | if (QoAlbum.MediaCount > 1) 124 | { 125 | filePath = downloadPath + "CD " + QoItem.MediaNumber.ToString().PadLeft(paddedDiscLength, '0') + ZlpPathHelper.DirectorySeparatorChar + trackTemplateConverted.TrimEnd() + audio_format; 126 | } 127 | else 128 | { 129 | filePath = downloadPath + trackTemplateConverted.TrimEnd() + audio_format; 130 | } 131 | 132 | // Download cover art 133 | try { await downloadFile.DownloadArtwork(downloadPath, QoAlbum); } catch (Exception ex) { qbdlxForm._qbdlxForm.logger.Error($"Failed to Download Cover Art. Error below:\r\n{ex}"); } 134 | 135 | // Check for Existing File 136 | if (CheckForExistingFile(filePath, paddedTrackLength, QoItem)) return; 137 | 138 | // Download and Save Track 139 | await DownloadAndSaveTrack(app_id, format_id, user_auth_token, app_secret, QoAlbum, QoItem, null, downloadPath, filePath, audio_format, paddedTrackLength); 140 | } 141 | catch (Exception downloadAlbumEx) 142 | { 143 | getInfo.updateDownloadOutput("\r\n\r\n" + downloadAlbumEx + "\r\n\r\n"); 144 | Console.WriteLine(downloadAlbumEx); 145 | return; 146 | } 147 | } 148 | catch (Exception downloadAlbumEx) 149 | { 150 | Console.WriteLine(downloadAlbumEx); 151 | return; 152 | } 153 | } 154 | 155 | public async Task DownloadPlaylistTrackAsync(string app_id, string album_id, string format_id, string audio_format, string user_auth_token, string app_secret, string downloadLocation, string artistTemplate, string albumTemplate, string trackTemplate, string playlistTemplate, Album QoAlbum, Item QoItem, Playlist QoPlaylist) 156 | { 157 | try 158 | { 159 | // Create padding for tracks 160 | CreatePadding(null, QoPlaylist); 161 | 162 | // Verify Streamable 163 | if (!VerifyStreamable(QoItem, paddedTrackLength)) return; 164 | 165 | try 166 | { 167 | // Setting up download and file path 168 | downloadPath = await downloadFile.createPath(downloadLocation, null, null, trackTemplate, playlistTemplate, null, paddedTrackLength, paddedDiscLength, QoAlbum, QoItem, QoPlaylist); 169 | string trackTemplateConverted = renameTemplates.renameTemplates(trackTemplate, paddedTrackLength, paddedDiscLength, audio_format, QoAlbum, QoItem, QoPlaylist); 170 | 171 | if (trackTemplateConverted.Contains(@"\")) 172 | { 173 | downloadPath = downloadPath + trackTemplateConverted.Substring(0, trackTemplateConverted.LastIndexOf(@"\")) + @"\"; 174 | trackTemplateConverted = trackTemplateConverted.Substring(trackTemplateConverted.LastIndexOf(@"\") + 1); 175 | Console.WriteLine(downloadPath); 176 | } 177 | 178 | filePath = downloadPath + trackTemplateConverted.TrimEnd() + audio_format; 179 | 180 | // Check for Existing File 181 | if (CheckForExistingFile(filePath, paddedTrackLength, QoItem)) return; 182 | 183 | // Download cover art 184 | try { await downloadFile.DownloadArtwork(downloadPath, QoAlbum); } catch (Exception ex) { Console.WriteLine($"Failed to Download Cover Art. Error below:\r\n{ex}"); } 185 | 186 | // Download and Save Track 187 | await DownloadAndSaveTrack(app_id, format_id, user_auth_token, app_secret, QoAlbum, QoItem, QoPlaylist, downloadPath, filePath, audio_format, paddedTrackLength); 188 | 189 | // Delete image used for embedded artwork 190 | CleanupArtwork(downloadPath); 191 | } 192 | catch (Exception downloadAlbumEx) 193 | { 194 | getInfo.updateDownloadOutput("\r\n\r\n" + downloadAlbumEx + "\r\n\r\n"); 195 | Console.WriteLine(downloadAlbumEx); 196 | return; 197 | } 198 | 199 | } 200 | catch (Exception downloadAlbumEx) 201 | { 202 | Console.WriteLine(downloadAlbumEx); 203 | return; 204 | } 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/Download/DownloadAlbum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using QopenAPI; 4 | using System.IO; 5 | using QobuzDownloaderX.Properties; 6 | using System.Text.RegularExpressions; 7 | using ZetaLongPaths; 8 | 9 | namespace QobuzDownloaderX 10 | { 11 | public static class StringExt 12 | { 13 | public static string Truncate(this string value, int maxLength) 14 | { 15 | // This is currently unused, but leaving this here in case it needs to be used in the future. 16 | if (string.IsNullOrEmpty(value)) return value; 17 | return value.Length <= maxLength ? value : value.Substring(0, maxLength); 18 | } 19 | } 20 | 21 | class DownloadAlbum 22 | { 23 | public Service QoService = new Service(); 24 | public User QoUser = new User(); 25 | public Album QoAlbum = new Album(); 26 | public Item QoItem = new Item(); 27 | public QopenAPI.Stream QoStream = new QopenAPI.Stream(); 28 | 29 | public int paddedTrackLength { get; set; } 30 | public int paddedDiscLength { get; set; } 31 | 32 | public string downloadPath { get; set; } 33 | 34 | PaddingNumbers padNumber = new PaddingNumbers(); 35 | DownloadTrack downloadTrack = new DownloadTrack(); 36 | DownloadFile downloadFile = new DownloadFile(); 37 | GetInfo getInfo = new GetInfo(); 38 | 39 | private async Task DownloadArtwork(string downloadPath, Album album) 40 | { 41 | try 42 | { 43 | qbdlxForm._qbdlxForm.logger.Debug("Downloading artwork..."); 44 | await downloadFile.DownloadArtwork(downloadPath, album); 45 | qbdlxForm._qbdlxForm.logger.Debug("Artwork download complete"); 46 | } 47 | catch (Exception ex) 48 | { 49 | qbdlxForm._qbdlxForm.logger.Error($"Artwork download failed: {ex}"); 50 | Console.WriteLine("Unable to download artwork"); 51 | } 52 | } 53 | 54 | private void DeleteEmbeddedArtwork(string downloadPath) 55 | { 56 | try 57 | { 58 | qbdlxForm._qbdlxForm.logger.Debug("Deleting embedded artwork..."); 59 | ZlpIOHelper.DeleteFile($"{downloadPath}{qbdlxForm._qbdlxForm.embeddedArtSize}.jpg"); 60 | } 61 | catch 62 | { 63 | qbdlxForm._qbdlxForm.logger.Warning("Unable to delete embedded artwork"); 64 | } 65 | } 66 | 67 | private async Task DownloadGoodiesAsync(string downloadPath, Album album) 68 | { 69 | try 70 | { 71 | foreach (var goody in album.Goodies) 72 | { 73 | getInfo.updateDownloadOutput($"{qbdlxForm._qbdlxForm.downloadOutputGoodyFound} "); 74 | 75 | if (goody.Url == null) 76 | { 77 | qbdlxForm._qbdlxForm.logger.Warning("No URL found for the goody, skipping."); 78 | getInfo.updateDownloadOutput($"\r\n{qbdlxForm._qbdlxForm.downloadOutputGoodyNoURL}\r\n"); 79 | continue; 80 | } 81 | 82 | qbdlxForm._qbdlxForm.logger.Debug("Downloading goody..."); 83 | await downloadFile.DownloadGoody(downloadPath, album, goody); 84 | qbdlxForm._qbdlxForm.logger.Debug("Goody download complete"); 85 | getInfo.updateDownloadOutput($"{qbdlxForm._qbdlxForm.downloadOutputDone}\r\n"); 86 | } 87 | } 88 | catch 89 | { 90 | qbdlxForm._qbdlxForm.logger.Warning("No goodies found or failed to download"); 91 | } 92 | } 93 | 94 | public async Task DownloadTracksAsync(string app_id, string album_id, string format_id, string audio_format, string user_auth_token, string app_secret, string downloadLocation, string artistTemplate, string albumTemplate, string trackTemplate, Album album) 95 | { 96 | foreach (var item in album.Tracks.Items) 97 | { 98 | try 99 | { 100 | qbdlxForm._qbdlxForm.logger.Debug("Downloading track..."); 101 | await downloadTrack.DownloadTrackAsync("album", app_id, album_id, format_id, audio_format, user_auth_token, app_secret, downloadLocation, artistTemplate, albumTemplate, trackTemplate, album, QoService.TrackGetWithAuth(app_id, item.Id.ToString(), user_auth_token)); 102 | qbdlxForm._qbdlxForm.logger.Debug("Track download complete"); 103 | } 104 | catch (Exception ex) 105 | { 106 | qbdlxForm._qbdlxForm.logger.Error($"Track download failed: {ex}"); 107 | getInfo.updateDownloadOutput($"\r\n{ex}"); 108 | Console.WriteLine(ex); 109 | return; 110 | } 111 | } 112 | } 113 | 114 | public async Task DownloadAlbumAsync(string app_id, string album_id, string format_id, string audio_format, string user_auth_token, string app_secret, string downloadLocation, string artistTemplate, string albumTemplate, string trackTemplate, Album QoAlbum) 115 | { 116 | qbdlxForm._qbdlxForm.logger.Debug("Starting album download (downloadAlbum)"); 117 | 118 | // Clear output text from DownloadTrack to avoid text from previous downloads sticking around. 119 | qbdlxForm._qbdlxForm.logger.Debug("Clearing output text"); 120 | downloadTrack.clearOutputText(); 121 | 122 | getInfo.outputText = null; 123 | 124 | if (QoAlbum.Streamable == false) 125 | { 126 | if (Settings.Default.streamableCheck == true) 127 | { 128 | qbdlxForm._qbdlxForm.logger.Debug("Streamable tag is set to false on Qobuz, and streamable check is enabled, skipping download"); 129 | getInfo.updateDownloadOutput(qbdlxForm._qbdlxForm.downloadOutputAlNotStream); 130 | getInfo.updateDownloadOutput("\r\n" + qbdlxForm._qbdlxForm.downloadOutputCompleted); 131 | return; 132 | } 133 | else 134 | { 135 | qbdlxForm._qbdlxForm.logger.Debug("Streamable tag is set to false on Qobuz, but streamable check is disabled, attempting download"); 136 | } 137 | } 138 | 139 | try 140 | { 141 | // Find the how many characters are needed for padding 142 | paddedTrackLength = padNumber.padTracks(QoAlbum); 143 | paddedDiscLength = padNumber.padTracks(QoAlbum); 144 | 145 | // Set download path 146 | downloadPath = await downloadFile.createPath(downloadLocation, artistTemplate, albumTemplate, trackTemplate, null, null, paddedTrackLength, paddedDiscLength, QoAlbum, null, null); 147 | qbdlxForm._qbdlxForm.logger.Debug("Download path: " + downloadPath); 148 | 149 | // Download artwork 150 | await DownloadArtwork(downloadPath, QoAlbum); 151 | 152 | // Download tracks 153 | await DownloadTracksAsync(app_id, album_id, format_id, audio_format, user_auth_token, app_secret, downloadLocation, artistTemplate, albumTemplate, trackTemplate, QoAlbum); 154 | 155 | // Delete image used for embedding artwork 156 | DeleteEmbeddedArtwork(downloadPath); 157 | 158 | // Set current output text 159 | getInfo.outputText = qbdlxForm._qbdlxForm.downloadOutput.Text; 160 | 161 | // Download goodies 162 | await DownloadGoodiesAsync(downloadPath, QoAlbum); 163 | 164 | // Tell user that download is completed 165 | qbdlxForm._qbdlxForm.logger.Debug("All downloads completed!"); 166 | 167 | // Write post template 168 | WritePostTemplate(QoAlbum); 169 | } 170 | catch (Exception downloadAlbumEx) 171 | { 172 | qbdlxForm._qbdlxForm.logger.Error("Error occured during downloadAlbum, error below:\r\n" + downloadAlbumEx); 173 | Console.WriteLine(downloadAlbumEx); 174 | return; 175 | } 176 | } 177 | 178 | private void WritePostTemplate(Album album) 179 | { 180 | try 181 | { 182 | // Not useful at all for normal users, but I use it so... yeah 183 | qbdlxForm._qbdlxForm.logger.Debug("Writing post template..."); 184 | var templateDate = DateTime.Parse(album.ReleaseDateOriginal).ToString("MMMM d, yyyy"); 185 | ZlpIOHelper.WriteAllText("post_template.txt", String.Empty); 186 | 187 | using (StreamWriter sw = File.AppendText("post_template.txt")) 188 | { 189 | sw.WriteLine("[center]"); 190 | sw.WriteLine($"[CoverArt]{album.Image.Large}[/CoverArt]"); 191 | sw.WriteLine($"[b]Release Date:[/b] {templateDate}"); 192 | sw.WriteLine($"[b]Genre:[/b] {album.Genre.Name.Replace("Alternativ und Indie", "Alternative").Replace("Hörbücher", "Comedy/Other")}"); 193 | sw.WriteLine(""); 194 | sw.WriteLine("[b]TRACKLIST[/b]"); 195 | sw.WriteLine("-----------------------------------"); 196 | 197 | foreach (var item in album.Tracks.Items) 198 | { 199 | sw.WriteLine(item.Version != null 200 | ? $"{item.TrackNumber}. {item.Title.TrimEnd()} ({item.Version})" 201 | : $"{item.TrackNumber}. {item.Title.TrimEnd()}"); 202 | } 203 | 204 | sw.WriteLine("-----------------------------------"); 205 | sw.WriteLine(""); 206 | sw.WriteLine("[b]DOWNLOADS[/b]"); 207 | sw.WriteLine("-----------------------------------"); 208 | sw.WriteLine($"[spoiler={Regex.Replace(album.Label.Name, @"\s+", " ")} / {album.UPC} / WEB]"); 209 | if (album.MaximumBitDepth > 16) 210 | { 211 | sw.WriteLine($"[format=FLAC / Lossless ({album.MaximumBitDepth.ToString()}bit/{album.MaximumSamplingRate.ToString()}kHz) / WEB]"); 212 | sw.WriteLine($"Uploaded by @{Settings.Default.postTemplateUsername.ToString()}"); 213 | sw.WriteLine(""); 214 | sw.WriteLine("DOWNLOAD"); 215 | sw.WriteLine("REPLACE THIS WITH URL"); 216 | sw.WriteLine("[/format]"); 217 | } 218 | sw.WriteLine("[format=FLAC / Lossless / WEB]"); 219 | sw.WriteLine($"Uploaded by @{Settings.Default.postTemplateUsername.ToString()}"); 220 | sw.WriteLine(""); 221 | sw.WriteLine("DOWNLOAD"); 222 | sw.WriteLine("REPLACE THIS WITH URL"); 223 | sw.WriteLine("[/format]"); 224 | sw.WriteLine("[format=MP3 / 320 / WEB]"); 225 | sw.WriteLine($"Uploaded by @{Settings.Default.postTemplateUsername.ToString()}"); 226 | sw.WriteLine(""); 227 | sw.WriteLine("DOWNLOAD"); 228 | sw.WriteLine("REPLACE THIS WITH URL"); 229 | sw.WriteLine("[/format]"); 230 | sw.WriteLine("[/spoiler]"); 231 | sw.WriteLine("-----------------------------------"); 232 | sw.WriteLine(""); 233 | sw.WriteLine("REPLACE WITH STREAM LINK"); 234 | sw.WriteLine(""); 235 | sw.WriteLine("[/center]"); 236 | } 237 | } 238 | catch (Exception ex) 239 | { 240 | qbdlxForm._qbdlxForm.logger.Error($"Error writing post template: {ex}"); 241 | } 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/RenameTemplates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.IO; 4 | using QopenAPI; 5 | using System.Text.RegularExpressions; 6 | using ZetaLongPaths; 7 | 8 | namespace QobuzDownloaderX 9 | { 10 | class RenameTemplates 11 | { 12 | public string GetSafeFilename(string filename) 13 | { 14 | // For converting illegal filename characters to an underscore. 15 | Console.WriteLine(string.Join("_", filename.TrimEnd().TrimEnd('.').TrimEnd('.').Split(Path.GetInvalidFileNameChars()))); 16 | return string.Join("_", filename.TrimEnd().TrimEnd('.').TrimEnd('.').Split(Path.GetInvalidFileNameChars())); 17 | } 18 | 19 | public string GetReleaseArtists(Album QoAlbum) 20 | { 21 | var mainArtists = QoAlbum.Artists.Where(a => a.Roles.Contains("main-artist")).ToList(); 22 | if (mainArtists.Count > 1) 23 | { 24 | var allButLastArtist = string.Join(", ", mainArtists.Take(mainArtists.Count - 1).Select(a => a.Name)); 25 | var lastArtist = mainArtists.Last().Name; 26 | return $"{allButLastArtist} & {lastArtist}"; 27 | } 28 | return QoAlbum.Artist.Name; 29 | } 30 | 31 | private string ReplaceParentalWarningTags(string template, bool isExplicit) 32 | { 33 | template = template.Replace("%trackpa%", isExplicit ? "Explicit" : "Clean"); 34 | template = template.Replace("%trackpashort%", isExplicit ? "E" : "C"); 35 | template = template.Replace("%trackpaifex%", isExplicit ? "Explicit" : ""); 36 | template = template.Replace("%trackpaifexshort%", isExplicit ? "E" : ""); 37 | template = template.Replace("%trackpaifcl%", isExplicit ? "" : "Clean"); 38 | template = template.Replace("%trackpaifclshort%", isExplicit ? "" : "C"); 39 | template = template.Replace("%trackpaenclosed%", isExplicit ? $"(Explicit)" : $"(Clean)"); 40 | template = template.Replace("%trackpaenclosed[]%", isExplicit ? $"[Explicit]" : $"[Clean]"); 41 | template = template.Replace("%trackpaenclosedshort%", isExplicit ? $"(E)" : $"(C)"); 42 | template = template.Replace("%trackpaenclosedshort[]%", isExplicit ? $"[E]" : $"[C]"); 43 | template = template.Replace("%trackpaifexenclosed%", isExplicit ? $"(Explicit)" : $""); 44 | template = template.Replace("%trackpaifexenclosed[]%", isExplicit ? $"[Explicit]" : $""); 45 | template = template.Replace("%trackpaifexenclosedshort%", isExplicit ? $"(E)" : $""); 46 | template = template.Replace("%trackpaifexenclosedshort[]%", isExplicit ? $"[E]" : $""); 47 | template = template.Replace("%trackpaifclenclosed%", isExplicit ? $"" : $"(Clean)"); 48 | template = template.Replace("%trackpaifclenclosed[]%", isExplicit ? $"" : $"[Clean]"); 49 | template = template.Replace("%trackpaifclenclosedshort%", isExplicit ? $"" : $"(C)"); 50 | template = template.Replace("%trackpaifclenclosedshort[]%", isExplicit ? $"" : $"[C]"); 51 | template = template.Replace("%albumpa%", isExplicit ? "Explicit" : "Clean"); 52 | template = template.Replace("%albumpashort%", isExplicit ? "E" : "C"); 53 | template = template.Replace("%albumpaifex%", isExplicit ? "Explicit" : ""); 54 | template = template.Replace("%albumpaifexshort%", isExplicit ? "E" : ""); 55 | template = template.Replace("%albumpaifcl%", isExplicit ? "" : "Clean"); 56 | template = template.Replace("%albumpaifclshort%", isExplicit ? "" : "C"); 57 | template = template.Replace("%albumpaenclosed%", isExplicit ? $"(Explicit)" : $"(Clean)"); 58 | template = template.Replace("%albumpaenclosed[]%", isExplicit ? $"[Explicit]" : $"[Clean]"); 59 | template = template.Replace("%albumpaenclosedshort%", isExplicit ? $"(E)" : $"(C)"); 60 | template = template.Replace("%albumpaenclosedshort[]%", isExplicit ? $"[E]" : $"[C]"); 61 | template = template.Replace("%albumpaifexenclosed%", isExplicit ? $"(Explicit)" : $""); 62 | template = template.Replace("%albumpaifexenclosed[]%", isExplicit ? $"[Explicit]" : $""); 63 | template = template.Replace("%albumpaifexenclosedshort%", isExplicit ? $"(E)" : $""); 64 | template = template.Replace("%albumpaifexenclosedshort[]%", isExplicit ? $"[E]" : $""); 65 | template = template.Replace("%albumpaifclenclosed%", isExplicit ? $"" : $"(Clean)"); 66 | template = template.Replace("%albumpaifclenclosed[]%", isExplicit ? $"" : $"[Clean]"); 67 | template = template.Replace("%albumpaifclenclosedshort%", isExplicit ? $"" : $"(C)"); 68 | template = template.Replace("%albumpaifclenclosedshort[]%", isExplicit ? $"" : $"[C]"); 69 | return template; 70 | } 71 | 72 | private string RenameFormatTemplate(string template, string formatId, string fileFormat, int maximumBitDepth, double maximumSamplingRate, string formatWithHiresQualityPlaceholder, string formatWithQualityPlaceholder) 73 | { 74 | fileFormat = fileFormat.ToUpper().TrimStart('.'); 75 | 76 | switch (formatId) 77 | { 78 | case "5": 79 | template = template 80 | .Replace(formatWithHiresQualityPlaceholder, fileFormat) 81 | .Replace(formatWithQualityPlaceholder, fileFormat); 82 | break; 83 | 84 | case "6": 85 | template = template 86 | .Replace(formatWithHiresQualityPlaceholder, fileFormat) 87 | .Replace(formatWithQualityPlaceholder, $"{fileFormat} ({maximumBitDepth}bit-{maximumSamplingRate}kHz)"); 88 | break; 89 | 90 | case "7": 91 | case "27": 92 | if (maximumBitDepth == 16) 93 | { 94 | template = template 95 | .Replace(formatWithHiresQualityPlaceholder, fileFormat) 96 | .Replace(formatWithQualityPlaceholder, $"{fileFormat} ({maximumBitDepth}bit-{maximumSamplingRate}kHz)"); 97 | } 98 | else if (maximumSamplingRate < 192) 99 | { 100 | template = template.Replace(formatWithQualityPlaceholder, formatWithHiresQualityPlaceholder); 101 | 102 | if (maximumSamplingRate < 96) 103 | { 104 | template = template.Replace(formatWithHiresQualityPlaceholder, $"{fileFormat} ({maximumBitDepth}bit-{maximumSamplingRate}kHz)"); 105 | } 106 | else if (maximumSamplingRate > 96 && maximumSamplingRate < 192) 107 | { 108 | if (formatId == "7" && maximumSamplingRate == 176.4) 109 | { 110 | template = template.Replace(formatWithHiresQualityPlaceholder, $"{fileFormat} (24bit-88.2kHz)"); 111 | } 112 | else if (formatId == "7") 113 | { 114 | template = template.Replace(formatWithHiresQualityPlaceholder, $"{fileFormat} (24bit-96kHz)"); 115 | } 116 | else 117 | { 118 | template = template.Replace(formatWithHiresQualityPlaceholder, $"{fileFormat} ({maximumBitDepth}bit-{maximumSamplingRate}kHz)"); 119 | } 120 | } 121 | else 122 | { 123 | template = template.Replace(formatWithHiresQualityPlaceholder, $"{fileFormat} (24bit-96kHz)"); 124 | } 125 | } 126 | else 127 | { 128 | template = template.Replace(formatWithQualityPlaceholder, formatWithHiresQualityPlaceholder); 129 | template = template.Replace(formatWithHiresQualityPlaceholder, $"{fileFormat} (24bit-192kHz)"); 130 | } 131 | break; 132 | } 133 | 134 | return template; 135 | } 136 | 137 | public string renameTemplates(string template, int paddedTrackLength, int paddedDiscLength, string fileFormat, Album QoAlbum, Item QoItem, Playlist QoPlaylist) 138 | { 139 | qbdlxForm._qbdlxForm.logger.Debug("Renaming user template - " + template); 140 | 141 | // Convert all text between % symbols to lowercase 142 | template = Regex.Replace(template, @"%(.*?)%", match => match.Value.ToLower()); 143 | 144 | // Keep backslashes to be used to make new folders 145 | if (template.Contains(ZlpPathHelper.DirectorySeparatorChar)) 146 | { 147 | template = template.Replace(@"\", "{backslash}").Replace(@"/", "{forwardslash}"); 148 | } 149 | 150 | // Artist Templates 151 | /* bro there ain't shit here */ 152 | 153 | // Track Templates 154 | if (QoItem != null) 155 | { 156 | template = ReplaceParentalWarningTags(template, QoItem.ParentalWarning); 157 | template = template 158 | .Replace("%trackid%", QoItem.Id.ToString()) 159 | .Replace("%trackartist%", QoItem?.Performer?.Name?.ToString()) 160 | .Replace("%trackcomposer%", QoItem?.Composer?.Name?.ToString()) 161 | .Replace("%tracknumber%", QoItem.TrackNumber.ToString().PadLeft(paddedTrackLength, '0')) 162 | .Replace("%isrc%", QoItem.ISRC.ToString()) 163 | .Replace("%trackbitdepth%", QoItem.MaximumBitDepth.ToString()) 164 | .Replace("%tracksamplerate%", QoItem.MaximumSamplingRate.ToString()) 165 | .Replace("%tracktitle%", QoItem.Version == null ? QoItem.Title : $"{QoItem.Title.TrimEnd()} ({QoItem.Version})"); 166 | 167 | // Track Format Templates 168 | template = template.Replace("%trackformat%", fileFormat.ToUpper().TrimStart('.')); 169 | template = RenameFormatTemplate(template, qbdlxForm._qbdlxForm.format_id, fileFormat, QoItem.MaximumBitDepth, QoItem.MaximumSamplingRate, "%trackformatwithhiresquality%", "%trackformatwithquality%"); 170 | } 171 | 172 | // Album Templates 173 | if (QoAlbum != null) 174 | { 175 | template = ReplaceParentalWarningTags(template, QoAlbum.ParentalWarning); 176 | template = template 177 | .Replace("%albumid%", QoAlbum.Id.ToString()) 178 | .Replace("%albumurl%", QoAlbum.Url.ToString()) 179 | .Replace("%artistname%", GetReleaseArtists(QoAlbum)) 180 | .Replace("%albumgenre%", QoAlbum?.Genre?.Name) 181 | .Replace("%albumcomposer%", QoAlbum?.Composer?.Name?.ToString()) 182 | .Replace("%label%", Regex.Replace(QoAlbum.Label.Name, @"\s+", " ")) // Qobuz sometimes has multiple spaces in place of where a single space should be when it comes to Labels 183 | .Replace("%copyright%", QoAlbum.Copyright) 184 | .Replace("%upc%", QoAlbum.UPC) 185 | .Replace("%releasedate%", QoAlbum.ReleaseDateOriginal) 186 | .Replace("%year%", UInt32.Parse(QoAlbum.ReleaseDateOriginal.Substring(0, 4)).ToString()) 187 | .Replace("%releasetype%", char.ToUpper(QoAlbum.ProductType.First()) + QoAlbum.ProductType.Substring(1).ToLower()) 188 | .Replace("%bitdepth%", QoAlbum.MaximumBitDepth.ToString()) 189 | .Replace("%samplerate%", QoAlbum.MaximumSamplingRate.ToString()) 190 | .Replace("%albumtitle%", QoAlbum.Version == null ? QoAlbum.Title : $"{QoAlbum.Title.TrimEnd()} ({QoAlbum.Version})") 191 | .Replace("%format%", fileFormat.ToUpper().TrimStart('.')); 192 | } 193 | 194 | if (QoPlaylist == null) 195 | { 196 | // Release Format Templates 197 | template = RenameFormatTemplate(template, qbdlxForm._qbdlxForm.format_id, fileFormat, QoAlbum.MaximumBitDepth, QoAlbum.MaximumSamplingRate, "%formatwithhiresquality%", "%formatwithquality%"); 198 | } 199 | else 200 | { 201 | // Playlist Templates 202 | template = template 203 | .Replace("%playlistid%", QoPlaylist.Id.ToString()) 204 | .Replace("%playlisttitle%", QoPlaylist.Name) 205 | .Replace("%format%", fileFormat.ToUpper().TrimStart('.')) 206 | .Replace("%formatwithhiresquality%", fileFormat.ToUpper().TrimStart('.')) 207 | .Replace("%formatwithquality%", fileFormat.ToUpper().TrimStart('.')); 208 | } 209 | 210 | // GetSafeFilename call to make sure path will be valid 211 | template = GetSafeFilename(template); 212 | 213 | // Remove any double spaces 214 | template = Regex.Replace(Regex.Replace(template.Replace("{backslash}", @"\").Replace("{forwardslash}", @"/"), @"\s+", " ").Replace(@" \", @"\"), @"\s+\\", " "); // Replace slash placeholders & remove double spaces 215 | 216 | qbdlxForm._qbdlxForm.logger.Debug("Template output - " + template); 217 | return template; 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Helpers/SearchPanelHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using QopenAPI; 3 | using QobuzDownloaderX.Properties; 4 | using System.Windows.Forms; 5 | using System.Drawing; 6 | using System.Threading.Tasks; 7 | 8 | namespace QobuzDownloaderX 9 | { 10 | class SearchPanelHelper 11 | { 12 | public Service QoService = new Service(); 13 | public User QoUser = new User(); 14 | public Item QoItem = new Item(); 15 | public SearchAlbumResult QoAlbumSearch = new SearchAlbumResult(); 16 | public SearchTrackResult QoTrackSearch = new SearchTrackResult(); 17 | 18 | public void SearchInitiate(string searchType, string app_id, string searchQuery, string user_auth_token) 19 | { 20 | if (searchType == "releases") 21 | { 22 | QoAlbumSearch = QoService.SearchAlbumsWithAuth(app_id, searchQuery, 25, 0, user_auth_token); 23 | PopulateTableAlbums(qbdlxForm._qbdlxForm, QoAlbumSearch); 24 | } 25 | else if (searchType == "tracks") 26 | { 27 | QoTrackSearch = QoService.SearchTracksWithAuth(app_id, searchQuery, 25, 0, user_auth_token); 28 | PopulateTableTracks(qbdlxForm._qbdlxForm, QoTrackSearch); 29 | } 30 | } 31 | 32 | public void PopulateTableAlbums(qbdlxForm mainForm, SearchAlbumResult QoAlbumSearch) 33 | { 34 | // Access the "items" array from the response 35 | var albums = QoAlbumSearch.Albums.Items; 36 | 37 | // Load the font name from the translation file 38 | string fontName = qbdlxForm._qbdlxForm.languageManager.GetTranslation("TranslationFont"); 39 | 40 | mainForm.Invoke((MethodInvoker)delegate () 41 | { 42 | TableLayoutPanel searchResultsTablePanel = mainForm.searchResultsTablePanel; 43 | searchResultsTablePanel.Controls.Clear(); 44 | searchResultsTablePanel.ColumnCount = 5; // Artwork, Artist, Title, Quality, Button 45 | searchResultsTablePanel.RowCount = 25; // Set row count based on the number of albums 46 | searchResultsTablePanel.AutoSize = true; 47 | 48 | // Set ColumnStyles to define the size of each column 49 | searchResultsTablePanel.ColumnStyles.Clear(); 50 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 70F)); // Artwork column 51 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200F)); // Artist name 52 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200F)); // Album title 53 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 140F)); // Quality 54 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 70F)); // Button column 55 | 56 | int rowIndex = 0; 57 | 58 | foreach (var album in albums) 59 | { 60 | // Add PictureBox for artwork 61 | PictureBox artwork = new PictureBox(); 62 | artwork.SizeMode = PictureBoxSizeMode.StretchImage; 63 | try { artwork.Load(album.Image.Large.ToString()); /* Using the thumbnail URL */ } catch { artwork.Image = Resources.qbdlx_new; /* Use QBDLX Icon as fallback */ } 64 | artwork.Width = 65; 65 | artwork.Height = 65; 66 | artwork.Anchor = AnchorStyles.None; // Center both horizontally and vertically 67 | searchResultsTablePanel.Controls.Add(artwork, 0, rowIndex); 68 | 69 | // Add Label for artist name 70 | System.Windows.Forms.Label artistName = new System.Windows.Forms.Label(); 71 | artistName.Text = album.Artist.Name.ToString(); 72 | artistName.AutoSize = true; // Disable auto-sizing to allow wrapping 73 | /*artistName.MaximumSize = new Size(0, 0);*/ // Word-wrap if needed 74 | artistName.TextAlign = ContentAlignment.MiddleCenter; // Center text horizontally and vertically 75 | artistName.Anchor = AnchorStyles.None; // Center within the cell 76 | artistName.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.LabelText); // Set text color 77 | artistName.Font = new Font(fontName, 10F, FontStyle.Regular); // Set font size and style 78 | searchResultsTablePanel.Controls.Add(artistName, 1, rowIndex); 79 | 80 | // Add Label for album title 81 | System.Windows.Forms.Label albumTitle = new System.Windows.Forms.Label(); 82 | albumTitle.Text = album.Title.ToString().TrimEnd(); 83 | if (album.Version != null) { albumTitle.Text = albumTitle.Text + " (" + album.Version + ")"; } 84 | if (album.ParentalWarning == true) { albumTitle.Text = "[E] " + albumTitle.Text; } // Add "[E]" if Qobuz lists the release with a parental warning 85 | albumTitle.AutoSize = true; 86 | /*albumTitle.MaximumSize = new Size(0, 0);*/ // Allow word-wrap 87 | albumTitle.TextAlign = ContentAlignment.MiddleCenter; // Center text horizontally and vertically 88 | albumTitle.Anchor = AnchorStyles.None; // Center within the cell 89 | albumTitle.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.LabelText); // Set text color 90 | albumTitle.Font = new Font(fontName, 10F, FontStyle.Regular); // Set font size and style 91 | searchResultsTablePanel.Controls.Add(albumTitle, 2, rowIndex); 92 | 93 | // Add Label for quality 94 | System.Windows.Forms.Label qualityLabel = new System.Windows.Forms.Label(); 95 | qualityLabel.Text = album.MaximumBitDepth.ToString() + "bit/" + album.MaximumSamplingRate + "kHz"; 96 | qualityLabel.AutoSize = true; 97 | qualityLabel.MaximumSize = new Size(0, 0); // Allow word-wrap 98 | qualityLabel.TextAlign = ContentAlignment.MiddleCenter; // Center text horizontally and vertically 99 | qualityLabel.Anchor = AnchorStyles.None; // Center within the cell 100 | 101 | // Set text color 102 | if (qualityLabel.Text.Contains("24bit")) 103 | { 104 | qualityLabel.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.HiResLabelText); 105 | } 106 | else 107 | { 108 | qualityLabel.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.LabelText); ; 109 | } 110 | 111 | qualityLabel.Font = new Font(fontName, 10F, FontStyle.Regular); // Set font size and style 112 | searchResultsTablePanel.Controls.Add(qualityLabel, 3, rowIndex); 113 | 114 | // Add Button for selecting album ID 115 | Button selectButton = new Button(); 116 | selectButton.Text = qbdlxForm._qbdlxForm.languageManager.GetTranslation("downloadButton"); 117 | selectButton.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.ButtonText); // Set button text color 118 | selectButton.BackColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.ButtonBackground); // Set button background color 119 | selectButton.Font = new Font(fontName, 8F, FontStyle.Regular); // Set font size and style 120 | selectButton.FlatStyle = FlatStyle.Flat; // Set FlatStyle to Flat 121 | selectButton.FlatAppearance.BorderSize = 0; // Set border size 122 | selectButton.FlatAppearance.MouseOverBackColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.HighlightedButtonBackground); // Set background color when hovering 123 | selectButton.FlatAppearance.MouseDownBackColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.ClickedButtonBackground); // Set background color when clicked 124 | string albumLink = "https://play.qobuz.com/album/" + album.Id.ToString(); // Store the album link 125 | selectButton.Click += (sender, e) => SendURL(mainForm, albumLink); 126 | selectButton.Anchor = AnchorStyles.None; // Center the button 127 | searchResultsTablePanel.Controls.Add(selectButton, 4, rowIndex); 128 | 129 | rowIndex++; 130 | } 131 | }); 132 | } 133 | 134 | public void PopulateTableTracks(qbdlxForm mainForm, SearchTrackResult QoTrackSearch) 135 | { 136 | // Access the "items" array from the response 137 | var tracks = QoTrackSearch.Tracks.Items; 138 | 139 | // Load the font name from the translation file 140 | string fontName = qbdlxForm._qbdlxForm.languageManager.GetTranslation("TranslationFont"); 141 | 142 | mainForm.Invoke((MethodInvoker)delegate () 143 | { 144 | TableLayoutPanel searchResultsTablePanel = mainForm.searchResultsTablePanel; 145 | searchResultsTablePanel.Controls.Clear(); 146 | searchResultsTablePanel.ColumnCount = 5; // Artwork, Artist, Title, Quality, Button 147 | searchResultsTablePanel.RowCount = 25; // Set row count based on the number of albums 148 | searchResultsTablePanel.AutoSize = true; 149 | 150 | // Set ColumnStyles to define the size of each column 151 | searchResultsTablePanel.ColumnStyles.Clear(); 152 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 70F)); // Artwork column 153 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200F)); // Artist name 154 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200F)); // Track title 155 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 140F)); // Quality 156 | searchResultsTablePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 70F)); // Button column 157 | 158 | int rowIndex = 0; 159 | 160 | foreach (var track in tracks) 161 | { 162 | // Add PictureBox for artwork 163 | PictureBox artwork = new PictureBox(); 164 | artwork.SizeMode = PictureBoxSizeMode.StretchImage; 165 | try { artwork.Load(track.Album.Image.Large.ToString()); /* Using the thumbnail URL */ } catch { artwork.Image = Resources.qbdlx_new; /* Use QBDLX Icon as fallback */ } 166 | artwork.Width = 65; 167 | artwork.Height = 65; 168 | artwork.Anchor = AnchorStyles.None; // Center both horizontally and vertically 169 | searchResultsTablePanel.Controls.Add(artwork, 0, rowIndex); 170 | 171 | // Add Label for artist name 172 | System.Windows.Forms.Label artistName = new System.Windows.Forms.Label(); 173 | artistName.Text = track.Performer.Name.ToString(); 174 | artistName.AutoSize = true; // Disable auto-sizing to allow wrapping 175 | /*artistName.MaximumSize = new Size(0, 0);*/ // Word-wrap if needed 176 | artistName.TextAlign = ContentAlignment.MiddleCenter; // Center text horizontally and vertically 177 | artistName.Anchor = AnchorStyles.None; // Center within the cell 178 | artistName.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.LabelText); // Set text color 179 | artistName.Font = new Font(fontName, 10F, FontStyle.Regular); // Set font size and style 180 | searchResultsTablePanel.Controls.Add(artistName, 1, rowIndex); 181 | 182 | // Add Label for track title 183 | System.Windows.Forms.Label trackTitle = new System.Windows.Forms.Label(); 184 | trackTitle.Text = track.Title.ToString().TrimEnd(); 185 | if (track.Version != null) { trackTitle.Text = trackTitle.Text + " (" + track.Version + ")"; } 186 | if (track.ParentalWarning == true) { trackTitle.Text = "[E] " + trackTitle.Text; } // Add "[E]" if Qobuz lists the track with a parental warning 187 | trackTitle.AutoSize = true; 188 | /*trackTitle.MaximumSize = new Size(0, 0);*/ // Allow word-wrap 189 | trackTitle.TextAlign = ContentAlignment.MiddleCenter; // Center text horizontally and vertically 190 | trackTitle.Anchor = AnchorStyles.None; // Center within the cell 191 | trackTitle.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.LabelText); // Set text color 192 | trackTitle.Font = new Font(fontName, 10F, FontStyle.Regular); // Set font size and style 193 | searchResultsTablePanel.Controls.Add(trackTitle, 2, rowIndex); 194 | 195 | // Add Label for quality 196 | System.Windows.Forms.Label qualityLabel = new System.Windows.Forms.Label(); 197 | qualityLabel.Text = track.MaximumBitDepth.ToString() + "bit/" + track.MaximumSamplingRate + "kHz"; 198 | qualityLabel.AutoSize = true; 199 | qualityLabel.MaximumSize = new Size(0, 0); // Allow word-wrap 200 | qualityLabel.TextAlign = ContentAlignment.MiddleCenter; // Center text horizontally and vertically 201 | qualityLabel.Anchor = AnchorStyles.None; // Center within the cell 202 | 203 | // Set text color 204 | if (qualityLabel.Text.Contains("24bit")) 205 | { 206 | qualityLabel.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.HiResLabelText); 207 | } 208 | else 209 | { 210 | qualityLabel.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.LabelText); 211 | } 212 | 213 | qualityLabel.Font = new Font(fontName, 10F, FontStyle.Regular); // Set font size and style 214 | searchResultsTablePanel.Controls.Add(qualityLabel, 3, rowIndex); 215 | 216 | // Add Button for selecting album ID 217 | Button selectButton = new Button(); 218 | selectButton.Text = qbdlxForm._qbdlxForm.languageManager.GetTranslation("downloadButton"); 219 | selectButton.ForeColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.ButtonText); // Set button text color 220 | selectButton.BackColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.ButtonBackground); // Set button background color 221 | selectButton.Font = new Font(fontName, 8F, FontStyle.Regular); // Set font size and style 222 | selectButton.FlatStyle = FlatStyle.Flat; // Set FlatStyle to Flat 223 | selectButton.FlatAppearance.BorderSize = 0; // Set border size 224 | selectButton.FlatAppearance.MouseOverBackColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.HighlightedButtonBackground); // Set background color when hovering 225 | selectButton.FlatAppearance.MouseDownBackColor = ColorTranslator.FromHtml(qbdlxForm._qbdlxForm._themeManager._currentTheme.ClickedButtonBackground); // Set background color when clicked 226 | string trackLink = "https://open.qobuz.com/track/" + track.Id.ToString(); // Store the track link 227 | selectButton.Click += (sender, e) => SendURL(mainForm, trackLink); 228 | selectButton.Anchor = AnchorStyles.None; // Center the button 229 | searchResultsTablePanel.Controls.Add(selectButton, 4, rowIndex); 230 | 231 | rowIndex++; 232 | } 233 | }); 234 | } 235 | 236 | public void SendURL(qbdlxForm mainForm, string url) 237 | { 238 | try 239 | { 240 | // Send URL to the input textbox, and start download 241 | mainForm.logger.Debug("Sending URL to download panel, and starting download"); 242 | TextBox inputTextbox = mainForm.inputTextbox; 243 | inputTextbox.Text = url; 244 | inputTextbox.ForeColor = Color.FromArgb(200, 200, 200); 245 | mainForm.downloaderButton_Click(this, EventArgs.Empty); 246 | mainForm.getLinkType(); 247 | } 248 | catch (Exception ex) 249 | { 250 | mainForm.logger.Error("Error on SendURL (SearchPanelHelper). Error below:\r\n" + ex); 251 | return; 252 | } 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /QobuzDownloaderX/Old/LoginForm-v2.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace QobuzDownloaderX 2 | { 3 | partial class LoginFrm 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(LoginFrm)); 32 | this.panel1 = new System.Windows.Forms.Panel(); 33 | this.disableLogin = new System.Windows.Forms.CheckBox(); 34 | this.verNumLabel2 = new System.Windows.Forms.Label(); 35 | this.md5Button = new System.Windows.Forms.Button(); 36 | this.exitLabel = new System.Windows.Forms.Label(); 37 | this.pictureBox1 = new System.Windows.Forms.PictureBox(); 38 | this.userAuthTokenTextbox = new System.Windows.Forms.TextBox(); 39 | this.loginButton = new System.Windows.Forms.Button(); 40 | this.panel3 = new System.Windows.Forms.Panel(); 41 | this.panel4 = new System.Windows.Forms.Panel(); 42 | this.emailTextbox = new System.Windows.Forms.TextBox(); 43 | this.passwordTextbox = new System.Windows.Forms.TextBox(); 44 | this.loginText = new System.Windows.Forms.Label(); 45 | this.getSecretBG = new System.ComponentModel.BackgroundWorker(); 46 | this.loginBG = new System.ComponentModel.BackgroundWorker(); 47 | this.visableCheckbox = new System.Windows.Forms.CheckBox(); 48 | this.altLoginLabel = new System.Windows.Forms.Label(); 49 | this.altLoginTutLabel = new System.Windows.Forms.Label(); 50 | this.userIdTextbox = new System.Windows.Forms.TextBox(); 51 | this.altLoginBG = new System.ComponentModel.BackgroundWorker(); 52 | this.aboutLabel = new System.Windows.Forms.Label(); 53 | this.panel1.SuspendLayout(); 54 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); 55 | this.SuspendLayout(); 56 | // 57 | // panel1 58 | // 59 | this.panel1.BackgroundImage = null; 60 | this.panel1.Controls.Add(this.aboutLabel); 61 | this.panel1.Controls.Add(this.disableLogin); 62 | this.panel1.Controls.Add(this.verNumLabel2); 63 | this.panel1.Controls.Add(this.md5Button); 64 | this.panel1.Controls.Add(this.exitLabel); 65 | this.panel1.Controls.Add(this.pictureBox1); 66 | this.panel1.Dock = System.Windows.Forms.DockStyle.Top; 67 | this.panel1.Location = new System.Drawing.Point(0, 0); 68 | this.panel1.Name = "panel1"; 69 | this.panel1.Size = new System.Drawing.Size(282, 175); 70 | this.panel1.TabIndex = 0; 71 | this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseMove); 72 | // 73 | // disableLogin 74 | // 75 | this.disableLogin.AutoSize = true; 76 | this.disableLogin.ForeColor = System.Drawing.Color.Transparent; 77 | this.disableLogin.Location = new System.Drawing.Point(12, 155); 78 | this.disableLogin.Name = "disableLogin"; 79 | this.disableLogin.Size = new System.Drawing.Size(90, 17); 80 | this.disableLogin.TabIndex = 34; 81 | this.disableLogin.Text = "Disable Login"; 82 | this.disableLogin.UseVisualStyleBackColor = true; 83 | this.disableLogin.Visible = false; 84 | // 85 | // verNumLabel2 86 | // 87 | this.verNumLabel2.BackColor = System.Drawing.Color.Transparent; 88 | this.verNumLabel2.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 89 | this.verNumLabel2.ForeColor = System.Drawing.Color.White; 90 | this.verNumLabel2.Location = new System.Drawing.Point(194, 157); 91 | this.verNumLabel2.Name = "verNumLabel2"; 92 | this.verNumLabel2.Size = new System.Drawing.Size(85, 18); 93 | this.verNumLabel2.TabIndex = 32; 94 | this.verNumLabel2.Text = "#.#.#.#"; 95 | this.verNumLabel2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; 96 | this.verNumLabel2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.verNumLabel2_MouseMove); 97 | // 98 | // md5Button 99 | // 100 | this.md5Button.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(112)))), ((int)(((byte)(239))))); 101 | this.md5Button.Enabled = false; 102 | this.md5Button.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20))))); 103 | this.md5Button.FlatAppearance.BorderSize = 2; 104 | this.md5Button.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 105 | this.md5Button.ForeColor = System.Drawing.Color.White; 106 | this.md5Button.Location = new System.Drawing.Point(227, 122); 107 | this.md5Button.Name = "md5Button"; 108 | this.md5Button.Size = new System.Drawing.Size(43, 27); 109 | this.md5Button.TabIndex = 9; 110 | this.md5Button.Text = "MD5"; 111 | this.md5Button.UseVisualStyleBackColor = false; 112 | this.md5Button.Visible = false; 113 | this.md5Button.Click += new System.EventHandler(this.md5Button_Click); 114 | // 115 | // exitLabel 116 | // 117 | this.exitLabel.AutoSize = true; 118 | this.exitLabel.BackColor = System.Drawing.Color.Transparent; 119 | this.exitLabel.Font = new System.Drawing.Font("Calibri", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 120 | this.exitLabel.ForeColor = System.Drawing.Color.Black; 121 | this.exitLabel.Location = new System.Drawing.Point(262, 0); 122 | this.exitLabel.Name = "exitLabel"; 123 | this.exitLabel.Size = new System.Drawing.Size(20, 23); 124 | this.exitLabel.TabIndex = 9; 125 | this.exitLabel.Text = "X"; 126 | this.exitLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; 127 | this.exitLabel.Click += new System.EventHandler(this.exitLabel_Click); 128 | // 129 | // pictureBox1 130 | // 131 | this.pictureBox1.BackColor = System.Drawing.Color.Transparent; 132 | this.pictureBox1.Image = null; 133 | this.pictureBox1.Location = new System.Drawing.Point(12, 52); 134 | this.pictureBox1.Name = "pictureBox1"; 135 | this.pictureBox1.Size = new System.Drawing.Size(258, 64); 136 | this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; 137 | this.pictureBox1.TabIndex = 29; 138 | this.pictureBox1.TabStop = false; 139 | this.pictureBox1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseMove); 140 | // 141 | // userAuthTokenTextbox 142 | // 143 | this.userAuthTokenTextbox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20))))); 144 | this.userAuthTokenTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; 145 | this.userAuthTokenTextbox.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 146 | this.userAuthTokenTextbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 147 | this.userAuthTokenTextbox.Location = new System.Drawing.Point(12, 255); 148 | this.userAuthTokenTextbox.Multiline = true; 149 | this.userAuthTokenTextbox.Name = "userAuthTokenTextbox"; 150 | this.userAuthTokenTextbox.Size = new System.Drawing.Size(237, 23); 151 | this.userAuthTokenTextbox.TabIndex = 35; 152 | this.userAuthTokenTextbox.Text = "user_auth_token"; 153 | this.userAuthTokenTextbox.Visible = false; 154 | this.userAuthTokenTextbox.Click += new System.EventHandler(this.userAuthTokenTextbox_Click); 155 | this.userAuthTokenTextbox.Leave += new System.EventHandler(this.userAuthTokenTextbox_Leave); 156 | // 157 | // loginButton 158 | // 159 | this.loginButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(112)))), ((int)(((byte)(239))))); 160 | this.loginButton.FlatAppearance.BorderSize = 0; 161 | this.loginButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; 162 | this.loginButton.Font = new System.Drawing.Font("Trebuchet MS", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 163 | this.loginButton.ForeColor = System.Drawing.Color.White; 164 | this.loginButton.Location = new System.Drawing.Point(12, 293); 165 | this.loginButton.Name = "loginButton"; 166 | this.loginButton.Size = new System.Drawing.Size(258, 30); 167 | this.loginButton.TabIndex = 2; 168 | this.loginButton.Text = "LOGIN"; 169 | this.loginButton.UseVisualStyleBackColor = false; 170 | this.loginButton.Click += new System.EventHandler(this.loginButton_Click); 171 | // 172 | // panel3 173 | // 174 | this.panel3.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 175 | this.panel3.Location = new System.Drawing.Point(12, 221); 176 | this.panel3.Name = "panel3"; 177 | this.panel3.Size = new System.Drawing.Size(258, 1); 178 | this.panel3.TabIndex = 2; 179 | // 180 | // panel4 181 | // 182 | this.panel4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 183 | this.panel4.Location = new System.Drawing.Point(12, 277); 184 | this.panel4.Name = "panel4"; 185 | this.panel4.Size = new System.Drawing.Size(258, 1); 186 | this.panel4.TabIndex = 2; 187 | // 188 | // emailTextbox 189 | // 190 | this.emailTextbox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20))))); 191 | this.emailTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; 192 | this.emailTextbox.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 193 | this.emailTextbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 194 | this.emailTextbox.Location = new System.Drawing.Point(12, 199); 195 | this.emailTextbox.Multiline = true; 196 | this.emailTextbox.Name = "emailTextbox"; 197 | this.emailTextbox.Size = new System.Drawing.Size(258, 23); 198 | this.emailTextbox.TabIndex = 7; 199 | this.emailTextbox.Text = "Email"; 200 | this.emailTextbox.Click += new System.EventHandler(this.emailTextbox_Click); 201 | this.emailTextbox.Leave += new System.EventHandler(this.emailTextbox_Leave); 202 | // 203 | // passwordTextbox 204 | // 205 | this.passwordTextbox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20))))); 206 | this.passwordTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; 207 | this.passwordTextbox.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 208 | this.passwordTextbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 209 | this.passwordTextbox.Location = new System.Drawing.Point(12, 255); 210 | this.passwordTextbox.Multiline = true; 211 | this.passwordTextbox.Name = "passwordTextbox"; 212 | this.passwordTextbox.Size = new System.Drawing.Size(237, 23); 213 | this.passwordTextbox.TabIndex = 8; 214 | this.passwordTextbox.Text = "Password"; 215 | this.passwordTextbox.Click += new System.EventHandler(this.passwordTextbox_Click); 216 | this.passwordTextbox.Leave += new System.EventHandler(this.passwordTextbox_Leave); 217 | // 218 | // loginText 219 | // 220 | this.loginText.Font = new System.Drawing.Font("Trebuchet MS", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 221 | this.loginText.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 222 | this.loginText.Location = new System.Drawing.Point(12, 349); 223 | this.loginText.Name = "loginText"; 224 | this.loginText.Size = new System.Drawing.Size(258, 23); 225 | this.loginText.TabIndex = 30; 226 | this.loginText.Text = "Waiting for login..."; 227 | this.loginText.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 228 | // 229 | // getSecretBG 230 | // 231 | this.getSecretBG.DoWork += new System.ComponentModel.DoWorkEventHandler(this.getSecretBG_DoWork); 232 | // 233 | // loginBG 234 | // 235 | this.loginBG.DoWork += new System.ComponentModel.DoWorkEventHandler(this.loginBG_DoWork); 236 | // 237 | // visableCheckbox 238 | // 239 | this.visableCheckbox.AutoSize = true; 240 | this.visableCheckbox.Location = new System.Drawing.Point(255, 256); 241 | this.visableCheckbox.Name = "visableCheckbox"; 242 | this.visableCheckbox.Size = new System.Drawing.Size(15, 14); 243 | this.visableCheckbox.TabIndex = 31; 244 | this.visableCheckbox.UseVisualStyleBackColor = true; 245 | this.visableCheckbox.CheckedChanged += new System.EventHandler(this.visableCheckbox_CheckedChanged); 246 | // 247 | // altLoginLabel 248 | // 249 | this.altLoginLabel.Font = new System.Drawing.Font("Trebuchet MS", 8.25F); 250 | this.altLoginLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 251 | this.altLoginLabel.Location = new System.Drawing.Point(12, 326); 252 | this.altLoginLabel.Name = "altLoginLabel"; 253 | this.altLoginLabel.Size = new System.Drawing.Size(258, 20); 254 | this.altLoginLabel.TabIndex = 32; 255 | this.altLoginLabel.Text = "Can\'t login? Click here"; 256 | this.altLoginLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 257 | this.altLoginLabel.Click += new System.EventHandler(this.altLoginLabel_Click); 258 | // 259 | // altLoginTutLabel 260 | // 261 | this.altLoginTutLabel.AutoSize = true; 262 | this.altLoginTutLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 263 | this.altLoginTutLabel.Location = new System.Drawing.Point(77, 180); 264 | this.altLoginTutLabel.Name = "altLoginTutLabel"; 265 | this.altLoginTutLabel.Size = new System.Drawing.Size(128, 13); 266 | this.altLoginTutLabel.TabIndex = 33; 267 | this.altLoginTutLabel.Text = "Click Here for Instructions"; 268 | this.altLoginTutLabel.Visible = false; 269 | this.altLoginTutLabel.Click += new System.EventHandler(this.altLoginTutLabel_Click); 270 | // 271 | // userIdTextbox 272 | // 273 | this.userIdTextbox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20))))); 274 | this.userIdTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None; 275 | this.userIdTextbox.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 276 | this.userIdTextbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102))))); 277 | this.userIdTextbox.Location = new System.Drawing.Point(12, 199); 278 | this.userIdTextbox.Multiline = true; 279 | this.userIdTextbox.Name = "userIdTextbox"; 280 | this.userIdTextbox.Size = new System.Drawing.Size(258, 23); 281 | this.userIdTextbox.TabIndex = 9; 282 | this.userIdTextbox.Text = "user_id"; 283 | this.userIdTextbox.Visible = false; 284 | this.userIdTextbox.Click += new System.EventHandler(this.userIdTextbox_Click); 285 | this.userIdTextbox.Leave += new System.EventHandler(this.userIdTextbox_Leave); 286 | // 287 | // altLoginBG 288 | // 289 | this.altLoginBG.DoWork += new System.ComponentModel.DoWorkEventHandler(this.altLoginBG_DoWork); 290 | // 291 | // aboutLabel 292 | // 293 | this.aboutLabel.AutoSize = true; 294 | this.aboutLabel.BackColor = System.Drawing.Color.Transparent; 295 | this.aboutLabel.Font = new System.Drawing.Font("Calibri", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 296 | this.aboutLabel.ForeColor = System.Drawing.Color.Black; 297 | this.aboutLabel.Location = new System.Drawing.Point(241, 0); 298 | this.aboutLabel.Name = "aboutLabel"; 299 | this.aboutLabel.Size = new System.Drawing.Size(15, 23); 300 | this.aboutLabel.TabIndex = 35; 301 | this.aboutLabel.Text = "i"; 302 | this.aboutLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter; 303 | this.aboutLabel.Click += new System.EventHandler(this.aboutLabel_Click); 304 | // 305 | // LoginFrm 306 | // 307 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 308 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 309 | this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20))))); 310 | this.ClientSize = new System.Drawing.Size(282, 392); 311 | this.Controls.Add(this.altLoginTutLabel); 312 | this.Controls.Add(this.altLoginLabel); 313 | this.Controls.Add(this.visableCheckbox); 314 | this.Controls.Add(this.loginText); 315 | this.Controls.Add(this.panel4); 316 | this.Controls.Add(this.panel3); 317 | this.Controls.Add(this.passwordTextbox); 318 | this.Controls.Add(this.emailTextbox); 319 | this.Controls.Add(this.loginButton); 320 | this.Controls.Add(this.panel1); 321 | this.Controls.Add(this.userIdTextbox); 322 | this.Controls.Add(this.userAuthTokenTextbox); 323 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; 324 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 325 | this.Name = "LoginFrm"; 326 | this.Text = "QobuzDLX | Login"; 327 | this.Load += new System.EventHandler(this.LoginFrm_Load); 328 | this.panel1.ResumeLayout(false); 329 | this.panel1.PerformLayout(); 330 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); 331 | this.ResumeLayout(false); 332 | this.PerformLayout(); 333 | 334 | } 335 | 336 | #endregion 337 | 338 | private System.Windows.Forms.Panel panel1; 339 | private System.Windows.Forms.PictureBox pictureBox1; 340 | private System.Windows.Forms.Button loginButton; 341 | private System.Windows.Forms.Panel panel3; 342 | private System.Windows.Forms.Panel panel4; 343 | private System.Windows.Forms.TextBox emailTextbox; 344 | private System.Windows.Forms.TextBox passwordTextbox; 345 | private System.Windows.Forms.Label exitLabel; 346 | private System.Windows.Forms.Button md5Button; 347 | private System.Windows.Forms.Label loginText; 348 | private System.ComponentModel.BackgroundWorker getSecretBG; 349 | private System.ComponentModel.BackgroundWorker loginBG; 350 | private System.Windows.Forms.Label verNumLabel2; 351 | private System.Windows.Forms.CheckBox visableCheckbox; 352 | private System.Windows.Forms.CheckBox disableLogin; 353 | private System.Windows.Forms.TextBox userAuthTokenTextbox; 354 | private System.Windows.Forms.Label altLoginLabel; 355 | private System.Windows.Forms.Label altLoginTutLabel; 356 | private System.Windows.Forms.TextBox userIdTextbox; 357 | private System.ComponentModel.BackgroundWorker altLoginBG; 358 | private System.Windows.Forms.Label aboutLabel; 359 | } 360 | } --------------------------------------------------------------------------------