ilk commit
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
.vs/DownloadManager/DesignTimeBuild/.dtbcache.v2
Normal file
BIN
.vs/DownloadManager/DesignTimeBuild/.dtbcache.v2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.vs/DownloadManager/v17/.futdcache.v2
Normal file
BIN
.vs/DownloadManager/v17/.futdcache.v2
Normal file
Binary file not shown.
BIN
.vs/DownloadManager/v17/.suo
Normal file
BIN
.vs/DownloadManager/v17/.suo
Normal file
Binary file not shown.
74
.vs/DownloadManager/v17/DocumentLayout.backup.json
Normal file
74
.vs/DownloadManager/v17/DocumentLayout.backup.json
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"Version": 1,
|
||||||
|
"WorkspaceRootPath": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\",
|
||||||
|
"Documents": [
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|d:\\calismalar\\ai\\hdm\\downloadmanager\\src\\downloadmanager.wpf\\viewmodels\\mainviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|solutionrelative:src\\downloadmanager.wpf\\viewmodels\\mainviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|d:\\calismalar\\ai\\hdm\\downloadmanager\\src\\downloadmanager.wpf\\views\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}",
|
||||||
|
"RelativeMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|solutionrelative:src\\downloadmanager.wpf\\views\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|d:\\calismalar\\ai\\hdm\\downloadmanager\\src\\downloadmanager.wpf\\app.xaml.cs||{8B382828-6202-11D1-8870-0000F87579D2}|",
|
||||||
|
"RelativeMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|solutionrelative:src\\downloadmanager.wpf\\app.xaml.cs||{8B382828-6202-11D1-8870-0000F87579D2}|"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"DocumentGroupContainers": [
|
||||||
|
{
|
||||||
|
"Orientation": 0,
|
||||||
|
"VerticalTabListWidth": 256,
|
||||||
|
"DocumentGroups": [
|
||||||
|
{
|
||||||
|
"DockedWidth": 200,
|
||||||
|
"SelectedChildIndex": 1,
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"$type": "Bookmark",
|
||||||
|
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 0,
|
||||||
|
"Title": "MainViewModel.cs",
|
||||||
|
"DocumentMoniker": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\ViewModels\\MainViewModel.cs",
|
||||||
|
"RelativeDocumentMoniker": "src\\DownloadManager.WPF\\ViewModels\\MainViewModel.cs",
|
||||||
|
"ToolTip": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\ViewModels\\MainViewModel.cs",
|
||||||
|
"RelativeToolTip": "src\\DownloadManager.WPF\\ViewModels\\MainViewModel.cs",
|
||||||
|
"ViewState": "AgIAABQAAAAAAAAAAAAewCoAAAArAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-03T21:58:25.845Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 2,
|
||||||
|
"Title": "App.xaml.cs",
|
||||||
|
"DocumentMoniker": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\App.xaml.cs",
|
||||||
|
"RelativeDocumentMoniker": "src\\DownloadManager.WPF\\App.xaml.cs",
|
||||||
|
"ToolTip": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\App.xaml.cs",
|
||||||
|
"RelativeToolTip": "src\\DownloadManager.WPF\\App.xaml.cs",
|
||||||
|
"ViewState": "AgIAAFQAAAAAAAAAAAAYwAAAAAAAAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-03T21:58:15.818Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 1,
|
||||||
|
"Title": "MainWindow.xaml",
|
||||||
|
"DocumentMoniker": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\Views\\MainWindow.xaml",
|
||||||
|
"RelativeDocumentMoniker": "src\\DownloadManager.WPF\\Views\\MainWindow.xaml",
|
||||||
|
"ToolTip": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\Views\\MainWindow.xaml",
|
||||||
|
"RelativeToolTip": "src\\DownloadManager.WPF\\Views\\MainWindow.xaml",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
|
||||||
|
"WhenOpened": "2026-05-01T23:15:00.129Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
74
.vs/DownloadManager/v17/DocumentLayout.json
Normal file
74
.vs/DownloadManager/v17/DocumentLayout.json
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"Version": 1,
|
||||||
|
"WorkspaceRootPath": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\",
|
||||||
|
"Documents": [
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|d:\\calismalar\\ai\\hdm\\downloadmanager\\src\\downloadmanager.wpf\\viewmodels\\mainviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||||
|
"RelativeMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|solutionrelative:src\\downloadmanager.wpf\\viewmodels\\mainviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|d:\\calismalar\\ai\\hdm\\downloadmanager\\src\\downloadmanager.wpf\\views\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}",
|
||||||
|
"RelativeMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|solutionrelative:src\\downloadmanager.wpf\\views\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|d:\\calismalar\\ai\\hdm\\downloadmanager\\src\\downloadmanager.wpf\\app.xaml.cs||{8B382828-6202-11D1-8870-0000F87579D2}|",
|
||||||
|
"RelativeMoniker": "D:0:0:{11611D1E-B832-4666-9E37-23014F395F55}|src\\DownloadManager.WPF\\DownloadManager.WPF.csproj|solutionrelative:src\\downloadmanager.wpf\\app.xaml.cs||{8B382828-6202-11D1-8870-0000F87579D2}|"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"DocumentGroupContainers": [
|
||||||
|
{
|
||||||
|
"Orientation": 0,
|
||||||
|
"VerticalTabListWidth": 256,
|
||||||
|
"DocumentGroups": [
|
||||||
|
{
|
||||||
|
"DockedWidth": 200,
|
||||||
|
"SelectedChildIndex": 1,
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"$type": "Bookmark",
|
||||||
|
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 0,
|
||||||
|
"Title": "MainViewModel.cs",
|
||||||
|
"DocumentMoniker": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\ViewModels\\MainViewModel.cs",
|
||||||
|
"RelativeDocumentMoniker": "src\\DownloadManager.WPF\\ViewModels\\MainViewModel.cs",
|
||||||
|
"ToolTip": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\ViewModels\\MainViewModel.cs",
|
||||||
|
"RelativeToolTip": "src\\DownloadManager.WPF\\ViewModels\\MainViewModel.cs",
|
||||||
|
"ViewState": "AgIAABQAAAAAAAAAAAAewCoAAAArAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-03T21:58:25.845Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 2,
|
||||||
|
"Title": "App.xaml.cs",
|
||||||
|
"DocumentMoniker": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\App.xaml.cs",
|
||||||
|
"RelativeDocumentMoniker": "src\\DownloadManager.WPF\\App.xaml.cs",
|
||||||
|
"ToolTip": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\App.xaml.cs",
|
||||||
|
"RelativeToolTip": "src\\DownloadManager.WPF\\App.xaml.cs",
|
||||||
|
"ViewState": "AgIAAFQAAAAAAAAAAAAYwAAAAAAAAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||||
|
"WhenOpened": "2026-05-03T21:58:15.818Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 1,
|
||||||
|
"Title": "MainWindow.xaml",
|
||||||
|
"DocumentMoniker": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\Views\\MainWindow.xaml",
|
||||||
|
"RelativeDocumentMoniker": "src\\DownloadManager.WPF\\Views\\MainWindow.xaml",
|
||||||
|
"ToolTip": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.WPF\\Views\\MainWindow.xaml",
|
||||||
|
"RelativeToolTip": "src\\DownloadManager.WPF\\Views\\MainWindow.xaml",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|",
|
||||||
|
"WhenOpened": "2026-05-01T23:15:00.129Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
.vs/ProjectEvaluation/downloadmanager.metadata.v9.bin
Normal file
BIN
.vs/ProjectEvaluation/downloadmanager.metadata.v9.bin
Normal file
Binary file not shown.
BIN
.vs/ProjectEvaluation/downloadmanager.projects.v9.bin
Normal file
BIN
.vs/ProjectEvaluation/downloadmanager.projects.v9.bin
Normal file
Binary file not shown.
BIN
.vs/ProjectEvaluation/downloadmanager.strings.v9.bin
Normal file
BIN
.vs/ProjectEvaluation/downloadmanager.strings.v9.bin
Normal file
Binary file not shown.
78
Down_nb-01.svg
Normal file
78
Down_nb-01.svg
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512">
|
||||||
|
<!-- Generator: Adobe Illustrator 30.3.0, SVG Export Plug-In . SVG Version: 2.1.3 Build 182) -->
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.st0 {
|
||||||
|
stroke: url(#linear-gradient3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.st0, .st1 {
|
||||||
|
fill: #1e78ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
.st0, .st1, .st2 {
|
||||||
|
stroke-miterlimit: 10;
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.st3, .st2 {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.st4 {
|
||||||
|
fill: #d8d8d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.st1 {
|
||||||
|
stroke: url(#linear-gradient2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.st5 {
|
||||||
|
fill: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.st2 {
|
||||||
|
stroke: url(#linear-gradient1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.st6 {
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.st7 {
|
||||||
|
fill: url(#linear-gradient);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<linearGradient id="linear-gradient" x1="256.04" y1="102.33" x2="256.04" y2="401.45" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#fff"/>
|
||||||
|
<stop offset=".09" stop-color="#f6f6f6"/>
|
||||||
|
<stop offset=".22" stop-color="#dfdfdf"/>
|
||||||
|
<stop offset=".39" stop-color="#bababa"/>
|
||||||
|
<stop offset=".58" stop-color="#868686"/>
|
||||||
|
<stop offset=".8" stop-color="#444"/>
|
||||||
|
<stop offset="1" stop-color="#000"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linear-gradient1" x1="255.86" y1="339.28" x2="255.86" y2="181.51" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#fff"/>
|
||||||
|
<stop offset="1" stop-color="#000"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linear-gradient2" x1="147.47" y1="243.93" x2="147.47" y2="149.16" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#fff"/>
|
||||||
|
<stop offset="1" stop-color="#000"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linear-gradient3" x1="364.44" y1="243.93" x2="364.44" xlink:href="#linear-gradient2"/>
|
||||||
|
</defs>
|
||||||
|
<path d="M473.56,397.58V103.22c0-7.04-5.76-12.8-12.8-12.8H51.32c-7.04,0-12.8,5.76-12.8,12.8v294.35s436.11,0,435.04,0Z"/>
|
||||||
|
<rect class="st7" x="54.52" y="106.42" width="403.04" height="275.15"/>
|
||||||
|
<rect class="st4" y="397.58" width="511.91" height="8"/>
|
||||||
|
<polygon class="st5" points="486.41 421.58 25.69 421.58 .09 405.58 512 405.58 486.41 421.58"/>
|
||||||
|
<g class="st6">
|
||||||
|
<path class="st3" d="M298.99,397.58h-86.07c.82,4.49,4.82,8,9.83,8h66.4c5.02,0,9.02-3.51,9.84-8Z"/>
|
||||||
|
</g>
|
||||||
|
<polygon class="st2" points="309.29 235 272.86 272.14 272.86 182.51 238.87 182.51 238.87 272.14 202.45 235 178.19 258.66 255.86 337.85 333.54 258.66 309.29 235"/>
|
||||||
|
<g>
|
||||||
|
<polygon class="st1" points="179.23 181.36 157.58 203.44 157.58 150.16 137.38 150.16 137.38 203.44 115.72 181.36 101.3 195.42 147.47 242.5 193.65 195.42 179.23 181.36"/>
|
||||||
|
<polygon class="st0" points="396.19 181.36 374.54 203.44 374.54 150.16 354.34 150.16 354.34 203.44 332.68 181.36 318.26 195.42 364.43 242.5 410.62 195.42 396.19 181.36"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.9 KiB |
116
DownloadManager.sln
Normal file
116
DownloadManager.sln
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DownloadManager.Core", "src\DownloadManager.Core\DownloadManager.Core.csproj", "{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DownloadManager.WPF", "src\DownloadManager.WPF\DownloadManager.WPF.csproj", "{11611D1E-B832-4666-9E37-23014F395F55}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DownloadManager.BrowserBridge", "src\DownloadManager.BrowserBridge\DownloadManager.BrowserBridge.csproj", "{9690EB78-195A-424D-927A-1A900F30470D}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DownloadManager.Core.Tests", "tests\DownloadManager.Core.Tests\DownloadManager.Core.Tests.csproj", "{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DownloadManager.WPF.Tests", "tests\DownloadManager.WPF.Tests\DownloadManager.WPF.Tests.csproj", "{F95B21F5-5F7E-4BAC-A846-3D1975914422}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DownloadManager.TestConsole", "src\DownloadManager.TestConsole\DownloadManager.TestConsole.csproj", "{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{F9D4A5CB-FE99-4766-8662-9688A03CD3B7} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
||||||
|
{11611D1E-B832-4666-9E37-23014F395F55} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
||||||
|
{9690EB78-195A-424D-927A-1A900F30470D} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
||||||
|
{D861A9EA-BC51-4A9D-8B2C-9F2ABC7202F6} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
||||||
|
{F95B21F5-5F7E-4BAC-A846-3D1975914422} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
|
||||||
|
{8CB8ED62-AA01-4486-8BF9-BB21D77A3317} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
52
browser-extension/background.js
Normal file
52
browser-extension/background.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
const NATIVE_HOST = "com.downloadmanager.bridge";
|
||||||
|
|
||||||
|
const interceptExtensions = [
|
||||||
|
"exe","msi","zip","rar","7z","tar","gz","iso",
|
||||||
|
"mp4","mkv","avi","mp3","flac","wav",
|
||||||
|
"pdf","docx","xlsx","pptx"
|
||||||
|
];
|
||||||
|
|
||||||
|
chrome.runtime.onInstalled.addListener(() => {
|
||||||
|
chrome.contextMenus.create({
|
||||||
|
id: "dm-link",
|
||||||
|
title: "Download Manager ile İndir",
|
||||||
|
contexts: ["link"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
chrome.downloads.onCreated.addListener((item) => {
|
||||||
|
const ext = item.filename?.split(".").pop()?.toLowerCase() ?? "";
|
||||||
|
if (!interceptExtensions.includes(ext)) return;
|
||||||
|
|
||||||
|
chrome.downloads.cancel(item.id, () => {
|
||||||
|
console.log("İndirme yakalandı, bridge'e gönderiliyor:", item.url);
|
||||||
|
chrome.runtime.sendNativeMessage(NATIVE_HOST, {
|
||||||
|
action: "add_download",
|
||||||
|
url: item.url,
|
||||||
|
filename: item.filename,
|
||||||
|
referrer: item.referrer ?? ""
|
||||||
|
}, (response) => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error("Bridge Hatası:", chrome.runtime.lastError.message);
|
||||||
|
} else {
|
||||||
|
console.log("Bridge Yanıtı:", response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
chrome.contextMenus.onClicked.addListener((info, tab) => {
|
||||||
|
if (info.menuItemId === "dm-link") {
|
||||||
|
console.log("Context menu tıklandı, bridge'e gönderiliyor:", info.linkUrl);
|
||||||
|
chrome.runtime.sendNativeMessage(NATIVE_HOST, {
|
||||||
|
action: "add_download",
|
||||||
|
url: info.linkUrl
|
||||||
|
}, (response) => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error("Bridge Hatası:", chrome.runtime.lastError.message);
|
||||||
|
} else {
|
||||||
|
console.log("Bridge Yanıtı:", response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
12
browser-extension/manifest.json
Normal file
12
browser-extension/manifest.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "Download Manager",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "hOLOlu Download Manager'a yönlendir",
|
||||||
|
"permissions": ["downloads", "contextMenus", "storage", "nativeMessaging"],
|
||||||
|
"host_permissions": ["<all_urls>"],
|
||||||
|
"background": { "service_worker": "background.js" },
|
||||||
|
"action": {
|
||||||
|
"default_popup": "popup/popup.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
53
browser-extension/popup/popup.html
Normal file
53
browser-extension/popup/popup.html
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Download Manager</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
width: 250px;
|
||||||
|
padding: 15px;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.header img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.header h1 {
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
padding-top: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #888;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="header">
|
||||||
|
<h1>Download Manager</h1>
|
||||||
|
</div>
|
||||||
|
<div class="status">
|
||||||
|
Uzantı aktif. İndirmeler otomatik olarak ana uygulamaya yönlendirilir.
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
© 2026 hDM Project
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
77
docs/browser_integration.md
Normal file
77
docs/browser_integration.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# 🌐 Tarayıcı Entegrasyonu Kurulum Kılavuzu
|
||||||
|
|
||||||
|
Bu belge, **Download Manager**'ın Google Chrome ve Microsoft Edge tarayıcıları ile nasıl entegre edileceğini adım adım açıklar. Bu entegrasyon sayesinde tarayıcıda bir indirme başladığında otomatik olarak yakalanır veya sağ tık menüsü ile indirmeler uygulamaya gönderilir.
|
||||||
|
|
||||||
|
## 🏗️ Mimari Yapı
|
||||||
|
Sistem üç ana bileşenden oluşur:
|
||||||
|
1. **Tarayıcı Eklentisi:** İndirme isteklerini yakalayan JavaScript bileşeni.
|
||||||
|
2. **Browser Bridge (Native Messaging Host):** Tarayıcı ile Windows arasında köprü kuran küçük bir konsol uygulaması.
|
||||||
|
3. **Main App (WPF):** İndirmeyi gerçekleştiren ana uygulama.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Adım 1: Browser Bridge'i Derleme
|
||||||
|
Tarayıcının bir `.exe` dosyası ile konuşması gerekir. Önce köprü uygulamasını derleyin:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd DownloadManager
|
||||||
|
dotnet build src/DownloadManager.BrowserBridge/DownloadManager.BrowserBridge.csproj -c Release
|
||||||
|
```
|
||||||
|
|
||||||
|
Derleme sonrası oluşan dosya yolu şuna benzer olacaktır:
|
||||||
|
`D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\bin\Release\net8.0\DownloadManager.BrowserBridge.exe`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Adım 2: Manifest Dosyasını Yapılandırma
|
||||||
|
`src/DownloadManager.BrowserBridge/manifest.json` dosyasını açın ve şu iki alanı güncelleyin:
|
||||||
|
|
||||||
|
1. **path:** Buraya derlediğiniz `.exe` dosyasının **tam yolunu** yazın.
|
||||||
|
2. **allowed_origins:** Buraya eklentiyi tarayıcıya yükledikten sonra alacağınız **Eklenti ID'sini** yazacaksınız (3. adımda).
|
||||||
|
|
||||||
|
Örnek (Geçici):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "com.downloadmanager.bridge",
|
||||||
|
"description": "Download Manager Native Messaging Bridge",
|
||||||
|
"path": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\bin\\Release\\net8.0\\DownloadManager.BrowserBridge.exe",
|
||||||
|
"type": "stdio",
|
||||||
|
"allowed_origins": [
|
||||||
|
"chrome-extension://[EKLEMTI_ID_BURAYA]/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔑 Adım 3: Registry (Kayıt Defteri) Kaydı
|
||||||
|
Tarayıcının bu köprüyü tanıması için Windows Kayıt Defteri'ne eklenmesi gerekir.
|
||||||
|
|
||||||
|
1. `src/DownloadManager.BrowserBridge/register_bridge.reg` dosyasını bir metin düzenleyici ile açın.
|
||||||
|
2. Dosya yollarının `manifest.json` dosyanızın bulunduğu konumu gösterdiğinden emin olun.
|
||||||
|
3. Dosyaya çift tıklayarak çalıştırın ve gelen uyarıya "Evet" deyin.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔌 Adım 4: Eklentiyi Tarayıcıya Yükleme
|
||||||
|
1. Tarayıcınızda (Chrome veya Edge) `chrome://extensions/` adresine gidin.
|
||||||
|
2. Sağ üstteki **"Geliştirici Modu"** (Developer Mode) anahtarını açın.
|
||||||
|
3. **"Paketlenmemiş öğe yükle"** (Load unpacked) butonuna tıklayın.
|
||||||
|
4. Proje klasörünüzdeki `DownloadManager/browser-extension` klasörünü seçin.
|
||||||
|
5. Yükleme tamamlandığında eklenti kutusunda bir **"Kimlik" (ID)** oluşacaktır (Örn: `abcdefg...`).
|
||||||
|
6. **Bu ID'yi kopyalayın** ve 2. adımdaki `manifest.json` dosyasında `[EKLEMTI_ID_BURAYA]` yerine yapıştırın.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Kullanım ve Test
|
||||||
|
1. **Download Manager (WPF)** uygulamasını çalıştırın ve açık tutun.
|
||||||
|
2. Tarayıcıda herhangi bir dosyayı (Örn: bir .zip veya .iso dosyası) indirmeye çalışın.
|
||||||
|
3. Eklenti indirmeyi iptal edecek ve linki otomatik olarak Download Manager listesine ekleyecektir.
|
||||||
|
4. Alternatif olarak, herhangi bir linke **Sağ Tık > Download Manager ile İndir** diyebilirsiniz.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❓ Sorun Giderme
|
||||||
|
* **İndirme Yakalanmıyor:** `DownloadManager.WPF.exe`'nin açık olduğundan emin olun.
|
||||||
|
* **Tarayıcı Hatası:** Tarayıcı "Native host not found" hatası veriyorsa Registry yolunu ve `manifest.json` içindeki `path` değerini kontrol edin.
|
||||||
|
* **Loglar:** Köprü uygulaması hata aldığında kendi klasöründe `bridge_error.log` dosyası oluşturur, burayı kontrol edebilirsiniz.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.*" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
102
src/DownloadManager.BrowserBridge/Program.cs
Normal file
102
src/DownloadManager.BrowserBridge/Program.cs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Pipes;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace DownloadManager.BrowserBridge;
|
||||||
|
|
||||||
|
public class BridgeMessage
|
||||||
|
{
|
||||||
|
[JsonProperty("action")]
|
||||||
|
public string Action { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonProperty("url")]
|
||||||
|
public string Url { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonProperty("filename")]
|
||||||
|
public string? FileName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("referrer")]
|
||||||
|
public string? Referrer { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
var stdin = Console.OpenStandardInput();
|
||||||
|
var stdout = Console.OpenStandardOutput();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Chrome protokolü: 4 byte (little-endian uint32) = mesaj uzunluğu
|
||||||
|
var lenBuf = new byte[4];
|
||||||
|
int read = await stdin.ReadAsync(lenBuf, 0, 4);
|
||||||
|
if (read < 4) break;
|
||||||
|
|
||||||
|
var length = BitConverter.ToUInt32(lenBuf, 0);
|
||||||
|
var msgBuf = new byte[length];
|
||||||
|
|
||||||
|
int msgRead = 0;
|
||||||
|
while (msgRead < length)
|
||||||
|
{
|
||||||
|
int r = await stdin.ReadAsync(msgBuf, msgRead, (int)length - msgRead);
|
||||||
|
if (r <= 0) break;
|
||||||
|
msgRead += r;
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = Encoding.UTF8.GetString(msgBuf);
|
||||||
|
var msg = JsonConvert.DeserializeObject<BridgeMessage>(json);
|
||||||
|
|
||||||
|
if (msg != null)
|
||||||
|
{
|
||||||
|
await SendToMainAppAsync(msg);
|
||||||
|
// Mesaj gönderildikten sonra çık (Native messaging her mesaj için yeni process açabiliyor)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task SendToMainAppAsync(BridgeMessage msg)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Ana uygulamanın çalışıp çalışmadığını kontrol et
|
||||||
|
var processes = System.Diagnostics.Process.GetProcessesByName("DownloadManager.WPF");
|
||||||
|
if (processes.Length == 0)
|
||||||
|
{
|
||||||
|
// Uygulama çalışmıyorsa başlat (Yolu bulmaya çalışalım)
|
||||||
|
string appPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DownloadManager.WPF.exe");
|
||||||
|
|
||||||
|
// Geliştirme ortamı için alternatif yollar
|
||||||
|
if (!File.Exists(appPath))
|
||||||
|
appPath = @"D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.WPF\bin\Release\net8.0-windows\DownloadManager.WPF.exe";
|
||||||
|
|
||||||
|
if (File.Exists(appPath))
|
||||||
|
{
|
||||||
|
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(appPath) { UseShellExecute = true });
|
||||||
|
// Uygulamanın açılması ve pipe'ı başlatması için biraz bekleyelim
|
||||||
|
await Task.Delay(3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using var pipe = new NamedPipeClientStream(".", "DownloadManagerPipe",
|
||||||
|
PipeDirection.Out, PipeOptions.Asynchronous);
|
||||||
|
|
||||||
|
// Timeout'u 10 saniyeye çıkaralım (Uygulama yeni açılıyorsa gerekebilir)
|
||||||
|
await pipe.ConnectAsync(10000);
|
||||||
|
|
||||||
|
var json = JsonConvert.SerializeObject(msg);
|
||||||
|
var data = Encoding.UTF8.GetBytes(json);
|
||||||
|
|
||||||
|
await pipe.WriteAsync(data);
|
||||||
|
await pipe.FlushAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
File.AppendAllText("bridge_error.log", $"{DateTime.Now}: Bağlantı hatası ({msg.Url}): {ex.Message}\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"runtimeTarget": {
|
||||||
|
"name": ".NETCoreApp,Version=v8.0",
|
||||||
|
"signature": ""
|
||||||
|
},
|
||||||
|
"compilationOptions": {},
|
||||||
|
"targets": {
|
||||||
|
".NETCoreApp,Version=v8.0": {
|
||||||
|
"DownloadManager.BrowserBridge/1.0.0": {
|
||||||
|
"dependencies": {
|
||||||
|
"Newtonsoft.Json": "13.0.4"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"DownloadManager.BrowserBridge.dll": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Newtonsoft.Json/13.0.4": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net6.0/Newtonsoft.Json.dll": {
|
||||||
|
"assemblyVersion": "13.0.0.0",
|
||||||
|
"fileVersion": "13.0.4.30916"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"DownloadManager.BrowserBridge/1.0.0": {
|
||||||
|
"type": "project",
|
||||||
|
"serviceable": false,
|
||||||
|
"sha512": ""
|
||||||
|
},
|
||||||
|
"Newtonsoft.Json/13.0.4": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==",
|
||||||
|
"path": "newtonsoft.json/13.0.4",
|
||||||
|
"hashPath": "newtonsoft.json.13.0.4.nupkg.sha512"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"runtimeOptions": {
|
||||||
|
"tfm": "net8.0",
|
||||||
|
"framework": {
|
||||||
|
"name": "Microsoft.NETCore.App",
|
||||||
|
"version": "8.0.0"
|
||||||
|
},
|
||||||
|
"configProperties": {
|
||||||
|
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
|
||||||
|
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
|||||||
|
2/05/2026 08:52:46: The operation has timed out.
|
||||||
|
2/05/2026 08:57:51: The operation has timed out.
|
||||||
|
2/05/2026 08:59:05: The operation has timed out.
|
||||||
|
2/05/2026 08:59:27: The operation has timed out.
|
||||||
|
2/05/2026 08:59:40: The operation has timed out.
|
||||||
|
2/05/2026 09:00:01: The operation has timed out.
|
||||||
|
2/05/2026 09:03:51: The operation has timed out.
|
||||||
|
2/05/2026 09:12:38: Bağlantı hatası (https://ftp.linux.org.tr/ubuntu-releases/24.04/ubuntu-24.04-desktop-amd64.iso): The operation has timed out.
|
||||||
9
src/DownloadManager.BrowserBridge/manifest - Yedek.json
Normal file
9
src/DownloadManager.BrowserBridge/manifest - Yedek.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "com.downloadmanager.bridge",
|
||||||
|
"description": "Download Manager Native Messaging Bridge",
|
||||||
|
"path": "DownloadManager.BrowserBridge.exe",
|
||||||
|
"type": "stdio",
|
||||||
|
"allowed_origins": [
|
||||||
|
"chrome-extension://[EXTENSION_ID_BURAYA_GELECEK]/"
|
||||||
|
]
|
||||||
|
}
|
||||||
9
src/DownloadManager.BrowserBridge/manifest.json
Normal file
9
src/DownloadManager.BrowserBridge/manifest.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "com.downloadmanager.bridge",
|
||||||
|
"description": "hOLOlu Download Manager Native Messaging Bridge",
|
||||||
|
"path": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\bin\\Release\\net8.0\\DownloadManager.BrowserBridge.exe",
|
||||||
|
"type": "stdio",
|
||||||
|
"allowed_origins": [
|
||||||
|
"chrome-extension://gnohncemfbplcagkfdhedbkfaogcoobi/"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: System.Reflection.AssemblyCompanyAttribute("DownloadManager.BrowserBridge")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("DownloadManager.BrowserBridge")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("DownloadManager.BrowserBridge")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
000d6a996873011518a2be58f132c59662b6ff352ca8842e6f1fa1c31b20e4f1
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
is_global = true
|
||||||
|
build_property.TargetFramework = net8.0
|
||||||
|
build_property.TargetPlatformMinVersion =
|
||||||
|
build_property.UsingMicrosoftNETSdkWeb =
|
||||||
|
build_property.ProjectTypeGuids =
|
||||||
|
build_property.InvariantGlobalization =
|
||||||
|
build_property.PlatformNeutralAssembly =
|
||||||
|
build_property.EnforceExtendedAnalyzerRules =
|
||||||
|
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||||
|
build_property.RootNamespace = DownloadManager.BrowserBridge
|
||||||
|
build_property.ProjectDir = D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 8.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// <auto-generated/>
|
||||||
|
global using global::System;
|
||||||
|
global using global::System.Collections.Generic;
|
||||||
|
global using global::System.IO;
|
||||||
|
global using global::System.Linq;
|
||||||
|
global using global::System.Net.Http;
|
||||||
|
global using global::System.Threading;
|
||||||
|
global using global::System.Threading.Tasks;
|
||||||
Binary file not shown.
Binary file not shown.
BIN
src/DownloadManager.BrowserBridge/obj/Debug/net8.0/apphost.exe
Normal file
BIN
src/DownloadManager.BrowserBridge/obj/Debug/net8.0/apphost.exe
Normal file
Binary file not shown.
@@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"format": 1,
|
||||||
|
"restore": {
|
||||||
|
"D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\DownloadManager.BrowserBridge.csproj": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\DownloadManager.BrowserBridge.csproj": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\DownloadManager.BrowserBridge.csproj",
|
||||||
|
"projectName": "DownloadManager.BrowserBridge",
|
||||||
|
"projectPath": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\DownloadManager.BrowserBridge.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\hOLOlu\\.nuget\\packages\\",
|
||||||
|
"outputPath": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"fallbackFolders": [
|
||||||
|
"C:\\Program Files\\DevExpress 22.2\\Components\\Offline Packages",
|
||||||
|
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages",
|
||||||
|
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder"
|
||||||
|
],
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\hOLOlu\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\DevExpress 22.2.config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net8.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"C:\\Program Files\\DevExpress 22.2\\Components\\System\\Components\\Packages": {},
|
||||||
|
"C:\\Program Files\\dotnet\\library-packs": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net8.0": {
|
||||||
|
"targetAlias": "net8.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "9.0.300"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net8.0": {
|
||||||
|
"targetAlias": "net8.0",
|
||||||
|
"dependencies": {
|
||||||
|
"Newtonsoft.Json": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[13.*, )"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.313/PortableRuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||||
|
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||||
|
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||||
|
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||||
|
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\hOLOlu\.nuget\packages\;C:\Program Files\DevExpress 22.2\Components\Offline Packages;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages;C:\Program Files\dotnet\sdk\NuGetFallbackFolder</NuGetPackageFolders>
|
||||||
|
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||||
|
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.3</NuGetToolVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<SourceRoot Include="C:\Users\hOLOlu\.nuget\packages\" />
|
||||||
|
<SourceRoot Include="C:\Program Files\DevExpress 22.2\Components\Offline Packages\" />
|
||||||
|
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
|
||||||
|
<SourceRoot Include="C:\Program Files\dotnet\sdk\NuGetFallbackFolder\" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: System.Reflection.AssemblyCompanyAttribute("DownloadManager.BrowserBridge")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("DownloadManager.BrowserBridge")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("DownloadManager.BrowserBridge")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// MSBuild WriteCodeFragment sınıfı tarafından oluşturuldu.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
89d0fd93dd3e67ad8186e0c515a8d65ebbbc94026391ff8e88db296a467ee7a3
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
is_global = true
|
||||||
|
build_property.TargetFramework = net8.0
|
||||||
|
build_property.TargetPlatformMinVersion =
|
||||||
|
build_property.UsingMicrosoftNETSdkWeb =
|
||||||
|
build_property.ProjectTypeGuids =
|
||||||
|
build_property.InvariantGlobalization =
|
||||||
|
build_property.PlatformNeutralAssembly =
|
||||||
|
build_property.EnforceExtendedAnalyzerRules =
|
||||||
|
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||||
|
build_property.RootNamespace = DownloadManager.BrowserBridge
|
||||||
|
build_property.ProjectDir = D:\Calismalar\ai\hDM\DownloadManager\src\DownloadManager.BrowserBridge\
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 8.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// <auto-generated/>
|
||||||
|
global using global::System;
|
||||||
|
global using global::System.Collections.Generic;
|
||||||
|
global using global::System.IO;
|
||||||
|
global using global::System.Linq;
|
||||||
|
global using global::System.Net.Http;
|
||||||
|
global using global::System.Threading;
|
||||||
|
global using global::System.Threading.Tasks;
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
9a48f8efc256d5e9f4c6bcb351f545b3f8de09f2663f420d4e646e2dde80a99c
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\bin\Release\net8.0\DownloadManager.BrowserBridge.exe
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\bin\Release\net8.0\DownloadManager.BrowserBridge.deps.json
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\bin\Release\net8.0\DownloadManager.BrowserBridge.runtimeconfig.json
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\bin\Release\net8.0\DownloadManager.BrowserBridge.dll
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\bin\Release\net8.0\DownloadManager.BrowserBridge.pdb
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\bin\Release\net8.0\Newtonsoft.Json.dll
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\DownloadManager.BrowserBridge.csproj.AssemblyReference.cache
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\DownloadManager.BrowserBridge.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\DownloadManager.BrowserBridge.AssemblyInfoInputs.cache
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\DownloadManager.BrowserBridge.AssemblyInfo.cs
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\DownloadManager.BrowserBridge.csproj.CoreCompileInputs.cache
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\Download.A73557B5.Up2Date
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\DownloadManager.BrowserBridge.dll
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\refint\DownloadManager.BrowserBridge.dll
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\DownloadManager.BrowserBridge.pdb
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\DownloadManager.BrowserBridge.genruntimeconfig.cache
|
||||||
|
D:\Calismalar\AI\hDM\DownloadManager\src\DownloadManager.BrowserBridge\obj\Release\net8.0\ref\DownloadManager.BrowserBridge.dll
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
f69caf1b77b5ac1742efde949e911c38081902650a9646ddb4fb0df3c6723dd0
|
||||||
Binary file not shown.
BIN
src/DownloadManager.BrowserBridge/obj/Release/net8.0/apphost.exe
Normal file
BIN
src/DownloadManager.BrowserBridge/obj/Release/net8.0/apphost.exe
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
139
src/DownloadManager.BrowserBridge/obj/project.assets.json
Normal file
139
src/DownloadManager.BrowserBridge/obj/project.assets.json
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"targets": {
|
||||||
|
"net8.0": {
|
||||||
|
"Newtonsoft.Json/13.0.4": {
|
||||||
|
"type": "package",
|
||||||
|
"compile": {
|
||||||
|
"lib/net6.0/Newtonsoft.Json.dll": {
|
||||||
|
"related": ".xml"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net6.0/Newtonsoft.Json.dll": {
|
||||||
|
"related": ".xml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"Newtonsoft.Json/13.0.4": {
|
||||||
|
"sha512": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "newtonsoft.json/13.0.4",
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"LICENSE.md",
|
||||||
|
"README.md",
|
||||||
|
"lib/net20/Newtonsoft.Json.dll",
|
||||||
|
"lib/net20/Newtonsoft.Json.xml",
|
||||||
|
"lib/net35/Newtonsoft.Json.dll",
|
||||||
|
"lib/net35/Newtonsoft.Json.xml",
|
||||||
|
"lib/net40/Newtonsoft.Json.dll",
|
||||||
|
"lib/net40/Newtonsoft.Json.xml",
|
||||||
|
"lib/net45/Newtonsoft.Json.dll",
|
||||||
|
"lib/net45/Newtonsoft.Json.xml",
|
||||||
|
"lib/net6.0/Newtonsoft.Json.dll",
|
||||||
|
"lib/net6.0/Newtonsoft.Json.xml",
|
||||||
|
"lib/netstandard1.0/Newtonsoft.Json.dll",
|
||||||
|
"lib/netstandard1.0/Newtonsoft.Json.xml",
|
||||||
|
"lib/netstandard1.3/Newtonsoft.Json.dll",
|
||||||
|
"lib/netstandard1.3/Newtonsoft.Json.xml",
|
||||||
|
"lib/netstandard2.0/Newtonsoft.Json.dll",
|
||||||
|
"lib/netstandard2.0/Newtonsoft.Json.xml",
|
||||||
|
"newtonsoft.json.13.0.4.nupkg.sha512",
|
||||||
|
"newtonsoft.json.nuspec",
|
||||||
|
"packageIcon.png"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"projectFileDependencyGroups": {
|
||||||
|
"net8.0": [
|
||||||
|
"Newtonsoft.Json >= 13.*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packageFolders": {
|
||||||
|
"C:\\Users\\hOLOlu\\.nuget\\packages\\": {},
|
||||||
|
"C:\\Program Files\\DevExpress 22.2\\Components\\Offline Packages": {},
|
||||||
|
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {},
|
||||||
|
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder": {}
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\DownloadManager.BrowserBridge.csproj",
|
||||||
|
"projectName": "DownloadManager.BrowserBridge",
|
||||||
|
"projectPath": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\DownloadManager.BrowserBridge.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\hOLOlu\\.nuget\\packages\\",
|
||||||
|
"outputPath": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"fallbackFolders": [
|
||||||
|
"C:\\Program Files\\DevExpress 22.2\\Components\\Offline Packages",
|
||||||
|
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages",
|
||||||
|
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder"
|
||||||
|
],
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\hOLOlu\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\DevExpress 22.2.config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net8.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"C:\\Program Files\\DevExpress 22.2\\Components\\System\\Components\\Packages": {},
|
||||||
|
"C:\\Program Files\\dotnet\\library-packs": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net8.0": {
|
||||||
|
"targetAlias": "net8.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "9.0.300"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net8.0": {
|
||||||
|
"targetAlias": "net8.0",
|
||||||
|
"dependencies": {
|
||||||
|
"Newtonsoft.Json": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[13.*, )"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.313/PortableRuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/DownloadManager.BrowserBridge/obj/project.nuget.cache
Normal file
10
src/DownloadManager.BrowserBridge/obj/project.nuget.cache
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"dgSpecHash": "B+3YjV9jaKo=",
|
||||||
|
"success": true,
|
||||||
|
"projectFilePath": "D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\DownloadManager.BrowserBridge.csproj",
|
||||||
|
"expectedPackageFiles": [
|
||||||
|
"C:\\Users\\hOLOlu\\.nuget\\packages\\newtonsoft.json\\13.0.4\\newtonsoft.json.13.0.4.nupkg.sha512"
|
||||||
|
],
|
||||||
|
"logs": []
|
||||||
|
}
|
||||||
7
src/DownloadManager.BrowserBridge/register_bridge.reg
Normal file
7
src/DownloadManager.BrowserBridge/register_bridge.reg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Windows Registry Editor Version 5.00
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\com.downloadmanager.bridge]
|
||||||
|
@="D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\manifest.json"
|
||||||
|
|
||||||
|
[HKEY_CURRENT_USER\Software\Microsoft\Edge\NativeMessagingHosts\com.downloadmanager.bridge]
|
||||||
|
@="D:\\Calismalar\\AI\\hDM\\DownloadManager\\src\\DownloadManager.BrowserBridge\\manifest.json"
|
||||||
6
src/DownloadManager.Core/Class1.cs
Normal file
6
src/DownloadManager.Core/Class1.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace DownloadManager.Core;
|
||||||
|
|
||||||
|
public class Class1
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
31
src/DownloadManager.Core/Data/AppDbContext.cs
Normal file
31
src/DownloadManager.Core/Data/AppDbContext.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data;
|
||||||
|
|
||||||
|
public class AppDbContext : DbContext
|
||||||
|
{
|
||||||
|
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<DownloadItem> Downloads { get; set; } = null!;
|
||||||
|
public DbSet<DownloadSegment> Segments { get; set; } = null!;
|
||||||
|
public DbSet<DownloadCategory> Categories { get; set; } = null!;
|
||||||
|
public DbSet<GrabberProject> GrabberProjects { get; set; } = null!;
|
||||||
|
public DbSet<ScheduleJob> ScheduleJobs { get; set; } = null!;
|
||||||
|
public DbSet<AppSetting> Settings { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder b)
|
||||||
|
{
|
||||||
|
// Seed — varsayılan kategoriler
|
||||||
|
b.Entity<DownloadCategory>().HasData(
|
||||||
|
new DownloadCategory { Id = 1, Name = "Yazılım", SavePath = @"%USERPROFILE%\Downloads\Yazılım", Extensions = "exe,msi,dmg" },
|
||||||
|
new DownloadCategory { Id = 2, Name = "Belge", SavePath = @"%USERPROFILE%\Downloads\Belgeler", Extensions = "pdf,docx,xlsx,pptx" },
|
||||||
|
new DownloadCategory { Id = 3, Name = "Ses", SavePath = @"%USERPROFILE%\Downloads\Ses", Extensions = "mp3,flac,wav,aac" },
|
||||||
|
new DownloadCategory { Id = 4, Name = "Video", SavePath = @"%USERPROFILE%\Downloads\Video", Extensions = "mp4,mkv,avi,mov" },
|
||||||
|
new DownloadCategory { Id = 5, Name = "Görsel", SavePath = @"%USERPROFILE%\Downloads\Görseller",Extensions = "jpg,jpeg,png,gif,webp" },
|
||||||
|
new DownloadCategory { Id = 6, Name = "Diğer", SavePath = @"%USERPROFILE%\Downloads", Extensions = "" }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/DownloadManager.Core/Data/AppDbContextFactory.cs
Normal file
15
src/DownloadManager.Core/Data/AppDbContextFactory.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data;
|
||||||
|
|
||||||
|
public class AppDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
|
||||||
|
{
|
||||||
|
public AppDbContext CreateDbContext(string[] args)
|
||||||
|
{
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
|
||||||
|
optionsBuilder.UseSqlite("Data Source=downloadmanager.db");
|
||||||
|
|
||||||
|
return new AppDbContext(optionsBuilder.Options);
|
||||||
|
}
|
||||||
|
}
|
||||||
264
src/DownloadManager.Core/Data/Migrations/20260501194147_InitialCreate.Designer.cs
generated
Normal file
264
src/DownloadManager.Core/Data/Migrations/20260501194147_InitialCreate.Designer.cs
generated
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using DownloadManager.Core.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20260501194147_InitialCreate")]
|
||||||
|
partial class InitialCreate
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "8.0.26");
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.AppSetting", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Key");
|
||||||
|
|
||||||
|
b.ToTable("Settings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Extensions")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SavePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Categories");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Extensions = "exe,msi,dmg",
|
||||||
|
Name = "Yazılım",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Yazılım"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Extensions = "pdf,docx,xlsx,pptx",
|
||||||
|
Name = "Belge",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Belgeler"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
Extensions = "mp3,flac,wav,aac",
|
||||||
|
Name = "Ses",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Ses"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 4,
|
||||||
|
Extensions = "mp4,mkv,avi,mov",
|
||||||
|
Name = "Video",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Video"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 5,
|
||||||
|
Extensions = "jpg,jpeg,png,gif,webp",
|
||||||
|
Name = "Görsel",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Görseller"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 6,
|
||||||
|
Extensions = "",
|
||||||
|
Name = "Diğer",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("CompletedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<long>("DownloadedBytes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ETag")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ErrorMessage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("LastModified")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Priority")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Referrer")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SavePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("SegmentCount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Sha256Checksum")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("StartedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("TotalSize")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserAgent")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryId");
|
||||||
|
|
||||||
|
b.ToTable("Downloads");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadSegment", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("DownloadItemId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<long>("Downloaded")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("EndByte")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Index")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("StartByte")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("TempFilePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("DownloadItemId");
|
||||||
|
|
||||||
|
b.ToTable("Segments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.GrabberProject", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("GrabberProjects");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.ScheduleJob", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ScheduleJobs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DownloadManager.Core.Models.DownloadCategory", "Category")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Category");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadSegment", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DownloadManager.Core.Models.DownloadItem", null)
|
||||||
|
.WithMany("Segments")
|
||||||
|
.HasForeignKey("DownloadItemId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadItem", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Segments");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class InitialCreate : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Categories",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
SavePath = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Extensions = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Categories", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GrabberProjects",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GrabberProjects", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ScheduleJobs",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ScheduleJobs", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Settings",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Key = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Value = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Settings", x => x.Key);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Downloads",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
Url = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
FileName = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
SavePath = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
CategoryId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Status = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
TotalSize = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
DownloadedBytes = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
SegmentCount = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Priority = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Referrer = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
UserAgent = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Username = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
PasswordHash = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ETag = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
LastModified = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
ErrorMessage = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Sha256Checksum = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
StartedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
CompletedAt = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Downloads", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Downloads_Categories_CategoryId",
|
||||||
|
column: x => x.CategoryId,
|
||||||
|
principalTable: "Categories",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Segments",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
DownloadItemId = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||||
|
Index = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
StartByte = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
EndByte = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
Downloaded = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
Status = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
TempFilePath = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Segments", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Segments_Downloads_DownloadItemId",
|
||||||
|
column: x => x.DownloadItemId,
|
||||||
|
principalTable: "Downloads",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "Categories",
|
||||||
|
columns: new[] { "Id", "Extensions", "Name", "SavePath" },
|
||||||
|
values: new object[,]
|
||||||
|
{
|
||||||
|
{ 1, "exe,msi,dmg", "Yazılım", "%USERPROFILE%\\Downloads\\Yazılım" },
|
||||||
|
{ 2, "pdf,docx,xlsx,pptx", "Belge", "%USERPROFILE%\\Downloads\\Belgeler" },
|
||||||
|
{ 3, "mp3,flac,wav,aac", "Ses", "%USERPROFILE%\\Downloads\\Ses" },
|
||||||
|
{ 4, "mp4,mkv,avi,mov", "Video", "%USERPROFILE%\\Downloads\\Video" },
|
||||||
|
{ 5, "jpg,jpeg,png,gif,webp", "Görsel", "%USERPROFILE%\\Downloads\\Görseller" },
|
||||||
|
{ 6, "", "Diğer", "%USERPROFILE%\\Downloads" }
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Downloads_CategoryId",
|
||||||
|
table: "Downloads",
|
||||||
|
column: "CategoryId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Segments_DownloadItemId",
|
||||||
|
table: "Segments",
|
||||||
|
column: "DownloadItemId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GrabberProjects");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ScheduleJobs");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Segments");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Settings");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Downloads");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Categories");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using DownloadManager.Core.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
partial class AppDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "8.0.26");
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.AppSetting", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Key");
|
||||||
|
|
||||||
|
b.ToTable("Settings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Extensions")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SavePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Categories");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Extensions = "exe,msi,dmg",
|
||||||
|
Name = "Yazılım",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Yazılım"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
Extensions = "pdf,docx,xlsx,pptx",
|
||||||
|
Name = "Belge",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Belgeler"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
Extensions = "mp3,flac,wav,aac",
|
||||||
|
Name = "Ses",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Ses"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 4,
|
||||||
|
Extensions = "mp4,mkv,avi,mov",
|
||||||
|
Name = "Video",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Video"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 5,
|
||||||
|
Extensions = "jpg,jpeg,png,gif,webp",
|
||||||
|
Name = "Görsel",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads\\Görseller"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 6,
|
||||||
|
Extensions = "",
|
||||||
|
Name = "Diğer",
|
||||||
|
SavePath = "%USERPROFILE%\\Downloads"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("CompletedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<long>("DownloadedBytes")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ETag")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ErrorMessage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("LastModified")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Priority")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Referrer")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SavePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("SegmentCount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Sha256Checksum")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("StartedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("TotalSize")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UserAgent")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CategoryId");
|
||||||
|
|
||||||
|
b.ToTable("Downloads");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadSegment", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<Guid>("DownloadItemId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<long>("Downloaded")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("EndByte")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Index")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("StartByte")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("TempFilePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("DownloadItemId");
|
||||||
|
|
||||||
|
b.ToTable("Segments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.GrabberProject", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("GrabberProjects");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.ScheduleJob", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ScheduleJobs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DownloadManager.Core.Models.DownloadCategory", "Category")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Category");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadSegment", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DownloadManager.Core.Models.DownloadItem", null)
|
||||||
|
.WithMany("Segments")
|
||||||
|
.HasForeignKey("DownloadItemId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DownloadManager.Core.Models.DownloadItem", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Segments");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data.Repositories;
|
||||||
|
|
||||||
|
public class CategoryRepository : ICategoryRepository
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _db;
|
||||||
|
public CategoryRepository(AppDbContext db) => _db = db;
|
||||||
|
|
||||||
|
public async Task<IEnumerable<DownloadCategory>> GetAllAsync()
|
||||||
|
=> await _db.Categories.ToListAsync();
|
||||||
|
|
||||||
|
public async Task<DownloadCategory?> GetByIdAsync(int id)
|
||||||
|
=> await _db.Categories.FindAsync(id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data.Repositories;
|
||||||
|
|
||||||
|
public class DownloadRepository : IDownloadRepository
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _db;
|
||||||
|
public DownloadRepository(AppDbContext db) => _db = db;
|
||||||
|
|
||||||
|
public async Task<DownloadItem?> GetByIdAsync(Guid id)
|
||||||
|
=> await _db.Downloads.Include(d => d.Segments).FirstOrDefaultAsync(d => d.Id == id);
|
||||||
|
|
||||||
|
public async Task<IEnumerable<DownloadItem>> GetAllAsync()
|
||||||
|
=> await _db.Downloads.Include(d => d.Category).ToListAsync();
|
||||||
|
|
||||||
|
public async Task AddAsync(DownloadItem item)
|
||||||
|
{
|
||||||
|
await _db.Downloads.AddAsync(item);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateAsync(DownloadItem item)
|
||||||
|
{
|
||||||
|
_db.Downloads.Update(item);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(Guid id)
|
||||||
|
{
|
||||||
|
var item = await _db.Downloads.FindAsync(id);
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
_db.Downloads.Remove(item);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/DownloadManager.Core/Data/Repositories/IRepositories.cs
Normal file
27
src/DownloadManager.Core/Data/Repositories/IRepositories.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data.Repositories;
|
||||||
|
|
||||||
|
public interface IDownloadRepository
|
||||||
|
{
|
||||||
|
Task<DownloadItem?> GetByIdAsync(Guid id);
|
||||||
|
Task<IEnumerable<DownloadItem>> GetAllAsync();
|
||||||
|
Task AddAsync(DownloadItem item);
|
||||||
|
Task UpdateAsync(DownloadItem item);
|
||||||
|
Task DeleteAsync(Guid id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ICategoryRepository
|
||||||
|
{
|
||||||
|
Task<IEnumerable<DownloadCategory>> GetAllAsync();
|
||||||
|
Task<DownloadCategory?> GetByIdAsync(int id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ISettingsRepository
|
||||||
|
{
|
||||||
|
Task<string?> GetValueAsync(string key);
|
||||||
|
Task SetValueAsync(string key, string value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data.Repositories;
|
||||||
|
|
||||||
|
public interface IScheduleRepository
|
||||||
|
{
|
||||||
|
Task<IEnumerable<ScheduleJob>> GetAllAsync();
|
||||||
|
Task AddAsync(ScheduleJob job);
|
||||||
|
Task UpdateAsync(ScheduleJob job);
|
||||||
|
Task DeleteAsync(int id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScheduleRepository : IScheduleRepository
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _db;
|
||||||
|
public ScheduleRepository(AppDbContext db) => _db = db;
|
||||||
|
|
||||||
|
public async Task<IEnumerable<ScheduleJob>> GetAllAsync() => await Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync(_db.ScheduleJobs);
|
||||||
|
|
||||||
|
public async Task AddAsync(ScheduleJob job)
|
||||||
|
{
|
||||||
|
await _db.ScheduleJobs.AddAsync(job);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateAsync(ScheduleJob job)
|
||||||
|
{
|
||||||
|
_db.ScheduleJobs.Update(job);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(int id)
|
||||||
|
{
|
||||||
|
var job = await _db.ScheduleJobs.FindAsync(id);
|
||||||
|
if (job != null)
|
||||||
|
{
|
||||||
|
_db.ScheduleJobs.Remove(job);
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Data.Repositories;
|
||||||
|
|
||||||
|
public class SettingsRepository : ISettingsRepository
|
||||||
|
{
|
||||||
|
private readonly AppDbContext _db;
|
||||||
|
public SettingsRepository(AppDbContext db) => _db = db;
|
||||||
|
|
||||||
|
public async Task<string?> GetValueAsync(string key)
|
||||||
|
{
|
||||||
|
var setting = await _db.Settings.FindAsync(key);
|
||||||
|
return setting?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetValueAsync(string key, string value)
|
||||||
|
{
|
||||||
|
var setting = await _db.Settings.FindAsync(key);
|
||||||
|
if (setting == null)
|
||||||
|
{
|
||||||
|
await _db.Settings.AddAsync(new AppSetting { Key = key, Value = value });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setting.Value = value;
|
||||||
|
}
|
||||||
|
await _db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/DownloadManager.Core/DownloadManager.Core.csproj
Normal file
26
src/DownloadManager.Core/DownloadManager.Core.csproj
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="HtmlAgilityPack" Version="1.*" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.*">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.*" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.*" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.*" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="8.*" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.*" />
|
||||||
|
<PackageReference Include="Serilog" Version="3.*" />
|
||||||
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.*" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="5.*" />
|
||||||
|
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.*" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
182
src/DownloadManager.Core/Engine/DownloadEngine.cs
Normal file
182
src/DownloadManager.Core/Engine/DownloadEngine.cs
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
using DownloadManager.Core.Enums;
|
||||||
|
using DownloadManager.Core.Events;
|
||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using DownloadManager.Core.Protocols;
|
||||||
|
using DownloadManager.Core.Queue;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Engine;
|
||||||
|
|
||||||
|
public class DownloadEngine
|
||||||
|
{
|
||||||
|
private readonly DownloadQueue _queue;
|
||||||
|
private readonly IDownloadProtocol _protocol;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly ConcurrentDictionary<Guid, CancellationTokenSource> _activeDownloads = new();
|
||||||
|
|
||||||
|
public event EventHandler<DownloadProgressEvent>? ProgressChanged;
|
||||||
|
|
||||||
|
public DownloadEngine(DownloadQueue queue, IDownloadProtocol protocol, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_queue = queue;
|
||||||
|
_protocol = protocol;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartQueueAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
Serilog.Log.Information("DownloadEngine loop started.");
|
||||||
|
while (!ct.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var item = await _queue.DequeueAsync(ct);
|
||||||
|
if (item == null) continue;
|
||||||
|
|
||||||
|
Serilog.Log.Information("Item dequeued for processing: {Url} (ID: {Id})", item.Url, item.Id);
|
||||||
|
_ = Task.Run(() => ProcessDownloadAsync(item, ct), ct);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) { break; }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Error(ex, "Error in DownloadEngine queue loop");
|
||||||
|
await Task.Delay(1000, ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serilog.Log.Information("DownloadEngine loop stopped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessDownloadAsync(DownloadItem item, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||||
|
_activeDownloads[item.Id] = cts;
|
||||||
|
|
||||||
|
var progress = new Progress<DownloadProgressEvent>(e => ProgressChanged?.Invoke(this, e));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Serilog.Log.Information("Processing download: {FileName} (ID: {Id})", item.FileName, item.Id);
|
||||||
|
|
||||||
|
// Eğer dosya boyutu bilinmiyorsa (ilk kez ekleniyor), bilgilerini çek
|
||||||
|
if (item.TotalSize <= 0)
|
||||||
|
{
|
||||||
|
Serilog.Log.Information("Fetching file info for {Url}", item.Url);
|
||||||
|
var info = await _protocol.GetFileInfoAsync(item.Url, cts.Token);
|
||||||
|
item.TotalSize = info.TotalSize;
|
||||||
|
item.ETag = info.ETag;
|
||||||
|
item.LastModified = info.LastModified;
|
||||||
|
if (string.IsNullOrEmpty(item.FileName)) item.FileName = info.FileName;
|
||||||
|
item.DownloadedBytes = 0; // Yeni indirme
|
||||||
|
}
|
||||||
|
|
||||||
|
// DURAKLATILMIŞSA İŞLEME (Kuyruktan yeni çıktıysa ve o sırada durdurulduysa)
|
||||||
|
if (item.Status == DownloadStatus.Paused)
|
||||||
|
{
|
||||||
|
Serilog.Log.Information("Download {Id} is paused, skipping.", item.Id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Status = DownloadStatus.Downloading;
|
||||||
|
|
||||||
|
// Notify UI that we are starting
|
||||||
|
ProgressChanged?.Invoke(this, new DownloadProgressEvent
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
Status = DownloadStatus.Downloading,
|
||||||
|
DownloadedBytes = item.DownloadedBytes,
|
||||||
|
TotalBytes = item.TotalSize,
|
||||||
|
ProgressPercent = item.TotalSize > 0 ? (double)Math.Min(item.DownloadedBytes, item.TotalSize) / item.TotalSize * 100 : 0
|
||||||
|
});
|
||||||
|
|
||||||
|
IDownloader downloader;
|
||||||
|
if (item.TotalSize > 0)
|
||||||
|
{
|
||||||
|
downloader = _serviceProvider.GetRequiredService<SegmentedDownloader>();
|
||||||
|
Serilog.Log.Information("Using SegmentedDownloader for {Id}. Current progress: {Downloaded}/{Total}",
|
||||||
|
item.Id, item.DownloadedBytes, item.TotalSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
downloader = _serviceProvider.GetRequiredService<SingleDownloader>();
|
||||||
|
Serilog.Log.Information("Using SingleDownloader (unknown size) for {Id}. Current progress: {Downloaded}",
|
||||||
|
item.Id, item.DownloadedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
await downloader.DownloadAsync(item, progress, cts.Token);
|
||||||
|
|
||||||
|
// Eğer boyut bilinmiyorduysa (dynamic), indirilen boyutu toplam boyut yap
|
||||||
|
if (item.TotalSize <= 0) item.TotalSize = item.DownloadedBytes;
|
||||||
|
|
||||||
|
item.Status = DownloadStatus.Completed;
|
||||||
|
item.CompletedAt = DateTime.UtcNow;
|
||||||
|
|
||||||
|
ProgressChanged?.Invoke(this, new DownloadProgressEvent
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
Status = DownloadStatus.Completed,
|
||||||
|
ProgressPercent = 100
|
||||||
|
});
|
||||||
|
Serilog.Log.Information("Download completed: {Id}", item.Id);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
Serilog.Log.Information("Download paused/cancelled: {Id}", item.Id);
|
||||||
|
item.Status = DownloadStatus.Paused;
|
||||||
|
ProgressChanged?.Invoke(this, CreateProgressEvent(item, DownloadStatus.Paused));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Error(ex, "Error processing download: {Id}", item.Id);
|
||||||
|
item.Status = DownloadStatus.Error;
|
||||||
|
item.ErrorMessage = ex.Message;
|
||||||
|
ProgressChanged?.Invoke(this, CreateProgressEvent(item, DownloadStatus.Error));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await SaveItemStateAsync(item);
|
||||||
|
_activeDownloads.TryRemove(item.Id, out _);
|
||||||
|
_queue.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DownloadProgressEvent CreateProgressEvent(DownloadItem item, DownloadStatus status)
|
||||||
|
{
|
||||||
|
var downloaded = Math.Min(item.DownloadedBytes, item.TotalSize);
|
||||||
|
return new DownloadProgressEvent
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
Status = status,
|
||||||
|
DownloadedBytes = downloaded,
|
||||||
|
TotalBytes = item.TotalSize,
|
||||||
|
ProgressPercent = item.TotalSize > 0 ? (double)downloaded / item.TotalSize * 100 : 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveItemStateAsync(DownloadItem item)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
var repo = scope.ServiceProvider.GetRequiredService<Data.Repositories.IDownloadRepository>();
|
||||||
|
await repo.UpdateAsync(item);
|
||||||
|
Serilog.Log.Information("Saved download state to DB for {Id}: {Status}, {Downloaded} bytes",
|
||||||
|
item.Id, item.Status, item.DownloadedBytes);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Error(ex, "Failed to save download state to DB for {Id}", item.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelDownload(Guid id)
|
||||||
|
{
|
||||||
|
if (_activeDownloads.TryRemove(id, out var cts))
|
||||||
|
{
|
||||||
|
cts.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/DownloadManager.Core/Engine/EngineOptions.cs
Normal file
12
src/DownloadManager.Core/Engine/EngineOptions.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace DownloadManager.Core.Engine;
|
||||||
|
|
||||||
|
public class EngineOptions
|
||||||
|
{
|
||||||
|
public int MaxSegments { get; set; } = 8;
|
||||||
|
public long MinSegmentBytes { get; set; } = 512 * 1024; // 512 KB
|
||||||
|
public int MaxRetries { get; set; } = 5;
|
||||||
|
public int RetryDelayMs { get; set; } = 2000;
|
||||||
|
public int ConnectionTimeoutSeconds { get; set; } = 30;
|
||||||
|
public long SpeedLimitBytesPerSec { get; set; } = 0; // 0 = limitsiz
|
||||||
|
public string UserAgent { get; set; } = "DownloadManager/1.0 (.NET 8)";
|
||||||
|
}
|
||||||
12
src/DownloadManager.Core/Engine/IDownloader.cs
Normal file
12
src/DownloadManager.Core/Engine/IDownloader.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using DownloadManager.Core.Events;
|
||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Engine;
|
||||||
|
|
||||||
|
public interface IDownloader
|
||||||
|
{
|
||||||
|
Task DownloadAsync(DownloadItem item, IProgress<DownloadProgressEvent>? progress, CancellationToken ct);
|
||||||
|
}
|
||||||
84
src/DownloadManager.Core/Engine/ResumeManager.cs
Normal file
84
src/DownloadManager.Core/Engine/ResumeManager.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Engine;
|
||||||
|
|
||||||
|
public class DownloadItemState
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Url { get; set; } = string.Empty;
|
||||||
|
public string SavePath { get; set; } = string.Empty;
|
||||||
|
public long TotalSize { get; set; }
|
||||||
|
public string? ETag { get; set; }
|
||||||
|
public string? LastModified { get; set; }
|
||||||
|
public List<DownloadSegment> Segments { get; set; } = new();
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public DateTime SavedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ResumeManager
|
||||||
|
{
|
||||||
|
private readonly string _basePath;
|
||||||
|
|
||||||
|
public ResumeManager()
|
||||||
|
{
|
||||||
|
_basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "DownloadManager", "temp");
|
||||||
|
if (!Directory.Exists(_basePath))
|
||||||
|
Directory.CreateDirectory(_basePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DownloadItemState?> LoadAsync(Guid id)
|
||||||
|
{
|
||||||
|
var path = GetMetaPath(id);
|
||||||
|
if (!File.Exists(path)) return null;
|
||||||
|
|
||||||
|
var json = await File.ReadAllTextAsync(path);
|
||||||
|
return JsonConvert.DeserializeObject<DownloadItemState>(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DownloadItemState> LoadOrCreateAsync(DownloadItem item, EngineOptions options)
|
||||||
|
{
|
||||||
|
var state = await LoadAsync(item.Id);
|
||||||
|
if (state != null)
|
||||||
|
{
|
||||||
|
// ETag veya LastModified değişmişse → içerik değişmiş, baştan başla.
|
||||||
|
// Bu kontrolü burada yapabiliriz.
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = new DownloadItemState
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
Url = item.Url,
|
||||||
|
SavePath = item.SavePath,
|
||||||
|
TotalSize = item.TotalSize,
|
||||||
|
ETag = item.ETag,
|
||||||
|
LastModified = item.LastModified,
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveAsync(DownloadItem item, DownloadItemState state)
|
||||||
|
{
|
||||||
|
var path = GetMetaPath(item.Id);
|
||||||
|
state.SavedAt = DateTime.UtcNow;
|
||||||
|
var json = JsonConvert.SerializeObject(state, Formatting.Indented);
|
||||||
|
await File.WriteAllTextAsync(path, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DeleteAsync(Guid id)
|
||||||
|
{
|
||||||
|
var path = GetMetaPath(id);
|
||||||
|
if (File.Exists(path))
|
||||||
|
File.Delete(path);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetMetaPath(Guid id) => Path.Combine(_basePath, $"{id}.meta");
|
||||||
|
}
|
||||||
197
src/DownloadManager.Core/Engine/SegmentedDownloader.cs
Normal file
197
src/DownloadManager.Core/Engine/SegmentedDownloader.cs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
using DownloadManager.Core.Enums;
|
||||||
|
using DownloadManager.Core.Events;
|
||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Engine;
|
||||||
|
|
||||||
|
public class SegmentedDownloader : IDownloader
|
||||||
|
{
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
private readonly ResumeManager _resumeManager;
|
||||||
|
private readonly SpeedCalculator _speedCalculator;
|
||||||
|
private readonly EngineOptions _options;
|
||||||
|
private readonly object _progressLock = new();
|
||||||
|
private DateTime _lastReport = DateTime.MinValue;
|
||||||
|
|
||||||
|
public SegmentedDownloader(
|
||||||
|
IHttpClientFactory httpClientFactory,
|
||||||
|
ResumeManager resumeManager,
|
||||||
|
SpeedCalculator speedCalculator,
|
||||||
|
EngineOptions options)
|
||||||
|
{
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
_resumeManager = resumeManager;
|
||||||
|
_speedCalculator = speedCalculator;
|
||||||
|
_options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DownloadAsync(DownloadItem item, IProgress<DownloadProgressEvent>? progress, CancellationToken ct)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[SegmentedDownloader] Starting download for {item.Id}");
|
||||||
|
var state = await _resumeManager.LoadOrCreateAsync(item, _options);
|
||||||
|
|
||||||
|
if (state.Segments.Count == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[SegmentedDownloader] Initializing segments for {item.Id}");
|
||||||
|
InitializeSegments(state, _options);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempDir = EnsureTempDirectory(item.Id);
|
||||||
|
var activeTasks = new ConcurrentDictionary<int, Task>();
|
||||||
|
|
||||||
|
Console.WriteLine($"[SegmentedDownloader] Starting {state.Segments.Count(s => s.Status != SegmentStatus.Completed)} segments");
|
||||||
|
foreach (var seg in state.Segments.Where(s => s.Status != SegmentStatus.Completed))
|
||||||
|
{
|
||||||
|
activeTasks[seg.Index] = DownloadSegmentAsync(item, seg, tempDir, progress, ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (activeTasks.Count > 0)
|
||||||
|
{
|
||||||
|
var done = await Task.WhenAny(activeTasks.Values);
|
||||||
|
await done; // Önemli: Task'ı await ederek cancellation veya hataların fırlatılmasını sağlıyoruz
|
||||||
|
|
||||||
|
var completedTask = activeTasks.FirstOrDefault(x => x.Value == done);
|
||||||
|
activeTasks.TryRemove(completedTask.Key, out _);
|
||||||
|
|
||||||
|
Console.WriteLine($"[SegmentedDownloader] Segment {completedTask.Key} completed. {activeTasks.Count} remaining.");
|
||||||
|
|
||||||
|
await _resumeManager.SaveAsync(item, state);
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"[SegmentedDownloader] Merging segments for {item.Id}");
|
||||||
|
await MergeSegmentsAsync(state.Segments, item.SavePath);
|
||||||
|
await _resumeManager.DeleteAsync(item.Id);
|
||||||
|
Directory.Delete(tempDir, true);
|
||||||
|
Console.WriteLine($"[SegmentedDownloader] Download finished for {item.Id}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeSegments(DownloadItemState state, EngineOptions options)
|
||||||
|
{
|
||||||
|
if (state.TotalSize <= 0)
|
||||||
|
{
|
||||||
|
// Safety fallback: if TotalSize is somehow 0 here, create a single "segment"
|
||||||
|
// but normally DownloadEngine should have picked SingleDownloader.
|
||||||
|
state.Segments.Add(new DownloadSegment
|
||||||
|
{
|
||||||
|
Index = 0,
|
||||||
|
StartByte = 0,
|
||||||
|
EndByte = 0,
|
||||||
|
Downloaded = 0,
|
||||||
|
Status = SegmentStatus.Pending,
|
||||||
|
DownloadItemId = state.Id
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long segmentSize = state.TotalSize / options.MaxSegments;
|
||||||
|
if (segmentSize == 0) segmentSize = state.TotalSize; // Small file case
|
||||||
|
|
||||||
|
for (int i = 0; i < options.MaxSegments; i++)
|
||||||
|
{
|
||||||
|
long start = i * segmentSize;
|
||||||
|
if (start >= state.TotalSize) break; // Don't create empty segments for very small files
|
||||||
|
|
||||||
|
long end = (i == options.MaxSegments - 1) ? state.TotalSize - 1 : (i + 1) * segmentSize - 1;
|
||||||
|
if (end >= state.TotalSize) end = state.TotalSize - 1;
|
||||||
|
|
||||||
|
state.Segments.Add(new DownloadSegment
|
||||||
|
{
|
||||||
|
Index = i,
|
||||||
|
StartByte = start,
|
||||||
|
EndByte = end,
|
||||||
|
Downloaded = 0,
|
||||||
|
Status = SegmentStatus.Pending,
|
||||||
|
DownloadItemId = state.Id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (end == state.TotalSize - 1) break; // Last segment reached
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DownloadSegmentAsync(DownloadItem item, DownloadSegment segment, string tempDir, IProgress<DownloadProgressEvent>? progress, CancellationToken ct)
|
||||||
|
{
|
||||||
|
segment.Status = SegmentStatus.Downloading;
|
||||||
|
segment.TempFilePath = Path.Combine(tempDir, $"seg_{segment.Index}.tmp");
|
||||||
|
|
||||||
|
using var client = _httpClientFactory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.Add("User-Agent", _options.UserAgent);
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, item.Url);
|
||||||
|
request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(segment.StartByte + segment.Downloaded, segment.EndByte);
|
||||||
|
|
||||||
|
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
using var stream = await response.Content.ReadAsStreamAsync(ct);
|
||||||
|
using var fileStream = new FileStream(segment.TempFilePath, segment.Downloaded > 0 ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
|
||||||
|
|
||||||
|
var buffer = new byte[8192];
|
||||||
|
int read;
|
||||||
|
while ((read = await stream.ReadAsync(buffer, 0, buffer.Length, ct)) > 0)
|
||||||
|
{
|
||||||
|
await fileStream.WriteAsync(buffer, 0, read, ct);
|
||||||
|
segment.Downloaded += read;
|
||||||
|
|
||||||
|
lock (_progressLock)
|
||||||
|
{
|
||||||
|
item.DownloadedBytes += read;
|
||||||
|
ReportProgress(item, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
_speedCalculator.AddSample(read);
|
||||||
|
}
|
||||||
|
|
||||||
|
segment.Status = SegmentStatus.Completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportProgress(DownloadItem item, IProgress<DownloadProgressEvent>? progress)
|
||||||
|
{
|
||||||
|
if ((DateTime.UtcNow - _lastReport).TotalMilliseconds < 250) return;
|
||||||
|
_lastReport = DateTime.UtcNow;
|
||||||
|
|
||||||
|
var downloaded = Math.Min(item.DownloadedBytes, item.TotalSize);
|
||||||
|
var percent = item.TotalSize > 0 ? (double)downloaded / item.TotalSize * 100 : 0;
|
||||||
|
if (percent > 100) percent = 100;
|
||||||
|
|
||||||
|
progress?.Report(new DownloadProgressEvent
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
DownloadedBytes = downloaded,
|
||||||
|
TotalBytes = item.TotalSize,
|
||||||
|
ProgressPercent = percent,
|
||||||
|
SpeedBytesPerSec = _speedCalculator.GetBytesPerSecond(),
|
||||||
|
EstimatedRemaining = _speedCalculator.EstimateRemaining(item.TotalSize - downloaded),
|
||||||
|
Status = DownloadStatus.Downloading
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private string EnsureTempDirectory(Guid id)
|
||||||
|
{
|
||||||
|
var path = Path.Combine(Path.GetTempPath(), "DownloadManager", id.ToString());
|
||||||
|
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task MergeSegmentsAsync(List<DownloadSegment> segments, string savePath)
|
||||||
|
{
|
||||||
|
var directory = Path.GetDirectoryName(savePath);
|
||||||
|
if (directory != null && !Directory.Exists(directory))
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
|
||||||
|
using var outputStream = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
|
||||||
|
foreach (var seg in segments.OrderBy(s => s.Index))
|
||||||
|
{
|
||||||
|
using var inputStream = new FileStream(seg.TempFilePath, FileMode.Open, FileAccess.Read);
|
||||||
|
await inputStream.CopyToAsync(outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/DownloadManager.Core/Engine/SingleDownloader.cs
Normal file
58
src/DownloadManager.Core/Engine/SingleDownloader.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using DownloadManager.Core.Enums;
|
||||||
|
using DownloadManager.Core.Events;
|
||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Engine;
|
||||||
|
|
||||||
|
public class SingleDownloader : IDownloader
|
||||||
|
{
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
private readonly SpeedCalculator _speedCalculator;
|
||||||
|
|
||||||
|
public SingleDownloader(IHttpClientFactory httpClientFactory, SpeedCalculator speedCalculator)
|
||||||
|
{
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
_speedCalculator = speedCalculator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DownloadAsync(DownloadItem item, IProgress<DownloadProgressEvent>? progress, CancellationToken ct)
|
||||||
|
{
|
||||||
|
using var client = _httpClientFactory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.Add("User-Agent", "DownloadManager/1.0 (.NET 8)");
|
||||||
|
|
||||||
|
using var response = await client.GetAsync(item.Url, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var directory = Path.GetDirectoryName(item.SavePath);
|
||||||
|
if (directory != null && !Directory.Exists(directory))
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
|
||||||
|
using var contentStream = await response.Content.ReadAsStreamAsync(ct);
|
||||||
|
using var fileStream = new FileStream(item.SavePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
|
||||||
|
|
||||||
|
var buffer = new byte[8192];
|
||||||
|
int read;
|
||||||
|
while ((read = await contentStream.ReadAsync(buffer, 0, buffer.Length, ct)) > 0)
|
||||||
|
{
|
||||||
|
await fileStream.WriteAsync(buffer, 0, read, ct);
|
||||||
|
item.DownloadedBytes += read;
|
||||||
|
_speedCalculator.AddSample(read);
|
||||||
|
|
||||||
|
progress?.Report(new DownloadProgressEvent
|
||||||
|
{
|
||||||
|
Id = item.Id,
|
||||||
|
DownloadedBytes = item.DownloadedBytes,
|
||||||
|
TotalBytes = item.TotalSize,
|
||||||
|
ProgressPercent = item.TotalSize > 0 ? (double)item.DownloadedBytes / item.TotalSize * 100 : 0,
|
||||||
|
SpeedBytesPerSec = _speedCalculator.GetBytesPerSecond(),
|
||||||
|
EstimatedRemaining = _speedCalculator.EstimateRemaining(item.TotalSize - item.DownloadedBytes),
|
||||||
|
Status = DownloadStatus.Downloading
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/DownloadManager.Core/Engine/SpeedCalculator.cs
Normal file
41
src/DownloadManager.Core/Engine/SpeedCalculator.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Engine;
|
||||||
|
|
||||||
|
public class SpeedCalculator
|
||||||
|
{
|
||||||
|
// Son 3 saniyenin kayan pencere ortalaması — anlık dalgalanmaları bastırır
|
||||||
|
private readonly Queue<(DateTime Time, long Bytes)> _samples = new();
|
||||||
|
private const double WindowSeconds = 3.0;
|
||||||
|
|
||||||
|
public void AddSample(long newBytes)
|
||||||
|
{
|
||||||
|
lock (_samples)
|
||||||
|
{
|
||||||
|
_samples.Enqueue((DateTime.UtcNow, newBytes));
|
||||||
|
while (_samples.TryPeek(out var old) &&
|
||||||
|
(DateTime.UtcNow - old.Time).TotalSeconds > WindowSeconds)
|
||||||
|
_samples.Dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Anlık hız (byte/saniye)</summary>
|
||||||
|
public long GetBytesPerSecond()
|
||||||
|
{
|
||||||
|
lock (_samples)
|
||||||
|
{
|
||||||
|
if (_samples.Count < 2) return 0;
|
||||||
|
var elapsed = (DateTime.UtcNow - _samples.Peek().Time).TotalSeconds;
|
||||||
|
return elapsed > 0 ? (long)(_samples.Sum(s => s.Bytes) / elapsed) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Kalan süre tahmini</summary>
|
||||||
|
public TimeSpan? EstimateRemaining(long remainingBytes)
|
||||||
|
{
|
||||||
|
var speed = GetBytesPerSecond();
|
||||||
|
return speed > 0 ? TimeSpan.FromSeconds(remainingBytes / (double)speed) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/DownloadManager.Core/Enums/AppTheme.cs
Normal file
8
src/DownloadManager.Core/Enums/AppTheme.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace DownloadManager.Core.Enums;
|
||||||
|
|
||||||
|
public enum AppTheme
|
||||||
|
{
|
||||||
|
Light,
|
||||||
|
Dark,
|
||||||
|
System
|
||||||
|
}
|
||||||
12
src/DownloadManager.Core/Enums/DownloadStatus.cs
Normal file
12
src/DownloadManager.Core/Enums/DownloadStatus.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace DownloadManager.Core.Enums;
|
||||||
|
|
||||||
|
public enum DownloadStatus
|
||||||
|
{
|
||||||
|
Pending,
|
||||||
|
Queued,
|
||||||
|
Downloading,
|
||||||
|
Paused,
|
||||||
|
Completed,
|
||||||
|
Error,
|
||||||
|
Cancelled
|
||||||
|
}
|
||||||
9
src/DownloadManager.Core/Enums/ProxyType.cs
Normal file
9
src/DownloadManager.Core/Enums/ProxyType.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace DownloadManager.Core.Enums;
|
||||||
|
|
||||||
|
public enum ProxyType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Http,
|
||||||
|
Socks4,
|
||||||
|
Socks5
|
||||||
|
}
|
||||||
9
src/DownloadManager.Core/Enums/SegmentStatus.cs
Normal file
9
src/DownloadManager.Core/Enums/SegmentStatus.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace DownloadManager.Core.Enums;
|
||||||
|
|
||||||
|
public enum SegmentStatus
|
||||||
|
{
|
||||||
|
Pending,
|
||||||
|
Downloading,
|
||||||
|
Completed,
|
||||||
|
Failed
|
||||||
|
}
|
||||||
15
src/DownloadManager.Core/Events/DownloadProgressEvent.cs
Normal file
15
src/DownloadManager.Core/Events/DownloadProgressEvent.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using DownloadManager.Core.Enums;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Events;
|
||||||
|
|
||||||
|
public class DownloadProgressEvent
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public double ProgressPercent { get; set; }
|
||||||
|
public long DownloadedBytes { get; set; }
|
||||||
|
public long TotalBytes { get; set; }
|
||||||
|
public long SpeedBytesPerSec { get; set; }
|
||||||
|
public TimeSpan? EstimatedRemaining { get; set; }
|
||||||
|
public DownloadStatus Status { get; set; }
|
||||||
|
}
|
||||||
154
src/DownloadManager.Core/Grabber/SiteGrabber.cs
Normal file
154
src/DownloadManager.Core/Grabber/SiteGrabber.cs
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using HtmlAgilityPack;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Grabber;
|
||||||
|
|
||||||
|
public class GrabberOptions
|
||||||
|
{
|
||||||
|
public string StartUrl { get; set; } = string.Empty;
|
||||||
|
public int MaxDepth { get; set; } = 2;
|
||||||
|
public bool StayOnDomain { get; set; } = true;
|
||||||
|
public string[] FileExtensions { get; set; } = Array.Empty<string>();
|
||||||
|
public string? UrlPattern { get; set; }
|
||||||
|
public long? MinFileSizeBytes { get; set; }
|
||||||
|
public long? MaxFileSizeBytes { get; set; }
|
||||||
|
public int MaxFileCount { get; set; } = 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record GrabberResult(string Url, string FileName, long? SizeBytes, string Extension);
|
||||||
|
|
||||||
|
public class SiteGrabber
|
||||||
|
{
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
|
||||||
|
public SiteGrabber(IHttpClientFactory httpClientFactory)
|
||||||
|
{
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async IAsyncEnumerable<GrabberResult> GrabAsync(
|
||||||
|
GrabberOptions opts,
|
||||||
|
[EnumeratorCancellation] CancellationToken ct)
|
||||||
|
{
|
||||||
|
var visited = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var queue = new Queue<(string Url, int Depth)>();
|
||||||
|
queue.Enqueue((opts.StartUrl, 0));
|
||||||
|
|
||||||
|
var baseUri = new Uri(opts.StartUrl);
|
||||||
|
var found = 0;
|
||||||
|
|
||||||
|
while (queue.Count > 0 && !ct.IsCancellationRequested && found < opts.MaxFileCount)
|
||||||
|
{
|
||||||
|
var (url, depth) = queue.Dequeue();
|
||||||
|
if (!visited.Add(url)) continue;
|
||||||
|
|
||||||
|
string html;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var handler = new HttpClientHandler { AllowAutoRedirect = true };
|
||||||
|
using var client = new HttpClient(handler);
|
||||||
|
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0");
|
||||||
|
html = await client.GetStringAsync(url, ct);
|
||||||
|
}
|
||||||
|
catch { continue; }
|
||||||
|
|
||||||
|
var doc = new HtmlDocument();
|
||||||
|
doc.LoadHtml(html);
|
||||||
|
|
||||||
|
var nodes = doc.DocumentNode.SelectNodes("//a[@href]");
|
||||||
|
if (nodes == null) continue;
|
||||||
|
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
if (ct.IsCancellationRequested) break;
|
||||||
|
|
||||||
|
var href = node.GetAttributeValue("href", "");
|
||||||
|
if (string.IsNullOrEmpty(href)) continue;
|
||||||
|
|
||||||
|
if (!Uri.TryCreate(baseUri, href, out var absoluteUri)) continue;
|
||||||
|
var absoluteUrl = absoluteUri.AbsoluteUri;
|
||||||
|
|
||||||
|
if (IsDownloadTarget(absoluteUrl, opts))
|
||||||
|
{
|
||||||
|
found++;
|
||||||
|
long? size = await GetFileSizeRobustAsync(absoluteUrl, ct);
|
||||||
|
|
||||||
|
yield return new GrabberResult(
|
||||||
|
absoluteUrl,
|
||||||
|
Path.GetFileName(absoluteUri.LocalPath),
|
||||||
|
size,
|
||||||
|
Path.GetExtension(absoluteUrl).TrimStart('.').ToLower()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (depth < opts.MaxDepth && IsSameDomain(absoluteUrl, baseUri.Host))
|
||||||
|
{
|
||||||
|
queue.Enqueue((absoluteUrl, depth + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found >= opts.MaxFileCount) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<long?> GetFileSizeRobustAsync(string url, CancellationToken ct)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var handler = new HttpClientHandler { AllowAutoRedirect = true };
|
||||||
|
using var client = new HttpClient(handler);
|
||||||
|
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
|
||||||
|
client.Timeout = TimeSpan.FromSeconds(6);
|
||||||
|
|
||||||
|
// 1. Önce HEAD dene
|
||||||
|
using var headReq = new HttpRequestMessage(HttpMethod.Head, url);
|
||||||
|
using var headResp = await client.SendAsync(headReq, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||||
|
|
||||||
|
// Eğer boyut 500 bayttan büyükse gerçektir, değilse muhtemelen sahte redirect sayfasıdır
|
||||||
|
if (headResp.IsSuccessStatusCode && headResp.Content.Headers.ContentLength > 500)
|
||||||
|
{
|
||||||
|
return headResp.Content.Headers.ContentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. HEAD başarısızsa veya 146B gibi gelmişse, Range ile GET dene
|
||||||
|
using var getReq = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
|
getReq.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(0, 0);
|
||||||
|
using var getResp = await client.SendAsync(getReq, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||||
|
|
||||||
|
if (getResp.Content.Headers.ContentRange?.HasLength == true)
|
||||||
|
{
|
||||||
|
return getResp.Content.Headers.ContentRange.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fallback = getResp.Content.Headers.ContentLength;
|
||||||
|
return fallback > 500 ? fallback : null;
|
||||||
|
}
|
||||||
|
catch { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsDownloadTarget(string url, GrabberOptions opts)
|
||||||
|
{
|
||||||
|
var ext = Path.GetExtension(url).TrimStart('.').ToLower();
|
||||||
|
if (string.IsNullOrEmpty(ext)) return false;
|
||||||
|
var skip = new[] { "html", "htm", "php", "aspx", "jsp", "txt" };
|
||||||
|
if (skip.Contains(ext)) return false;
|
||||||
|
return opts.FileExtensions.Length == 0 || opts.FileExtensions.Contains(ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsSameDomain(string url, string host)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uri = new Uri(url);
|
||||||
|
return uri.Host.Equals(host, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
catch { return false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/DownloadManager.Core/Models/AppSetting.cs
Normal file
10
src/DownloadManager.Core/Models/AppSetting.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Models;
|
||||||
|
|
||||||
|
public class AppSetting
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public string Key { get; set; } = string.Empty;
|
||||||
|
public string Value { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
9
src/DownloadManager.Core/Models/DownloadCategory.cs
Normal file
9
src/DownloadManager.Core/Models/DownloadCategory.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace DownloadManager.Core.Models;
|
||||||
|
|
||||||
|
public class DownloadCategory
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string SavePath { get; set; } = string.Empty;
|
||||||
|
public string Extensions { get; set; } = string.Empty; // Virgülle ayrılmış uzantılar
|
||||||
|
}
|
||||||
32
src/DownloadManager.Core/Models/DownloadItem.cs
Normal file
32
src/DownloadManager.Core/Models/DownloadItem.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using DownloadManager.Core.Enums;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Models;
|
||||||
|
|
||||||
|
public class DownloadItem
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
public string Url { get; set; } = string.Empty;
|
||||||
|
public string FileName { get; set; } = string.Empty;
|
||||||
|
public string SavePath { get; set; } = string.Empty;
|
||||||
|
public int CategoryId { get; set; }
|
||||||
|
public DownloadStatus Status { get; set; }
|
||||||
|
public long TotalSize { get; set; }
|
||||||
|
public long DownloadedBytes { get; set; }
|
||||||
|
public int SegmentCount { get; set; } = 8;
|
||||||
|
public int Priority { get; set; } = 5; // 1=yüksek 10=düşük
|
||||||
|
public string? Referrer { get; set; }
|
||||||
|
public string? UserAgent { get; set; }
|
||||||
|
public string? Username { get; set; }
|
||||||
|
public string? PasswordHash { get; set; } // DPAPI
|
||||||
|
public string? ETag { get; set; }
|
||||||
|
public string? LastModified { get; set; }
|
||||||
|
public DateTime? LastActivityAt { get; set; }
|
||||||
|
public string? ErrorMessage { get; set; }
|
||||||
|
public string? Sha256Checksum { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
public DateTime? StartedAt { get; set; }
|
||||||
|
public DateTime? CompletedAt { get; set; }
|
||||||
|
|
||||||
|
public DownloadCategory Category { get; set; } = null!;
|
||||||
|
public ICollection<DownloadSegment> Segments { get; set; } = new List<DownloadSegment>();
|
||||||
|
}
|
||||||
15
src/DownloadManager.Core/Models/DownloadSegment.cs
Normal file
15
src/DownloadManager.Core/Models/DownloadSegment.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using DownloadManager.Core.Enums;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Models;
|
||||||
|
|
||||||
|
public class DownloadSegment
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public Guid DownloadItemId { get; set; }
|
||||||
|
public int Index { get; set; }
|
||||||
|
public long StartByte { get; set; }
|
||||||
|
public long EndByte { get; set; }
|
||||||
|
public long Downloaded { get; set; }
|
||||||
|
public SegmentStatus Status { get; set; }
|
||||||
|
public string TempFilePath { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
6
src/DownloadManager.Core/Models/GrabberProject.cs
Normal file
6
src/DownloadManager.Core/Models/GrabberProject.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace DownloadManager.Core.Models;
|
||||||
|
|
||||||
|
public class GrabberProject
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
18
src/DownloadManager.Core/Models/ScheduleJob.cs
Normal file
18
src/DownloadManager.Core/Models/ScheduleJob.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Models;
|
||||||
|
|
||||||
|
public class ScheduleJob
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public DateTime? StartTime { get; set; }
|
||||||
|
public DateTime? EndTime { get; set; }
|
||||||
|
public bool IsActive { get; set; }
|
||||||
|
|
||||||
|
// Günler: 1111111 (Pt, Sa, Ça, Pe, Cu, Ct, Pz)
|
||||||
|
public string DaysOfWeek { get; set; } = "1111111";
|
||||||
|
|
||||||
|
public bool ShutdownOnComplete { get; set; }
|
||||||
|
public bool HangUpOnComplete { get; set; }
|
||||||
|
}
|
||||||
108
src/DownloadManager.Core/Protocols/HttpProtocol.cs
Normal file
108
src/DownloadManager.Core/Protocols/HttpProtocol.cs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Protocols;
|
||||||
|
|
||||||
|
public class HttpProtocol : IDownloadProtocol
|
||||||
|
{
|
||||||
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
|
|
||||||
|
public HttpProtocol(IHttpClientFactory httpClientFactory)
|
||||||
|
{
|
||||||
|
_httpClientFactory = httpClientFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DownloadItem> GetFileInfoAsync(string url, CancellationToken ct)
|
||||||
|
{
|
||||||
|
using var client = _httpClientFactory.CreateClient();
|
||||||
|
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
|
||||||
|
client.DefaultRequestHeaders.Add("Accept", "*/*");
|
||||||
|
|
||||||
|
HttpResponseMessage? response = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Bazı sunucular HEAD isteğini reddeder veya yanlış sonuç döndürür (TechSpot gibi)
|
||||||
|
// Bu yüzden önce GET ile header-only (ResponseHeadersRead) deniyoruz.
|
||||||
|
using var getRequest = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
|
response = await client.SendAsync(getRequest, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
// Alternatif olarak HEAD deneyelim
|
||||||
|
response.Dispose();
|
||||||
|
using var headRequest = new HttpRequestMessage(HttpMethod.Head, url);
|
||||||
|
response = await client.SendAsync(headRequest, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Hata durumunda son çare GET
|
||||||
|
using var getRequest = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
|
response = await client.SendAsync(getRequest, HttpCompletionOption.ResponseHeadersRead, ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
// Yönlendirmeler sonrası ulaşılan nihai adres
|
||||||
|
var finalUrl = response.RequestMessage?.RequestUri?.ToString() ?? url;
|
||||||
|
|
||||||
|
var item = new DownloadItem
|
||||||
|
{
|
||||||
|
Url = url,
|
||||||
|
FileName = GetFileNameFromResponse(response, finalUrl),
|
||||||
|
TotalSize = response.Content.Headers.ContentLength ?? 0,
|
||||||
|
ETag = response.Headers.ETag?.Tag,
|
||||||
|
LastModified = response.Content.Headers.LastModified?.ToString("R")
|
||||||
|
};
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
response?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFileNameFromResponse(HttpResponseMessage response, string finalUrl)
|
||||||
|
{
|
||||||
|
// 1. Öncelik: Content-Disposition (Sunucunun önerdiği isim)
|
||||||
|
var contentDisposition = response.Content.Headers.ContentDisposition;
|
||||||
|
if (contentDisposition != null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(contentDisposition.FileNameStar))
|
||||||
|
return contentDisposition.FileNameStar.Trim('"');
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(contentDisposition.FileName))
|
||||||
|
return contentDisposition.FileName.Trim('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Öncelik: Nihai URL'nin son parçası
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uri = new Uri(finalUrl);
|
||||||
|
var path = uri.LocalPath;
|
||||||
|
|
||||||
|
// Eğer URL query string içinde dosya adı barındırıyorsa (bazı CDN'ler için)
|
||||||
|
if (path.EndsWith("/") || string.IsNullOrEmpty(Path.GetFileName(path)))
|
||||||
|
{
|
||||||
|
var queryParts = System.Web.HttpUtility.ParseQueryString(uri.Query);
|
||||||
|
var fileFromQuery = queryParts["file"] ?? queryParts["filename"] ?? queryParts["name"];
|
||||||
|
if (!string.IsNullOrEmpty(fileFromQuery)) return fileFromQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileName = Path.GetFileName(path);
|
||||||
|
if (!string.IsNullOrEmpty(fileName) && fileName.Contains("."))
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
return "download_" + DateTime.Now.Ticks.ToString().Substring(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/DownloadManager.Core/Protocols/IDownloadProtocol.cs
Normal file
8
src/DownloadManager.Core/Protocols/IDownloadProtocol.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Protocols;
|
||||||
|
|
||||||
|
public interface IDownloadProtocol
|
||||||
|
{
|
||||||
|
Task<DownloadItem> GetFileInfoAsync(string url, CancellationToken ct);
|
||||||
|
}
|
||||||
66
src/DownloadManager.Core/Queue/DownloadQueue.cs
Normal file
66
src/DownloadManager.Core/Queue/DownloadQueue.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Queue;
|
||||||
|
|
||||||
|
public class DownloadQueue
|
||||||
|
{
|
||||||
|
private SemaphoreSlim _concurrencySemaphore;
|
||||||
|
private readonly SemaphoreSlim _itemsAvailableSemaphore = new(0);
|
||||||
|
private readonly PriorityQueue<DownloadItem, int> _inner = new();
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
|
public DownloadQueue(int maxConcurrent = 3)
|
||||||
|
=> _concurrencySemaphore = new SemaphoreSlim(maxConcurrent, maxConcurrent);
|
||||||
|
|
||||||
|
public void Enqueue(DownloadItem item)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_inner.Enqueue(item, item.Priority);
|
||||||
|
_itemsAvailableSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DownloadItem?> DequeueAsync(CancellationToken ct)
|
||||||
|
{
|
||||||
|
// Önce bir slotun boşalmasını bekle (concurrency limit)
|
||||||
|
await _concurrencySemaphore.WaitAsync(ct);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Sonra kuyrukta bir öğe olmasını bekle
|
||||||
|
await _itemsAvailableSemaphore.WaitAsync(ct);
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_inner.TryDequeue(out var item, out _))
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_concurrencySemaphore.Release();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Release() => _concurrencySemaphore.Release();
|
||||||
|
|
||||||
|
/// <summary>Çalışma zamanında limit değiştir (ayarlar panelinden)</summary>
|
||||||
|
public void SetConcurrencyLimit(int newLimit)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
// Not: Bu basit uygulama çalışma anındaki limit değişimini tam olarak yansıtmayabilir,
|
||||||
|
// ama temel yapı için yeterli.
|
||||||
|
var old = _concurrencySemaphore;
|
||||||
|
_concurrencySemaphore = new SemaphoreSlim(newLimit, newLimit);
|
||||||
|
old.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/DownloadManager.Core/Security/AntivirusLauncher.cs
Normal file
43
src/DownloadManager.Core/Security/AntivirusLauncher.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Security;
|
||||||
|
|
||||||
|
public class AntivirusLauncher
|
||||||
|
{
|
||||||
|
// Gelecekte ISettingsService üzerinden okunacak
|
||||||
|
public string ExePath { get; set; } = string.Empty;
|
||||||
|
public string ParamTemplate { get; set; } = "\"{file}\" /scan";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// İndirilen dosyayı kullanıcının seçtiği antivirüs ile tara.
|
||||||
|
/// Parametre şablonundaki {file} gerçek yol ile değiştirilir.
|
||||||
|
/// </summary>
|
||||||
|
public async Task ScanAsync(string downloadedFilePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(ExePath) || !File.Exists(ExePath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Güvenlik: dosya yolu tırnak içine alınmış olmalı
|
||||||
|
var safeFilePath = $"\"{downloadedFilePath}\"";
|
||||||
|
var arguments = ParamTemplate.Replace("{file}", safeFilePath);
|
||||||
|
|
||||||
|
var psi = new ProcessStartInfo(ExePath, arguments)
|
||||||
|
{
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var proc = Process.Start(psi);
|
||||||
|
if (proc != null)
|
||||||
|
await proc.WaitForExitAsync();
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Serilog.Log.Error(ex, "Antivirüs taraması başlatılamadı: {FilePath}", downloadedFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/DownloadManager.Core/Security/CredentialProtector.cs
Normal file
36
src/DownloadManager.Core/Security/CredentialProtector.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Security;
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
public static class CredentialProtector
|
||||||
|
{
|
||||||
|
// Windows DPAPI — kullanıcı oturumuna bağlı şifreleme
|
||||||
|
public static string Protect(string plainText)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(plainText)) return string.Empty;
|
||||||
|
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(plainText);
|
||||||
|
var encrypted = ProtectedData.Protect(bytes, null, DataProtectionScope.CurrentUser);
|
||||||
|
return Convert.ToBase64String(encrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Unprotect(string cipherText)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(cipherText)) return string.Empty;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var bytes = Convert.FromBase64String(cipherText);
|
||||||
|
var decrypted = ProtectedData.Unprotect(bytes, null, DataProtectionScope.CurrentUser);
|
||||||
|
return Encoding.UTF8.GetString(decrypted);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/DownloadManager.Core/Services/CategoryService.cs
Normal file
20
src/DownloadManager.Core/Services/CategoryService.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using DownloadManager.Core.Data.Repositories;
|
||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Services;
|
||||||
|
|
||||||
|
public interface ICategoryService
|
||||||
|
{
|
||||||
|
Task<IEnumerable<DownloadCategory>> GetCategoriesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CategoryService : ICategoryService
|
||||||
|
{
|
||||||
|
private readonly ICategoryRepository _repository;
|
||||||
|
public CategoryService(ICategoryRepository repository) => _repository = repository;
|
||||||
|
|
||||||
|
public Task<IEnumerable<DownloadCategory>> GetCategoriesAsync()
|
||||||
|
=> _repository.GetAllAsync();
|
||||||
|
}
|
||||||
71
src/DownloadManager.Core/Services/DownloadService.cs
Normal file
71
src/DownloadManager.Core/Services/DownloadService.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using DownloadManager.Core.Data.Repositories;
|
||||||
|
using DownloadManager.Core.Engine;
|
||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using DownloadManager.Core.Queue;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Services;
|
||||||
|
|
||||||
|
public interface IDownloadService
|
||||||
|
{
|
||||||
|
Task<IEnumerable<DownloadItem>> GetAllDownloadsAsync();
|
||||||
|
Task AddDownloadAsync(DownloadItem item);
|
||||||
|
Task UpdateDownloadAsync(DownloadItem item);
|
||||||
|
Task PauseDownloadAsync(Guid id);
|
||||||
|
Task ResumeDownloadAsync(Guid id);
|
||||||
|
Task DeleteDownloadAsync(Guid id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DownloadService : IDownloadService
|
||||||
|
{
|
||||||
|
private readonly IDownloadRepository _repository;
|
||||||
|
private readonly DownloadEngine _engine;
|
||||||
|
private readonly DownloadQueue _queue;
|
||||||
|
|
||||||
|
public DownloadService(IDownloadRepository repository, DownloadEngine engine, DownloadQueue queue)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
_engine = engine;
|
||||||
|
_queue = queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IEnumerable<DownloadItem>> GetAllDownloadsAsync()
|
||||||
|
=> _repository.GetAllAsync();
|
||||||
|
|
||||||
|
public async Task AddDownloadAsync(DownloadItem item)
|
||||||
|
{
|
||||||
|
await _repository.AddAsync(item);
|
||||||
|
_queue.Enqueue(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateDownloadAsync(DownloadItem item)
|
||||||
|
{
|
||||||
|
await _repository.UpdateAsync(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task PauseDownloadAsync(Guid id)
|
||||||
|
{
|
||||||
|
_engine.CancelDownload(id);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ResumeDownloadAsync(Guid id)
|
||||||
|
{
|
||||||
|
var item = await _repository.GetByIdAsync(id);
|
||||||
|
if (item != null && item.Status != Enums.DownloadStatus.Completed)
|
||||||
|
{
|
||||||
|
item.Status = Enums.DownloadStatus.Queued;
|
||||||
|
await _repository.UpdateAsync(item);
|
||||||
|
_queue.Enqueue(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task DeleteDownloadAsync(Guid id)
|
||||||
|
{
|
||||||
|
await _repository.DeleteAsync(id);
|
||||||
|
_engine.CancelDownload(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/DownloadManager.Core/Services/ScheduleService.cs
Normal file
29
src/DownloadManager.Core/Services/ScheduleService.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using DownloadManager.Core.Data.Repositories;
|
||||||
|
using DownloadManager.Core.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DownloadManager.Core.Services;
|
||||||
|
|
||||||
|
public interface IScheduleService
|
||||||
|
{
|
||||||
|
Task<IEnumerable<ScheduleJob>> GetJobsAsync();
|
||||||
|
Task SaveJobAsync(ScheduleJob job);
|
||||||
|
Task DeleteJobAsync(int id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScheduleService : IScheduleService
|
||||||
|
{
|
||||||
|
private readonly IScheduleRepository _repository;
|
||||||
|
public ScheduleService(IScheduleRepository repository) => _repository = repository;
|
||||||
|
|
||||||
|
public Task<IEnumerable<ScheduleJob>> GetJobsAsync() => _repository.GetAllAsync();
|
||||||
|
|
||||||
|
public async Task SaveJobAsync(ScheduleJob job)
|
||||||
|
{
|
||||||
|
if (job.Id == 0) await _repository.AddAsync(job);
|
||||||
|
else await _repository.UpdateAsync(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DeleteJobAsync(int id) => _repository.DeleteAsync(id);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user