├── -assets
├── Login_Alt.png
├── Login_Alt_Annotated.png
├── Login_Normal.png
├── Login_Normal_Annotated.png
├── Login_Open_Settings.png
├── QBDLX1.png
├── QBDLX2.png
├── QBDLX3.png
├── QBDLX4.png
└── Settings_screen.png
├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── 1-bug-report.md
│ ├── 2-feature-request.md
│ └── 3-enhancement-request.md
└── workflows
│ ├── build-dotnet-desktop-app.yml
│ └── build-on-new-tag.yml
├── .gitignore
├── LICENSE
├── QobuzDownloaderX.sln
├── QobuzDownloaderX
├── App.config
├── Models
│ ├── Download
│ │ ├── DownloadItem.cs
│ │ ├── DownloadItemInfo.cs
│ │ └── DownloadItemPaths.cs
│ └── UI
│ │ └── SearchResultRow.cs
├── Program.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── QobuzDownloaderX.csproj
├── Resources
│ ├── Fonts
│ │ ├── HankenGrotesk-Italic-VariableFont_wght.ttf
│ │ └── HankenGrotesk-VariableFont_wght.ttf
│ ├── Untitled-1.png
│ ├── login-frame.png
│ ├── qbdlx-white.png
│ ├── qbdlx.png
│ ├── qbdlx_icon.ico
│ ├── qobuz_logo_dark.svg
│ ├── qobuz_logo_store.png
│ ├── settings_icon.png
│ └── smr-audio.png
├── Settings.cs
├── Shared
│ ├── DownloadLogger.cs
│ ├── DownloadManager.cs
│ ├── FontManager.cs
│ ├── Globals.cs
│ ├── GoodiesFileType.cs
│ ├── InvolvedPersonRoleType.cs
│ ├── QobuzApiServiceManager.cs
│ ├── TaggingOptions.cs
│ └── Tools
│ │ ├── AudioFileTagger.cs
│ │ ├── ControlTools.cs
│ │ ├── DownloadUrlParser.cs
│ │ ├── FileTools.cs
│ │ ├── InvolvedPersonRoleMapping.cs
│ │ ├── MD5Tools.cs
│ │ ├── PerformersParser.cs
│ │ ├── QualityStringMappings.cs
│ │ └── StringTools.cs
├── View
│ ├── AboutForm.Designer.cs
│ ├── AboutForm.cs
│ ├── AboutForm.resx
│ ├── FlexibleMessageBox.cs
│ ├── HeadlessForm.Designer.cs
│ ├── HeadlessForm.cs
│ ├── LoginForm.Designer.cs
│ ├── LoginForm.cs
│ ├── LoginForm.resx
│ ├── MainForm.Designer.cs
│ ├── MainForm.cs
│ ├── MainForm.resx
│ ├── SearchForm.Designer.cs
│ ├── SearchForm.cs
│ ├── SearchForm.resx
│ ├── SettingsForm.Designer.cs
│ ├── SettingsForm.cs
│ └── SettingsForm.resx
└── app.manifest
└── README.md
/-assets/Login_Alt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/Login_Alt.png
--------------------------------------------------------------------------------
/-assets/Login_Alt_Annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/Login_Alt_Annotated.png
--------------------------------------------------------------------------------
/-assets/Login_Normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/Login_Normal.png
--------------------------------------------------------------------------------
/-assets/Login_Normal_Annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/Login_Normal_Annotated.png
--------------------------------------------------------------------------------
/-assets/Login_Open_Settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/Login_Open_Settings.png
--------------------------------------------------------------------------------
/-assets/QBDLX1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/QBDLX1.png
--------------------------------------------------------------------------------
/-assets/QBDLX2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/QBDLX2.png
--------------------------------------------------------------------------------
/-assets/QBDLX3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/QBDLX3.png
--------------------------------------------------------------------------------
/-assets/QBDLX4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/QBDLX4.png
--------------------------------------------------------------------------------
/-assets/Settings_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/-assets/Settings_screen.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CD1607: The file needs documentation headers.
4 | dotnet_diagnostic.CD1607.severity = none
5 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/1-bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "🐞 Bug Report"
3 | about: "Report an issue to help the project improve."
4 | title: "[Bug] "
5 | labels: "Type: Bug"
6 | assignees: ''
7 |
8 | ---
9 |
10 | # **🐞 Bug Report**
11 |
12 | ## **Describe the bug**
13 |
14 |
15 | *
16 |
17 | ---
18 |
19 | ### **Is this a regression?**
20 |
21 |
22 |
23 | ---
24 |
25 | ### **To Reproduce**
26 |
27 |
33 |
34 |
35 |
36 | 1.
37 | 2.
38 | 3.
39 | 4.
40 |
41 | ---
42 |
43 | ### **Expected behaviour**
44 |
45 |
46 | *
47 |
48 | ---
49 |
50 | ### **Screenshots**
51 |
52 |
53 | ---
54 |
55 | ### **Additional context**
56 |
57 |
58 | *
59 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/2-feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "🚀🆕 Feature Request"
3 | about: "Suggest an idea or possible new feature for this project."
4 | title: "[Feature Request] "
5 | labels: "Type: Feature"
6 | assignees:
7 |
8 | ---
9 |
10 | # **🚀 Feature Request**
11 |
12 | ## **Is your feature request related to a problem? Please describe.**
13 |
14 |
15 | *
16 |
17 | ---
18 |
19 | ## **Describe the solution you'd like**
20 |
21 |
22 | *
23 |
24 | ---
25 |
26 | ## **Describe alternatives you've considered**
27 |
28 |
29 | *
30 |
31 | ---
32 |
33 | ### **Additional context**
34 |
35 |
36 | *
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/3-enhancement-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "🚀➕ Enhancement Request"
3 | about: "Suggest an enhancement for this project. Improve an existing feature"
4 | title: "[Enhancement] "
5 | labels: "Type: Enhancement"
6 | assignees:
7 |
8 | ---
9 |
10 | # **🚀 Enhancement Request**
11 |
12 | ## **Is your enhancement request related to a problem? Please describe.**
13 |
14 |
15 | *
16 |
17 | ---
18 |
19 | ## **Describe the solution you'd like**
20 |
21 |
22 | *
23 |
24 | ---
25 |
26 | ## **Describe alternatives you've considered**
27 |
28 |
29 | *
30 |
31 | ---
32 |
33 | ### **Additional context**
34 |
35 |
36 | *
37 |
--------------------------------------------------------------------------------
/.github/workflows/build-dotnet-desktop-app.yml:
--------------------------------------------------------------------------------
1 | name: Build QobuzDownloaderX-MOD
2 |
3 | on:
4 | workflow_dispatch:
5 | # push:
6 | # branches:
7 | # - "master"
8 |
9 | jobs:
10 | build:
11 | runs-on: windows-2022
12 |
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v4.1.7
16 |
17 | # - name: Setup .NET
18 | # uses: actions/setup-dotnet@v1.5.0
19 | # with:
20 | # dotnet-version: 4.8
21 |
22 | - name: Setup MSBuild
23 | uses: microsoft/setup-msbuild@v2
24 |
25 | - name: Setup NuGet
26 | uses: nuget/setup-nuget@v2.0.0
27 |
28 | - name: Restore NuGet Packages
29 | run: nuget restore QobuzDownloaderX.sln
30 |
31 | - name: Build Solutions
32 | run: |
33 | msbuild.exe QobuzDownloaderX.sln /p:platform="Any CPU" /p:configuration="Release"
34 |
35 | - name: Upload artifacts
36 | uses: actions/upload-artifact@v4.3.6
37 | with:
38 | name: QBDLX-MOD_${{github.ref_name}}
39 | path: QobuzDownloaderX\bin\Release
40 |
41 |
--------------------------------------------------------------------------------
/.github/workflows/build-on-new-tag.yml:
--------------------------------------------------------------------------------
1 | name: Automatic Build on Tag update
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | build:
10 | runs-on: windows-2022
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v4.1.7
15 |
16 | # - name: Setup .NET
17 | # uses: actions/setup-dotnet@v1.5.0
18 | # with:
19 | # dotnet-version: 4.8
20 |
21 | - name: Setup MSBuild
22 | uses: microsoft/setup-msbuild@v2
23 |
24 | - name: Setup NuGet
25 | uses: nuget/setup-nuget@v2.0.0
26 |
27 | - name: Restore NuGet Packages
28 | run: nuget restore QobuzDownloaderX.sln
29 |
30 | - name: Build Solutions
31 | run: |
32 | msbuild.exe QobuzDownloaderX.sln /p:platform="Any CPU" /p:configuration="Release"
33 |
34 | - name: Upload artifacts
35 | uses: actions/upload-artifact@v4.3.6
36 | with:
37 | name: QBDLX-MOD_${{ github.event.release.tag_name }}
38 | path: QobuzDownloaderX\bin\Release
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 | .vshistory/
343 |
344 | # BeatPulse healthcheck temp database
345 | healthchecksdb
346 |
347 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
348 | MigrationBackup/
349 |
350 | # Ionide (cross platform F# VS Code tools) working folder
351 | .ionide/
--------------------------------------------------------------------------------
/QobuzDownloaderX.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.5.33502.453
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 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {30F1E966-EEA5-4BC6-A52F-BD0F48C574D0}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/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 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | True
38 |
39 |
40 | True
41 |
42 |
43 | True
44 |
45 |
46 | True
47 |
48 |
49 | True
50 |
51 |
52 | True
53 |
54 |
55 | True
56 |
57 |
58 | True
59 |
60 |
61 | True
62 |
63 |
64 | True
65 |
66 |
67 | True
68 |
69 |
70 | True
71 |
72 |
73 | True
74 |
75 |
76 | True
77 |
78 |
79 | True
80 |
81 |
82 | False
83 |
84 |
85 | True
86 |
87 |
88 | True
89 |
90 |
91 |
92 |
93 |
94 | False
95 |
96 |
97 | False
98 |
99 |
100 | False
101 |
102 |
103 | True
104 |
105 |
106 | 27
107 |
108 |
109 | .flac
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | 0
119 |
120 |
121 | 2
122 |
123 |
124 | True
125 |
126 |
127 | 100
128 |
129 |
130 | 1
131 |
132 |
133 | -
134 |
135 |
136 | True
137 |
138 |
139 | True
140 |
141 |
142 | True
143 |
144 |
145 | ,
146 |
147 |
148 | &
149 |
150 |
151 | False
152 |
153 |
154 | True
155 |
156 |
157 | True
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/QobuzDownloaderX/Models/Download/DownloadItem.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX.Models
2 | {
3 | public class DownloadItem
4 | {
5 | public string Type { get; set; }
6 | public string Id { get; set; }
7 | public string Url { get; set; }
8 |
9 | public DownloadItem(string url)
10 | {
11 | Url = url;
12 | }
13 |
14 | public bool IsEmpty()
15 | {
16 | return Type == null || Id == null;
17 | }
18 |
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/QobuzDownloaderX/Models/Download/DownloadItemInfo.cs:
--------------------------------------------------------------------------------
1 | using QobuzApiSharp.Models.Content;
2 | using QobuzDownloaderX.Shared;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace QobuzDownloaderX.Models
7 | {
8 | public class DownloadItemInfo
9 | {
10 | public string DisplayQuality { get; set; }
11 |
12 | // Important strings
13 | public string DownloadItemID { get; set; }
14 |
15 | public string Stream { get; set; }
16 |
17 | // Info for creating paths
18 | public DownloadItemPaths CurrentDownloadPaths { get; set; }
19 |
20 | // Info / Tagging strings
21 | public string TrackVersionName { get; set; }
22 |
23 | public bool? Advisory { get; set; }
24 | public string AlbumArtist { get; set; }
25 | public string[] AlbumArtists { get; set; }
26 | public string AlbumName { get; set; }
27 | public string PerformerName { get; set; }
28 | public string[] PerformerNames { get; set; }
29 | public string ComposerName { get; set; }
30 | public string[] ComposerNames { get; set; }
31 | public string ProducerName { get; set; }
32 | public string[] ProducerNames { get; set; }
33 | public string LabelName { get; set; }
34 | public string InvolvedPeople { get; set; }
35 | public string TrackName { get; set; }
36 | public string Copyright { get; set; }
37 | public string Genre { get; set; }
38 | public string ReleaseDate { get; set; }
39 | public string Isrc { get; set; }
40 | public string Upc { get; set; }
41 | public string FrontCoverImgUrl { get; set; }
42 | public string FrontCoverImgTagUrl { get; set; }
43 | public string FrontCoverImgBoxUrl { get; set; }
44 | public string MediaType { get; set; }
45 | public string Url { get; set; }
46 |
47 | // Info / Tagging numbers
48 | public int DiscNumber { get; set; }
49 |
50 | public int DiscTotal { get; set; }
51 | public int TrackNumber { get; set; }
52 | public int TrackTotal { get; set; }
53 | public long Duration { get; set; }
54 |
55 | public DownloadItemInfo()
56 | {
57 | CurrentDownloadPaths = new DownloadItemPaths();
58 | }
59 |
60 | public void SetAlbumDownloadInfo(Album qobuzAlbum)
61 | {
62 | SetAlbumCoverArtUrls(qobuzAlbum);
63 | SetAlbumTaggingInfo(qobuzAlbum);
64 | SetAlbumPaths(qobuzAlbum);
65 | }
66 |
67 | private void ClearAlbumTaggingInfo()
68 | {
69 | // Clear tag strings
70 | AlbumArtist = null;
71 | AlbumArtists = null;
72 | AlbumName = null;
73 | LabelName = null;
74 | Genre = null;
75 | ReleaseDate = null;
76 | Upc = null;
77 | MediaType = null;
78 | Url = null;
79 |
80 | // Clear tag numbers
81 | TrackTotal = 0;
82 | DiscTotal = 0;
83 |
84 | // Clear tagbased Paths
85 | CurrentDownloadPaths.AlbumArtistPath = null;
86 | CurrentDownloadPaths.AlbumNamePath = null;
87 | }
88 |
89 | private void ClearTrackTaggingInfo()
90 | {
91 | // Clear tag strings
92 | PerformerName = null;
93 | PerformerNames = null;
94 | ComposerName = null;
95 | ComposerNames = null;
96 | ProducerName = null;
97 | ProducerNames = null;
98 | InvolvedPeople = null;
99 | TrackName = null;
100 | TrackVersionName = null;
101 | Advisory = null;
102 | Copyright = null;
103 | Isrc = null;
104 |
105 | // Clear tag numbers
106 | TrackNumber = 0;
107 | DiscNumber = 0;
108 | Duration = 0;
109 |
110 | // Clear tagbased Paths
111 | CurrentDownloadPaths.TrackNamePath = null;
112 | }
113 |
114 | private void SetAlbumCoverArtUrls(Album qobuzAlbum)
115 | {
116 | // Grab cover art link
117 | FrontCoverImgUrl = qobuzAlbum.Image.Large;
118 | // Get 150x150 artwork for cover art box
119 | FrontCoverImgBoxUrl = FrontCoverImgUrl.Replace("_600.jpg", "_150.jpg");
120 | // Get selected artwork size for tagging
121 | FrontCoverImgTagUrl = FrontCoverImgUrl.Replace("_600.jpg", $"_{Globals.TaggingOptions.ArtSize}.jpg");
122 | // Get max sized artwork ("_org.jpg" is compressed version of the original "_org.jpg")
123 | FrontCoverImgUrl = FrontCoverImgUrl.Replace("_600.jpg", "_org.jpg");
124 | }
125 |
126 | // Set Album tagging info
127 | public void SetAlbumTaggingInfo(Album qobuzAlbum)
128 | {
129 | ClearAlbumTaggingInfo();
130 |
131 | AlbumArtists = GetArtistNames(qobuzAlbum.Artists, InvolvedPersonRoleType.MainArtist);
132 | string[] featuredArtists = GetArtistNames(qobuzAlbum.Artists, InvolvedPersonRoleType.FeaturedArtist);
133 | string albumArtists = MergeFeaturedArtistsWithMainArtists(AlbumArtists, featuredArtists);
134 | // Add Features Artists to Album Artists.
135 | AlbumArtists = AlbumArtists.Concat(featuredArtists).ToArray();
136 | if (!string.IsNullOrEmpty(albumArtists) && Globals.TaggingOptions.MergePerformers)
137 | {
138 | // User Main-Artists by default
139 | AlbumArtist = albumArtists;
140 | }
141 | else
142 | {
143 | AlbumArtist = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Artist.Name);
144 | }
145 | // Qobuz doesn't return an array of Albumartists for compilations, so use singular AlbumArtist
146 | if (AlbumArtists.Length < 1)
147 | {
148 | AlbumArtists = new string[] { AlbumArtist };
149 | }
150 |
151 | AlbumName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Title.Trim());
152 | string albumVersionName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Version?.Trim());
153 | // Add album version to AlbumName if present
154 | AlbumName += (albumVersionName == null ? "" : " (" + albumVersionName + ")");
155 | LabelName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Label.Name);
156 |
157 | Genre = StringTools.DecodeEncodedNonAsciiCharacters(qobuzAlbum.Genre.Name);
158 | ReleaseDate = StringTools.FormatDateTimeOffset(qobuzAlbum.ReleaseDateStream);
159 | Upc = qobuzAlbum.Upc;
160 | MediaType = qobuzAlbum.ReleaseType;
161 | Url = qobuzAlbum.Url;
162 |
163 | // Grab tag ints
164 | TrackTotal = qobuzAlbum.TracksCount.GetValueOrDefault();
165 | DiscTotal = qobuzAlbum.MediaCount.GetValueOrDefault();
166 | }
167 |
168 | // Set Album tagbased Paths
169 | private void SetAlbumPaths(Album qobuzAlbum)
170 | {
171 | // Grab sample rate and bit depth for album.
172 | (string displayQuality, string qualityPathLocal) = QualityStringMappings.GetQualityStrings(Globals.FormatIdString, qobuzAlbum);
173 | DisplayQuality = displayQuality;
174 | CurrentDownloadPaths.QualityPath = qualityPathLocal;
175 |
176 | // If AlbumArtist or AlbumName goes over set MaxLength number of characters, limit them to the MaxLength
177 | CurrentDownloadPaths.AlbumArtistPath = StringTools.TrimToMaxLength(StringTools.GetSafeFilename(AlbumArtist), Globals.MaxLength);
178 | CurrentDownloadPaths.AlbumNamePath = StringTools.TrimToMaxLength(StringTools.GetSafeFilename(AlbumName), Globals.MaxLength);
179 | }
180 |
181 | // Set Track tagging info
182 | public void SetTrackTaggingInfo(Track qobuzTrack)
183 | {
184 | ClearTrackTaggingInfo();
185 |
186 | PerformersParser performersParser = new PerformersParser(qobuzTrack.Performers);
187 |
188 | PerformerNames = performersParser.GetPerformersWithRole(InvolvedPersonRoleType.MainArtist);
189 | string[] featuredArtists = performersParser.GetPerformersWithRole(InvolvedPersonRoleType.FeaturedArtist);
190 | string trackArtists = MergeFeaturedArtistsWithMainArtists(PerformerNames, featuredArtists);
191 | // Add Features Artists to Album Artists.
192 | PerformerNames = PerformerNames.Concat(featuredArtists).ToArray();
193 | if (!string.IsNullOrEmpty(trackArtists) && Globals.TaggingOptions.MergePerformers)
194 | {
195 | // User MainArtist Performers by default
196 | PerformerName = trackArtists;
197 | }
198 | else
199 | {
200 | PerformerName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Performer?.Name);
201 | }
202 | // If no performer name, use album artist
203 | if (string.IsNullOrEmpty(PerformerName))
204 | {
205 | PerformerName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Album?.Artist?.Name);
206 | }
207 | // Qobuz could return an unknown role for the Track Artists. Use singular PerformerName as fallback
208 | if (PerformerNames.Length < 1)
209 | {
210 | PerformerNames = new string[] { PerformerName };
211 | }
212 |
213 | ComposerNames = performersParser.GetPerformersWithRole(InvolvedPersonRoleType.Composer).ToArray();
214 | string composers = StringTools.MergeDoubleDelimitedList(ComposerNames, Globals.TaggingOptions.PrimaryListSeparator, Globals.TaggingOptions.ListEndSeparator);
215 | if (!string.IsNullOrEmpty(composers) && Globals.TaggingOptions.MergePerformers)
216 | {
217 | ComposerName = composers;
218 | }
219 | else
220 | {
221 | ComposerName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Composer?.Name);
222 | }
223 | // Qobuz could return an unknown role for the Composers. Use singular ComposerName as fallback
224 | if (ComposerNames.Length < 1)
225 | {
226 | ComposerNames = new string[] { ComposerName };
227 | }
228 |
229 | ProducerNames = performersParser.GetPerformersWithRole(InvolvedPersonRoleType.Producer).ToArray();
230 | ProducerName = StringTools.MergeDoubleDelimitedList(ProducerNames, Globals.TaggingOptions.PrimaryListSeparator, Globals.TaggingOptions.ListEndSeparator);
231 | InvolvedPeople = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Performers);
232 | TrackName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Title.Trim());
233 | TrackVersionName = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Version?.Trim());
234 | // Add track version to TrackName
235 | TrackName += (TrackVersionName == null ? "" : " (" + TrackVersionName + ")");
236 |
237 | Advisory = qobuzTrack.ParentalWarning;
238 | Copyright = StringTools.DecodeEncodedNonAsciiCharacters(qobuzTrack.Copyright);
239 | Isrc = qobuzTrack.Isrc;
240 |
241 | // Grab tag numbers
242 | TrackNumber = qobuzTrack.TrackNumber.GetValueOrDefault();
243 | DiscNumber = qobuzTrack.MediaNumber.GetValueOrDefault();
244 | Duration = qobuzTrack.Duration.GetValueOrDefault();
245 |
246 | // Paths
247 | SetTrackPaths();
248 | }
249 |
250 | // Set Track tagbased Paths
251 | private void SetTrackPaths()
252 | {
253 | CurrentDownloadPaths.PerformerNamePath = StringTools.TrimToMaxLength(StringTools.GetSafeFilename(PerformerName), Globals.MaxLength);
254 | CurrentDownloadPaths.TrackNamePath = StringTools.GetSafeFilename(TrackName);
255 | }
256 |
257 | ///
258 | /// Get the Artist names with given role as an array
259 | ///
260 | ///
261 | ///
262 | ///
263 | public string[] GetArtistNames(List artists, InvolvedPersonRoleType role)
264 | {
265 | return artists.Where(artist => artist.Roles.Exists(roleString => InvolvedPersonRoleMapping.GetRoleByString(roleString) == role))
266 | .Select(artist => artist.Name)
267 | .ToArray();
268 | }
269 |
270 | public string MergeFeaturedArtistsWithMainArtists(string[] mainArtists, string[] featuresArtists)
271 | {
272 | string mergedMainArtists = StringTools.MergeDoubleDelimitedList(mainArtists, Globals.TaggingOptions.PrimaryListSeparator, Globals.TaggingOptions.ListEndSeparator);
273 | string mergedFeaturedArtists = StringTools.MergeDoubleDelimitedList(featuresArtists, Globals.TaggingOptions.PrimaryListSeparator, Globals.TaggingOptions.ListEndSeparator);
274 |
275 | if (string.IsNullOrEmpty(mergedFeaturedArtists))
276 | {
277 | return mergedMainArtists;
278 | }
279 | else
280 | {
281 | return $"{mergedMainArtists} Feat. {mergedFeaturedArtists}";
282 | }
283 |
284 | }
285 | }
286 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Models/Download/DownloadItemPaths.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX.Models
2 | {
3 | public class DownloadItemPaths
4 | {
5 | public string QualityPath { get; set; }
6 | public string FinalTrackNamePath { get; set; }
7 | public string FinalTrackNameVersionPath { get; set; }
8 | public string FullTrackFileName { get; set; }
9 | public string FullTrackFilePath { get; set; }
10 | public string AlbumArtistPath { get; set; }
11 | public string PerformerNamePath { get; set; }
12 | public string AlbumNamePath { get; set; }
13 | public string TrackNamePath { get; set; }
14 | public string VersionNamePath { get; set; }
15 | public string Path1Full { get; set; }
16 | public string Path2Full { get; set; }
17 | public string Path3Full { get; set; }
18 | public string Path4Full { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/QobuzDownloaderX/Models/UI/SearchResultRow.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX.Models.UI
2 | {
3 | public class SearchResultRow
4 | {
5 | public string ThumbnailUrl { get; set; }
6 | public string Artist { get; set; }
7 | public string Title { get; set; }
8 | public bool Explicit { get; set; }
9 | public string FormattedDuration { get; set; }
10 | public string WebPlayerUrl { get; set; }
11 | public string StoreUrl { get; set; }
12 | public int TrackCount { get; set; }
13 | public string ReleaseDate { get; set; }
14 | public bool HiRes { get; set; }
15 | public string FormattedQuality { get; set; }
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/QobuzDownloaderX/Program.cs:
--------------------------------------------------------------------------------
1 | using Bluegrams.Application;
2 | using QobuzDownloaderX.Shared;
3 | using System;
4 | using System.Globalization;
5 | using System.Windows.Forms;
6 |
7 | namespace QobuzDownloaderX
8 | {
9 | internal static class Program
10 | {
11 | ///
12 | /// The main entry point for the application.
13 | ///
14 | [STAThread]
15 | private static void Main()
16 | {
17 | // Make the default settings class portable
18 | PortableSettingsProviderBase.SettingsDirectory = FileTools.GetInitializedSettingsDir();
19 | // Global override of every settings "Roaming" property.
20 | PortableSettingsProviderBase.AllRoaming = true;
21 | PortableJsonSettingsProvider.ApplyProvider(Properties.Settings.Default);
22 |
23 | // Use en-US formatting everywhere for consistency
24 | var culture = CultureInfo.GetCultureInfo("en-US");
25 |
26 | //Culture for any thread
27 | CultureInfo.DefaultThreadCurrentCulture = culture;
28 |
29 | //Culture for UI in any thread
30 | CultureInfo.DefaultThreadCurrentUICulture = culture;
31 |
32 | Application.EnableVisualStyles();
33 | Application.SetCompatibleTextRenderingDefault(false);
34 |
35 | // Create logging dir and clean older logs if present
36 | Globals.LoggingDir = FileTools.GetInitializedLogDir();
37 |
38 | // Initialise forms
39 | Globals.LoginForm = new LoginForm();
40 | Globals.AboutForm = new AboutForm();
41 | Globals.SettingsForm = new SettingsForm();
42 |
43 | // Register EventHandler to release resources on exit
44 | Application.ApplicationExit += ApplicationExit;
45 |
46 | Application.Run(Globals.LoginForm);
47 | }
48 |
49 | private static void ApplicationExit(object sender, EventArgs e)
50 | {
51 | QobuzApiServiceManager.ReleaseApiService();
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information Globals.AboutForm 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("Qobuz music downloader")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("DJDoubleD")]
12 | [assembly: AssemblyProduct("QobuzDownloaderX")]
13 | [assembly: AssemblyCopyright("Copyright © 2024")]
14 | [assembly: AssemblyTrademark("DJDoubleD")]
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 | [assembly: AssemblyVersion("1.2.7.0")]
36 | [assembly: AssemblyFileVersion("1.2.7.0")]
37 | [assembly: NeutralResourcesLanguage("")]
38 |
--------------------------------------------------------------------------------
/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.Byte[].
65 | ///
66 | internal static byte[] HKGrotesk {
67 | get {
68 | object obj = ResourceManager.GetObject("HKGrotesk", resourceCulture);
69 | return ((byte[])(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Byte[].
75 | ///
76 | internal static byte[] HKGrotesk_Italic {
77 | get {
78 | object obj = ResourceManager.GetObject("HKGrotesk_Italic", resourceCulture);
79 | return ((byte[])(obj));
80 | }
81 | }
82 |
83 | ///
84 | /// Looks up a localized resource of type System.Drawing.Bitmap.
85 | ///
86 | internal static System.Drawing.Bitmap login_frame {
87 | get {
88 | object obj = ResourceManager.GetObject("login_frame", 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 qbdlx {
97 | get {
98 | object obj = ResourceManager.GetObject("qbdlx", resourceCulture);
99 | return ((System.Drawing.Bitmap)(obj));
100 | }
101 | }
102 |
103 | ///
104 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
105 | ///
106 | internal static System.Drawing.Icon qbdlx_icon {
107 | get {
108 | object obj = ResourceManager.GetObject("qbdlx_icon", resourceCulture);
109 | return ((System.Drawing.Icon)(obj));
110 | }
111 | }
112 |
113 | ///
114 | /// Looks up a localized resource of type System.Drawing.Bitmap.
115 | ///
116 | internal static System.Drawing.Bitmap qbdlx_white {
117 | get {
118 | object obj = ResourceManager.GetObject("qbdlx_white", resourceCulture);
119 | return ((System.Drawing.Bitmap)(obj));
120 | }
121 | }
122 |
123 | ///
124 | /// Looks up a localized resource of type System.Drawing.Bitmap.
125 | ///
126 | internal static System.Drawing.Bitmap qobuz_logo_store {
127 | get {
128 | object obj = ResourceManager.GetObject("qobuz_logo_store", resourceCulture);
129 | return ((System.Drawing.Bitmap)(obj));
130 | }
131 | }
132 |
133 | ///
134 | /// Looks up a localized resource of type System.Drawing.Bitmap.
135 | ///
136 | internal static System.Drawing.Bitmap settings_icon {
137 | get {
138 | object obj = ResourceManager.GetObject("settings_icon", 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 smr_audio {
147 | get {
148 | object obj = ResourceManager.GetObject("smr_audio", resourceCulture);
149 | return ((System.Drawing.Bitmap)(obj));
150 | }
151 | }
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/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\Fonts\HankenGrotesk-VariableFont_wght.ttf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
123 |
124 |
125 | ..\Resources\Fonts\HankenGrotesk-Italic-VariableFont_wght.ttf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
126 |
127 |
128 | ..\Resources\login-frame.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
129 |
130 |
131 | ..\Resources\qbdlx.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
132 |
133 |
134 | ..\Resources\qbdlx_icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
135 |
136 |
137 | ..\Resources\qbdlx-white.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
138 |
139 |
140 | ..\Resources\qobuz_logo_store.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
141 |
142 |
143 | ..\Resources\settings_icon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
144 |
145 |
146 | ..\Resources\smr-audio.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
147 |
148 |
--------------------------------------------------------------------------------
/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 | 0
103 |
104 |
105 | 2
106 |
107 |
108 | True
109 |
110 |
111 | 100
112 |
113 |
114 | 1
115 |
116 |
117 | -
118 |
119 |
120 | True
121 |
122 |
123 | True
124 |
125 |
126 | True
127 |
128 |
129 | ,
130 |
131 |
132 | &
133 |
134 |
135 | False
136 |
137 |
138 | True
139 |
140 |
141 | True
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/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.1
13 | 512
14 | true
15 |
16 | false
17 |
18 |
19 | publish\
20 | true
21 | Disk
22 | false
23 | Foreground
24 | 7
25 | Days
26 | false
27 | false
28 | true
29 | 0
30 | 1.0.0.%2a
31 | false
32 | true
33 | ..\README.md
34 |
35 |
36 | AnyCPU
37 | true
38 | embedded
39 | false
40 | bin\Debug\
41 | DEBUG;TRACE
42 | prompt
43 | 4
44 | Auto
45 |
46 |
47 |
48 |
49 | AnyCPU
50 | pdbonly
51 | true
52 | bin\Release\
53 | TRACE
54 | prompt
55 | 4
56 | true
57 |
58 |
59 | Resources\qbdlx_icon.ico
60 |
61 |
62 | QobuzDownloaderX.Program
63 |
64 |
65 | app.manifest
66 |
67 |
68 | app.manifest
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 | Form
108 |
109 |
110 | HeadlessForm.cs
111 |
112 |
113 | Form
114 |
115 |
116 | MainForm.cs
117 |
118 |
119 | Form
120 |
121 |
122 | AboutForm.cs
123 |
124 |
125 | Form
126 |
127 |
128 | LoginForm.cs
129 |
130 |
131 |
132 | Form
133 |
134 |
135 | SearchForm.cs
136 |
137 |
138 |
139 |
140 |
141 | Form
142 |
143 |
144 | SettingsForm.cs
145 |
146 |
147 | MainForm.cs
148 | Designer
149 |
150 |
151 | AboutForm.cs
152 |
153 |
154 | LoginForm.cs
155 |
156 |
157 | SearchForm.cs
158 | Designer
159 |
160 |
161 | ResXFileCodeGenerator
162 | Resources.Designer.cs
163 | Designer
164 |
165 |
166 | True
167 | Resources.resx
168 | True
169 |
170 |
171 | SettingsForm.cs
172 |
173 |
174 |
175 | SettingsSingleFileGenerator
176 | Settings.Designer.cs
177 |
178 |
179 | True
180 | Settings.settings
181 | True
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 | False
222 | Microsoft .NET Framework 4.8 %28x86 and x64%29
223 | true
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | 13.0.3
235 |
236 |
237 | 1.4.1
238 |
239 |
240 | 0.2.2
241 |
242 |
243 | 0.0.8
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
270 |
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/Fonts/HankenGrotesk-Italic-VariableFont_wght.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/Fonts/HankenGrotesk-Italic-VariableFont_wght.ttf
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/Fonts/HankenGrotesk-VariableFont_wght.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/Fonts/HankenGrotesk-VariableFont_wght.ttf
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/Untitled-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/Untitled-1.png
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/login-frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/login-frame.png
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/qbdlx-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/qbdlx-white.png
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/qbdlx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/qbdlx.png
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/qbdlx_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/qbdlx_icon.ico
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/qobuz_logo_dark.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/qobuz_logo_store.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/qobuz_logo_store.png
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/settings_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/settings_icon.png
--------------------------------------------------------------------------------
/QobuzDownloaderX/Resources/smr-audio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DJDoubleD/QobuzDownloaderX-MOD/993c708f594faaab36ca4b3a97e4a7b84676ecf2/QobuzDownloaderX/Resources/smr-audio.png
--------------------------------------------------------------------------------
/QobuzDownloaderX/Settings.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX.Properties
2 | {
3 | // This class allows you to handle specific events on the settings class:
4 | // The SettingChanging event is raised before a setting's value is changed.
5 | // The PropertyChanged event is raised after a setting's value is changed.
6 | // The SettingsLoaded event is raised after the setting values are loaded.
7 | // The SettingsSaving event is raised before the setting values are saved.
8 | internal sealed partial class Settings
9 | {
10 | public Settings()
11 | {
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 | {
22 | // Add code to handle the SettingChangingEvent event here.
23 | }
24 |
25 | private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e)
26 | {
27 | // Add code to handle the SettingsSaving event here.
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/DownloadLogger.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System;
3 | using System.IO;
4 | using System.Windows.Forms;
5 | using System.Linq;
6 |
7 | namespace QobuzDownloaderX.Shared
8 | {
9 | public class DownloadLogger
10 | {
11 | public readonly string downloadErrorLogPath = Path.Combine(Globals.LoggingDir, "Download_Errors.log");
12 | public delegate void DownloadEnded();
13 | private readonly DownloadEnded updateUiOnDownloadEnd;
14 | private TextBox ScreenOutputTextBox { get; }
15 |
16 | public string DownloadLogPath { get; set; }
17 |
18 | public DownloadLogger(TextBox outputTextBox, DownloadEnded updateUiOnDownloadEnd)
19 | {
20 | ScreenOutputTextBox = outputTextBox;
21 | this.updateUiOnDownloadEnd = updateUiOnDownloadEnd;
22 | }
23 |
24 | public void RemovePreviousErrorLog()
25 | {
26 | // Remove previous download error log
27 | if (System.IO.File.Exists(downloadErrorLogPath))
28 | {
29 | System.IO.File.Delete(downloadErrorLogPath);
30 | }
31 | }
32 |
33 | ///
34 | /// Add string to download log file, screen or both.
35 | /// When a line is written to the log file, a timestamp is added as prefix for non blank lines.
36 | /// For writing to log file, blank lines are filtered exept if the given string starts with a blank line.
37 | /// Use AddEmptyDownloadLogLine to create empty devider lines in the download log.
38 | ///
39 | /// String to be logged
40 | /// Should string be logged to file?
41 | /// Should string be logged to screen?
42 | public void AddDownloadLogLine(string logEntry, bool logToFile, bool logToScreen = false)
43 | {
44 | if (string.IsNullOrEmpty(logEntry)) return;
45 |
46 | if (logToScreen) ScreenOutputTextBox?.Invoke(new Action(() => ScreenOutputTextBox.AppendText(logEntry)));
47 |
48 | if (logToFile)
49 | {
50 | var logEntries = logEntry.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
51 | .Select(logLine => string.IsNullOrWhiteSpace(logLine) ? logLine : $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} : {logLine}");
52 |
53 | // Filter out all empty lines exept if the logEntry started with an empty line to avoid blank lines for each newline in UI
54 | var filteredLogEntries = logEntries.Aggregate(new List(), (accumulator, current) =>
55 | {
56 | if (accumulator.Count == 0 || !string.IsNullOrWhiteSpace(current))
57 | {
58 | accumulator.Add(current);
59 | }
60 |
61 | return accumulator;
62 | });
63 |
64 | System.IO.File.AppendAllLines(DownloadLogPath, filteredLogEntries);
65 | }
66 | }
67 |
68 | ///
69 | /// Convenience method to add [ERROR] prefix to logged string before calling AddDownloadLogLine.
70 | ///
71 | /// String to be logged
72 | /// Should string be logged to file?
73 | /// Should string be logged to screen?
74 | public void AddDownloadLogErrorLine(string logEntry, bool logToFile, bool logToScreen = false)
75 | {
76 | AddDownloadLogLine($"[ERROR] {logEntry}", logToFile, logToScreen);
77 | }
78 |
79 | ///
80 | /// Convenience method to add empty spacing line to log.
81 | ///
82 | /// Should string be logged to file?
83 | /// Should string be logged to screen?
84 | public void AddEmptyDownloadLogLine(bool logToFile, bool logToScreen = false)
85 | {
86 | AddDownloadLogLine($"{Environment.NewLine}{Environment.NewLine}", logToFile, logToScreen);
87 | }
88 |
89 | public void AddDownloadErrorLogLines(IEnumerable logEntries)
90 | {
91 | if (logEntries == null && !logEntries.Any()) return;
92 |
93 | System.IO.File.AppendAllLines(downloadErrorLogPath, logEntries);
94 | }
95 |
96 | public void AddDownloadErrorLogLine(string logEntry)
97 | {
98 | AddDownloadErrorLogLines(new string[] { logEntry });
99 | }
100 |
101 | ///
102 | /// Standardized logging when global download task fails.
103 | /// After logging, disabled controls are re-enabled.
104 | ///
105 | /// Name of the failed download task
106 | /// Exception thrown by task
107 | public void LogDownloadTaskException(string downloadTaskType, Exception downloadEx)
108 | {
109 | // If there is an issue trying to, or during the download, show error info.
110 | ClearUiLogComponent();
111 | AddDownloadLogErrorLine($"{downloadTaskType} Download Task ERROR. Details saved to error log.{Environment.NewLine}", true, true);
112 |
113 | AddDownloadErrorLogLine($"{downloadTaskType} Download Task ERROR.");
114 | AddDownloadErrorLogLine(downloadEx.ToString());
115 | AddDownloadErrorLogLine(Environment.NewLine);
116 | updateUiOnDownloadEnd?.Invoke();
117 | }
118 |
119 | public void LogFinishedDownloadJob(bool noErrorsOccured)
120 | {
121 | AddEmptyDownloadLogLine(true, true);
122 |
123 | // Say that downloading is completed.
124 | if (noErrorsOccured)
125 | {
126 | AddDownloadLogLine("Download job completed! All downloaded files will be located in your chosen path.", true, true);
127 | }
128 | else
129 | {
130 | AddDownloadLogLine("Download job completed with warnings and/or errors! Some or all files could be missing!", true, true);
131 | }
132 |
133 | updateUiOnDownloadEnd?.Invoke();
134 | }
135 |
136 | public void ClearUiLogComponent()
137 | {
138 | ScreenOutputTextBox.Invoke(new Action(() => ScreenOutputTextBox.Text = String.Empty));
139 | }
140 | }
141 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/FontManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.Drawing.Text;
4 | using System.Runtime.InteropServices;
5 |
6 | namespace QobuzDownloaderX.Shared
7 | {
8 | public static class FontManager
9 | {
10 | private static readonly PrivateFontCollection _fontCollection = new PrivateFontCollection();
11 |
12 | static FontManager()
13 | {
14 | // Add HKGrotesk font to PrivateFontCollection
15 | AddFont(Properties.Resources.HKGrotesk);
16 | }
17 |
18 | [DllImport("gdi32.dll")]
19 | private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts);
20 |
21 | private static void AddFont(byte[] fontData)
22 | {
23 | // Converteer byte array naar IntPtr en voeg toe aan de fontcollectie
24 | IntPtr fontPtr = Marshal.AllocCoTaskMem(fontData.Length);
25 | Marshal.Copy(fontData, 0, fontPtr, fontData.Length);
26 |
27 | // We HAVE to do this to register the font to the system (Weird .NET bug !)
28 | uint cFonts = 0;
29 | AddFontMemResourceEx(fontPtr, (uint)fontData.Length, IntPtr.Zero, ref cFonts);
30 |
31 | _fontCollection.AddMemoryFont(fontPtr, fontData.Length);
32 | Marshal.FreeCoTaskMem(fontPtr);
33 | }
34 |
35 | public static FontFamily GetFontFamily(string fontFamilyName)
36 | {
37 | return Array.Find(_fontCollection.Families, f => f.Name == fontFamilyName);
38 | }
39 |
40 | public static Font CreateFont(string fontFamilyName, float size, FontStyle style = FontStyle.Regular)
41 | {
42 | var fontFamily = GetFontFamily(fontFamilyName);
43 | return fontFamily != null ? new Font(fontFamily, size, style) : null;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Globals.cs:
--------------------------------------------------------------------------------
1 | using QobuzApiSharp.Models.User;
2 |
3 | namespace QobuzDownloaderX.Shared
4 | {
5 | internal static class Globals
6 | {
7 | public const string GITHUB_LATEST_VERSION_URL = "https://api.github.com/repos/DJDoubleD/QobuzDownloaderX-MOD/releases/latest";
8 | public const string GITHUB_LATEST_URL = "https://github.com/DJDoubleD/QobuzDownloaderX-MOD/releases/latest";
9 | public const string GITHUB_REPO_URL = "https://github.com/DJDoubleD/QobuzDownloaderX-MOD";
10 | public const string GITHUB_ImAiiR_REPO_URL = "https://github.com/ImAiiR/QobuzDownloaderX";
11 | public const string GITHUB_ALT_LOGIN_TUTORIAL_URL = "https://github.com/DJDoubleD/QobuzDownloaderX-MOD/wiki/Logging-In-(The-Alternate-Way)";
12 | public const string WEBPLAYER_BASE_URL = "https://play.qobuz.com";
13 | public const string USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0";
14 | public const string DEFAULT_COVER_ART_URL = "https://static.qobuz.com/images/covers/01/00/2013072600001_150.jpg";
15 |
16 | // Forms
17 | public static LoginForm LoginForm { get; set; }
18 | public static QobuzDownloaderX QbdlxForm { get; set; }
19 | public static AboutForm AboutForm { get; set; }
20 | public static SearchForm SearchForm { get; set; }
21 | public static SettingsForm SettingsForm { get; set; }
22 |
23 | // Login
24 | public static Login Login { get; set; }
25 |
26 | // Tagging options
27 | public static TaggingOptions TaggingOptions { get; set; }
28 |
29 | // Audio quality selection
30 | public static string FormatIdString { get; set; }
31 | public static string AudioFileType { get; set; }
32 |
33 | // Additional user selections
34 | public static int MaxLength { get; set; }
35 | public static string FileNameTemplateString { get; set; }
36 |
37 | // Logs
38 | public static string LoggingDir { get; set; }
39 | }
40 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/GoodiesFileType.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX.Models.Content
2 | {
3 | public enum GoodiesFileType
4 | {
5 | VIDEO = 24,
6 | BOOKLET = 21
7 | }
8 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/InvolvedPersonRoleType.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX.Shared
2 | {
3 | public enum InvolvedPersonRoleType
4 | {
5 | Composer,
6 | Conductor,
7 | FeaturedArtist,
8 | Instruments,
9 | Lyricist,
10 | MainArtist,
11 | Miscellaneous,
12 | MixArtist,
13 | Producer,
14 | Publisher
15 | }
16 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/QobuzApiServiceManager.cs:
--------------------------------------------------------------------------------
1 | using QobuzApiSharp.Service;
2 | using System;
3 |
4 | namespace QobuzDownloaderX.Shared
5 | {
6 | public static class QobuzApiServiceManager
7 | {
8 | private static QobuzApiService apiService;
9 |
10 | public static QobuzApiService GetApiService()
11 | {
12 | if (apiService == null)
13 | {
14 | throw new InvalidOperationException("QobuzApiService not initialized");
15 | }
16 |
17 | return apiService;
18 | }
19 |
20 | public static void Initialize(string appId, string appSecret)
21 | {
22 | apiService?.Dispose();
23 | apiService = new QobuzApiService(appId, appSecret);
24 | }
25 |
26 | public static void Initialize()
27 | {
28 | apiService?.Dispose();
29 | apiService = new QobuzApiService();
30 | }
31 |
32 | public static void ReleaseApiService()
33 | {
34 | if (apiService != null)
35 | {
36 | using (apiService)
37 | {
38 | apiService = null;
39 | }
40 | }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/TaggingOptions.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX.Shared
2 | {
3 | internal class TaggingOptions
4 | {
5 | public bool WriteAlbumNameTag { get; set; }
6 | public bool WriteAlbumArtistTag { get; set; }
7 | public bool WriteTrackTitleTag { get; set; }
8 | public bool WriteTrackArtistTag { get; set; }
9 | public bool WriteTrackNumberTag { get; set; }
10 | public bool WriteTrackTotalTag { get; set; }
11 | public bool WriteDiskNumberTag { get; set; }
12 | public bool WriteDiskTotalTag { get; set; }
13 | public bool WriteReleaseYearTag { get; set; }
14 | public bool WriteReleaseDateTag { get; set; }
15 | public bool WriteGenreTag { get; set; }
16 | public bool WriteComposerTag { get; set; }
17 | public bool WriteCopyrightTag { get; set; }
18 | public bool WriteIsrcTag { get; set; }
19 | public bool WriteMediaTypeTag { get; set; }
20 | public bool WriteUpcTag { get; set; }
21 | public bool WriteExplicitTag { get; set; }
22 | public bool WriteCommentTag { get; set; }
23 | public bool WriteCoverImageTag { get; set; }
24 | public bool WriteProducerTag { get; set; }
25 | public bool WriteLabelTag { get; set; }
26 | public bool WriteInvolvedPeopleTag { get; set; }
27 | public bool MergePerformers { get; set; }
28 | public bool WriteUrlTag { get; set; }
29 | public string CommentTag { get; set; }
30 | public string ArtSize { get; set; }
31 | public string PrimaryListSeparator { get; set; }
32 | public string ListEndSeparator { get; set; }
33 | }
34 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Tools/AudioFileTagger.cs:
--------------------------------------------------------------------------------
1 | using QobuzDownloaderX.Models;
2 | using System;
3 | using TagLib;
4 |
5 | namespace QobuzDownloaderX.Shared
6 | {
7 | public static class AudioFileTagger
8 | {
9 | // Add Metadata to audiofiles in ID3v2 for mp3 and Vorbis Comment for FLAC
10 | public static void AddMetaDataTags(DownloadItemInfo fileInfo, string tagFilePath, string tagCoverArtFilePath, DownloadLogger logger)
11 | {
12 | // Set file to tag
13 | var tfile = TagLib.File.Create(tagFilePath);
14 | tfile.RemoveTags(TagTypes.Id3v1);
15 |
16 | // Use ID3v2.4 as default mp3 tag version
17 | TagLib.Id3v2.Tag.DefaultVersion = 4;
18 | TagLib.Id3v2.Tag.ForceDefaultVersion = true;
19 |
20 | switch (Globals.AudioFileType)
21 | {
22 | case ".mp3":
23 |
24 | // For custom / troublesome tags.
25 | TagLib.Id3v2.Tag customId3v2 = (TagLib.Id3v2.Tag)tfile.GetTag(TagTypes.Id3v2, true);
26 |
27 | // Saving cover art to file(s)
28 | if (Globals.TaggingOptions.WriteCoverImageTag)
29 | {
30 | try
31 | {
32 | // Define cover art to use for MP3 file(s)
33 | TagLib.Id3v2.AttachmentFrame pic = new TagLib.Id3v2.AttachmentFrame
34 | {
35 | TextEncoding = TagLib.StringType.Latin1,
36 | MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg,
37 | Type = TagLib.PictureType.FrontCover,
38 | Data = TagLib.ByteVector.FromPath(tagCoverArtFilePath)
39 | };
40 |
41 | // Save cover art to MP3 file.
42 | tfile.Tag.Pictures = new TagLib.IPicture[1] { pic };
43 | tfile.Save();
44 | }
45 | catch
46 | {
47 | logger.AddDownloadLogErrorLine($"Cover art tag failed, .jpg still exists?...{Environment.NewLine}", true, true);
48 | }
49 | }
50 |
51 | // Track Title tag, version is already added to name if available
52 | if (Globals.TaggingOptions.WriteTrackTitleTag) { tfile.Tag.Title = fileInfo.TrackName; }
53 |
54 | // Album Title tag, version is already added to name if available
55 | if (Globals.TaggingOptions.WriteAlbumNameTag) { tfile.Tag.Album = fileInfo.AlbumName; }
56 |
57 | // Album Artist tag
58 | if (Globals.TaggingOptions.WriteAlbumArtistTag)
59 | {
60 | if (Globals.TaggingOptions.MergePerformers)
61 | {
62 | tfile.Tag.AlbumArtists = new string[] { fileInfo.AlbumArtist };
63 | }
64 | else
65 | {
66 | tfile.Tag.AlbumArtists = fileInfo.AlbumArtists;
67 | }
68 | }
69 |
70 | // Track Artist tag
71 | if (Globals.TaggingOptions.WriteTrackArtistTag)
72 | {
73 | if (Globals.TaggingOptions.MergePerformers)
74 | {
75 | tfile.Tag.Performers = new string[] { fileInfo.PerformerName };
76 | }
77 | else
78 | {
79 | tfile.Tag.Performers = fileInfo.PerformerNames;
80 | }
81 | }
82 |
83 | // Composer tag
84 | if (Globals.TaggingOptions.WriteComposerTag)
85 | {
86 | if (Globals.TaggingOptions.MergePerformers)
87 | {
88 | tfile.Tag.Composers = new string[] { fileInfo.ComposerName };
89 | }
90 | else
91 | {
92 | tfile.Tag.Composers = fileInfo.ComposerNames;
93 | }
94 | }
95 |
96 | // Label tag
97 | if (Globals.TaggingOptions.WriteLabelTag) { tfile.Tag.Publisher = fileInfo.LabelName; }
98 |
99 | // InvolvedPeople tag
100 | if (Globals.TaggingOptions.WriteInvolvedPeopleTag) { customId3v2.SetTextFrame("TIPL", fileInfo.InvolvedPeople); }
101 |
102 | // Release Year tag (writes to "TDRC" (recording date) Frame)
103 | if (Globals.TaggingOptions.WriteReleaseYearTag) { tfile.Tag.Year = UInt32.Parse(fileInfo.ReleaseDate.Substring(0, 4)); }
104 |
105 | // Release Date tag (use "TDRL" (release date) Frame for full date)
106 | if (Globals.TaggingOptions.WriteReleaseDateTag) { customId3v2.SetTextFrame("TDRL", fileInfo.ReleaseDate); }
107 |
108 | // Genre tag
109 | if (Globals.TaggingOptions.WriteGenreTag) { tfile.Tag.Genres = new string[] { fileInfo.Genre }; }
110 |
111 | // Disc Number tag
112 | if (Globals.TaggingOptions.WriteDiskNumberTag) { tfile.Tag.Disc = Convert.ToUInt32(fileInfo.DiscNumber); }
113 |
114 | // Total Discs tag
115 | if (Globals.TaggingOptions.WriteDiskTotalTag) { tfile.Tag.DiscCount = Convert.ToUInt32(fileInfo.DiscTotal); }
116 |
117 | // Total Tracks tag
118 | if (Globals.TaggingOptions.WriteTrackTotalTag) { tfile.Tag.TrackCount = Convert.ToUInt32(fileInfo.TrackTotal); }
119 |
120 | // Track Number tag
121 | // !! Set Track Number after Total Tracks to prevent taglib-sharp from re-formatting the field to a "two-digit zero-filled value" !!
122 | if (Globals.TaggingOptions.WriteTrackNumberTag)
123 | {
124 | // Set TRCK tag manually to prevent using "two-digit zero-filled value"
125 | // See https://github.com/mono/taglib-sharp/pull/240 where this change was introduced in taglib-sharp v2.3
126 | // Original command: tfile.Tag.Track = Convert.ToUInt32(TrackNumber);
127 | customId3v2.SetNumberFrame("TRCK", Convert.ToUInt32(fileInfo.TrackNumber), tfile.Tag.TrackCount);
128 | }
129 |
130 | // Comment tag
131 | if (Globals.TaggingOptions.WriteCommentTag) { tfile.Tag.Comment = Globals.TaggingOptions.CommentTag; }
132 |
133 | // Copyright tag
134 | if (Globals.TaggingOptions.WriteCopyrightTag) { tfile.Tag.Copyright = fileInfo.Copyright; }
135 |
136 | // ISRC tag
137 | if (Globals.TaggingOptions.WriteIsrcTag) { tfile.Tag.ISRC = fileInfo.Isrc; }
138 |
139 | // Release Type tag
140 | if (fileInfo.MediaType != null && Globals.TaggingOptions.WriteMediaTypeTag) { customId3v2.SetTextFrame("TMED", fileInfo.MediaType); }
141 |
142 | // Album store URL tag
143 | if (fileInfo.Url != null && Globals.TaggingOptions.WriteUrlTag) { customId3v2.SetTextFrame("WCOM", fileInfo.Url); }
144 |
145 | // Save all selected tags to file
146 | tfile.Save();
147 |
148 | break;
149 |
150 | case ".flac":
151 |
152 | // For custom / troublesome tags.
153 | TagLib.Ogg.XiphComment custom = (TagLib.Ogg.XiphComment)tfile.GetTag(TagLib.TagTypes.Xiph);
154 |
155 | // Saving cover art to file(s)
156 | if (Globals.TaggingOptions.WriteCoverImageTag)
157 | {
158 | try
159 | {
160 | // Define cover art to use for FLAC file(s)
161 | TagLib.Id3v2.AttachmentFrame pic = new TagLib.Id3v2.AttachmentFrame
162 | {
163 | TextEncoding = TagLib.StringType.Latin1,
164 | MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg,
165 | Type = TagLib.PictureType.FrontCover,
166 | Data = TagLib.ByteVector.FromPath(tagCoverArtFilePath)
167 | };
168 |
169 | // Save cover art to FLAC file.
170 | tfile.Tag.Pictures = new TagLib.IPicture[1] { pic };
171 | tfile.Save();
172 | }
173 | catch
174 | {
175 | logger.AddDownloadLogErrorLine($"Cover art tag failed, .jpg still exists?...{Environment.NewLine}", true, true);
176 | }
177 | }
178 |
179 | // Track Title tag, version is already added to name if available
180 | if (Globals.TaggingOptions.WriteTrackTitleTag) { tfile.Tag.Title = fileInfo.TrackName; }
181 |
182 | // Album Title tag, version is already added to name if available
183 | if (Globals.TaggingOptions.WriteAlbumNameTag) { tfile.Tag.Album = fileInfo.AlbumName; }
184 |
185 | // Album Artist tag
186 | if (Globals.TaggingOptions.WriteAlbumArtistTag)
187 | {
188 | if (Globals.TaggingOptions.MergePerformers)
189 | {
190 | tfile.Tag.AlbumArtists = new string[] { fileInfo.AlbumArtist };
191 | }
192 | else
193 | {
194 | tfile.Tag.AlbumArtists = fileInfo.AlbumArtists;
195 | }
196 | }
197 |
198 | // Track Artist tag
199 | if (Globals.TaggingOptions.WriteTrackArtistTag)
200 | {
201 | if (Globals.TaggingOptions.MergePerformers)
202 | {
203 | tfile.Tag.Performers = new string[] { fileInfo.PerformerName };
204 | }
205 | else
206 | {
207 | tfile.Tag.Performers = fileInfo.PerformerNames;
208 | }
209 | }
210 |
211 | // Composer tag
212 | if (Globals.TaggingOptions.WriteComposerTag)
213 | {
214 | if (Globals.TaggingOptions.MergePerformers)
215 | {
216 | tfile.Tag.Composers = new string[] { fileInfo.ComposerName };
217 | }
218 | else
219 | {
220 | tfile.Tag.Composers = fileInfo.ComposerNames;
221 | }
222 | }
223 |
224 | // Label tag
225 | if (Globals.TaggingOptions.WriteLabelTag)
226 | {
227 | tfile.Tag.Publisher = fileInfo.LabelName; // Writes to the official ORGANIZATION field
228 | custom.SetField("LABEL", fileInfo.LabelName);
229 | }
230 |
231 | // Producer tag
232 | if (Globals.TaggingOptions.WriteProducerTag)
233 | {
234 | if (Globals.TaggingOptions.MergePerformers)
235 | {
236 | custom.SetField("PRODUCER", fileInfo.ProducerName);
237 | }
238 | else
239 | {
240 | custom.SetField("PRODUCER", fileInfo.ProducerNames);
241 | }
242 | }
243 |
244 | // InvolvedPeople tag
245 | if (Globals.TaggingOptions.WriteInvolvedPeopleTag) { custom.SetField("INVOLVEDPEOPLE", fileInfo.InvolvedPeople); }
246 |
247 | // Release Year tag (The "tfile.Tag.Year" field actually writes to the DATE tag, so use custom tag)
248 | if (Globals.TaggingOptions.WriteReleaseYearTag) { custom.SetField("YEAR", fileInfo.ReleaseDate.Substring(0, 4)); }
249 |
250 | // Release Date tag
251 | if (Globals.TaggingOptions.WriteReleaseDateTag) { custom.SetField("DATE", fileInfo.ReleaseDate); }
252 |
253 | // Genre tag
254 | if (Globals.TaggingOptions.WriteGenreTag) { tfile.Tag.Genres = new string[] { fileInfo.Genre }; }
255 |
256 | // Track Number tag
257 | if (Globals.TaggingOptions.WriteTrackNumberTag)
258 | {
259 | tfile.Tag.Track = Convert.ToUInt32(fileInfo.TrackNumber);
260 | // Override TRACKNUMBER tag again to prevent using "two-digit zero-filled value"
261 | // See https://github.com/mono/taglib-sharp/pull/240 where this change was introduced in taglib-sharp v2.3
262 | custom.SetField("TRACKNUMBER", Convert.ToUInt32(fileInfo.TrackNumber));
263 | }
264 |
265 | // Disc Number tag
266 | if (Globals.TaggingOptions.WriteDiskNumberTag) { tfile.Tag.Disc = Convert.ToUInt32(fileInfo.DiscNumber); }
267 |
268 | // Total Discs tag
269 | if (Globals.TaggingOptions.WriteDiskTotalTag) { tfile.Tag.DiscCount = Convert.ToUInt32(fileInfo.DiscTotal); }
270 |
271 | // Total Tracks tag
272 | if (Globals.TaggingOptions.WriteTrackTotalTag) { tfile.Tag.TrackCount = Convert.ToUInt32(fileInfo.TrackTotal); }
273 |
274 | // Comment tag
275 | if (Globals.TaggingOptions.WriteCommentTag) { tfile.Tag.Comment = Globals.TaggingOptions.CommentTag; }
276 |
277 | // Copyright tag
278 | if (Globals.TaggingOptions.WriteCopyrightTag) { tfile.Tag.Copyright = fileInfo.Copyright; }
279 |
280 | // UPC tag
281 | if (Globals.TaggingOptions.WriteUpcTag) { custom.SetField("UPC", fileInfo.Upc); }
282 |
283 | // ISRC tag
284 | if (Globals.TaggingOptions.WriteIsrcTag) { tfile.Tag.ISRC = fileInfo.Isrc; }
285 |
286 | // Release Type tag
287 | if (fileInfo.MediaType != null && Globals.TaggingOptions.WriteMediaTypeTag)
288 | {
289 | custom.SetField("MEDIATYPE", fileInfo.MediaType);
290 | }
291 |
292 | // Explicit tag
293 | if (Globals.TaggingOptions.WriteExplicitTag)
294 | {
295 | if (fileInfo.Advisory == true) { custom.SetField("ITUNESADVISORY", "1"); } else { custom.SetField("ITUNESADVISORY", "0"); }
296 | }
297 |
298 | // Album store URL tag
299 | if (fileInfo.Url != null && Globals.TaggingOptions.WriteUrlTag) { custom.SetField("URL", fileInfo.Url); }
300 |
301 | // Save all selected tags to file
302 | tfile.Save();
303 |
304 | break;
305 | }
306 | }
307 | }
308 | }
309 |
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Tools/ControlTools.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Windows.Forms;
3 |
4 | namespace QobuzDownloaderX.Shared
5 | {
6 | internal static class ControlTools
7 | {
8 | public static void SetDoubleBuffered(Control control)
9 | {
10 | // set instance non-public property with name "DoubleBuffered" to true
11 | typeof(Control).InvokeMember("DoubleBuffered",
12 | BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
13 | null, control, new object[] { true });
14 | }
15 |
16 | public static void RemoveControls(Control userControl)
17 | {
18 | while (userControl.Controls.Count > 0)
19 | {
20 | Control control = userControl.Controls[0];
21 | if (control.HasChildren)
22 | {
23 | RemoveControls(control); // Recursively remove and dispose all children
24 | }
25 | userControl.Controls.Remove(control);
26 | control.Dispose(); // Remove Control to clear from memory
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Tools/DownloadUrlParser.cs:
--------------------------------------------------------------------------------
1 | using QobuzDownloaderX.Models;
2 | using System.Linq;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace QobuzDownloaderX.Shared
6 |
7 | {
8 | public static class DownloadUrlParser
9 | {
10 | // Pre-compiled supported download URL patterns
11 | private static readonly Regex[] DownloadUrlRegExes = {
12 | new Regex("https:\\/\\/(?:.*?).qobuz.com\\/(?.*?)\\/(?.*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled),
13 | new Regex("https:\\/\\/(?:.*?).qobuz.com\\/(?:.*?)\\/(?.*?)\\/(?.*?)\\/(?download-streaming-albums)\\/(?.*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled),
14 | new Regex("https:\\/\\/(?:.*?).qobuz.com\\/(?:.*?)\\/(?.*?)\\/(?.*?)\\/(?.*?)$", RegexOptions.IgnoreCase | RegexOptions.Compiled)
15 | };
16 |
17 | // Supported types of links. "interpreter" = "artist" in store links
18 | public static readonly string[] LinkTypes = { "album", "track", "artist", "label", "user", "playlist", "interpreter" };
19 |
20 | public static DownloadItem ParseDownloadUrl(string downloadUrl)
21 | {
22 | DownloadItem downloadItem = new DownloadItem(downloadUrl);
23 |
24 | if (string.IsNullOrEmpty(downloadUrl))
25 | {
26 | return downloadItem;
27 | }
28 |
29 | foreach (Regex regEx in DownloadUrlRegExes)
30 | {
31 | Match matches = regEx.Match(downloadUrl);
32 |
33 | if (matches.Success)
34 | {
35 | if (!LinkTypes.Contains(matches.Result("${Type}"))) continue;
36 |
37 | // Valid Type found, set DownloadItem values
38 | downloadItem.Type = matches.Result("${Type}");
39 | downloadItem.Id = matches.Result("${id}")?.TrimEnd('/');
40 |
41 | // In store links, "interpreter" = "artist"
42 | if (downloadItem.Type == "interpreter") downloadItem.Type = "artist";
43 |
44 | break;
45 | }
46 | }
47 |
48 | return downloadItem;
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Tools/FileTools.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System;
3 | using System.Reflection;
4 | using System.Windows.Forms;
5 |
6 | namespace QobuzDownloaderX.Shared
7 | {
8 | internal static class FileTools
9 | {
10 | public static void DeleteFilesByAge(string folderPath, int maxAgeInDays)
11 | {
12 | if (!Directory.Exists(folderPath)) return;
13 |
14 | DateTime thresholdDate = DateTime.Now.AddDays(-maxAgeInDays);
15 |
16 | foreach (var file in Directory.GetFiles(folderPath))
17 | {
18 | FileInfo fileInfo = new FileInfo(file);
19 | if (fileInfo.LastWriteTime < thresholdDate)
20 | {
21 | File.Delete(file);
22 | }
23 | }
24 | }
25 |
26 | public static string GetInitializedLogDir()
27 | {
28 | string logDirPath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "logs");
29 | if (!System.IO.Directory.Exists(logDirPath)) System.IO.Directory.CreateDirectory(logDirPath);
30 | DeleteFilesByAge(logDirPath, 1);
31 |
32 | return logDirPath;
33 | }
34 |
35 | public static string GetInitializedSettingsDir()
36 | {
37 | string settingsDirPath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "settings");
38 | if (!System.IO.Directory.Exists(settingsDirPath)) System.IO.Directory.CreateDirectory(settingsDirPath);
39 |
40 | return settingsDirPath;
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Tools/InvolvedPersonRoleMapping.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 |
7 | namespace QobuzDownloaderX.Shared
8 | {
9 | public static class InvolvedPersonRoleMapping
10 | {
11 | private static readonly Dictionary> RoleMappings = new Dictionary>
12 | {
13 | { InvolvedPersonRoleType.Miscellaneous, new List { "A&R Director", "A&R", "AAndRAdministrator", "Additional Production",
14 | "AHH", "Assistant Mixer", "AssistantEngineer", "Assistant Producer", "Asst. Recording Engineer", "AssociatedPerformer",
15 | "Author", "Choir", "Chorus Master", "Contractor", "Co-Producer", "Engineer", "Masterer", "Mastering Engineer", "MasteringEngineer",
16 | "Misc.Prod.", "Mixer", "Mixing Engineer", "Music Production", "Orchestra", "Performance Arranger", "Programming", "Programmer",
17 | "RecordingEngineer", "Soloist", "StudioPersonnel", "Vocals", "Writer"} }, // Default if not mapped!
18 | { InvolvedPersonRoleType.Composer, new List { "Composer", "ComposerLyricist", "Composer-Lyricist" } },
19 | { InvolvedPersonRoleType.Conductor, new List { "Conductor" } },
20 | { InvolvedPersonRoleType.FeaturedArtist, new List {"FeaturedArtist", "Featuring", "featured-artist" } },
21 | { InvolvedPersonRoleType.Instruments, new List { "Bass Guitar", "Cello", "Drums", "Guitar", "Horn", "Keyboards", "Percussion",
22 | "Piano", "Trombone", "Tuba", "Trumpet", "Viola", "Violin" } },
23 | { InvolvedPersonRoleType.Lyricist, new List { "Lyricist", "ComposerLyricist", "Composer-Lyricist" } },
24 | { InvolvedPersonRoleType.MainArtist, new List { "MainArtist", "main-artist", "Performer" } },
25 | { InvolvedPersonRoleType.MixArtist, new List { "Remixer", "Re-Mixer"} },
26 | { InvolvedPersonRoleType.Producer, new List { "Producer"} },
27 | { InvolvedPersonRoleType.Publisher, new List { "Publisher", "MusicPublisher" } }
28 | };
29 |
30 | public static List GetStringsByRole(InvolvedPersonRoleType role)
31 | {
32 | if (RoleMappings.TryGetValue(role, out var strings))
33 | {
34 | return strings;
35 | }
36 | return new List();
37 | }
38 |
39 | public static InvolvedPersonRoleType GetRoleByString(string involvedPersonString)
40 | {
41 | // We just return PerformerRoleType.Miscellaneous if no match as we don't know all possible values
42 | // Search is done by ignoring case for maximum success.
43 | return RoleMappings.FirstOrDefault(kvp => kvp.Value.Contains(involvedPersonString, StringComparer.OrdinalIgnoreCase)).Key;
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Tools/MD5Tools.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 | using System.Text;
4 |
5 | namespace QobuzDownloaderX.Shared
6 | {
7 | internal static class MD5Tools
8 | {
9 |
10 | public static string GetMd5Hash(MD5 md5Hash, string input)
11 | {
12 | // Convert the input string to a byte array and compute the hash.
13 | byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));
14 |
15 | // Create a new StringBuilder to collect the bytes and create a string.
16 | StringBuilder sBuilder = new StringBuilder();
17 |
18 | // Loop through each byte of the hashed data and format each one as a hexadecimal string.
19 | for (int i = 0; i < data.Length; i++)
20 | {
21 | sBuilder.Append(data[i].ToString("x2"));
22 | }
23 |
24 | // Return the hexadecimal string.
25 | return sBuilder.ToString();
26 | }
27 |
28 | // Verify a hash against a string.
29 | public static bool VerifyMd5Hash(MD5 md5Hash, string input, string hash)
30 | {
31 | // Hash the input.
32 | string hashOfInput = GetMd5Hash(md5Hash, input);
33 |
34 | // Create a StringComparer and compare the hashes.
35 | StringComparer comparer = StringComparer.OrdinalIgnoreCase;
36 |
37 | return comparer.Compare(hashOfInput, hash) == 0;
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Tools/PerformersParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace QobuzDownloaderX.Shared
6 | {
7 | internal class PerformersParser
8 | {
9 | private readonly Dictionary> performers;
10 |
11 | public PerformersParser(string performersFullString)
12 | {
13 | performers = new Dictionary>();
14 |
15 | if (!string.IsNullOrEmpty(performersFullString))
16 | {
17 | performers = performersFullString
18 | .Split(new string[] { " - " }, StringSplitOptions.None) // Split performers by " - " because some roles include '-'
19 | .Select(performer => performer.Split(',')) // Split name & roles in best effort by ',', first part is name, next parts roles
20 | .GroupBy(parts => parts[0].Trim()) // Group performers by name since they can occure multiple times
21 | .ToDictionary(group => group.Key,
22 | group => group.SelectMany(parts => parts.Skip(1).Select(role => role.Trim())).Distinct().ToList()); // Flatten roles by performer and remove duplicates
23 | }
24 | }
25 |
26 | public string[] GetPerformersWithRole(InvolvedPersonRoleType role)
27 | {
28 | var roleStrings = InvolvedPersonRoleMapping.GetStringsByRole(role);
29 | return performers.Keys
30 | .Where(key => performers[key].Exists(value => roleStrings.Contains(value, StringComparer.OrdinalIgnoreCase)))
31 | .ToArray();
32 | }
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Tools/QualityStringMappings.cs:
--------------------------------------------------------------------------------
1 | using QobuzApiSharp.Models.Content;
2 | using System.Collections.Generic;
3 |
4 | namespace QobuzDownloaderX.Shared
5 | {
6 | public static class QualityStringMappings
7 | {
8 | private static readonly Dictionary QualityMappings = new Dictionary()
9 | {
10 | {"5", ("MP3 320kbps CBR", "MP3")},
11 | {"6", ("FLAC (16bit/44.1kHz)", "FLAC (16bit-44.1kHz)")},
12 | {"7", ("FLAC (24bit/96kHz)", "FLAC (24bit-96kHz)")},
13 | {"27", ("FLAC (24bit/192kHz)", "FLAC (24bit-196kHz)")}
14 | };
15 |
16 | private static readonly Dictionary MaximumBitDepthAndSampleRateMappings = new Dictionary()
17 | {
18 | {"5", (0, 0)}, // N/A, using 0 for easy calculation check.
19 | {"6", (16, 44.1)},
20 | {"7", (24, 96)},
21 | {"27", (24, 196)}
22 | };
23 | public static double GetMaxBitDepth(string formatIdString)
24 | {
25 | if (MaximumBitDepthAndSampleRateMappings.TryGetValue(formatIdString, out var value))
26 | return value.Item1;
27 | else
28 | throw new KeyNotFoundException($"QualityFormatID '{formatIdString}' not found.");
29 | }
30 |
31 | public static double GetMaxSampleRate(string formatIdString)
32 | {
33 | if (MaximumBitDepthAndSampleRateMappings.TryGetValue(formatIdString, out var value))
34 | return value.Item2;
35 | else
36 | throw new KeyNotFoundException($"QualityFormatID '{formatIdString}' not found.");
37 | }
38 |
39 | public static (string displayQuality, string pathSafeQuality) GetQualityStrings(string formatIdString)
40 | {
41 | if (QualityMappings.TryGetValue(formatIdString, out var value))
42 | return value;
43 | else
44 | throw new KeyNotFoundException($"QualityFormatID '{formatIdString}' not found.");
45 | }
46 |
47 | public static (string displayQuality, string pathSafeQuality) GetQualityStrings(string formatIdString, Album qobuzAlbum)
48 | {
49 | // Get Max bitDepth & sampleRate from API result.
50 | double bitDepth = qobuzAlbum.MaximumBitDepth.GetValueOrDefault();
51 | double sampleRate = qobuzAlbum.MaximumSamplingRate.GetValueOrDefault();
52 |
53 | double maxSelectedQuality = GetMaxBitDepth(formatIdString) * GetMaxSampleRate(formatIdString);
54 | double maxItemQuality = bitDepth * sampleRate;
55 |
56 | // Limit to selected quality if album quality is higher.
57 | if (maxSelectedQuality <= maxItemQuality)
58 | {
59 | return GetQualityStrings(formatIdString);
60 | }
61 |
62 | var displayQuality = "FLAC (" + bitDepth + "bit/" + sampleRate + "kHz)";
63 | var pathSafeQuality = displayQuality.Replace(@"\", "-").Replace("/", "-");
64 |
65 | return (displayQuality, pathSafeQuality);
66 | }
67 |
68 | }
69 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/Shared/Tools/StringTools.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text.RegularExpressions;
6 |
7 | namespace QobuzDownloaderX.Shared
8 | {
9 | internal static class StringTools
10 | {
11 | ///
12 | /// Decodes the encoded non ascii characters.
13 | ///
14 | /// The value.
15 | /// The decoded string.
16 | public static string DecodeEncodedNonAsciiCharacters(string value)
17 | {
18 | if (value != null)
19 | {
20 | return Regex.Replace(
21 | value,
22 | @"\\u(?[a-zA-Z0-9]{4})",
23 | m => ((char)int.Parse(m.Groups["Value"].Value, NumberStyles.HexNumber)).ToString());
24 | }
25 | else
26 | {
27 | return null;
28 | }
29 | }
30 |
31 | // For converting illegal filename characters to an underscore.
32 | public static string GetSafeFilename(string filename)
33 | {
34 | if (filename != null)
35 | {
36 | // Trim + Remove all (combinations off) trailing dots and spaces
37 | string result = Regex.Replace(filename.Trim(), @"[. ]+$", "");
38 | return string.Join("_", result.Split(Path.GetInvalidFileNameChars()));
39 | }
40 | else
41 | {
42 | return null;
43 | }
44 | }
45 |
46 | // Nullsafe trimming of string to given max length with removal of leading and trailing spaces.
47 | public static string TrimToMaxLength(string text, int maxLength = 36)
48 | {
49 | if (text != null)
50 | {
51 | string result = text.Trim();
52 | return result.Substring(0, Math.Min(result.Length, maxLength)).Trim();
53 | }
54 | else
55 | {
56 | return "";
57 | }
58 | }
59 |
60 | public static string FormatDateTimeOffset(DateTimeOffset? dateTimeOffset)
61 | {
62 | if (dateTimeOffset != null)
63 | {
64 | return dateTimeOffset.GetValueOrDefault().ToString("yyyy-MM-dd");
65 | }
66 | else
67 | {
68 | return "";
69 | }
70 | }
71 |
72 | public static string FormatDurationInSeconds(double durationInSeconds)
73 | {
74 | TimeSpan duration = TimeSpan.FromSeconds(durationInSeconds);
75 |
76 | return duration.TotalHours < 1 ?
77 | $"{duration:mm\\:ss}" :
78 | $"{duration:hh\\:mm\\:ss}";
79 | }
80 |
81 | public static string MergeDoubleDelimitedList(string[] stringList, string initialDelimiter, string finalDelimiter)
82 | {
83 | if (stringList != null)
84 | {
85 | string result;
86 | if (stringList.Length > 1)
87 | {
88 | result = string.Join(initialDelimiter, stringList.Take(stringList.Length - 1)) + finalDelimiter + stringList.LastOrDefault();
89 | }
90 | else
91 | {
92 | result = stringList.FirstOrDefault();
93 | }
94 |
95 | return DecodeEncodedNonAsciiCharacters(result);
96 | }
97 | else
98 | {
99 | return "";
100 | }
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/View/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.exitLabel = new System.Windows.Forms.Label();
34 | this.pictureBox1 = new System.Windows.Forms.PictureBox();
35 | this.verNumLabel2 = new System.Windows.Forms.Label();
36 | this.label1 = new System.Windows.Forms.Label();
37 | this.label2 = new System.Windows.Forms.Label();
38 | this.label3 = new System.Windows.Forms.Label();
39 | this.ModDevLinkLabel = new System.Windows.Forms.LinkLabel();
40 | this.OrigDevLinkLabel = new System.Windows.Forms.LinkLabel();
41 | this.panel1.SuspendLayout();
42 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
43 | this.SuspendLayout();
44 | //
45 | // panel1
46 | //
47 | this.panel1.BackgroundImage = global::QobuzDownloaderX.Properties.Resources.login_frame;
48 | this.panel1.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
49 | this.panel1.Controls.Add(this.exitLabel);
50 | this.panel1.Controls.Add(this.pictureBox1);
51 | this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
52 | this.panel1.Location = new System.Drawing.Point(0, 0);
53 | this.panel1.Name = "panel1";
54 | this.panel1.Size = new System.Drawing.Size(365, 95);
55 | this.panel1.TabIndex = 1;
56 | this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseMove);
57 | //
58 | // exitLabel
59 | //
60 | this.exitLabel.AutoSize = true;
61 | this.exitLabel.BackColor = System.Drawing.Color.Transparent;
62 | this.exitLabel.Font = new System.Drawing.Font("Calibri", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
63 | this.exitLabel.ForeColor = System.Drawing.Color.Black;
64 | this.exitLabel.Location = new System.Drawing.Point(345, 0);
65 | this.exitLabel.Name = "exitLabel";
66 | this.exitLabel.Size = new System.Drawing.Size(20, 23);
67 | this.exitLabel.TabIndex = 9;
68 | this.exitLabel.Text = "X";
69 | this.exitLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter;
70 | this.exitLabel.Click += new System.EventHandler(this.exitLabel_Click);
71 | //
72 | // pictureBox1
73 | //
74 | this.pictureBox1.BackColor = System.Drawing.Color.Transparent;
75 | this.pictureBox1.Image = global::QobuzDownloaderX.Properties.Resources.qbdlx_white;
76 | this.pictureBox1.Location = new System.Drawing.Point(53, 12);
77 | this.pictureBox1.Name = "pictureBox1";
78 | this.pictureBox1.Size = new System.Drawing.Size(258, 64);
79 | this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
80 | this.pictureBox1.TabIndex = 29;
81 | this.pictureBox1.TabStop = false;
82 | this.pictureBox1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseMove);
83 | //
84 | // verNumLabel2
85 | //
86 | this.verNumLabel2.BackColor = System.Drawing.Color.Transparent;
87 | this.verNumLabel2.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
88 | this.verNumLabel2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
89 | this.verNumLabel2.Location = new System.Drawing.Point(12, 98);
90 | this.verNumLabel2.Name = "verNumLabel2";
91 | this.verNumLabel2.Size = new System.Drawing.Size(341, 18);
92 | this.verNumLabel2.TabIndex = 0;
93 | this.verNumLabel2.Text = "#.#.#.#";
94 | this.verNumLabel2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
95 | this.verNumLabel2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.verNumLabel2_MouseMove);
96 | //
97 | // label1
98 | //
99 | this.label1.AutoSize = true;
100 | this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
101 | this.label1.Location = new System.Drawing.Point(79, 178);
102 | this.label1.Name = "label1";
103 | this.label1.Size = new System.Drawing.Size(207, 13);
104 | this.label1.TabIndex = 35;
105 | this.label1.Text = "Inspired By - Qo-DL by Sorrow and DashLt";
106 | this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
107 | //
108 | // label2
109 | //
110 | this.label2.AutoSize = true;
111 | this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
112 | this.label2.Location = new System.Drawing.Point(47, 201);
113 | this.label2.Name = "label2";
114 | this.label2.Size = new System.Drawing.Size(271, 26);
115 | this.label2.TabIndex = 36;
116 | this.label2.Text = "Thanks to the users on Github and Telegram for offering\r\nbug reports and ideas!";
117 | this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
118 | //
119 | // label3
120 | //
121 | this.label3.AutoSize = true;
122 | this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
123 | this.label3.Location = new System.Drawing.Point(15, 237);
124 | this.label3.Name = "label3";
125 | this.label3.Size = new System.Drawing.Size(334, 26);
126 | this.label3.TabIndex = 37;
127 | this.label3.Text = "IF YOU PAID FOR THIS PROGRAM, YOU HAVE BEEN SCAMMED!\r\nTHIS SOFTWARE IS COMPLETELY" +
128 | " FREE AND OPEN-SOURCE.";
129 | this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
130 | //
131 | // ModDevLinkLabel
132 | //
133 | this.ModDevLinkLabel.ActiveLinkColor = System.Drawing.Color.DarkGray;
134 | this.ModDevLinkLabel.AutoSize = true;
135 | this.ModDevLinkLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
136 | this.ModDevLinkLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline;
137 | this.ModDevLinkLabel.LinkColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
138 | this.ModDevLinkLabel.Location = new System.Drawing.Point(110, 132);
139 | this.ModDevLinkLabel.Name = "ModDevLinkLabel";
140 | this.ModDevLinkLabel.Size = new System.Drawing.Size(144, 13);
141 | this.ModDevLinkLabel.TabIndex = 1;
142 | this.ModDevLinkLabel.TabStop = true;
143 | this.ModDevLinkLabel.Text = "Mod Developer - DJDoubleD";
144 | this.ModDevLinkLabel.VisitedLinkColor = System.Drawing.Color.Gray;
145 | this.ModDevLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.ModDevLinkLabel_LinkClicked);
146 | //
147 | // OrigDevLinkLabel
148 | //
149 | this.OrigDevLinkLabel.ActiveLinkColor = System.Drawing.Color.DarkGray;
150 | this.OrigDevLinkLabel.AutoSize = true;
151 | this.OrigDevLinkLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
152 | this.OrigDevLinkLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline;
153 | this.OrigDevLinkLabel.LinkColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
154 | this.OrigDevLinkLabel.Location = new System.Drawing.Point(121, 155);
155 | this.OrigDevLinkLabel.Name = "OrigDevLinkLabel";
156 | this.OrigDevLinkLabel.Size = new System.Drawing.Size(122, 13);
157 | this.OrigDevLinkLabel.TabIndex = 38;
158 | this.OrigDevLinkLabel.TabStop = true;
159 | this.OrigDevLinkLabel.Text = "Original Developer - AiiR";
160 | this.OrigDevLinkLabel.VisitedLinkColor = System.Drawing.Color.Gray;
161 | this.OrigDevLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.OrigDevLinkLabel_LinkClicked);
162 | //
163 | // AboutForm
164 | //
165 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
166 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
167 | this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
168 | this.ClientSize = new System.Drawing.Size(365, 289);
169 | this.Controls.Add(this.OrigDevLinkLabel);
170 | this.Controls.Add(this.ModDevLinkLabel);
171 | this.Controls.Add(this.label3);
172 | this.Controls.Add(this.label2);
173 | this.Controls.Add(this.label1);
174 | this.Controls.Add(this.verNumLabel2);
175 | this.Controls.Add(this.panel1);
176 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
177 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
178 | this.Name = "AboutForm";
179 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
180 | this.Text = "QobuzDLX | About";
181 | this.Load += new System.EventHandler(this.AboutForm_Load);
182 | this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.AboutForm_MouseMove);
183 | this.panel1.ResumeLayout(false);
184 | this.panel1.PerformLayout();
185 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
186 | this.ResumeLayout(false);
187 | this.PerformLayout();
188 |
189 | }
190 |
191 | #endregion
192 |
193 | private System.Windows.Forms.Panel panel1;
194 | private System.Windows.Forms.Label verNumLabel2;
195 | private System.Windows.Forms.Label exitLabel;
196 | private System.Windows.Forms.PictureBox pictureBox1;
197 | private System.Windows.Forms.Label label1;
198 | private System.Windows.Forms.Label label2;
199 | private System.Windows.Forms.Label label3;
200 | private System.Windows.Forms.LinkLabel ModDevLinkLabel;
201 | private System.Windows.Forms.LinkLabel OrigDevLinkLabel;
202 | }
203 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/View/AboutForm.cs:
--------------------------------------------------------------------------------
1 | using QobuzDownloaderX.Shared;
2 | using QobuzDownloaderX.View;
3 | using System;
4 | using System.Diagnostics;
5 | using System.Reflection;
6 | using System.Windows.Forms;
7 |
8 | namespace QobuzDownloaderX
9 | {
10 | public partial class AboutForm : HeadlessForm
11 | {
12 | public AboutForm()
13 | {
14 | InitializeComponent();
15 | }
16 |
17 | private void AboutForm_Load(object sender, EventArgs e)
18 | {
19 | // Get and display version number.
20 | verNumLabel2.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString();
21 | }
22 |
23 | private void exitLabel_Click(object sender, EventArgs e)
24 | {
25 | this.Close();
26 | }
27 |
28 | private void panel1_MouseMove(object sender, MouseEventArgs e)
29 | {
30 | if (e.Button == MouseButtons.Left)
31 | {
32 | ReleaseCapture();
33 | SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
34 | }
35 | }
36 |
37 | private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
38 | {
39 | if (e.Button == MouseButtons.Left)
40 | {
41 | ReleaseCapture();
42 | SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
43 | }
44 | }
45 |
46 | private void verNumLabel2_MouseMove(object sender, MouseEventArgs e)
47 | {
48 | if (e.Button == MouseButtons.Left)
49 | {
50 | ReleaseCapture();
51 | SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
52 | }
53 | }
54 |
55 | private void AboutForm_MouseMove(object sender, MouseEventArgs e)
56 | {
57 | if (e.Button == MouseButtons.Left)
58 | {
59 | ReleaseCapture();
60 | SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
61 | }
62 | }
63 |
64 | private void ModDevLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
65 | {
66 | Process.Start(Globals.GITHUB_REPO_URL);
67 | }
68 |
69 | private void OrigDevLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
70 | {
71 | Process.Start(Globals.GITHUB_ImAiiR_REPO_URL);
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/View/HeadlessForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX.View
2 | {
3 | partial class HeadlessForm
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 | this.components = new System.ComponentModel.Container();
32 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
33 | this.ClientSize = new System.Drawing.Size(800, 450);
34 | this.Text = "Form1";
35 | }
36 |
37 | #endregion
38 | }
39 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/View/HeadlessForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Windows.Forms;
4 |
5 | namespace QobuzDownloaderX.View
6 | {
7 | public partial class HeadlessForm : Form
8 | {
9 | public HeadlessForm()
10 | {
11 | InitializeComponent();
12 | }
13 |
14 | public const int WM_NCLBUTTONDOWN = 0xA1;
15 | public const int HT_CAPTION = 0x2;
16 |
17 | [DllImportAttribute("user32.dll")]
18 | public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
19 |
20 | [DllImportAttribute("user32.dll")]
21 | public static extern bool ReleaseCapture();
22 | }
23 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/View/LoginForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX
2 | {
3 | partial class LoginForm
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(LoginForm));
32 | this.topPanel = new System.Windows.Forms.Panel();
33 | this.settingsPictureBox = new System.Windows.Forms.PictureBox();
34 | this.aboutLabel = new System.Windows.Forms.Label();
35 | this.verNumLabel2 = new System.Windows.Forms.Label();
36 | this.exitLabel = new System.Windows.Forms.Label();
37 | this.bannerPictureBox = new System.Windows.Forms.PictureBox();
38 | this.userAuthTokenTextbox = new System.Windows.Forms.TextBox();
39 | this.loginButton = new System.Windows.Forms.Button();
40 | this.emailDividerPanel = new System.Windows.Forms.Panel();
41 | this.passwordDividerPanel = 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.loginBG = new System.ComponentModel.BackgroundWorker();
46 | this.visableCheckbox = new System.Windows.Forms.CheckBox();
47 | this.altLoginLabel = new System.Windows.Forms.Label();
48 | this.altLoginTutLabel = new System.Windows.Forms.Label();
49 | this.userIdTextbox = new System.Windows.Forms.TextBox();
50 | this.topPanel.SuspendLayout();
51 | ((System.ComponentModel.ISupportInitialize)(this.settingsPictureBox)).BeginInit();
52 | ((System.ComponentModel.ISupportInitialize)(this.bannerPictureBox)).BeginInit();
53 | this.SuspendLayout();
54 | //
55 | // topPanel
56 | //
57 | this.topPanel.BackgroundImage = global::QobuzDownloaderX.Properties.Resources.login_frame;
58 | this.topPanel.Controls.Add(this.settingsPictureBox);
59 | this.topPanel.Controls.Add(this.aboutLabel);
60 | this.topPanel.Controls.Add(this.verNumLabel2);
61 | this.topPanel.Controls.Add(this.exitLabel);
62 | this.topPanel.Controls.Add(this.bannerPictureBox);
63 | this.topPanel.Dock = System.Windows.Forms.DockStyle.Top;
64 | this.topPanel.Location = new System.Drawing.Point(0, 0);
65 | this.topPanel.Name = "topPanel";
66 | this.topPanel.Size = new System.Drawing.Size(282, 175);
67 | this.topPanel.TabIndex = 0;
68 | this.topPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.TopPanel_MouseMove);
69 | //
70 | // settingsPictureBox
71 | //
72 | this.settingsPictureBox.BackColor = System.Drawing.Color.Transparent;
73 | this.settingsPictureBox.Image = global::QobuzDownloaderX.Properties.Resources.settings_icon;
74 | this.settingsPictureBox.InitialImage = global::QobuzDownloaderX.Properties.Resources.settings_icon;
75 | this.settingsPictureBox.Location = new System.Drawing.Point(219, 4);
76 | this.settingsPictureBox.Name = "settingsPictureBox";
77 | this.settingsPictureBox.Size = new System.Drawing.Size(15, 15);
78 | this.settingsPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
79 | this.settingsPictureBox.TabIndex = 36;
80 | this.settingsPictureBox.TabStop = false;
81 | this.settingsPictureBox.Click += new System.EventHandler(this.OpenSettings_Click);
82 | //
83 | // aboutLabel
84 | //
85 | this.aboutLabel.AutoSize = true;
86 | this.aboutLabel.BackColor = System.Drawing.Color.Transparent;
87 | this.aboutLabel.Font = new System.Drawing.Font("Calibri", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
88 | this.aboutLabel.ForeColor = System.Drawing.Color.Black;
89 | this.aboutLabel.Location = new System.Drawing.Point(241, 0);
90 | this.aboutLabel.Name = "aboutLabel";
91 | this.aboutLabel.Size = new System.Drawing.Size(15, 23);
92 | this.aboutLabel.TabIndex = 35;
93 | this.aboutLabel.Text = "i";
94 | this.aboutLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter;
95 | this.aboutLabel.Click += new System.EventHandler(this.AboutLabel_Click);
96 | //
97 | // verNumLabel2
98 | //
99 | this.verNumLabel2.BackColor = System.Drawing.Color.Transparent;
100 | this.verNumLabel2.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
101 | this.verNumLabel2.ForeColor = System.Drawing.Color.White;
102 | this.verNumLabel2.Location = new System.Drawing.Point(194, 157);
103 | this.verNumLabel2.Name = "verNumLabel2";
104 | this.verNumLabel2.Size = new System.Drawing.Size(85, 18);
105 | this.verNumLabel2.TabIndex = 32;
106 | this.verNumLabel2.Text = "#.#.#.#";
107 | this.verNumLabel2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
108 | this.verNumLabel2.MouseMove += new System.Windows.Forms.MouseEventHandler(this.VerNumLabel2_MouseMove);
109 | //
110 | // exitLabel
111 | //
112 | this.exitLabel.AutoSize = true;
113 | this.exitLabel.BackColor = System.Drawing.Color.Transparent;
114 | this.exitLabel.Font = new System.Drawing.Font("Calibri", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
115 | this.exitLabel.ForeColor = System.Drawing.Color.Black;
116 | this.exitLabel.Location = new System.Drawing.Point(262, 0);
117 | this.exitLabel.Name = "exitLabel";
118 | this.exitLabel.Size = new System.Drawing.Size(20, 23);
119 | this.exitLabel.TabIndex = 9;
120 | this.exitLabel.Text = "X";
121 | this.exitLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter;
122 | this.exitLabel.Click += new System.EventHandler(this.ExitLabel_Click);
123 | //
124 | // bannerPictureBox
125 | //
126 | this.bannerPictureBox.BackColor = System.Drawing.Color.Transparent;
127 | this.bannerPictureBox.Image = global::QobuzDownloaderX.Properties.Resources.qbdlx_white;
128 | this.bannerPictureBox.Location = new System.Drawing.Point(12, 52);
129 | this.bannerPictureBox.Name = "bannerPictureBox";
130 | this.bannerPictureBox.Size = new System.Drawing.Size(258, 64);
131 | this.bannerPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
132 | this.bannerPictureBox.TabIndex = 29;
133 | this.bannerPictureBox.TabStop = false;
134 | this.bannerPictureBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.PictureBox1_MouseMove);
135 | //
136 | // userAuthTokenTextbox
137 | //
138 | this.userAuthTokenTextbox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
139 | this.userAuthTokenTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None;
140 | this.userAuthTokenTextbox.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
141 | this.userAuthTokenTextbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
142 | this.userAuthTokenTextbox.Location = new System.Drawing.Point(12, 255);
143 | this.userAuthTokenTextbox.Multiline = true;
144 | this.userAuthTokenTextbox.Name = "userAuthTokenTextbox";
145 | this.userAuthTokenTextbox.Size = new System.Drawing.Size(237, 23);
146 | this.userAuthTokenTextbox.TabIndex = 35;
147 | this.userAuthTokenTextbox.Text = "user_auth_token";
148 | this.userAuthTokenTextbox.Visible = false;
149 | this.userAuthTokenTextbox.Click += new System.EventHandler(this.UserAuthTokenTextbox_Click);
150 | this.userAuthTokenTextbox.Leave += new System.EventHandler(this.UserAuthTokenTextbox_Leave);
151 | //
152 | // loginButton
153 | //
154 | this.loginButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(112)))), ((int)(((byte)(239)))));
155 | this.loginButton.FlatAppearance.BorderSize = 0;
156 | this.loginButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
157 | this.loginButton.Font = new System.Drawing.Font("Trebuchet MS", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
158 | this.loginButton.ForeColor = System.Drawing.Color.White;
159 | this.loginButton.Location = new System.Drawing.Point(12, 293);
160 | this.loginButton.Name = "loginButton";
161 | this.loginButton.Size = new System.Drawing.Size(258, 30);
162 | this.loginButton.TabIndex = 2;
163 | this.loginButton.Text = "LOGIN";
164 | this.loginButton.UseVisualStyleBackColor = false;
165 | this.loginButton.Click += new System.EventHandler(this.LoginButton_Click);
166 | //
167 | // emailDividerPanel
168 | //
169 | this.emailDividerPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
170 | this.emailDividerPanel.Location = new System.Drawing.Point(12, 221);
171 | this.emailDividerPanel.Name = "emailDividerPanel";
172 | this.emailDividerPanel.Size = new System.Drawing.Size(258, 1);
173 | this.emailDividerPanel.TabIndex = 2;
174 | //
175 | // passwordDividerPanel
176 | //
177 | this.passwordDividerPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
178 | this.passwordDividerPanel.Location = new System.Drawing.Point(12, 277);
179 | this.passwordDividerPanel.Name = "passwordDividerPanel";
180 | this.passwordDividerPanel.Size = new System.Drawing.Size(258, 1);
181 | this.passwordDividerPanel.TabIndex = 2;
182 | //
183 | // emailTextbox
184 | //
185 | this.emailTextbox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
186 | this.emailTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None;
187 | this.emailTextbox.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
188 | this.emailTextbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
189 | this.emailTextbox.Location = new System.Drawing.Point(12, 199);
190 | this.emailTextbox.Multiline = true;
191 | this.emailTextbox.Name = "emailTextbox";
192 | this.emailTextbox.Size = new System.Drawing.Size(258, 23);
193 | this.emailTextbox.TabIndex = 7;
194 | this.emailTextbox.Text = "Email";
195 | this.emailTextbox.Click += new System.EventHandler(this.EmailTextbox_Click);
196 | this.emailTextbox.Leave += new System.EventHandler(this.EmailTextbox_Leave);
197 | //
198 | // passwordTextbox
199 | //
200 | this.passwordTextbox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
201 | this.passwordTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None;
202 | this.passwordTextbox.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
203 | this.passwordTextbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
204 | this.passwordTextbox.Location = new System.Drawing.Point(12, 255);
205 | this.passwordTextbox.Multiline = true;
206 | this.passwordTextbox.Name = "passwordTextbox";
207 | this.passwordTextbox.Size = new System.Drawing.Size(237, 23);
208 | this.passwordTextbox.TabIndex = 8;
209 | this.passwordTextbox.Text = "Password";
210 | this.passwordTextbox.Click += new System.EventHandler(this.PasswordTextbox_Click);
211 | this.passwordTextbox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.PasswordTextbox_KeyDown);
212 | this.passwordTextbox.Leave += new System.EventHandler(this.PasswordTextbox_Leave);
213 | //
214 | // loginText
215 | //
216 | this.loginText.Font = new System.Drawing.Font("Trebuchet MS", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
217 | this.loginText.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
218 | this.loginText.Location = new System.Drawing.Point(12, 349);
219 | this.loginText.Name = "loginText";
220 | this.loginText.Size = new System.Drawing.Size(258, 23);
221 | this.loginText.TabIndex = 30;
222 | this.loginText.Text = "Waiting for login...";
223 | this.loginText.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
224 | //
225 | // loginBG
226 | //
227 | this.loginBG.DoWork += new System.ComponentModel.DoWorkEventHandler(this.LoginBG_DoWork);
228 | //
229 | // visableCheckbox
230 | //
231 | this.visableCheckbox.AutoSize = true;
232 | this.visableCheckbox.Location = new System.Drawing.Point(255, 256);
233 | this.visableCheckbox.Name = "visableCheckbox";
234 | this.visableCheckbox.Size = new System.Drawing.Size(15, 14);
235 | this.visableCheckbox.TabIndex = 31;
236 | this.visableCheckbox.UseVisualStyleBackColor = true;
237 | this.visableCheckbox.CheckedChanged += new System.EventHandler(this.VisibleCheckbox_CheckedChanged);
238 | //
239 | // altLoginLabel
240 | //
241 | this.altLoginLabel.Font = new System.Drawing.Font("Trebuchet MS", 8.25F);
242 | this.altLoginLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
243 | this.altLoginLabel.Location = new System.Drawing.Point(12, 326);
244 | this.altLoginLabel.Name = "altLoginLabel";
245 | this.altLoginLabel.Size = new System.Drawing.Size(258, 20);
246 | this.altLoginLabel.TabIndex = 32;
247 | this.altLoginLabel.Text = "Can\'t login? Click here";
248 | this.altLoginLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
249 | this.altLoginLabel.Click += new System.EventHandler(this.AltLoginLabel_Click);
250 | //
251 | // altLoginTutLabel
252 | //
253 | this.altLoginTutLabel.AutoSize = true;
254 | this.altLoginTutLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
255 | this.altLoginTutLabel.Location = new System.Drawing.Point(77, 180);
256 | this.altLoginTutLabel.Name = "altLoginTutLabel";
257 | this.altLoginTutLabel.Size = new System.Drawing.Size(128, 13);
258 | this.altLoginTutLabel.TabIndex = 33;
259 | this.altLoginTutLabel.Text = "Click Here for Instructions";
260 | this.altLoginTutLabel.Visible = false;
261 | this.altLoginTutLabel.Click += new System.EventHandler(this.AltLoginTutLabel_Click);
262 | //
263 | // userIdTextbox
264 | //
265 | this.userIdTextbox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
266 | this.userIdTextbox.BorderStyle = System.Windows.Forms.BorderStyle.None;
267 | this.userIdTextbox.Font = new System.Drawing.Font("Trebuchet MS", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
268 | this.userIdTextbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
269 | this.userIdTextbox.Location = new System.Drawing.Point(12, 199);
270 | this.userIdTextbox.Multiline = true;
271 | this.userIdTextbox.Name = "userIdTextbox";
272 | this.userIdTextbox.Size = new System.Drawing.Size(258, 23);
273 | this.userIdTextbox.TabIndex = 9;
274 | this.userIdTextbox.Text = "user_id";
275 | this.userIdTextbox.Visible = false;
276 | this.userIdTextbox.Click += new System.EventHandler(this.UserIdTextbox_Click);
277 | this.userIdTextbox.Leave += new System.EventHandler(this.UserIdTextbox_Leave);
278 | //
279 | // LoginForm
280 | //
281 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
282 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
283 | this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
284 | this.ClientSize = new System.Drawing.Size(282, 392);
285 | this.Controls.Add(this.altLoginTutLabel);
286 | this.Controls.Add(this.altLoginLabel);
287 | this.Controls.Add(this.visableCheckbox);
288 | this.Controls.Add(this.loginText);
289 | this.Controls.Add(this.passwordDividerPanel);
290 | this.Controls.Add(this.emailDividerPanel);
291 | this.Controls.Add(this.passwordTextbox);
292 | this.Controls.Add(this.emailTextbox);
293 | this.Controls.Add(this.loginButton);
294 | this.Controls.Add(this.topPanel);
295 | this.Controls.Add(this.userIdTextbox);
296 | this.Controls.Add(this.userAuthTokenTextbox);
297 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
298 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
299 | this.Name = "LoginForm";
300 | this.Text = "QobuzDLX | Login";
301 | this.Load += new System.EventHandler(this.LoginFrm_Load);
302 | this.topPanel.ResumeLayout(false);
303 | this.topPanel.PerformLayout();
304 | ((System.ComponentModel.ISupportInitialize)(this.settingsPictureBox)).EndInit();
305 | ((System.ComponentModel.ISupportInitialize)(this.bannerPictureBox)).EndInit();
306 | this.ResumeLayout(false);
307 | this.PerformLayout();
308 |
309 | }
310 |
311 | #endregion
312 |
313 | private System.Windows.Forms.Panel topPanel;
314 | private System.Windows.Forms.PictureBox bannerPictureBox;
315 | private System.Windows.Forms.Button loginButton;
316 | private System.Windows.Forms.Panel emailDividerPanel;
317 | private System.Windows.Forms.Panel passwordDividerPanel;
318 | private System.Windows.Forms.TextBox emailTextbox;
319 | private System.Windows.Forms.TextBox passwordTextbox;
320 | private System.Windows.Forms.Label exitLabel;
321 | private System.Windows.Forms.Label loginText;
322 | private System.ComponentModel.BackgroundWorker loginBG;
323 | private System.Windows.Forms.Label verNumLabel2;
324 | private System.Windows.Forms.CheckBox visableCheckbox;
325 | private System.Windows.Forms.TextBox userAuthTokenTextbox;
326 | private System.Windows.Forms.Label altLoginLabel;
327 | private System.Windows.Forms.Label altLoginTutLabel;
328 | private System.Windows.Forms.TextBox userIdTextbox;
329 | private System.Windows.Forms.Label aboutLabel;
330 | private System.Windows.Forms.PictureBox settingsPictureBox;
331 | }
332 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/View/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.panel1 = new System.Windows.Forms.Panel();
35 | this.exitLabel = new System.Windows.Forms.Label();
36 | this.logoPictureBox = new System.Windows.Forms.PictureBox();
37 | this.containerScrollPanel = new System.Windows.Forms.Panel();
38 | this.searchTypeSelect = new System.Windows.Forms.ComboBox();
39 | ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit();
40 | this.SuspendLayout();
41 | //
42 | // searchInput
43 | //
44 | this.searchInput.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
45 | this.searchInput.BorderStyle = System.Windows.Forms.BorderStyle.None;
46 | this.searchInput.ForeColor = System.Drawing.Color.White;
47 | this.searchInput.Location = new System.Drawing.Point(118, 45);
48 | this.searchInput.Multiline = true;
49 | this.searchInput.Name = "searchInput";
50 | this.searchInput.Size = new System.Drawing.Size(682, 20);
51 | this.searchInput.TabIndex = 0;
52 | this.searchInput.WordWrap = false;
53 | this.searchInput.KeyDown += new System.Windows.Forms.KeyEventHandler(this.SearchInput_KeyDown);
54 | //
55 | // searchButton
56 | //
57 | this.searchButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(112)))), ((int)(((byte)(239)))));
58 | this.searchButton.FlatAppearance.BorderSize = 0;
59 | this.searchButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
60 | this.searchButton.ForeColor = System.Drawing.Color.White;
61 | this.searchButton.Location = new System.Drawing.Point(806, 40);
62 | this.searchButton.Name = "searchButton";
63 | this.searchButton.Size = new System.Drawing.Size(120, 23);
64 | this.searchButton.TabIndex = 1;
65 | this.searchButton.Text = "Search";
66 | this.searchButton.UseVisualStyleBackColor = false;
67 | this.searchButton.Click += new System.EventHandler(this.SearchButton_Click);
68 | //
69 | // panel1
70 | //
71 | this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
72 | this.panel1.Location = new System.Drawing.Point(118, 63);
73 | this.panel1.Name = "panel1";
74 | this.panel1.Size = new System.Drawing.Size(680, 1);
75 | this.panel1.TabIndex = 87;
76 | //
77 | // exitLabel
78 | //
79 | this.exitLabel.AutoSize = true;
80 | this.exitLabel.BackColor = System.Drawing.Color.Transparent;
81 | this.exitLabel.Font = new System.Drawing.Font("Calibri", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
82 | this.exitLabel.ForeColor = System.Drawing.Color.White;
83 | this.exitLabel.Location = new System.Drawing.Point(906, 8);
84 | this.exitLabel.Name = "exitLabel";
85 | this.exitLabel.Size = new System.Drawing.Size(20, 23);
86 | this.exitLabel.TabIndex = 89;
87 | this.exitLabel.Text = "X";
88 | this.exitLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter;
89 | this.exitLabel.Click += new System.EventHandler(this.ExitLabel_Click);
90 | this.exitLabel.MouseLeave += new System.EventHandler(this.ExitLabel_MouseLeave);
91 | this.exitLabel.MouseHover += new System.EventHandler(this.ExitLabel_MouseHover);
92 | //
93 | // logoPictureBox
94 | //
95 | this.logoPictureBox.BackColor = System.Drawing.Color.Transparent;
96 | this.logoPictureBox.Image = global::QobuzDownloaderX.Properties.Resources.qbdlx_white;
97 | this.logoPictureBox.Location = new System.Drawing.Point(12, 9);
98 | this.logoPictureBox.Name = "logoPictureBox";
99 | this.logoPictureBox.Size = new System.Drawing.Size(105, 26);
100 | this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
101 | this.logoPictureBox.TabIndex = 90;
102 | this.logoPictureBox.TabStop = false;
103 | //
104 | // containerScrollPanel
105 | //
106 | this.containerScrollPanel.AutoScroll = true;
107 | this.containerScrollPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(33)))), ((int)(((byte)(33)))));
108 | this.containerScrollPanel.Location = new System.Drawing.Point(12, 70);
109 | this.containerScrollPanel.Name = "containerScrollPanel";
110 | this.containerScrollPanel.Size = new System.Drawing.Size(914, 451);
111 | this.containerScrollPanel.TabIndex = 4;
112 | //
113 | // searchTypeSelect
114 | //
115 | this.searchTypeSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
116 | this.searchTypeSelect.FormattingEnabled = true;
117 | this.searchTypeSelect.Items.AddRange(new object[] {
118 | "Album",
119 | "Track"});
120 | this.searchTypeSelect.Location = new System.Drawing.Point(12, 43);
121 | this.searchTypeSelect.Name = "searchTypeSelect";
122 | this.searchTypeSelect.Size = new System.Drawing.Size(100, 21);
123 | this.searchTypeSelect.TabIndex = 3;
124 | //
125 | // SearchForm
126 | //
127 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
128 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
129 | this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
130 | this.ClientSize = new System.Drawing.Size(938, 533);
131 | this.Controls.Add(this.exitLabel);
132 | this.Controls.Add(this.searchTypeSelect);
133 | this.Controls.Add(this.containerScrollPanel);
134 | this.Controls.Add(this.logoPictureBox);
135 | this.Controls.Add(this.panel1);
136 | this.Controls.Add(this.searchButton);
137 | this.Controls.Add(this.searchInput);
138 | this.Cursor = System.Windows.Forms.Cursors.Default;
139 | this.DoubleBuffered = true;
140 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
141 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
142 | this.Name = "SearchForm";
143 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
144 | this.Text = "QobuzDLX | Search";
145 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.SearchForm_FormClosed);
146 | this.Load += new System.EventHandler(this.SearchForm_Load);
147 | this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.SearchForm_MouseMove);
148 | ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit();
149 | this.ResumeLayout(false);
150 | this.PerformLayout();
151 |
152 | }
153 |
154 | #endregion
155 |
156 | private System.Windows.Forms.TextBox searchInput;
157 | private System.Windows.Forms.Button searchButton;
158 | private System.Windows.Forms.Panel panel1;
159 | private System.Windows.Forms.Label exitLabel;
160 | private System.Windows.Forms.PictureBox logoPictureBox;
161 | private System.Windows.Forms.Panel containerScrollPanel;
162 | private System.Windows.Forms.ComboBox searchTypeSelect;
163 | }
164 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/View/SettingsForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace QobuzDownloaderX
2 | {
3 | partial class SettingsForm
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(SettingsForm));
32 | this.LogoPictureBox = new System.Windows.Forms.PictureBox();
33 | this.SystemGroupBox = new System.Windows.Forms.GroupBox();
34 | this.SaveButton = new System.Windows.Forms.Button();
35 | this.DividerPanel2 = new System.Windows.Forms.Panel();
36 | this.DividerPanel1 = new System.Windows.Forms.Panel();
37 | this.AppSecretTextBox = new System.Windows.Forms.TextBox();
38 | this.AppSecretLabel = new System.Windows.Forms.Label();
39 | this.AppIdTextBox = new System.Windows.Forms.TextBox();
40 | this.AppIDLabel = new System.Windows.Forms.Label();
41 | this.ExitLabel = new System.Windows.Forms.Label();
42 | this.SettingsTitleLabel = new System.Windows.Forms.Label();
43 | ((System.ComponentModel.ISupportInitialize)(this.LogoPictureBox)).BeginInit();
44 | this.SystemGroupBox.SuspendLayout();
45 | this.SuspendLayout();
46 | //
47 | // LogoPictureBox
48 | //
49 | this.LogoPictureBox.BackColor = System.Drawing.Color.Transparent;
50 | this.LogoPictureBox.Image = global::QobuzDownloaderX.Properties.Resources.qbdlx_white;
51 | this.LogoPictureBox.Location = new System.Drawing.Point(12, 12);
52 | this.LogoPictureBox.Name = "LogoPictureBox";
53 | this.LogoPictureBox.Size = new System.Drawing.Size(105, 26);
54 | this.LogoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
55 | this.LogoPictureBox.TabIndex = 91;
56 | this.LogoPictureBox.TabStop = false;
57 | this.LogoPictureBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.LogoPictureBox_MouseMove);
58 | //
59 | // SystemGroupBox
60 | //
61 | this.SystemGroupBox.Controls.Add(this.SaveButton);
62 | this.SystemGroupBox.Controls.Add(this.DividerPanel2);
63 | this.SystemGroupBox.Controls.Add(this.DividerPanel1);
64 | this.SystemGroupBox.Controls.Add(this.AppSecretTextBox);
65 | this.SystemGroupBox.Controls.Add(this.AppSecretLabel);
66 | this.SystemGroupBox.Controls.Add(this.AppIdTextBox);
67 | this.SystemGroupBox.Controls.Add(this.AppIDLabel);
68 | this.SystemGroupBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
69 | this.SystemGroupBox.Location = new System.Drawing.Point(12, 56);
70 | this.SystemGroupBox.Name = "SystemGroupBox";
71 | this.SystemGroupBox.Size = new System.Drawing.Size(476, 159);
72 | this.SystemGroupBox.TabIndex = 93;
73 | this.SystemGroupBox.TabStop = false;
74 | this.SystemGroupBox.Text = "System";
75 | //
76 | // SaveButton
77 | //
78 | this.SaveButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(112)))), ((int)(((byte)(239)))));
79 | this.SaveButton.FlatAppearance.BorderSize = 0;
80 | this.SaveButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
81 | this.SaveButton.ForeColor = System.Drawing.Color.White;
82 | this.SaveButton.Location = new System.Drawing.Point(14, 126);
83 | this.SaveButton.Name = "SaveButton";
84 | this.SaveButton.Size = new System.Drawing.Size(120, 23);
85 | this.SaveButton.TabIndex = 3;
86 | this.SaveButton.Text = "Save";
87 | this.SaveButton.UseVisualStyleBackColor = false;
88 | this.SaveButton.Click += new System.EventHandler(this.SaveButton_Click);
89 | //
90 | // DividerPanel2
91 | //
92 | this.DividerPanel2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
93 | this.DividerPanel2.Location = new System.Drawing.Point(14, 103);
94 | this.DividerPanel2.Name = "DividerPanel2";
95 | this.DividerPanel2.Size = new System.Drawing.Size(445, 1);
96 | this.DividerPanel2.TabIndex = 95;
97 | //
98 | // DividerPanel1
99 | //
100 | this.DividerPanel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
101 | this.DividerPanel1.Location = new System.Drawing.Point(14, 61);
102 | this.DividerPanel1.Name = "DividerPanel1";
103 | this.DividerPanel1.Size = new System.Drawing.Size(445, 1);
104 | this.DividerPanel1.TabIndex = 94;
105 | //
106 | // AppSecretTextBox
107 | //
108 | this.AppSecretTextBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
109 | this.AppSecretTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
110 | this.AppSecretTextBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
111 | this.AppSecretTextBox.Location = new System.Drawing.Point(14, 84);
112 | this.AppSecretTextBox.Multiline = true;
113 | this.AppSecretTextBox.Name = "AppSecretTextBox";
114 | this.AppSecretTextBox.Size = new System.Drawing.Size(420, 20);
115 | this.AppSecretTextBox.TabIndex = 2;
116 | this.AppSecretTextBox.WordWrap = false;
117 | //
118 | // AppSecretLabel
119 | //
120 | this.AppSecretLabel.AutoSize = true;
121 | this.AppSecretLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
122 | this.AppSecretLabel.Location = new System.Drawing.Point(11, 68);
123 | this.AppSecretLabel.Name = "AppSecretLabel";
124 | this.AppSecretLabel.Size = new System.Drawing.Size(60, 13);
125 | this.AppSecretLabel.TabIndex = 92;
126 | this.AppSecretLabel.Text = "App Secret";
127 | //
128 | // AppIdTextBox
129 | //
130 | this.AppIdTextBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
131 | this.AppIdTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
132 | this.AppIdTextBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
133 | this.AppIdTextBox.Location = new System.Drawing.Point(14, 42);
134 | this.AppIdTextBox.Multiline = true;
135 | this.AppIdTextBox.Name = "AppIdTextBox";
136 | this.AppIdTextBox.Size = new System.Drawing.Size(420, 20);
137 | this.AppIdTextBox.TabIndex = 1;
138 | this.AppIdTextBox.WordWrap = false;
139 | //
140 | // AppIDLabel
141 | //
142 | this.AppIDLabel.AutoSize = true;
143 | this.AppIDLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
144 | this.AppIDLabel.Location = new System.Drawing.Point(11, 26);
145 | this.AppIDLabel.Name = "AppIDLabel";
146 | this.AppIDLabel.Size = new System.Drawing.Size(40, 13);
147 | this.AppIDLabel.TabIndex = 90;
148 | this.AppIDLabel.Text = "App ID";
149 | //
150 | // ExitLabel
151 | //
152 | this.ExitLabel.AutoSize = true;
153 | this.ExitLabel.BackColor = System.Drawing.Color.Transparent;
154 | this.ExitLabel.Font = new System.Drawing.Font("Calibri", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
155 | this.ExitLabel.ForeColor = System.Drawing.Color.White;
156 | this.ExitLabel.Location = new System.Drawing.Point(468, 9);
157 | this.ExitLabel.Name = "ExitLabel";
158 | this.ExitLabel.Size = new System.Drawing.Size(20, 23);
159 | this.ExitLabel.TabIndex = 4;
160 | this.ExitLabel.Text = "X";
161 | this.ExitLabel.TextAlign = System.Drawing.ContentAlignment.TopCenter;
162 | this.ExitLabel.Click += new System.EventHandler(this.ExitLabel_Click);
163 | this.ExitLabel.MouseLeave += new System.EventHandler(this.ExitLabel_MouseLeave);
164 | this.ExitLabel.MouseHover += new System.EventHandler(this.ExitLabel_MouseHover);
165 | //
166 | // SettingsTitleLabel
167 | //
168 | this.SettingsTitleLabel.AutoSize = true;
169 | this.SettingsTitleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
170 | this.SettingsTitleLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(88)))), ((int)(((byte)(92)))), ((int)(((byte)(102)))));
171 | this.SettingsTitleLabel.Location = new System.Drawing.Point(205, 13);
172 | this.SettingsTitleLabel.Name = "SettingsTitleLabel";
173 | this.SettingsTitleLabel.Size = new System.Drawing.Size(90, 25);
174 | this.SettingsTitleLabel.TabIndex = 95;
175 | this.SettingsTitleLabel.Text = "Settings";
176 | //
177 | // SettingsForm
178 | //
179 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
180 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
181 | this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
182 | this.ClientSize = new System.Drawing.Size(500, 227);
183 | this.Controls.Add(this.SettingsTitleLabel);
184 | this.Controls.Add(this.ExitLabel);
185 | this.Controls.Add(this.SystemGroupBox);
186 | this.Controls.Add(this.LogoPictureBox);
187 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
188 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
189 | this.Name = "SettingsForm";
190 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
191 | this.Text = "QobuzDLX | Settings";
192 | this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.SettingsForm_MouseMove);
193 | ((System.ComponentModel.ISupportInitialize)(this.LogoPictureBox)).EndInit();
194 | this.SystemGroupBox.ResumeLayout(false);
195 | this.SystemGroupBox.PerformLayout();
196 | this.ResumeLayout(false);
197 | this.PerformLayout();
198 |
199 | }
200 |
201 | #endregion
202 |
203 | private System.Windows.Forms.PictureBox LogoPictureBox;
204 | private System.Windows.Forms.GroupBox SystemGroupBox;
205 | private System.Windows.Forms.Panel DividerPanel2;
206 | private System.Windows.Forms.Panel DividerPanel1;
207 | private System.Windows.Forms.TextBox AppSecretTextBox;
208 | private System.Windows.Forms.Label AppSecretLabel;
209 | private System.Windows.Forms.TextBox AppIdTextBox;
210 | private System.Windows.Forms.Label AppIDLabel;
211 | private System.Windows.Forms.Label ExitLabel;
212 | private System.Windows.Forms.Button SaveButton;
213 | private System.Windows.Forms.Label SettingsTitleLabel;
214 | }
215 | }
--------------------------------------------------------------------------------
/QobuzDownloaderX/View/SettingsForm.cs:
--------------------------------------------------------------------------------
1 | using QobuzDownloaderX.Properties;
2 | using QobuzDownloaderX.View;
3 | using System;
4 | using System.Drawing;
5 | using System.Windows.Forms;
6 |
7 | namespace QobuzDownloaderX
8 | {
9 | public partial class SettingsForm : HeadlessForm
10 | {
11 | public SettingsForm()
12 | {
13 | InitializeComponent();
14 | AppIdTextBox.Text = Settings.Default.appId;
15 | AppSecretTextBox.Text = Settings.Default.appSecret;
16 |
17 | // Add clear buttons
18 | AddClearTextButton(AppIdTextBox);
19 | AddClearTextButton(AppSecretTextBox);
20 |
21 | // Don't select entire existing value in textbox when it's loaded
22 | AppIdTextBox.Select(AppIdTextBox.Text.Length, 0);
23 | }
24 |
25 | private void AddClearTextButton(TextBox textBox)
26 | {
27 | Button clearButton = new Button
28 | {
29 | Text = "X",
30 | Size = new Size(25, textBox.ClientSize.Height + 2),
31 | Location = new Point(textBox.Location.X + textBox.Width + 2, textBox.Location.Y),
32 | Cursor = Cursors.Default,
33 | FlatStyle = FlatStyle.Flat
34 | };
35 | clearButton.FlatAppearance.BorderSize = 0;
36 | clearButton.Click += (s, e) => textBox.Clear();
37 |
38 | // Add the button to the same container as the text box
39 | textBox.Parent.Controls.Add(clearButton);
40 | }
41 |
42 | private void SettingsForm_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 LogoPictureBox_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 ExitLabel_Click(object sender, EventArgs e)
61 | {
62 | this.Close();
63 | }
64 |
65 | private void ExitLabel_MouseHover(object sender, EventArgs e)
66 | {
67 | ExitLabel.ForeColor = Color.FromArgb(0, 112, 239);
68 | }
69 |
70 | private void ExitLabel_MouseLeave(object sender, EventArgs e)
71 | {
72 | ExitLabel.ForeColor = Color.White;
73 | }
74 |
75 | private void SaveButton_Click(object sender, EventArgs e)
76 | {
77 | Settings.Default.appId = AppIdTextBox.Text;
78 | Settings.Default.appSecret = AppSecretTextBox.Text;
79 |
80 | Settings.Default.Save();
81 |
82 | FlexibleMessageBox.Show(
83 | "Settings saved!",
84 | "Settings Saved",
85 | MessageBoxButtons.OK,
86 | MessageBoxIcon.Information
87 | );
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/QobuzDownloaderX/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](../../releases/latest)
4 | [](../../releases/latest)
5 | [](../../releases)
6 | [](../../releases)
7 | [](https://learn.microsoft.com/en-us/dotnet/csharp/)
8 | [](./LICENSE)
9 |
10 | -----
11 |
12 | 
13 |
14 | # About
15 |
16 | QobuzDownloaderX-MOD is a program for downloading music streams from the streaming platform [Qobuz](https://qobuz.com).
17 |
18 | You can only download 30 second previews with a free account, a Studio (Family) account is needed to download full content.
19 |
20 | This Modded version of the [original QobuzDownloaderX](https://github.com/ImAiiR/QobuzDownloaderX) by [AiiR](https://github.com/ImAiiR) was created for educational purposes.
21 | Some changes include:
22 |
23 | - Isolated Qobuz API interaction to a separate library project [QobuzApiSharp](https://github.com/DJDoubleD/QobuzApiSharp).
24 | - Complete refactoring of backend code.
25 | - Added completely revamped search function.
26 | - Added comprehensive logging.
27 | - Lots of new features and bugfixes.
28 |
29 | See release notes in [releases](../../releases) section for full overview of all fixes and changes.
30 | Check out the [Wiki](../../wiki) for more information about using QBDLX-MOD.
31 |
32 | # Disclaimer & Legal
33 |
34 | I will not be responsible for how you use QBDLX-MOD (QobuzDownloaderX-MOD).
35 |
36 | This program ***DOES NOT*** include...
37 |
38 | - Code to bypass Qobuz's region restrictions.
39 | - Qobuz app IDs or secrets.
40 |
41 | QBDLX-MOD 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).
42 |
43 | QBDLX-MOD uses the Qobuz API, but is not endorsed, certified or otherwise approved in any way by Qobuz.
44 |
45 | Qobuz brand and name is the registered trademark of its respective owner.
46 |
47 | QBDLX-MOD has no partnership, sponsorship or endorsement with Qobuz.
48 |
49 | By using QBDLX-MOD, you agree to the following:
50 |
--------------------------------------------------------------------------------