ilk commit

This commit is contained in:
hOLOlu
2026-05-04 01:19:04 +03:00
commit 5f33557f2d
2072 changed files with 75437 additions and 0 deletions

View File

@@ -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>

View 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");
}
}
}

View File

@@ -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"
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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.

View 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]/"
]
}

View 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/"
]
}

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@@ -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.

View File

@@ -0,0 +1 @@
000d6a996873011518a2be58f132c59662b6ff352ca8842e6f1fa1c31b20e4f1

View File

@@ -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 =

View File

@@ -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;

View File

@@ -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"
}
}
}
}
}

View File

@@ -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>

View File

@@ -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" />

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@@ -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.

View File

@@ -0,0 +1 @@
89d0fd93dd3e67ad8186e0c515a8d65ebbbc94026391ff8e88db296a467ee7a3

View File

@@ -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 =

View File

@@ -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;

View File

@@ -0,0 +1 @@
9a48f8efc256d5e9f4c6bcb351f545b3f8de09f2663f420d4e646e2dde80a99c

View File

@@ -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

View File

@@ -0,0 +1 @@
f69caf1b77b5ac1742efde949e911c38081902650a9646ddb4fb0df3c6723dd0

View 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"
}
}
}
}

View 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": []
}

View 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"

View File

@@ -0,0 +1,6 @@
namespace DownloadManager.Core;
public class Class1
{
}

View 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 = "" }
);
}
}

View 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);
}
}

View 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
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View 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);
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View 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>

View 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();
}
}
}

View 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)";
}

View 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);
}

View 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");
}

View 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);
}
}
}

View 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
});
}
}
}

View 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;
}
}

View File

@@ -0,0 +1,8 @@
namespace DownloadManager.Core.Enums;
public enum AppTheme
{
Light,
Dark,
System
}

View File

@@ -0,0 +1,12 @@
namespace DownloadManager.Core.Enums;
public enum DownloadStatus
{
Pending,
Queued,
Downloading,
Paused,
Completed,
Error,
Cancelled
}

View File

@@ -0,0 +1,9 @@
namespace DownloadManager.Core.Enums;
public enum ProxyType
{
None,
Http,
Socks4,
Socks5
}

View File

@@ -0,0 +1,9 @@
namespace DownloadManager.Core.Enums;
public enum SegmentStatus
{
Pending,
Downloading,
Completed,
Failed
}

View 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; }
}

View 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; }
}
}

View 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;
}

View 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
}

View 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>();
}

View 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;
}

View File

@@ -0,0 +1,6 @@
namespace DownloadManager.Core.Models;
public class GrabberProject
{
public int Id { get; set; }
}

View 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; }
}

View 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);
}
}

View File

@@ -0,0 +1,8 @@
using DownloadManager.Core.Models;
namespace DownloadManager.Core.Protocols;
public interface IDownloadProtocol
{
Task<DownloadItem> GetFileInfoAsync(string url, CancellationToken ct);
}

View 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();
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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();
}

View 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);
}
}

View 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);
}

View File

@@ -0,0 +1,113 @@
using DownloadManager.Core.Models;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace DownloadManager.Core.Services;
public class SchedulerWatchdog
{
private readonly IScheduleService _scheduleService;
private readonly IDownloadService _downloadService;
private Timer? _timer;
private bool _isProcessing;
public SchedulerWatchdog(IScheduleService scheduleService, IDownloadService downloadService)
{
_scheduleService = scheduleService;
_downloadService = downloadService;
}
public void Start()
{
// Her 30 saniyede bir kontrol et
_timer = new Timer(async _ => await CheckScheduleAsync(), null, TimeSpan.Zero, TimeSpan.FromSeconds(30));
Serilog.Log.Information("Zamanlayıcı İzleyici (Watchdog) başlatıldı.");
}
public void Stop()
{
_timer?.Dispose();
Serilog.Log.Information("Zamanlayıcı İzleyici durduruldu.");
}
private async Task CheckScheduleAsync()
{
if (_isProcessing) return;
_isProcessing = true;
try
{
var jobs = await _scheduleService.GetJobsAsync();
var activeJob = jobs.FirstOrDefault(j => j.IsActive);
if (activeJob == null) return;
var now = DateTime.Now;
var currentTime = now.TimeOfDay;
// Gün kontrolü
int dayIndex = (int)now.DayOfWeek; // 0=Pazar, 1=Pazartesi...
// Model Pt=0, Pz=6 şeklinde varsayılmıştı (1111111)
// .NET DayOfWeek: Sunday=0, Monday=1...
// Dönüştürelim: Pt=0 için
int adjustedIndex = dayIndex == 0 ? 6 : dayIndex - 1;
if (activeJob.DaysOfWeek[adjustedIndex] == '0') return;
// Başlatma kontrolü
if (activeJob.StartTime.HasValue)
{
var startTime = activeJob.StartTime.Value.TimeOfDay;
// Başlangıç vaktinden sonraki 1 dakika içindeysek başlat (sürekli tetiklenmemesi için)
if (currentTime >= startTime && currentTime < startTime.Add(TimeSpan.FromMinutes(1)))
{
await StartScheduledDownloads();
}
}
// Durdurma kontrolü
if (activeJob.EndTime.HasValue)
{
var endTime = activeJob.EndTime.Value.TimeOfDay;
if (currentTime >= endTime && currentTime < endTime.Add(TimeSpan.FromMinutes(1)))
{
await StopAllDownloads();
}
}
}
catch (Exception ex)
{
Serilog.Log.Error(ex, "Zamanlayıcı kontrolü sırasında hata oluştu.");
}
finally
{
_isProcessing = false;
}
}
private async Task StartScheduledDownloads()
{
Serilog.Log.Information("Zamanlayıcı: İndirmeler başlatılıyor...");
var downloads = await _downloadService.GetAllDownloadsAsync();
var pending = downloads.Where(d => d.Status == Enums.DownloadStatus.Paused || d.Status == Enums.DownloadStatus.Pending);
foreach (var item in pending)
{
await _downloadService.ResumeDownloadAsync(item.Id);
}
}
private async Task StopAllDownloads()
{
Serilog.Log.Information("Zamanlayıcı: İndirmeler durduruluyor...");
var downloads = await _downloadService.GetAllDownloadsAsync();
var active = downloads.Where(d => d.Status == Enums.DownloadStatus.Downloading || d.Status == Enums.DownloadStatus.Queued);
foreach (var item in active)
{
await _downloadService.PauseDownloadAsync(item.Id);
}
}
}

View File

@@ -0,0 +1,22 @@
using DownloadManager.Core.Data.Repositories;
using System.Threading.Tasks;
namespace DownloadManager.Core.Services;
public interface ISettingsService
{
Task<string?> GetAsync(string key, string? defaultValue = null);
Task SetAsync(string key, string value);
}
public class SettingsService : ISettingsService
{
private readonly ISettingsRepository _repository;
public SettingsService(ISettingsRepository repository) => _repository = repository;
public async Task<string?> GetAsync(string key, string? defaultValue = null)
=> await _repository.GetValueAsync(key) ?? defaultValue;
public Task SetAsync(string key, string value)
=> _repository.SetValueAsync(key, value);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
"configProperties": {
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

View File

@@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@@ -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.Core")]
[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.Core")]
[assembly: System.Reflection.AssemblyTitleAttribute("DownloadManager.Core")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// MSBuild WriteCodeFragment sınıfı tarafından oluşturuldu.

View File

@@ -0,0 +1 @@
46bf396899510cb77858921f8a30d4ed1bbe8d1ebf3aa0a92a7b12cea57c65d4

View File

@@ -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.Core
build_property.ProjectDir = D:\Calismalar\ai\hDM\DownloadManager\src\DownloadManager.Core\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 8.0
build_property.EnableCodeStyleSeverity =

View File

@@ -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;

View File

@@ -0,0 +1 @@
4d79c1e07d3393b6086287484da8294d3710161dbd1296f2efebb760c1a85310

Some files were not shown because too many files have changed in this diff Show More