diff --git a/.editorconfig b/.editorconfig
index 9ac4f101b7..5bfd224680 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -45,7 +45,7 @@ csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_within_query_expression_clauses = true
csharp_wrap_before_eq = false
place_attribute_on_same_line = "never"
-csharp_style_namespace_declarations = file_scoped:warning
+# csharp_style_namespace_declarations = file_scoped:warning
# Indentation preferences
csharp_indent_block_contents = true
@@ -65,3 +65,10 @@ dotnet_code_quality_unused_parameters = all:warning
dotnet_remove_unnecessary_suppression_exclusions = none:warning
dotnet_style_namespace_match_folder = true:suggestion
+
+# unused usings
+dotnet_diagnostic.IDE0005.severity = warning
+
+# CS4014: Because this call is not awaited, execution of the current method continues before the call is completed.
+# Consider applying the 'await' operator to the result of the call.
+dotnet_diagnostic.CS4014.severity = error
\ No newline at end of file
diff --git a/documentation/Program.cs b/documentation/Program.cs
index b5db76754b..0e2886a294 100644
--- a/documentation/Program.cs
+++ b/documentation/Program.cs
@@ -1,20 +1,19 @@
using System.Diagnostics;
-static string GetSolutionParentFolder()
+static string GetSolutionParentFolder()
{
var strExeFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
- return Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(strExeFilePath))))!;
+ return Path.GetDirectoryName(
+ Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(strExeFilePath))))!;
}
static Process NpmCommand(string command)
{
- var startInfo = new ProcessStartInfo()
+ var startInfo = new ProcessStartInfo
{
- FileName = "npm",
- Arguments = command,
- WorkingDirectory = GetSolutionParentFolder()
- };
- var proc = new Process() { StartInfo = startInfo };
+ FileName = "npm", Arguments = command, WorkingDirectory = GetSolutionParentFolder()
+ };
+ var proc = new Process { StartInfo = startInfo };
return proc;
}
diff --git a/documentation/docs/developer-guide/contributing/SECURITY.md b/documentation/docs/developer-guide/contributing/SECURITY.md
index 0f91680a0d..fbcebc8a9e 100644
--- a/documentation/docs/developer-guide/contributing/SECURITY.md
+++ b/documentation/docs/developer-guide/contributing/SECURITY.md
@@ -7,7 +7,8 @@ Breaking changes are reported in the [changelog/history](https://docs.qdraw.nl/d
| Version | Supported |
|---------|--------------------|
-| 0.5.x | :white_check_mark: |
+| 0.6.x | :white_check_mark: |
+| 0.5.x | :x: |
| 0.4.x | :x: |
| < 0.3 | :x: |
diff --git a/documentation/static/openapi/openapi.json b/documentation/static/openapi/openapi.json
index 184577234e..39a4ca64d6 100644
--- a/documentation/static/openapi/openapi.json
+++ b/documentation/static/openapi/openapi.json
@@ -4501,39 +4501,6 @@
"parameters": [],
"extensions": {}
},
- "/api/health/application-insights": {
- "operations": {
- "Get": {
- "tags": [
- {
- "name": "Health",
- "extensions": {},
- "unresolvedReference": false
- }
- ],
- "summary": "Add Application Insights script to user context",
- "parameters": [],
- "responses": {
- "200": {
- "description": "Ok",
- "headers": {},
- "content": {},
- "links": {},
- "extensions": {},
- "unresolvedReference": false
- }
- },
- "callbacks": {},
- "deprecated": false,
- "security": [],
- "servers": [],
- "extensions": {}
- }
- },
- "servers": [],
- "parameters": [],
- "extensions": {}
- },
"/api/health/version": {
"operations": {
"Post": {
@@ -11856,54 +11823,6 @@
"extensions": {},
"unresolvedReference": false
},
- "applicationInsightsConnectionString": {
- "type": "string",
- "readOnly": false,
- "writeOnly": false,
- "allOf": [],
- "oneOf": [],
- "anyOf": [],
- "required": [],
- "properties": {},
- "additionalPropertiesAllowed": true,
- "enum": [],
- "nullable": true,
- "deprecated": false,
- "extensions": {},
- "unresolvedReference": false
- },
- "applicationInsightsLog": {
- "type": "boolean",
- "readOnly": false,
- "writeOnly": false,
- "allOf": [],
- "oneOf": [],
- "anyOf": [],
- "required": [],
- "properties": {},
- "additionalPropertiesAllowed": true,
- "enum": [],
- "nullable": true,
- "deprecated": false,
- "extensions": {},
- "unresolvedReference": false
- },
- "applicationInsightsDatabaseTracking": {
- "type": "boolean",
- "readOnly": false,
- "writeOnly": false,
- "allOf": [],
- "oneOf": [],
- "anyOf": [],
- "required": [],
- "properties": {},
- "additionalPropertiesAllowed": true,
- "enum": [],
- "nullable": true,
- "deprecated": false,
- "extensions": {},
- "unresolvedReference": false
- },
"maxDegreesOfParallelism": {
"type": "integer",
"format": "int32",
diff --git a/pipelines/azure/app-ci.yml b/pipelines/azure/app-ci.yml
index 7d647b512f..b6fce7bb42 100644
--- a/pipelines/azure/app-ci.yml
+++ b/pipelines/azure/app-ci.yml
@@ -62,6 +62,7 @@ stages:
- template: /pipelines/azure/steps/build_server.yml
parameters:
vstest: false
+ readyToRunEnabled: true
runtimeArg: '--runtime "win-x64,linux-x64,osx-x64,osx-arm64"'
nugetPackages: $(NUGET_PACKAGES)
diff --git a/pipelines/azure/steps/build_server.yml b/pipelines/azure/steps/build_server.yml
index 251e65153f..c43cd2bcd7 100644
--- a/pipelines/azure/steps/build_server.yml
+++ b/pipelines/azure/steps/build_server.yml
@@ -6,6 +6,9 @@ parameters:
nugetPackages: $(Pipeline.Workspace)/.nuget/packages
steps:
+ - template: /pipelines/azure/steps/cache_nuget_packages.yml
+ - template: /pipelines/azure/steps/cache_dependencies.yml
+
- task: DotNetCoreCLI@2
displayName: "[Nuke/server] dotnet tool restore"
continueOnError: true
@@ -14,11 +17,8 @@ steps:
custom: tool
arguments: "restore --tool-manifest starsky/.config/dotnet-tools.json"
- - template: /pipelines/azure/steps/cache_nuget_packages.yml
- - template: /pipelines/azure/steps/cache_dependencies.yml
-
- task: PowerShell@2
- displayName: "Nuke SonarBuildTest (with tests) "
+ displayName: "Nuke SonarBuildTest (with tests)"
condition: and(succeeded(), eq('${{ parameters.vstest }}', true) )
env:
BUILD_SOURCEBRANCH: $(Build.SourceBranch)
diff --git a/pipelines/azure/steps/use_dotnet_version.yml b/pipelines/azure/steps/use_dotnet_version.yml
index 26c0d4f540..83c2e853a2 100644
--- a/pipelines/azure/steps/use_dotnet_version.yml
+++ b/pipelines/azure/steps/use_dotnet_version.yml
@@ -1,6 +1,6 @@
steps:
- task: UseDotNet@2
- displayName: 'Use .NET Core sdk 8.0.101'
+ displayName: 'Use .NET SDK 8.0.101'
enabled: true
inputs:
packageType: sdk
diff --git a/starsky-tools/build-tools/dotnet-sdk-version-update.js b/starsky-tools/build-tools/dotnet-sdk-version-update.js
index e50e83c176..90af0cb8fb 100644
--- a/starsky-tools/build-tools/dotnet-sdk-version-update.js
+++ b/starsky-tools/build-tools/dotnet-sdk-version-update.js
@@ -419,7 +419,7 @@ async function updateAzureYmlFile(filePathList, sdkVersion) {
fileContent = fileContent.replace(
displayNameTagRegex,
displayNameTag +
- "'Use .NET Core sdk " +
+ "'Use .NET SDK " +
sdkVersion +
"'"
);
diff --git a/starsky/Directory.Build.props b/starsky/Directory.Build.props
index 4e2a2566b8..4bf5014ddb 100644
--- a/starsky/Directory.Build.props
+++ b/starsky/Directory.Build.props
@@ -1,9 +1,15 @@
-
- $(OverwriteRuntimeIdentifier)
- true
- "Copyright (c) 2018 - $([System.DateTime]::Now.ToString(`yyyy`)) Qdraw and other authors"
- Qdraw and contributors
- Qdraw
-
+
+ $(OverwriteRuntimeIdentifier)
+ true
+ "Copyright (c) 2018 - $([System.DateTime]::Now.ToString(`yyyy`)) Qdraw and other authors"
+ Qdraw and contributors
+ Qdraw
+
+
+ true
+ $(NoWarn);CS1591;CS1573
+ true
+
+
\ No newline at end of file
diff --git a/starsky/build/Build.cs b/starsky/build/Build.cs
index 0d90de1b60..6ba7f2bb53 100644
--- a/starsky/build/Build.cs
+++ b/starsky/build/Build.cs
@@ -64,7 +64,7 @@ public sealed class Build : NukeBuild
/// Nuget & NPM dependencies are always installed
///
[Parameter("Skip Dependencies download e.g. exiftool / " +
- "geo data, nuget/npm deps are always installed")]
+ "geo data, nuget/npm deps are always installed")]
readonly bool NoDependencies;
///
@@ -94,7 +94,7 @@ bool IsPublishDisabled()
///
/// only if overwritten
[Parameter("Overwrite branch name")]
- readonly string Branch;
+ readonly string Branch = string.Empty;
///
/// Overwrite Branch name
@@ -104,7 +104,7 @@ string GetBranchName()
{
var branchName = Branch;
if ( !string.IsNullOrEmpty(branchName) &&
- branchName.StartsWith("refs/heads/") )
+ branchName.StartsWith("refs/heads/") )
{
branchName = branchName.Replace("refs/heads/", "");
}
@@ -147,7 +147,7 @@ List GetRuntimesWithoutGeneric()
/// Solution .sln file
///
[Solution(SuppressBuildProjectCheck = true)]
- readonly Solution Solution;
+ readonly Solution Solution = new();
///
/// List of output projects
@@ -236,7 +236,7 @@ void ShowSettingsInfo()
$"Current RID: {RuntimeIdentifier.GetCurrentRuntimeIdentifier()}");
Log.Information("SolutionParentFolder: " +
- WorkingDirectory.GetSolutionParentFolder());
+ WorkingDirectory.GetSolutionParentFolder());
Log.Information(NoClient
? "Client is: disabled"
@@ -251,8 +251,8 @@ void ShowSettingsInfo()
: "Publish: enabled");
Log.Information(NoSonar ||
- string.IsNullOrEmpty(SonarQube.GetSonarKey()) ||
- string.IsNullOrEmpty(SonarQube.GetSonarToken())
+ string.IsNullOrEmpty(SonarQube.GetSonarKey()) ||
+ string.IsNullOrEmpty(SonarQube.GetSonarToken())
? "Sonarcloud scan: disabled"
: "Sonarcloud scan: enabled");
@@ -311,7 +311,7 @@ void ShowSettingsInfo()
Configuration);
DotnetTestHelper.TestNetCoreGenericCommand(Configuration,
IsUnitTestDisabled());
- DotnetGenericHelper.DownloadDependencies(Configuration, GeoCliCsproj,
+ DotnetGenericHelper.DownloadDependencies(Configuration, GeoCliCsproj,
NoDependencies, GenericRuntimeName);
MergeCoverageFiles.Merge(IsUnitTestDisabled());
SonarQube.SonarEnd(IsUnitTestDisabled(), NoSonar);
diff --git a/starsky/build/_build.csproj b/starsky/build/_build.csproj
index 03c5c7511c..44aea6ec99 100644
--- a/starsky/build/_build.csproj
+++ b/starsky/build/_build.csproj
@@ -1,35 +1,35 @@
-
- Exe
- net8.0
-
- CS0649;CS0169
- ..
- ..
- 1
-
- true
-
+
+ Exe
+ net8.0
+
+ CS0649;CS0169
+ ..
+ ..
+ 1
+ enable
+ {b0a4bfd3-6321-4962-a15c-142aace9a4c9}
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
+
+
+
+
-
-
-
-
diff --git a/starsky/build/helpers/ClientHelper.cs b/starsky/build/helpers/ClientHelper.cs
index a74c81b3fb..be47afe6d6 100644
--- a/starsky/build/helpers/ClientHelper.cs
+++ b/starsky/build/helpers/ClientHelper.cs
@@ -6,7 +6,7 @@
namespace helpers
{
-
+
public static class ClientHelper
{
public static string GetClientAppFolder()
@@ -20,26 +20,26 @@ public static string GetClientAppFolder()
throw new DirectoryNotFoundException("rootDirectory is null, this is wrong");
return Path.Combine(rootDirectory, ClientAppFolder);
}
-
+
public static void NpmPreflight()
{
Log.Information("Checking if Npm (and implicit: Node) is installed, will fail if not on this step");
Run(NpmBaseCommand, "-v");
-
+
Log.Information("Checking if Node is installed, will fail if not on this step");
Run(NodeBaseCommand, "-v");
- }
-
+ }
+
public static void ClientCiCommand()
{
Run(NpmBaseCommand, "ci --legacy-peer-deps --prefer-offline --no-audit --no-fund", GetClientAppFolder());
}
-
+
public static void ClientBuildCommand()
{
Run(NpmBaseCommand, "run build", GetClientAppFolder());
}
-
+
public static void ClientTestCommand()
{
Run(NpmBaseCommand, "run test:ci", GetClientAppFolder());
diff --git a/starsky/build/helpers/CoverageReportHelper.cs b/starsky/build/helpers/CoverageReportHelper.cs
index 0bf2d85bdc..4e107f02f6 100644
--- a/starsky/build/helpers/CoverageReportHelper.cs
+++ b/starsky/build/helpers/CoverageReportHelper.cs
@@ -4,7 +4,6 @@
namespace helpers
{
-
public static class CoverageReportHelper
{
static void Information(string value)
@@ -12,14 +11,14 @@ static void Information(string value)
Log.Information(value);
}
- public static string GenerateHtml(bool noUnitTest)
+ public static string? GenerateHtml(bool noUnitTest)
{
if ( noUnitTest )
{
Information(">> MergeCoverageFiles is disable due the --no-unit-test flag");
return null;
}
-
+
Information(">> Next: MergeCoverageFiles ");
var rootDirectory = Directory.GetParent(AppDomain.CurrentDomain
@@ -38,17 +37,15 @@ public static string GenerateHtml(bool noUnitTest)
var args = new[]
{
- $"-reports:{outputCoverageFile}",
- $"-targetdir:{reportFolder}",
+ $"-reports:{outputCoverageFile}", $"-targetdir:{reportFolder}",
"-reporttypes:HtmlInline"
};
Palmmedia.ReportGenerator.Core.Program.Main(args);
-
+
Information(">> ReportGenerator done");
return reportFolder;
}
-
}
}
diff --git a/starsky/build/helpers/DotnetGenericHelper.cs b/starsky/build/helpers/DotnetGenericHelper.cs
index 38105987c1..e23212ec2a 100644
--- a/starsky/build/helpers/DotnetGenericHelper.cs
+++ b/starsky/build/helpers/DotnetGenericHelper.cs
@@ -122,7 +122,7 @@ public static void PublishNetCoreGenericCommand(Configuration configuration,
static string BasePath()
{
return Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory)
- ?.Parent?.Parent?.Parent?.FullName;
+ ?.Parent?.Parent?.Parent?.FullName!;
}
}
}
diff --git a/starsky/build/helpers/DotnetRuntimeSpecificHelper.cs b/starsky/build/helpers/DotnetRuntimeSpecificHelper.cs
index f9ddc80a76..568eed030e 100644
--- a/starsky/build/helpers/DotnetRuntimeSpecificHelper.cs
+++ b/starsky/build/helpers/DotnetRuntimeSpecificHelper.cs
@@ -20,7 +20,7 @@ public static class DotnetRuntimeSpecificHelper
static string BasePath()
{
return Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory)
- ?.Parent?.Parent?.Parent?.FullName;
+ ?.Parent?.Parent?.Parent?.FullName!;
}
public static void Clean(List runtimesWithoutGeneric)
@@ -37,7 +37,7 @@ public static void Clean(List runtimesWithoutGeneric)
var runtimeZip = $"{ZipperHelper.ZipPrefix}{runtime}.zip";
Log.Information("\tRuntimeZip: " + runtimeZip + " exists: " +
- File.Exists(runtimeZip));
+ File.Exists(runtimeZip));
if ( File.Exists(runtimeZip) )
{
File.Delete(runtimeZip);
@@ -95,7 +95,7 @@ public static void CopyDependenciesFiles(bool noDependencies,
// For Windows its not needed to copy unix dependencies
if ( runtime.StartsWith("win") &&
- Directory.Exists(Path.Combine(runtimeTempFolder, "exiftool-unix")) )
+ Directory.Exists(Path.Combine(runtimeTempFolder, "exiftool-unix")) )
{
Directory.Delete(Path.Combine(runtimeTempFolder, "exiftool-unix"), true);
Log.Information("removed exiftool-unix for windows");
@@ -103,7 +103,7 @@ public static void CopyDependenciesFiles(bool noDependencies,
// ReSharper disable once InvertIf
if ( runtime.StartsWith("win") &&
- File.Exists(Path.Combine(runtimeTempFolder, "exiftool.tar.gz")) )
+ File.Exists(Path.Combine(runtimeTempFolder, "exiftool.tar.gz")) )
{
File.Delete(Path.Combine(runtimeTempFolder, "exiftool.tar.gz"));
Log.Information("removed exiftool.tar.gz for windows");
@@ -123,7 +123,7 @@ public static void BuildNetCoreCommand(Solution solution, Configuration
configuration, string runtime, bool isReadyToRunEnabled)
{
Log.Information("> dotnet build next for: solution: " + solution + " runtime: " +
- runtime);
+ runtime);
var readyToRunArgument =
RuntimeIdentifier.IsReadyToRunSupported(runtime) && isReadyToRunEnabled
@@ -166,7 +166,7 @@ public static void PublishNetCoreGenericCommand(Configuration configuration,
foreach ( var publishProject in Build.PublishProjectsList )
{
Log.Information(">> next publishProject: " +
- publishProject + " runtime: " + runtime);
+ publishProject + " runtime: " + runtime);
var publishProjectFullPath = Path.Combine(
WorkingDirectory.GetSolutionParentFolder(),
diff --git a/starsky/build/helpers/DotnetTestHelper.cs b/starsky/build/helpers/DotnetTestHelper.cs
index 1d753df7de..4a32d729f2 100644
--- a/starsky/build/helpers/DotnetTestHelper.cs
+++ b/starsky/build/helpers/DotnetTestHelper.cs
@@ -24,39 +24,39 @@ static bool DirectoryExists(string path)
public static void TestNetCoreGenericCommand(Configuration configuration, bool noUnitTest)
{
Information(">> next: TestNetCoreGenericCommand");
-
- if(noUnitTest)
+
+ if ( noUnitTest )
{
Information($">> TestNetCore is disable due the --no-unit-test flag");
return;
}
-
+
var projects = GetFilesHelper.GetFiles("*test/*.csproj");
if ( projects.Count == 0 )
{
- throw new FileNotFoundException("missing tests in *test/*.csproj" );
+ throw new FileNotFoundException("missing tests in *test/*.csproj");
}
-
- foreach(var project in projects)
+
+ foreach ( var project in projects )
{
var projectFullPath = Path.Combine(WorkingDirectory.GetSolutionParentFolder(),
project);
Information("Testing project " + project);
- var testParentPath = Directory.GetParent(projectFullPath)?.FullName;
+ var testParentPath = Directory.GetParent(projectFullPath)?.FullName!;
Information("testParentPath " + testParentPath);
/* clean test results */
- var testResultsFolder = Path.Combine(testParentPath!, "TestResults");
- if (DirectoryExists(testResultsFolder))
+ var testResultsFolder = Path.Combine(testParentPath, "TestResults");
+ if ( DirectoryExists(testResultsFolder) )
{
Information(">> Removing folder => " + testResultsFolder);
- Directory.Delete(testResultsFolder,true);
+ Directory.Delete(testResultsFolder, true);
}
-
+
var runSettingsFile = Path.Combine(
WorkingDirectory.GetSolutionParentFolder(), "build.vstest.runsettings");
-
+
Log.Information("runSettingsFile " + runSettingsFile);
try
@@ -66,7 +66,7 @@ public static void TestNetCoreGenericCommand(Configuration configuration, bool n
.SetConfiguration(configuration)
.EnableNoRestore()
.EnableNoBuild()
- .SetVerbosity(DotNetVerbosity.Normal)
+ .SetVerbosity(DotNetVerbosity.normal)
.SetLoggers("trx;LogFileName=test_results.trx")
.SetDataCollector("XPlat Code Coverage")
.SetSettingsFile(runSettingsFile)
@@ -83,7 +83,6 @@ public static void TestNetCoreGenericCommand(Configuration configuration, bool n
throw;
}
-
var coverageEnum = GetFilesHelper.GetFiles("**/coverage.opencover.xml");
foreach ( var coverageItem in coverageEnum )
@@ -92,16 +91,18 @@ public static void TestNetCoreGenericCommand(Configuration configuration, bool n
}
// Get the FirstOrDefault() but there is no LINQ here
- var coverageFilePath = Path.Combine(testParentPath, "netcore-coverage.opencover.xml");
+ var coverageFilePath =
+ Path.Combine(testParentPath, "netcore-coverage.opencover.xml");
Information("next copy: coverageFilePath " + coverageFilePath);
- foreach(var item in coverageEnum)
+ foreach ( var item in coverageEnum )
{
- CopyFile(Path.Combine(WorkingDirectory.GetSolutionParentFolder(), item),
+ CopyFile(Path.Combine(WorkingDirectory.GetSolutionParentFolder(), item),
coverageFilePath, FileExistsPolicy.Overwrite);
}
- if (!FileExists(coverageFilePath)) {
+ if ( !FileExists(coverageFilePath) )
+ {
throw new FileNotFoundException("CoverageFile missing " + coverageFilePath);
}
}
@@ -111,5 +112,5 @@ static bool FileExists(string path)
{
return File.Exists(path);
}
- }
+ }
}
diff --git a/starsky/build/helpers/GetFilesHelper.cs b/starsky/build/helpers/GetFilesHelper.cs
index 6184f8ed0e..c579098608 100644
--- a/starsky/build/helpers/GetFilesHelper.cs
+++ b/starsky/build/helpers/GetFilesHelper.cs
@@ -11,7 +11,7 @@ public static class GetFilesHelper
public static List GetFiles(string globSearch)
{
Matcher matcher = new();
- matcher.AddIncludePatterns( new List{globSearch});
+ matcher.AddIncludePatterns(new List { globSearch });
var result = matcher.Execute(
new DirectoryInfoWrapper(
new DirectoryInfo(WorkingDirectory.GetSolutionParentFolder())));
diff --git a/starsky/build/helpers/HttpQuery.cs b/starsky/build/helpers/HttpQuery.cs
index ed6bc7dafc..7bb478d542 100644
--- a/starsky/build/helpers/HttpQuery.cs
+++ b/starsky/build/helpers/HttpQuery.cs
@@ -4,17 +4,13 @@
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
-using JetBrains.Annotations;
using Serilog;
-
namespace helpers;
public static class HttpQuery
{
-
- [ItemCanBeNull]
- public static async Task GetJsonFromApi(string apiUrl)
+ public static async Task GetJsonFromApi(string apiUrl)
{
try
{
@@ -28,27 +24,26 @@ public static async Task GetJsonFromApi(string apiUrl)
// Read the content as a string
return await response.Content.ReadAsStringAsync();
}
- catch ( HttpRequestException exception)
+ catch ( HttpRequestException exception )
{
Log.Information($"GetJsonFromApi {exception.StatusCode} {exception.Message}");
return null;
}
}
-
+
public static Version ParseJsonVersionNumbers(string json)
{
var jsonDocument = JsonDocument.Parse(json);
var rootElement = jsonDocument.RootElement;
var versionsResult = new List();
- if (rootElement.TryGetProperty("versions", out var versionsElement))
+ if ( rootElement.TryGetProperty("versions", out var versionsElement) )
{
- versionsResult = versionsElement.EnumerateArray().Select(v => v.GetString()).ToList();
+ versionsResult = versionsElement.EnumerateArray().Select(v => v.GetString())
+ .Cast().ToList();
}
- var hightestVersion = versionsResult.MaxBy(v => new Version(v));
+ var hightestVersion = versionsResult.MaxBy(v => new Version(v))!;
return new Version(hightestVersion);
}
}
-
-
diff --git a/starsky/build/helpers/MergeCoverageFiles.cs b/starsky/build/helpers/MergeCoverageFiles.cs
index fa29cf37a2..fc655ddac8 100644
--- a/starsky/build/helpers/MergeCoverageFiles.cs
+++ b/starsky/build/helpers/MergeCoverageFiles.cs
@@ -10,90 +10,97 @@ static void Information(string value)
{
Log.Information(value);
}
-
+
static bool FileExists(string value)
{
return File.Exists(value);
}
-
+
static void DeleteFile(string value)
{
File.Delete(value);
}
-
+
static void MoveFile(string from, string to)
{
- File.Move(from,to, true);
+ File.Move(from, to, true);
}
-
+
static void CopyFile(string from, string to)
{
- File.Copy(from,to, true);
+ File.Copy(from, to, true);
}
-
+
public static void Merge(bool noUnitTest)
{
var rootDirectory = Directory.GetParent(AppDomain.CurrentDomain
.BaseDirectory!)!.Parent!.Parent!.Parent!.FullName;
-
- if(noUnitTest)
+
+ if ( noUnitTest )
{
Information($">> MergeCoverageFiles is disable due the --no-unit-test flag");
return;
}
var jestCoverageFile = Path.Combine(rootDirectory, "starsky/clientapp/coverage/cobertura-coverage.xml");
- if (! FileExists(jestCoverageFile)) {
+ if ( !FileExists(jestCoverageFile) )
+ {
throw new FileNotFoundException($"Missing jest coverage file {jestCoverageFile}");
}
var netCoreCoverageFile = Path.Combine(rootDirectory, "starskytest/netcore-coverage.opencover.xml");
- if (! FileExists(netCoreCoverageFile)) {
+ if ( !FileExists(netCoreCoverageFile) )
+ {
throw new FileNotFoundException($"Missing .NET coverage file ${netCoreCoverageFile}");
}
- var outputCoverageFile = Path.Combine(rootDirectory,"starskytest/coverage-merge-cobertura.xml");
+ var outputCoverageFile = Path.Combine(rootDirectory, "starskytest/coverage-merge-cobertura.xml");
- if (FileExists(outputCoverageFile)) {
+ if ( FileExists(outputCoverageFile) )
+ {
DeleteFile(outputCoverageFile);
}
- var outputCoverageSonarQubeFile = Path.Combine(rootDirectory,"starskytest/coverage-merge-sonarqube.xml");
+ var outputCoverageSonarQubeFile = Path.Combine(rootDirectory, "starskytest/coverage-merge-sonarqube.xml");
- if (FileExists(outputCoverageSonarQubeFile)) {
+ if ( FileExists(outputCoverageSonarQubeFile) )
+ {
DeleteFile(outputCoverageSonarQubeFile);
}
// Gets the coverage file from the client folder
- if (FileExists($"./starsky/clientapp/coverage/cobertura-coverage.xml")) {
+ if ( FileExists($"./starsky/clientapp/coverage/cobertura-coverage.xml") )
+ {
Information($"Copy ./starsky/clientapp/coverage/cobertura-coverage.xml ./starskytest/jest-coverage.cobertura.xml");
CopyFile($"./starsky/clientapp/coverage/cobertura-coverage.xml", $"./starskytest/jest-coverage.cobertura.xml");
}
-
- var args = new []
+
+ var args = new[]
{
$"-reports:{rootDirectory}/starskytest/*coverage.*.xml",
$"-targetdir:{rootDirectory}/starskytest/",
"-reporttypes:Cobertura;SonarQube"
};
-
+
Palmmedia.ReportGenerator.Core.Program.Main(args);
// And rename it
MoveFile($"{rootDirectory}/starskytest/Cobertura.xml", outputCoverageFile);
MoveFile($"{rootDirectory}/starskytest/SonarQube.xml", outputCoverageSonarQubeFile);
-
- if (!FileExists(outputCoverageSonarQubeFile)) {
+
+ if ( !FileExists(outputCoverageSonarQubeFile) )
+ {
throw new FileNotFoundException($"Missing Sonarqube coverage file {outputCoverageSonarQubeFile}");
}
Information($"Sonarqube Coverage file is ready: {outputCoverageSonarQubeFile}");
- if (!FileExists(outputCoverageFile)) {
+ if ( !FileExists(outputCoverageFile) )
+ {
throw new FileNotFoundException($"Missing Cobertura coverage file {outputCoverageFile}");
}
-
+
Information($"Cobertura Coverage file is ready: {outputCoverageFile}");
}
}
-
+
}
diff --git a/starsky/build/helpers/ProjectCheckNetCoreCommandHelper.cs b/starsky/build/helpers/ProjectCheckNetCoreCommandHelper.cs
index 908ab498ac..ac6a69efd8 100644
--- a/starsky/build/helpers/ProjectCheckNetCoreCommandHelper.cs
+++ b/starsky/build/helpers/ProjectCheckNetCoreCommandHelper.cs
@@ -1,11 +1,14 @@
using System;
+using System.Collections.Generic;
using System.IO;
+using System.Text.RegularExpressions;
+using Serilog;
using static SimpleExec.Command;
using static build.Build;
namespace helpers
{
- public static class ProjectCheckNetCoreCommandHelper
+ public static partial class ProjectCheckNetCoreCommandHelper
{
static string GetBuildToolsFolder()
{
@@ -13,29 +16,86 @@ static string GetBuildToolsFolder()
.BaseDirectory;
if ( baseDirectory == null )
throw new DirectoryNotFoundException("base directory is null, this is wrong");
- var slnRootDirectory = Directory.GetParent(baseDirectory)?.Parent?.Parent?.Parent?.Parent?.FullName;
+ var slnRootDirectory = Directory.GetParent(baseDirectory)?.Parent?.Parent?.Parent
+ ?.Parent?.FullName;
if ( slnRootDirectory == null )
throw new DirectoryNotFoundException("slnRootDirectory is null, this is wrong");
return Path.Combine(slnRootDirectory, BuildToolsPath);
}
-
+
+ static void CheckForNullable()
+ {
+ var projects = GetFilesHelper.GetFiles("**.csproj");
+ var missingProjects = new List();
+ foreach ( var project in projects )
+ {
+ var projectFullPath =
+ Path.Combine(WorkingDirectory.GetSolutionParentFolder(), project);
+ var projectContent = File.ReadAllText(projectFullPath);
+
+ if ( !projectContent.Contains("enable") )
+ {
+ missingProjects.Add(project);
+ }
+ }
+
+ if ( missingProjects.Count > 0 )
+ {
+ throw new ArgumentException("Missing enable in: " +
+ string.Join(" , ", missingProjects) + " projects " +
+ "Please add enable to the .csproj files");
+ }
+ }
+
+ static void ProjectGuid()
+ {
+ var projects = GetFilesHelper.GetFiles("**.csproj");
+ var uniqueGuids = new List();
+
+ foreach ( var project in projects )
+ {
+ var projectFullPath =
+ Path.Combine(WorkingDirectory.GetSolutionParentFolder(), project);
+ var projectContent = File.ReadAllText(projectFullPath);
+
+ var fileXmlMatch = ProjectGuidRegex().Match(projectContent);
+
+ if ( !fileXmlMatch.Success )
+ {
+ throw new NotSupportedException($"✖ {project} - No ProjectGuid in file");
+ }
+
+ if ( uniqueGuids.Contains(fileXmlMatch.Groups[0].Value) )
+ {
+ throw new NotSupportedException($"✖ {project} - ProjectGuid is not Unique");
+ }
+
+ uniqueGuids.Add(fileXmlMatch.Groups[0].Value);
+ Log.Information($"✓ {project} - Is Ok");
+ }
+ }
+
public static void ProjectCheckNetCoreCommand()
{
+ CheckForNullable();
+
+ ProjectGuid();
+
ClientHelper.NpmPreflight();
// check branch names on CI
// release-version-check.js triggers app-version-update.js to update the csproj and package.json files
Run(NpmBaseCommand, "run release-version-check", GetBuildToolsFolder());
-
- /* Checks for valid Project GUIDs in csproj files */
- Run(NpmBaseCommand, "run project-guid", GetBuildToolsFolder());
-
+
/* List of nuget packages */
Run(NpmBaseCommand, "run nuget-package-list", GetBuildToolsFolder());
}
const string BuildToolsPath = "starsky-tools/build-tools/";
+
+ [GeneratedRegex(
+ "(){(([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}))}(<\\/ProjectGuid>)",
+ RegexOptions.IgnoreCase, "nl-NL")]
+ private static partial Regex ProjectGuidRegex();
}
}
-
-
diff --git a/starsky/build/helpers/RuntimeIdentifier.cs b/starsky/build/helpers/RuntimeIdentifier.cs
index 985182872a..70976c669b 100644
--- a/starsky/build/helpers/RuntimeIdentifier.cs
+++ b/starsky/build/helpers/RuntimeIdentifier.cs
@@ -13,8 +13,8 @@ public static string GetCurrentRuntimeIdentifier()
{
os = "win";
}
- if ( RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
- RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) )
+ if ( RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
+ RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) )
{
os = "linux";
}
@@ -22,9 +22,9 @@ public static string GetCurrentRuntimeIdentifier()
{
os = "osx";
}
-
+
var architecture = RuntimeInformation.OSArchitecture.ToString().ToLower();
-
+
return $"{os}-{architecture}";
}
@@ -70,7 +70,7 @@ public static string GetCurrentRuntimeIdentifier()
["linux-x64", "linux-arm", "linux-arm64", "osx-x64", "osx-arm64"]
}
};
-
+
public static bool IsReadyToRunSupported(string toRuntimeIdentifier)
{
var currentIdentifier = GetCurrentRuntimeIdentifier();
@@ -79,7 +79,7 @@ public static bool IsReadyToRunSupported(string toRuntimeIdentifier)
static bool IsReadyToRunSupported(string currentIdentifier, string toRuntimeIdentifier)
{
- if (SupportedPlatforms.TryGetValue(currentIdentifier, out var supportedTargets))
+ if ( SupportedPlatforms.TryGetValue(currentIdentifier, out var supportedTargets) )
{
return supportedTargets.Contains(toRuntimeIdentifier);
}
diff --git a/starsky/build/helpers/SonarQube.cs b/starsky/build/helpers/SonarQube.cs
index 0166f90756..bec314f44f 100644
--- a/starsky/build/helpers/SonarQube.cs
+++ b/starsky/build/helpers/SonarQube.cs
@@ -18,33 +18,37 @@ namespace helpers
public static class SonarQube
{
public const string SonarQubePackageName = "dotnet-sonarscanner";
+
///
/// @see: https://www.nuget.org/packages/dotnet-sonarscanner
///
public const string SonarQubePackageVersion = "6.1.0";
-
- public const string SonarQubeDotnetSonarScannerApi = "https://api.nuget.org/v3-flatcontainer/dotnet-sonarscanner/index.json";
+
+ public const string SonarQubeDotnetSonarScannerApi =
+ "https://api.nuget.org/v3-flatcontainer/dotnet-sonarscanner/index.json";
public const string GitCommand = "git";
public const string DefaultBranchName = "master";
public static void InstallSonarTool(bool noUnitTest, bool noSonar)
{
- if(noUnitTest)
+ if ( noUnitTest )
{
Information($">> SonarBegin is disable due the --no-unit-test flag");
return;
}
- if( noSonar ) {
+
+ if ( noSonar )
+ {
Information($">> SonarBegin is disable due the --no-sonar flag");
return;
}
CheckLatestVersionDotNetSonarScanner().Wait();
-
+
var rootDirectory = Directory.GetParent(AppDomain.CurrentDomain
.BaseDirectory!)!.Parent!.Parent!.Parent!.FullName;
-
+
Log.Information(rootDirectory);
var envs =
@@ -52,8 +56,8 @@ public static void InstallSonarTool(bool noUnitTest, bool noSonar)
IReadOnlyDictionary;
var toolList = DotNet($"tool list", rootDirectory, envs, null, true);
- if ( toolList.Any(p => p.Text.Contains(SonarQubePackageName)
- && toolList.Any(p => p.Text.Contains(SonarQubePackageVersion)) ))
+ if ( toolList.Any(p => p.Text.Contains(SonarQubePackageName)
+ && toolList.Any(p => p.Text.Contains(SonarQubePackageVersion))) )
{
Log.Information("Next: tool restore");
DotNet($"tool restore", rootDirectory, envs, null, true);
@@ -72,7 +76,7 @@ public static void InstallSonarTool(bool noUnitTest, bool noSonar)
.SetVersion(SonarQubePackageVersion));
}
- private static string EnvironmentVariable(string input)
+ private static string? EnvironmentVariable(string input)
{
return Environment.GetEnvironmentVariable(input);
}
@@ -88,22 +92,22 @@ static async Task CheckLatestVersionDotNetSonarScanner()
if ( result == null )
{
Log.Information($"Nuget API is not available, " +
- $"so skip checking the latest version of {SonarQubePackageName}");
+ $"so skip checking the latest version of {SonarQubePackageName}");
return;
}
-
+
var latestVersionByApi = HttpQuery.ParseJsonVersionNumbers(result);
if ( latestVersionByApi > new Version(SonarQubePackageVersion) )
{
Log.Warning($"Please upgrade to the latest version " +
- $"of dotnet-sonarscanner {latestVersionByApi} \n\n" +
- "Update the following values: \n" +
- $"- build/helpers/SonarQube.cs -> SonarQubePackageVersion to {latestVersionByApi} \n" +
+ $"of dotnet-sonarscanner {latestVersionByApi} \n\n" +
+ "Update the following values: \n" +
+ $"- build/helpers/SonarQube.cs -> SonarQubePackageVersion to {latestVersionByApi} \n" +
"The _build project will auto update: \n" +
- "- .config/dotnet-tools.json");
+ "- .config/dotnet-tools.json");
}
}
-
+
private static void IsJavaInstalled()
{
@@ -111,46 +115,53 @@ private static void IsJavaInstalled()
Run(Build.JavaBaseCommand, "-version");
}
- public static string GetSonarToken()
+ public static string? GetSonarToken()
{
var sonarToken = EnvironmentVariable("STARSKY_SONAR_TOKEN");
- if( string.IsNullOrEmpty(sonarToken) ) {
+ if ( string.IsNullOrEmpty(sonarToken) )
+ {
sonarToken = EnvironmentVariable("SONAR_TOKEN");
}
+
return sonarToken;
}
- public static string GetSonarKey()
+ public static string? GetSonarKey()
{
return EnvironmentVariable("STARSKY_SONAR_KEY");
}
-
- public static bool SonarBegin(bool noUnitTest, bool noSonar, string branchName, string clientAppProject, string coverageFile)
+
+ public static bool SonarBegin(bool noUnitTest, bool noSonar, string branchName,
+ string clientAppProject, string coverageFile)
{
-
Information($">> SonarQube key={GetSonarKey()}");
-
+
var sonarToken = GetSonarToken();
var organisation = EnvironmentVariable("STARSKY_SONAR_ORGANISATION");
var url = EnvironmentVariable("STARSKY_SONAR_URL");
- if(string.IsNullOrEmpty(url)) {
+ if ( string.IsNullOrEmpty(url) )
+ {
url = "https://sonarcloud.io";
}
- if( string.IsNullOrEmpty(GetSonarKey()) || string.IsNullOrEmpty(sonarToken) || string.IsNullOrEmpty(organisation) ) {
- Information($">> SonarQube is disabled $ key={GetSonarKey()}|token={sonarToken}|organisation={organisation}");
+ if ( string.IsNullOrEmpty(GetSonarKey()) || string.IsNullOrEmpty(sonarToken) ||
+ string.IsNullOrEmpty(organisation) )
+ {
+ Information(
+ $">> SonarQube is disabled $ key={GetSonarKey()}|token={sonarToken}|organisation={organisation}");
return false;
}
- if(noUnitTest)
+ if ( noUnitTest )
{
Information($">> SonarBegin is disable due the --no-unit-test flag");
return false;
}
- if( noSonar ) {
+ if ( noSonar )
+ {
Information($">> SonarBegin is disable due the --no-sonar flag");
return false;
}
@@ -159,36 +170,40 @@ public static bool SonarBegin(bool noUnitTest, bool noSonar, string branchName,
// Current branch name
var mainRepoPath = Directory.GetParent(".")?.FullName;
-
- var (gitBranchName,_) = ReadAsync(GitCommand, " branch --show-current", mainRepoPath!).Result;
+
+ var (gitBranchName, _) =
+ ReadAsync(GitCommand, " branch --show-current", mainRepoPath!).Result;
// allow to overwrite the branch name
- if (string.IsNullOrEmpty(branchName) && !string.IsNullOrEmpty(gitBranchName)) {
+ if ( string.IsNullOrEmpty(branchName) && !string.IsNullOrEmpty(gitBranchName) )
+ {
branchName = gitBranchName.Trim(); // fallback as (no branch)
}
-
+
// replace default value to master
- if (branchName == "(no branch)" || string.IsNullOrEmpty(branchName)) {
+ if ( branchName == "(no branch)" || string.IsNullOrEmpty(branchName) )
+ {
branchName = DefaultBranchName;
}
-
+
/* this should fix No inputs were found in config file 'tsconfig.json'. */
- var tsconfig = Path.Combine(clientAppProject,"tsconfig.json");
+ var tsconfig = Path.Combine(clientAppProject, "tsconfig.json");
Information(">> tsconfig: " + tsconfig);
// For Pull Requests
- var isPrBuild = EnvironmentVariable("GITHUB_ACTIONS") != null &&
- EnvironmentVariable("GITHUB_JOB") != null &&
- EnvironmentVariable("GITHUB_BASE_REF") != null &&
+ var isPrBuild = EnvironmentVariable("GITHUB_ACTIONS") != null &&
+ EnvironmentVariable("GITHUB_JOB") != null &&
+ EnvironmentVariable("GITHUB_BASE_REF") != null &&
!string.IsNullOrEmpty(EnvironmentVariable("PR_NUMBER_GITHUB"));
-
+
var githubPrNumber = EnvironmentVariable("PR_NUMBER_GITHUB");
- var githubBaseBranch = EnvironmentVariable("GITHUB_BASE_REF");
- var githubRepoSlug = EnvironmentVariable("GITHUB_REPOSITORY");
-
+ var githubBaseBranch = EnvironmentVariable("GITHUB_BASE_REF");
+ var githubRepoSlug = EnvironmentVariable("GITHUB_REPOSITORY");
+
Information($">> Selecting Branch: {branchName}");
-
- var sonarQubeCoverageFile = Path.Combine(WorkingDirectory.GetSolutionParentFolder(), coverageFile);
+
+ var sonarQubeCoverageFile =
+ Path.Combine(WorkingDirectory.GetSolutionParentFolder(), coverageFile);
Information(">> SonarQubeCoverageFile: " + sonarQubeCoverageFile);
var sonarArguments = new StringBuilder()
@@ -199,37 +214,39 @@ public static bool SonarBegin(bool noUnitTest, bool noSonar, string branchName,
.Append($"/k:{GetSonarKey()} ")
.Append($"/n:Starsky ")
.Append($"/d:sonar.token={sonarToken} ")
- .Append($"/o:" + organisation +" ")
+ .Append($"/o:" + organisation + " ")
.Append($"/d:sonar.typescript.tsconfigPath={tsconfig} ")
.Append($"/d:sonar.coverageReportPaths={sonarQubeCoverageFile} ")
.Append($"/d:sonar.exclusions=**/build/*,**/build/helpers/*," +
- "**/documentation/*,"+
- "**/Interfaces/IQuery.cs," +
- $"**/setupTests.js,**/react-app-env.d.ts,**/service-worker.ts," +
- $"*webhtmlcli/**/*.js,**/wwwroot/js/**/*,**/*/Migrations/*,**/*spec.tsx," +
- $"**/*stories.tsx,**/*spec.ts,**/src/main.tsx,**/src/index.tsx,**/src/style/css/vendor/*,**/node_modules/*," +
- $"**/prestorybook.js,**/vite.config.ts,**/.storybook/**,**/jest.setup.ts," +
- $"**/_bigimages-helper.js ")
+ "**/documentation/*," +
+ "**/Interfaces/IQuery.cs," +
+ $"**/setupTests.js,**/react-app-env.d.ts,**/service-worker.ts," +
+ $"*webhtmlcli/**/*.js,**/wwwroot/js/**/*,**/*/Migrations/*,**/*spec.tsx," +
+ $"**/*stories.tsx,**/*spec.ts,**/src/main.tsx,**/src/index.tsx,**/src/style/css/vendor/*,**/node_modules/*," +
+ $"**/prestorybook.js,**/vite.config.ts,**/.storybook/**,**/jest.setup.ts," +
+ $"**/_bigimages-helper.js ")
.Append($"/d:sonar.coverage.exclusions=**/build/*,**/build/helpers/*," +
- "**/documentation/*,"+
- "**/Interfaces/IQuery.cs," +
- $"**/setupTests.js,**/react-app-env.d.ts,**/service-worker.ts," +
- $"*webhtmlcli/**/*.js,**/wwwroot/js/**/*,**/*/Migrations/*," +
- $"**/*spec.ts,**/*stories.tsx,**/*spec.tsx,**/src/main.tsx,**/src/index.tsx,**/node_modules/*," +
- $"**/prestorybook.js,**/vite.config.ts,**/.storybook/**,**/jest.setup.ts," +
- $"**/_bigimages-helper.js ");
-
+ "**/documentation/*," +
+ "**/Interfaces/IQuery.cs," +
+ $"**/setupTests.js,**/react-app-env.d.ts,**/service-worker.ts," +
+ $"*webhtmlcli/**/*.js,**/wwwroot/js/**/*,**/*/Migrations/*," +
+ $"**/*spec.ts,**/*stories.tsx,**/*spec.tsx,**/src/main.tsx,**/src/index.tsx,**/node_modules/*," +
+ $"**/prestorybook.js,**/vite.config.ts,**/.storybook/**,**/jest.setup.ts," +
+ $"**/_bigimages-helper.js ");
+
// Normal build
- if (!isPrBuild) {
+ if ( !isPrBuild )
+ {
Information($">> Normal Build (non-pr)");
sonarArguments
.Append($"/d:sonar.branch.name={branchName} ");
}
-
+
// Pull Request Build
- if (isPrBuild) {
+ if ( isPrBuild )
+ {
Information($">> PR Build isPRBuild={true} githubPrNumber " +
- $"{githubPrNumber} githubBaseBranch {githubBaseBranch} githubRepoSlug {githubRepoSlug}");
+ $"{githubPrNumber} githubBaseBranch {githubBaseBranch} githubRepoSlug {githubRepoSlug}");
sonarArguments
.Append($"/d:sonar.pullrequest.key={githubPrNumber} ")
@@ -247,17 +264,20 @@ public static bool SonarBegin(bool noUnitTest, bool noSonar, string branchName,
public static void SonarEnd(bool noUnitTest, bool noSonar)
{
var sonarToken = GetSonarToken();
- if( string.IsNullOrEmpty(sonarToken) ) {
+ if ( string.IsNullOrEmpty(sonarToken) )
+ {
Information($">> SonarQube is disabled $ login={sonarToken}");
return;
}
- if(noUnitTest)
+ if ( noUnitTest )
{
Information($">> SonarEnd is disable due the --no-unit-test flag");
return;
}
- if( noSonar ) {
+
+ if ( noSonar )
+ {
Information($">> SonarEnd is disable due the --no-sonar flag");
return;
}
@@ -268,8 +288,8 @@ public static void SonarEnd(bool noUnitTest, bool noSonar)
.Append($"/d:sonar.token={sonarToken} ");
DotNet(sonarArguments.ToString());
-
+
Log.Information("- - - - - - - - - - Sonar done - - - - - - - - - - \n");
}
- }
+ }
}
diff --git a/starsky/build/helpers/TrxParserHelper.cs b/starsky/build/helpers/TrxParserHelper.cs
index 6e344a7628..2bf89047bc 100644
--- a/starsky/build/helpers/TrxParserHelper.cs
+++ b/starsky/build/helpers/TrxParserHelper.cs
@@ -40,7 +40,7 @@ public static void DisplayFileTests(string trxFullFilePath)
return;
}
- var results = new List>();
+ var results = new List>();
foreach ( var unitTestResult in unitTestResultsList )
{
if ( unitTestResult.Attribute("outcome")?.Value != "Failed" )
@@ -59,7 +59,7 @@ public static void DisplayFileTests(string trxFullFilePath)
?.Element(TeamTestNamespace + "StackTrace")?.Value;
results.Add(
- new Tuple(testName, message,
+ new Tuple(testName, message,
stackTrace));
}
diff --git a/starsky/build/helpers/WorkingDirectory.cs b/starsky/build/helpers/WorkingDirectory.cs
index 44f23b7ee9..30e3f24147 100644
--- a/starsky/build/helpers/WorkingDirectory.cs
+++ b/starsky/build/helpers/WorkingDirectory.cs
@@ -4,9 +4,10 @@ namespace helpers;
public static class WorkingDirectory
{
- public static string GetSolutionParentFolder()
+ public static string GetSolutionParentFolder()
{
var strExeFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
- return Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(strExeFilePath))));
+ return Path.GetDirectoryName(
+ Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(strExeFilePath))))!;
}
}
diff --git a/starsky/build/helpers/ZipperHelper.cs b/starsky/build/helpers/ZipperHelper.cs
index 1479c972a3..6bce0f25b1 100644
--- a/starsky/build/helpers/ZipperHelper.cs
+++ b/starsky/build/helpers/ZipperHelper.cs
@@ -7,11 +7,11 @@
namespace helpers
{
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "S1118:Add a 'protected' constructor " +
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage",
+ "S1118:Add a 'protected' constructor " +
"or the 'static' keyword to the class declaration", Justification = "Not production code.")]
public sealed class ZipperHelper
{
-
public const string ZipPrefix = "starsky-";
static string BasePath()
@@ -19,14 +19,15 @@ static string BasePath()
return Directory.GetParent(AppDomain.CurrentDomain
.BaseDirectory)!.Parent!.Parent!.Parent!.FullName;
}
-
+
public static void ZipGeneric()
{
var fromFolder = Path.Join(BasePath(), Build.GenericRuntimeName);
-
+
if ( !Directory.Exists(fromFolder) )
{
- throw new DirectoryNotFoundException($"dir {Build.GenericRuntimeName} not found {fromFolder}");
+ throw new DirectoryNotFoundException(
+ $"dir {Build.GenericRuntimeName} not found {fromFolder}");
}
var zipPath = Path.Join(BasePath(),
@@ -36,18 +37,18 @@ public static void ZipGeneric()
{
File.Delete(zipPath);
}
-
+
Log.Information($"next: {Build.GenericRuntimeName} zip ~ {fromFolder} -> {zipPath}");
- ZipFile.CreateFromDirectory(fromFolder,
+ ZipFile.CreateFromDirectory(fromFolder,
zipPath);
}
-
+
public static void ZipRuntimes(List getRuntimesWithoutGeneric)
{
if ( getRuntimesWithoutGeneric.Count == 0 )
{
Log.Information("There are no runtime specific items selected\n" +
- "So skip ZipRuntimes");
+ "So skip ZipRuntimes");
return;
}
@@ -57,37 +58,38 @@ public static void ZipRuntimes(List getRuntimesWithoutGeneric)
if ( !Directory.Exists(runtimeFullPath) )
{
- throw new DirectoryNotFoundException($"dir {Build.GenericRuntimeName} not found ~ {runtimeFullPath}");
+ throw new DirectoryNotFoundException(
+ $"dir {Build.GenericRuntimeName} not found ~ {runtimeFullPath}");
}
var zipPath = Path.Join(BasePath(),
ZipPrefix + runtime + ".zip");
-
+
if ( File.Exists(zipPath) )
{
File.Delete(zipPath);
}
Log.Information($"next: {runtime} zip ~ {runtimeFullPath} -> {zipPath}");
-
+
ZipFile.CreateFromDirectory(runtimeFullPath, zipPath);
}
}
const string CoverageReportZip = "coverage-report.zip";
-
- public static void ZipHtmlCoverageReport(string fromFolder,
+
+ public static void ZipHtmlCoverageReport(string? fromFolder,
bool noUnitTest)
{
if ( noUnitTest )
{
Log.Information(">> ZipHtmlCoverageReport " +
- "is disable due the --no-unit-test flag\n" +
- "So skip ZipHtmlCoverageReport");
+ "is disable due the --no-unit-test flag\n" +
+ "So skip ZipHtmlCoverageReport");
return;
}
-
- if ( !Directory.Exists(fromFolder) )
+
+ if ( string.IsNullOrEmpty(fromFolder) || !Directory.Exists(fromFolder) )
{
throw new DirectoryNotFoundException($"dir {fromFolder} not found");
}
@@ -98,9 +100,9 @@ public static void ZipHtmlCoverageReport(string fromFolder,
{
File.Delete(zipPath);
}
-
+
Log.Information($"next: zip {fromFolder} -> {zipPath}");
- ZipFile.CreateFromDirectory(fromFolder,
+ ZipFile.CreateFromDirectory(fromFolder,
zipPath);
}
}
diff --git a/starsky/docs/migrations.md b/starsky/docs/migrations.md
index d5dc298980..4e16a88843 100644
--- a/starsky/docs/migrations.md
+++ b/starsky/docs/migrations.md
@@ -1,66 +1,67 @@
-
# Add Migrations
+
This document describes how to add a migration to the application.
## Install Dotnet EF as global installer
+
```bash
dotnet tool install -g dotnet-ef
```
## Or update to latest version
+
```bash
dotnet tool update --global dotnet-ef
```
```bash
# https://www.nuget.org/packages/dotnet-ef#versions-body-tab
-dotnet tool update --global dotnet-ef --version 6.0.12
+dotnet tool update --global dotnet-ef --version 8.0.1
```
-## Set constance for EF Core
-Define constance in `starsky.foundation.database.csproj`
-```xml
- ENABLE_DEFAULT_DATABASE
-
-```
-or mysql:
+## Set constance for EF Core (optional)
+
+Define constance in `starsky.foundation.database.csproj` (to remove it after the migration)
+
+only when used mysql:
+
```xml
- ENABLE_MYSQL_DATABASE
+
+ENABLE_MYSQL_DATABASE
```
## Run Migration
+
+> Please think about this issue:
+> `warning CS8981: The type name 'limitdataprotectionkeyslength' only contains lower-cased ascii characters.`
+> `Such names may become reserved for the language`
+
+Run the following command:
+
```bash
cd starsky/starsky.foundation.database
-dotnet ef --startup-project ../starsky/starsky.csproj --project starsky.foundation.database.csproj migrations add test
+dotnet ef --project starsky.foundation.database.csproj migrations add test
```
The migration should be ready :)
-You should test **both** with MySQL and SQLite.
+You should test **both** with MySQL and SQLite.
For MySql its the easiest to run the database and/or the application with docker-compose.
+We use this for the migration:
+https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli#from-a-design-time-factory
+
+## Remove
+
+remove the following value if added:
+
+```
+ ENABLE_MYSQL_DATABASE
+```
## undo latest migration
```bash
cd starsky/starsky.foundation.database
-dotnet ef --startup-project ../starsky/starsky.csproj --project starsky.foundation.database.csproj migrations remove --force
+dotnet ef --project starsky.foundation.database.csproj migrations remove --force
```
-## Instead of setting constance (is replaced by defined constance)
-
-This is not needed anymore but kept here for explaining what is done in the code
-
-(Optional) : See code : SetupDatabaseTypes.cs
-
-```c#
- // dirty hack
- _services.AddDbContext(options =>
- options.UseSqlite(_appSettings.DatabaseConnection,
- b =>
- {
- if (! string.IsNullOrWhiteSpace(foundationDatabaseName) )
- {
- b.MigrationsAssembly(foundationDatabaseName);
- }
- }));
-```
\ No newline at end of file
diff --git a/starsky/format.sh b/starsky/format.sh
new file mode 100755
index 0000000000..56b7b791db
--- /dev/null
+++ b/starsky/format.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# Loop over the output of the find command
+while IFS= read -r file; do
+ # Process each file here, for example, print the file name
+ echo "Processing file: $file"
+ dotnet format $file
+
+done < <(find . -iname '*.csproj')
diff --git a/starsky/nuget-packages-list.json b/starsky/nuget-packages-list.json
index 2db87b4e9a..777e60b6ad 100644
--- a/starsky/nuget-packages-list.json
+++ b/starsky/nuget-packages-list.json
@@ -14,11 +14,8 @@
"MedallionShell 1.6.2",
"RazorLight 2.3.1",
"SixLabors.ImageSharp 3.1.2",
- "SixLabors.ImageSharp.Drawing 2.1.0",
+ "SixLabors.ImageSharp.Drawing 2.1.1",
"Microsoft.AspNetCore.Authorization 8.0.1",
- "Microsoft.ApplicationInsights.WorkerService 2.22.0",
- "Microsoft.Extensions.DependencyInjection.Abstractions 8.0.0",
- "Microsoft.Extensions.Logging 8.0.0",
"Microsoft.EntityFrameworkCore.Analyzers 8.0.1",
"Microsoft.EntityFrameworkCore.Design 8.0.1",
"Microsoft.EntityFrameworkCore.InMemory 8.0.1",
@@ -29,8 +26,7 @@
"Microsoft.Extensions.Identity.Stores 8.0.1",
"Pomelo.EntityFrameworkCore.MySql 8.0.0-beta.2",
"System.ComponentModel.Annotations 5.0.0",
- "Microsoft.ApplicationInsights 2.22.0",
- "Microsoft.ApplicationInsights.DependencyCollector 2.22.0",
+ "Microsoft.Extensions.DependencyInjection.Abstractions 8.0.0",
"Microsoft.CSharp 4.7.0",
"Microsoft.Extensions.Configuration 8.0.0",
"Microsoft.Extensions.Configuration.Binder 8.0.1",
@@ -43,20 +39,21 @@
"GeoTimeZone 5.3.0",
"XmpCore 6.1.10.1",
"MetadataExtractor 2.8.1",
- "Microsoft.ApplicationInsights.AspNetCore 2.22.0",
"Microsoft.Extensions.Logging.Console 8.0.0",
"OpenTelemetry 1.7.0",
"OpenTelemetry.Api 1.7.0",
"OpenTelemetry.Exporter.OpenTelemetryProtocol 1.7.0",
"OpenTelemetry.Extensions.Hosting 1.7.0",
"OpenTelemetry.Instrumentation.AspNetCore 1.7.0",
+ "OpenTelemetry.Instrumentation.EntityFrameworkCore 1.0.0-beta.10",
"OpenTelemetry.Instrumentation.Runtime 1.7.0",
+ "System.Diagnostics.DiagnosticSource 8.0.0",
"System.Text.Json 8.0.1",
"Microsoft.AspNetCore.Identity.EntityFrameworkCore 8.0.1",
"Microsoft.Extensions.Hosting 8.0.0",
- "Microsoft.NET.Test.Sdk 17.8.0",
- "MSTest.TestAdapter 3.1.1",
- "MSTest.TestFramework 3.1.1",
+ "Microsoft.NET.Test.Sdk 17.9.0",
+ "MSTest.TestAdapter 3.2.0",
+ "MSTest.TestFramework 3.2.0",
"coverlet.collector 6.0.0",
"Microsoft.AspNetCore.Mvc.Formatters.Json 2.2.0",
"Microsoft.AspNetCore.Identity 2.2.0"
diff --git a/starsky/starsky.feature.demo/Helpers/CleanDemoDataServiceCli.cs b/starsky/starsky.feature.demo/Helpers/CleanDemoDataServiceCli.cs
index 36a3fcef77..7ed3887ba3 100644
--- a/starsky/starsky.feature.demo/Helpers/CleanDemoDataServiceCli.cs
+++ b/starsky/starsky.feature.demo/Helpers/CleanDemoDataServiceCli.cs
@@ -20,8 +20,8 @@ public class CleanDemoDataServiceCli
private readonly ISynchronize _sync;
private readonly IConsole _console;
- public CleanDemoDataServiceCli(AppSettings appSettings,
- IHttpClientHelper httpClientHelper, ISelectorStorage selectorStorage,
+ public CleanDemoDataServiceCli(AppSettings appSettings,
+ IHttpClientHelper httpClientHelper, ISelectorStorage selectorStorage,
IWebLogger webLogger, IConsole console,
ISynchronize sync)
{
@@ -38,14 +38,14 @@ public CleanDemoDataServiceCli(AppSettings appSettings,
public async Task SeedCli(string[] args)
{
_appSettings.Verbose = ArgsHelper.NeedVerbose(args);
-
- if ( ArgsHelper.NeedHelp(args))
+
+ if ( ArgsHelper.NeedHelp(args) )
{
_appSettings.ApplicationType = AppSettings.StarskyAppType.DemoSeed;
new ArgsHelper(_appSettings, _console).NeedHelpShowDialog();
return;
}
-
+
await CleanDemoDataService.DownloadAsync(_appSettings, _httpClientHelper, _hostStorage, _subPathStorage, _webLogger);
await _sync.Sync("/");
}
diff --git a/starsky/starsky.feature.demo/Services/CleanDemoDataService.cs b/starsky/starsky.feature.demo/Services/CleanDemoDataService.cs
index 0e9608688a..5bc3c4e202 100644
--- a/starsky/starsky.feature.demo/Services/CleanDemoDataService.cs
+++ b/starsky/starsky.feature.demo/Services/CleanDemoDataService.cs
@@ -53,17 +53,17 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
var appSettings = scope.ServiceProvider.GetRequiredService();
var logger = scope.ServiceProvider.GetRequiredService();
- if (appSettings.DemoUnsafeDeleteStorageFolder != true || appSettings.ApplicationType != AppSettings.StarskyAppType.WebController )
+ if ( appSettings.DemoUnsafeDeleteStorageFolder != true || appSettings.ApplicationType != AppSettings.StarskyAppType.WebController )
{
return false;
}
-
- if ( Environment.GetEnvironmentVariable("app__storageFolder") == null)
+
+ if ( Environment.GetEnvironmentVariable("app__storageFolder") == null )
{
logger.LogError("[demo mode on] Environment variable app__storageFolder is not set");
return null;
}
-
+
var selectorStorage = scope.ServiceProvider.GetRequiredService();
var sync = scope.ServiceProvider.GetRequiredService();
var subStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
@@ -71,8 +71,8 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
var httpClientHelper = scope.ServiceProvider.GetRequiredService();
CleanData(subStorage, logger);
- await DownloadAsync(appSettings, httpClientHelper,hostStorage,subStorage, logger);
- await sync.Sync("/",PushToSockets);
+ await DownloadAsync(appSettings, httpClientHelper, hostStorage, subStorage, logger);
+ await sync.Sync("/", PushToSockets);
return true;
}
@@ -83,14 +83,14 @@ internal static void CleanData(IStorage subStorage, IWebLogger logger)
logger.LogError("stfolder exists so exit");
return;
}
-
+
// parent directories
var directories = subStorage.GetDirectories("/");
foreach ( var directory in directories )
{
subStorage.FolderDelete(directory);
}
-
+
// clean files in root
var getAllFiles = subStorage.GetAllFilesInDirectory("/")
.Where(p => p != "/.gitkeep" && p != "/.gitignore").ToList();
@@ -111,7 +111,7 @@ internal async Task PushToSockets(List updatedList)
using var scope = _serviceScopeFactory.CreateScope();
var connectionsService = scope.ServiceProvider.GetRequiredService();
var notificationQuery = scope.ServiceProvider.GetRequiredService();
-
+
var webSocketResponse =
new ApiNotificationResponseModel>(filtered,
ApiNotificationType.CleanDemoData);
@@ -119,17 +119,17 @@ internal async Task PushToSockets(List updatedList)
await notificationQuery.AddNotification(webSocketResponse);
return true;
}
-
+
private const string DemoFolderName = "demo";
-
+
internal static PublishManifestDemo? Deserialize(string result, IWebLogger webLogger, IStorage hostStorage, string settingsJsonFullPath)
{
PublishManifestDemo? data = null;
try
{
- data = JsonSerializer.Deserialize(result);
+ data = JsonSerializer.Deserialize(result);
}
- catch ( JsonException exception)
+ catch ( JsonException exception )
{
webLogger.LogError("[Deserialize] catch-ed", exception);
// and delete to retry
@@ -138,7 +138,7 @@ internal async Task PushToSockets(List updatedList)
return data;
}
-
+
internal static async Task DownloadAsync(AppSettings appSettings,
IHttpClientHelper httpClientHelper, IStorage hostStorage,
IStorage subStorage, IWebLogger webLogger)
@@ -148,28 +148,28 @@ internal static async Task DownloadAsync(AppSettings appSettings,
webLogger.LogError("DemoData is empty");
return false;
}
-
+
webLogger.LogInformation("Download demo data");
var cacheFolder = Path.Combine(appSettings.TempFolder, DemoFolderName);
hostStorage.CreateDirectory(cacheFolder);
-
+
foreach ( var (jsonUrl, dir) in appSettings.DemoData )
{
hostStorage.CreateDirectory(Path.Combine(cacheFolder, dir));
var settingsJsonFullPath =
Path.Combine(cacheFolder, dir, "_settings.json");
- if ( !hostStorage.ExistFile(settingsJsonFullPath) && !await httpClientHelper.Download(jsonUrl, settingsJsonFullPath))
+ if ( !hostStorage.ExistFile(settingsJsonFullPath) && !await httpClientHelper.Download(jsonUrl, settingsJsonFullPath) )
{
webLogger.LogInformation("Skip due not exists: " + settingsJsonFullPath);
continue;
}
-
+
var result = await StreamToStringHelper.StreamToStringAsync(
hostStorage.ReadStream(settingsJsonFullPath));
- var data = Deserialize(result, webLogger, hostStorage, settingsJsonFullPath);
+ var data = Deserialize(result, webLogger, hostStorage, settingsJsonFullPath);
if ( data == null )
{
webLogger.LogError("[DownloadAsync] data is null");
@@ -177,29 +177,29 @@ internal static async Task DownloadAsync(AppSettings appSettings,
}
var baseUrl = jsonUrl.Replace("_settings.json", string.Empty); // ends with slash
-
- foreach ( var keyValuePairKey in data.Copy.Where(p => p.Value
- && p.Key.Contains("1000")).Select(p => p.Key) )
+
+ foreach ( var keyValuePairKey in data.Copy.Where(p => p.Value
+ && p.Key.Contains("1000")).Select(p => p.Key) )
{
- var regex = new Regex("\\?.+$",
+ var regex = new Regex("\\?.+$",
RegexOptions.None, TimeSpan.FromMilliseconds(100));
var fileName =
FilenamesHelper.GetFileName(regex.Replace(keyValuePairKey, string.Empty));
- var cacheFilePath = Path.Combine(cacheFolder,dir, fileName);
+ var cacheFilePath = Path.Combine(cacheFolder, dir, fileName);
if ( !hostStorage.ExistFile(cacheFilePath) )
{
- await httpClientHelper.Download(baseUrl+ keyValuePairKey,cacheFilePath);
+ await httpClientHelper.Download(baseUrl + keyValuePairKey, cacheFilePath);
}
subStorage.CreateDirectory(dir);
-
+
await subStorage.WriteStreamAsync(
hostStorage.ReadStream(cacheFilePath),
PathHelper.AddSlash(dir) + fileName);
}
}
-
+
webLogger.LogInformation("Demo data seed done");
return true;
}
diff --git a/starsky/starsky.feature.demo/starsky.feature.demo.csproj b/starsky/starsky.feature.demo/starsky.feature.demo.csproj
index b94fb07507..0942437a55 100644
--- a/starsky/starsky.feature.demo/starsky.feature.demo.csproj
+++ b/starsky/starsky.feature.demo/starsky.feature.demo.csproj
@@ -1,18 +1,20 @@
-
- net8.0
-
- {34d46dc0-f965-46bf-9178-b83a9a6627e7}
- 0.6.0-beta.0
- enable
- starsky.feature.demo
+
+ net8.0
+
+ {34d46dc0-f965-46bf-9178-b83a9a6627e7}
+ 0.6.0-beta.0
+ enable
+ starsky.feature.demo
+
-
-
-
-
-
-
+
+
+
+
+
+ true
+
diff --git a/starsky/starsky.feature.export/Interfaces/IExport.cs b/starsky/starsky.feature.export/Interfaces/IExport.cs
index a0fe34af47..0dd5b81d9d 100644
--- a/starsky/starsky.feature.export/Interfaces/IExport.cs
+++ b/starsky/starsky.feature.export/Interfaces/IExport.cs
@@ -15,6 +15,6 @@ Task>> PreflightAsync(
bool collections = true,
bool thumbnail = false);
- Tuple StatusIsReady(string zipOutputFileName);
+ Tuple StatusIsReady(string zipOutputFileName);
}
}
diff --git a/starsky/starsky.feature.export/Services/ExportService.cs b/starsky/starsky.feature.export/Services/ExportService.cs
index a597e1c0b0..3ddf9158c0 100644
--- a/starsky/starsky.feature.export/Services/ExportService.cs
+++ b/starsky/starsky.feature.export/Services/ExportService.cs
@@ -23,246 +23,260 @@
using starsky.foundation.thumbnailgeneration.Interfaces;
[assembly: InternalsVisibleTo("starskytest")]
-namespace starsky.feature.export.Services
+
+namespace starsky.feature.export.Services;
+
+///
+/// Also known as Download
+///
+[Service(typeof(IExport), InjectionLifetime = InjectionLifetime.Scoped)]
+public class ExportService : IExport
{
+ private readonly IQuery _query;
+ private readonly AppSettings _appSettings;
+ private readonly IStorage _iStorage;
+ private readonly IStorage _hostFileSystemStorage;
+ private readonly IWebLogger _logger;
+ private readonly IThumbnailService _thumbnailService;
+
+ public ExportService(IQuery query, AppSettings appSettings,
+ ISelectorStorage selectorStorage, IWebLogger logger, IThumbnailService thumbnailService)
+ {
+ _appSettings = appSettings;
+ _query = query;
+ _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
+ _hostFileSystemStorage =
+ selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
+ _thumbnailService = thumbnailService;
+ _logger = logger;
+ }
+
///
- /// Also known as Download
+ /// Export preflight
///
- [Service(typeof(IExport), InjectionLifetime = InjectionLifetime.Scoped)]
- public class ExportService: IExport
+ /// list of subPaths
+ /// is stack collections enabled
+ /// should export thumbnail or not
+ /// zipHash, fileIndexResultsList
+ public async Task>> PreflightAsync(
+ string[] inputFilePaths,
+ bool collections = true,
+ bool thumbnail = false)
{
- private readonly IQuery _query;
- private readonly AppSettings _appSettings;
- private readonly IStorage _iStorage;
- private readonly IStorage _hostFileSystemStorage;
- private readonly IWebLogger _logger;
- private readonly IThumbnailService _thumbnailService;
-
- public ExportService(IQuery query, AppSettings appSettings,
- ISelectorStorage selectorStorage, IWebLogger logger, IThumbnailService thumbnailService)
- {
- _appSettings = appSettings;
- _query = query;
- _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
- _hostFileSystemStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
- _thumbnailService = thumbnailService;
- _logger = logger;
- }
+ // the result list
+ var fileIndexResultsList = new List();
- ///
- /// Export preflight
- ///
- /// list of subPaths
- /// is stack collections enabled
- /// should export thumbnail or not
- /// zipHash, fileIndexResultsList
- public async Task>> PreflightAsync(
- string[] inputFilePaths,
- bool collections = true,
- bool thumbnail = false)
+ foreach ( var subPath in inputFilePaths )
{
- // the result list
- var fileIndexResultsList = new List();
-
- foreach ( var subPath in inputFilePaths )
+ var detailView = _query.SingleItem(subPath, null, collections, false);
+ if ( string.IsNullOrEmpty(detailView?.FileIndexItem?.FilePath) )
{
- var detailView = _query.SingleItem(subPath, null, collections, false);
- if (string.IsNullOrEmpty(detailView?.FileIndexItem?.FilePath))
- {
- StatusCodesHelper.ReturnExifStatusError(new FileIndexItem(subPath),
- FileIndexItem.ExifStatus.NotFoundNotInIndex,
- fileIndexResultsList);
- continue;
- }
-
- if ( _iStorage.IsFolderOrFile(detailView.FileIndexItem.FilePath) ==
- FolderOrFileModel.FolderOrFileTypeList.Deleted )
- {
- StatusCodesHelper.ReturnExifStatusError(detailView.FileIndexItem,
- FileIndexItem.ExifStatus.NotFoundSourceMissing,
- fileIndexResultsList);
- continue;
- }
-
- if ( detailView.FileIndexItem.IsDirectory == true )
- {
- await AddFileIndexResultsListForDirectory(detailView, fileIndexResultsList);
- continue;
- }
-
- // Now Add Collection based images
- AddCollectionBasedImages(detailView, fileIndexResultsList, collections, subPath);
+ StatusCodesHelper.ReturnExifStatusError(new FileIndexItem(subPath),
+ FileIndexItem.ExifStatus.NotFoundNotInIndex,
+ fileIndexResultsList);
+ continue;
}
- var isThumbnail = thumbnail ? "TN" : "SR"; // has:notHas
- var zipHash = isThumbnail + GetName(fileIndexResultsList);
-
- return new Tuple>(zipHash, fileIndexResultsList);
- }
+ if ( _iStorage.IsFolderOrFile(detailView.FileIndexItem.FilePath) ==
+ FolderOrFileModel.FolderOrFileTypeList.Deleted )
+ {
+ StatusCodesHelper.ReturnExifStatusError(detailView.FileIndexItem,
+ FileIndexItem.ExifStatus.NotFoundSourceMissing,
+ fileIndexResultsList);
+ continue;
+ }
- private async Task AddFileIndexResultsListForDirectory(DetailView detailView, List fileIndexResultsList)
- {
- var allFilesInFolder =
- await _query.GetAllRecursiveAsync(detailView?
- .FileIndexItem?.FilePath!);
- foreach ( var item in
- allFilesInFolder.
- Where(item => item.FilePath != null && _iStorage.ExistFile(item.FilePath)) )
+ if ( detailView.FileIndexItem.IsDirectory == true )
{
- item.Status = FileIndexItem.ExifStatus.Ok;
- fileIndexResultsList.Add(item);
+ await AddFileIndexResultsListForDirectory(detailView, fileIndexResultsList);
+ continue;
}
+
+ // Now Add Collection based images
+ AddCollectionBasedImages(detailView, fileIndexResultsList, collections, subPath);
}
- private void AddCollectionBasedImages(DetailView detailView, List fileIndexResultsList, bool collections, string subPath)
+ var isThumbnail = thumbnail ? "TN" : "SR"; // has:notHas
+ var zipHash = isThumbnail + GetName(fileIndexResultsList);
+
+ return new Tuple>(zipHash, fileIndexResultsList);
+ }
+
+ private async Task AddFileIndexResultsListForDirectory(DetailView detailView,
+ List fileIndexResultsList)
+ {
+ var allFilesInFolder =
+ await _query.GetAllRecursiveAsync(detailView
+ .FileIndexItem?.FilePath!);
+ foreach ( var item in
+ allFilesInFolder.Where(item =>
+ item.FilePath != null && _iStorage.ExistFile(item.FilePath)) )
{
- var collectionSubPathList = DetailView.GetCollectionSubPathList(detailView.FileIndexItem!, collections, subPath);
- foreach ( var item in collectionSubPathList )
- {
- var itemFileIndexItem = _query.SingleItem(item, null,
- false, false)?.FileIndexItem;
- if ( itemFileIndexItem == null ) continue;
- itemFileIndexItem.Status = FileIndexItem.ExifStatus.Ok;
- fileIndexResultsList.Add(itemFileIndexItem);
- }
+ item.Status = FileIndexItem.ExifStatus.Ok;
+ fileIndexResultsList.Add(item);
}
+ }
- ///
- /// Based on the preflight create a Zip Export
- ///
- /// Result of Preflight
- /// isThumbnail?
- /// filename of zip file (no extension)
- /// nothing
- public async Task CreateZip(List fileIndexResultsList, bool thumbnail,
- string zipOutputFileName)
+ private void AddCollectionBasedImages(DetailView detailView,
+ List fileIndexResultsList, bool collections, string subPath)
+ {
+ var collectionSubPathList =
+ DetailView.GetCollectionSubPathList(detailView.FileIndexItem!, collections, subPath);
+ foreach ( var item in collectionSubPathList )
{
- var filePaths = await CreateListToExport(fileIndexResultsList, thumbnail);
- var fileNames = await FilePathToFileNameAsync(filePaths, thumbnail);
-
- new Zipper().CreateZip(_appSettings.TempFolder,filePaths,fileNames,zipOutputFileName);
-
- // Write a single file to be sure that writing is ready
- var doneFileFullPath = Path.Combine(_appSettings.TempFolder,zipOutputFileName) + ".done";
- await _hostFileSystemStorage.
- WriteStreamAsync(StringToStreamHelper.StringToStream("OK"), doneFileFullPath);
- if(_appSettings.IsVerbose()) _logger.LogInformation("[CreateZip] Zip done: " + doneFileFullPath);
+ var itemFileIndexItem = _query.SingleItem(item, null,
+ false, false)?.FileIndexItem;
+ if ( itemFileIndexItem == null ) continue;
+ itemFileIndexItem.Status = FileIndexItem.ExifStatus.Ok;
+ fileIndexResultsList.Add(itemFileIndexItem);
}
-
- ///
- /// This list will be included in the zip - Export is called Download in the UI
- ///
- /// the items
- /// add the thumbnail or the source image
- /// list of file paths
- public async Task> CreateListToExport(List fileIndexResultsList, bool thumbnail)
+ }
+
+ ///
+ /// Based on the preflight create a Zip Export
+ ///
+ /// Result of Preflight
+ /// isThumbnail?
+ /// filename of zip file (no extension)
+ /// nothing
+ public async Task CreateZip(List fileIndexResultsList, bool thumbnail,
+ string zipOutputFileName)
+ {
+ var filePaths = await CreateListToExport(fileIndexResultsList, thumbnail);
+ var fileNames = await FilePathToFileNameAsync(filePaths, thumbnail);
+
+ new Zipper().CreateZip(_appSettings.TempFolder, filePaths, fileNames, zipOutputFileName);
+
+ // Write a single file to be sure that writing is ready
+ var doneFileFullPath = Path.Combine(_appSettings.TempFolder, zipOutputFileName) + ".done";
+ await _hostFileSystemStorage.WriteStreamAsync(StringToStreamHelper.StringToStream("OK"),
+ doneFileFullPath);
+ if ( _appSettings.IsVerbose() )
+ _logger.LogInformation("[CreateZip] Zip done: " + doneFileFullPath);
+ }
+
+ ///
+ /// This list will be included in the zip - Export is called Download in the UI
+ ///
+ /// the items
+ /// add the thumbnail or the source image
+ /// list of file paths
+ public async Task> CreateListToExport(List fileIndexResultsList,
+ bool thumbnail)
+ {
+ var filePaths = new List();
+
+ foreach ( var item in fileIndexResultsList.Where(p =>
+ p.Status == FileIndexItem.ExifStatus.Ok && p.FileHash != null).ToList() )
{
- var filePaths = new List();
+ if ( thumbnail )
+ {
+ var sourceThumb = Path.Combine(_appSettings.ThumbnailTempFolder,
+ ThumbnailNameHelper.Combine(item.FileHash!, ThumbnailSize.Large, true));
+
+ await _thumbnailService
+ .CreateThumbAsync(item.FilePath!, item.FileHash!, true);
+
+ filePaths.Add(sourceThumb);
+ continue;
+ }
- foreach ( var item in fileIndexResultsList.Where(p =>
- p.Status == FileIndexItem.ExifStatus.Ok && p.FileHash != null ).ToList() )
+ var sourceFile = _appSettings.DatabasePathToFilePath(item.FilePath!);
+
+ if ( !_hostFileSystemStorage.ExistFile(sourceFile) )
{
- if ( thumbnail )
- {
- var sourceThumb = Path.Combine(_appSettings.ThumbnailTempFolder,
- ThumbnailNameHelper.Combine(item.FileHash!, ThumbnailSize.Large, true));
-
- await _thumbnailService
- .CreateThumbAsync(item.FilePath!, item.FileHash, true);
-
- filePaths.Add(sourceThumb);
- continue;
- }
-
- var sourceFile = _appSettings.DatabasePathToFilePath(item.FilePath!);
-
- if ( !_hostFileSystemStorage.ExistFile(sourceFile) )
- {
- continue;
- }
-
- // the jpeg file for example
- filePaths.Add(sourceFile);
-
- // when there is .xmp sidecar file (but only when file is a RAW file, ignored when for example jpeg)
- if ( !ExtensionRolesHelper.IsExtensionForceXmp(item.FilePath) ||
- !_iStorage.ExistFile(
- ExtensionRolesHelper.ReplaceExtensionWithXmp(
- item.FilePath)) ) continue;
-
- var xmpFileFullPath = _appSettings.DatabasePathToFilePath(
- ExtensionRolesHelper.ReplaceExtensionWithXmp(
- item.FilePath));
-
- if ( !_hostFileSystemStorage.ExistFile(xmpFileFullPath) )
- {
- continue;
- }
- filePaths.Add(xmpFileFullPath);
+ continue;
}
- return filePaths;
- }
-
- ///
- /// Get the filename (in case of thumbnail the source image name)
- ///
- /// the full file paths
- /// copy the thumbnail (true) or the source image (false)
- ///
- internal async Task> FilePathToFileNameAsync(IEnumerable filePaths, bool thumbnail)
- {
- var fileNames = new List();
- foreach ( var filePath in filePaths )
+
+ // the jpeg file for example
+ filePaths.Add(sourceFile);
+
+ // when there is .xmp sidecar file (but only when file is a RAW file, ignored when for example jpeg)
+ if ( !ExtensionRolesHelper.IsExtensionForceXmp(item.FilePath) ||
+ !_iStorage.ExistFile(
+ ExtensionRolesHelper.ReplaceExtensionWithXmp(
+ item.FilePath)) ) continue;
+
+ var xmpFileFullPath = _appSettings.DatabasePathToFilePath(
+ ExtensionRolesHelper.ReplaceExtensionWithXmp(
+ item.FilePath));
+
+ if ( !_hostFileSystemStorage.ExistFile(xmpFileFullPath) )
{
- if ( thumbnail )
- {
- // We use base32 fileHashes but export
- // the file with the original name
-
- var thumbFilename = Path.GetFileNameWithoutExtension(filePath);
- var subPath = await _query.GetSubPathByHashAsync(thumbFilename);
- var filename = subPath?.Split('/').LastOrDefault(); // first a string
- fileNames.Add(filename);
- continue;
- }
- fileNames.Add(Path.GetFileName(filePath));
+ continue;
}
- return fileNames;
+
+ filePaths.Add(xmpFileFullPath);
}
- ///
- /// to create a unique name of the zip using c# get hashcode
- ///
- /// list of objects with fileHashes
- /// unique 'get hashcode' string
- private static string GetName(IEnumerable fileIndexResultsList)
+ return filePaths;
+ }
+
+ ///
+ /// Get the filename (in case of thumbnail the source image name)
+ ///
+ /// the full file paths
+ /// copy the thumbnail (true) or the source image (false)
+ ///
+ internal async Task> FilePathToFileNameAsync(IEnumerable filePaths,
+ bool thumbnail)
+ {
+ var fileNames = new List();
+ foreach ( var filePath in filePaths )
{
- var tempFileNameStringBuilder = new StringBuilder();
- foreach ( var item in fileIndexResultsList )
+ if ( thumbnail )
{
- tempFileNameStringBuilder.Append(item.FileHash);
+ // We use base32 fileHashes but export
+ // the file with the original name
+
+ var thumbFilename = Path.GetFileNameWithoutExtension(filePath);
+ var subPath = await _query.GetSubPathByHashAsync(thumbFilename);
+ var filename = subPath?.Split('/').LastOrDefault()!; // first a string
+ fileNames.Add(filename);
+ continue;
}
- // to be sure that the max string limit
- var shortName = tempFileNameStringBuilder.ToString().GetHashCode()
- .ToString(CultureInfo.InvariantCulture).ToLower().Replace("-","A");
-
- return shortName;
+
+ fileNames.Add(Path.GetFileName(filePath));
}
- ///
- /// Is Zip Ready?
- ///
- /// fileName without extension
- /// null if status file is not found, true if done file exist
- public Tuple StatusIsReady(string zipOutputFileName)
+ return fileNames;
+ }
+
+ ///
+ /// to create a unique name of the zip using c# get hashcode
+ ///
+ /// list of objects with fileHashes
+ /// unique 'get hashcode' string
+ private static string GetName(IEnumerable fileIndexResultsList)
+ {
+ var tempFileNameStringBuilder = new StringBuilder();
+ foreach ( var item in fileIndexResultsList )
{
- var sourceFullPath = Path.Combine(_appSettings.TempFolder,zipOutputFileName) + ".zip";
- var doneFileFullPath = Path.Combine(_appSettings.TempFolder,zipOutputFileName) + ".done";
+ tempFileNameStringBuilder.Append(item.FileHash);
+ }
- if ( !_hostFileSystemStorage.ExistFile(sourceFullPath) ) return new Tuple(null,null);
+ // to be sure that the max string limit
+ var shortName = tempFileNameStringBuilder.ToString().GetHashCode()
+ .ToString(CultureInfo.InvariantCulture).ToLower().Replace("-", "A");
- // Read a single file to be sure that writing is ready
- return new Tuple(_hostFileSystemStorage.ExistFile(doneFileFullPath), sourceFullPath);
- }
+ return shortName;
+ }
+
+ ///
+ /// Is Zip Ready?
+ ///
+ /// fileName without extension
+ /// null if status file is not found, true if done file exist
+ public Tuple StatusIsReady(string zipOutputFileName)
+ {
+ var sourceFullPath = Path.Combine(_appSettings.TempFolder, zipOutputFileName) + ".zip";
+ var doneFileFullPath = Path.Combine(_appSettings.TempFolder, zipOutputFileName) + ".done";
+
+ if ( !_hostFileSystemStorage.ExistFile(sourceFullPath) )
+ return new Tuple(null, null);
+
+ // Read a single file to be sure that writing is ready
+ return new Tuple(_hostFileSystemStorage.ExistFile(doneFileFullPath),
+ sourceFullPath);
}
}
diff --git a/starsky/starsky.feature.export/starsky.feature.export.csproj b/starsky/starsky.feature.export/starsky.feature.export.csproj
index 6cf00f8bf2..753b8bce37 100644
--- a/starsky/starsky.feature.export/starsky.feature.export.csproj
+++ b/starsky/starsky.feature.export/starsky.feature.export.csproj
@@ -4,11 +4,12 @@
net8.0
{09ccbfa9-612f-4d5e-ae27-c0c06e7114c9}
0.6.0-beta.0
+ enable
-
+
-
-
+
+
diff --git a/starsky/starsky.feature.geolookup/Models/GeoCacheStatus.cs b/starsky/starsky.feature.geolookup/Models/GeoCacheStatus.cs
index 7714784466..7b722292e0 100644
--- a/starsky/starsky.feature.geolookup/Models/GeoCacheStatus.cs
+++ b/starsky/starsky.feature.geolookup/Models/GeoCacheStatus.cs
@@ -5,7 +5,7 @@ public enum StatusType
Total,
Current
}
-
+
public class GeoCacheStatus
{
public int Total { get; set; }
diff --git a/starsky/starsky.feature.geolookup/Models/GeoLocationModel.cs b/starsky/starsky.feature.geolookup/Models/GeoLocationModel.cs
index 1bf3c5a864..f2a69c0c36 100644
--- a/starsky/starsky.feature.geolookup/Models/GeoLocationModel.cs
+++ b/starsky/starsky.feature.geolookup/Models/GeoLocationModel.cs
@@ -6,7 +6,7 @@ public sealed class GeoLocationModel
public double Latitude { get; set; }
public double Longitude { get; set; }
-
+
public string? LocationCity { get; set; }
public string? LocationCountry { get; set; }
public string? LocationCountryCode { get; set; }
diff --git a/starsky/starsky.feature.geolookup/Services/GeoBackgroundTask.cs b/starsky/starsky.feature.geolookup/Services/GeoBackgroundTask.cs
index 93709bbbe0..592e5580ac 100644
--- a/starsky/starsky.feature.geolookup/Services/GeoBackgroundTask.cs
+++ b/starsky/starsky.feature.geolookup/Services/GeoBackgroundTask.cs
@@ -29,8 +29,8 @@ public class GeoBackgroundTask : IGeoBackgroundTask
private readonly GeoIndexGpx _geoIndexGpx;
private readonly IGeoReverseLookup _geoReverseLookup;
- public GeoBackgroundTask(AppSettings appSettings, ISelectorStorage selectorStorage,
- IGeoLocationWrite geoLocationWrite, IMemoryCache memoryCache,
+ public GeoBackgroundTask(AppSettings appSettings, ISelectorStorage selectorStorage,
+ IGeoLocationWrite geoLocationWrite, IMemoryCache memoryCache,
IWebLogger logger, IGeoReverseLookup geoReverseLookup)
{
_appSettings = appSettings;
@@ -42,7 +42,7 @@ public GeoBackgroundTask(AppSettings appSettings, ISelectorStorage selectorStora
_geoIndexGpx = new GeoIndexGpx(_appSettings, _iStorage, logger, memoryCache);
_geoReverseLookup = geoReverseLookup;
}
-
+
public async Task> GeoBackgroundTaskAsync(
string f = "/",
bool index = true,
@@ -53,18 +53,18 @@ public async Task> GeoBackgroundTaskAsync(
var listOfFiles = _iStorage.GetAllFilesInDirectory(f)
.Where(ExtensionRolesHelper.IsExtensionSyncSupported).ToList();
- var fileIndexList = await _readMeta
+ var fileIndexList = await _readMeta
.ReadExifAndXmpFromFileAddFilePathHashAsync(listOfFiles);
-
+
var toMetaFilesUpdate = new List();
if ( index )
{
toMetaFilesUpdate =
await _geoIndexGpx
.LoopFolderAsync(fileIndexList);
-
+
if ( _appSettings.IsVerbose() ) Console.Write("¬");
-
+
await _geoLocationWrite
.LoopFolderAsync(toMetaFilesUpdate, false);
}
@@ -72,7 +72,7 @@ await _geoLocationWrite
fileIndexList =
await _geoReverseLookup
.LoopFolderLookup(fileIndexList, overwriteLocationNames);
-
+
if ( fileIndexList.Count >= 1 )
{
await _geoLocationWrite.LoopFolderAsync(
@@ -86,8 +86,8 @@ await _geoLocationWrite.LoopFolderAsync(
foreach ( var item in fileIndexList.GroupBy(i => i.FilePath).Select(g => g.First())
.ToList() )
{
- var newThumb = (await new FileHash(_iStorage).GetHashCodeAsync(item.FilePath!)).Key;
- if ( item.FileHash == newThumb) continue;
+ var newThumb = ( await new FileHash(_iStorage).GetHashCodeAsync(item.FilePath!) ).Key;
+ if ( item.FileHash == newThumb ) continue;
new ThumbnailFileMoveAllSizes(_thumbnailStorage).FileMove(item.FileHash!, newThumb);
if ( _appSettings.IsVerbose() )
_logger.LogInformation("[/api/geo/sync] thumb rename + `" + item.FileHash + "`" + newThumb);
diff --git a/starsky/starsky.feature.geolookup/Services/GeoCacheStatusService.cs b/starsky/starsky.feature.geolookup/Services/GeoCacheStatusService.cs
index 4d62417cf2..c87952a4c7 100644
--- a/starsky/starsky.feature.geolookup/Services/GeoCacheStatusService.cs
+++ b/starsky/starsky.feature.geolookup/Services/GeoCacheStatusService.cs
@@ -8,41 +8,41 @@ namespace starsky.feature.geolookup.Services
public class GeoCacheStatusService
{
private readonly IMemoryCache? _cache;
-
- public GeoCacheStatusService( IMemoryCache? memoryCache = null)
+
+ public GeoCacheStatusService(IMemoryCache? memoryCache = null)
{
_cache = memoryCache;
}
public GeoCacheStatus Status(string path)
{
- if(_cache == null || string.IsNullOrWhiteSpace(path)) return new GeoCacheStatus{Total = -1};
+ if ( _cache == null || string.IsNullOrWhiteSpace(path) ) return new GeoCacheStatus { Total = -1 };
var totalCacheName = nameof(GeoCacheStatus) + path + StatusType.Total;
var result = new GeoCacheStatus();
-
- if(_cache.TryGetValue(totalCacheName, out var statusObjectTotal) &&
- TryParse(statusObjectTotal?.ToString(), out var totalStatus))
+
+ if ( _cache.TryGetValue(totalCacheName, out var statusObjectTotal) &&
+ TryParse(statusObjectTotal?.ToString(), out var totalStatus) )
{
result.Total = totalStatus;
}
-
+
var currentCacheName = nameof(GeoCacheStatus) + path + StatusType.Current;
- if(_cache.TryGetValue(currentCacheName, out var statusObjectCurrent) &&
- TryParse(statusObjectCurrent?.ToString(), out var currentStatus))
+ if ( _cache.TryGetValue(currentCacheName, out var statusObjectCurrent) &&
+ TryParse(statusObjectCurrent?.ToString(), out var currentStatus) )
{
result.Current = currentStatus;
}
-
+
return result;
}
-
+
public void StatusUpdate(string path, int current, StatusType type)
{
- if(_cache == null || string.IsNullOrWhiteSpace(path)) return;
-
+ if ( _cache == null || string.IsNullOrWhiteSpace(path) ) return;
+
var queryGeoCacheName = nameof(GeoCacheStatus) + path + type;
- _cache.Set(queryGeoCacheName, current, new TimeSpan(10,0,0));
+ _cache.Set(queryGeoCacheName, current, new TimeSpan(10, 0, 0));
}
}
diff --git a/starsky/starsky.feature.geolookup/Services/GeoCli.cs b/starsky/starsky.feature.geolookup/Services/GeoCli.cs
index af3a030d7d..73f404b2b8 100644
--- a/starsky/starsky.feature.geolookup/Services/GeoCli.cs
+++ b/starsky/starsky.feature.geolookup/Services/GeoCli.cs
@@ -36,8 +36,8 @@ public sealed class GeoCli
private readonly IWebLogger _logger;
[SuppressMessage("Usage", "S107: Constructor has 8 parameters, which is greater than the 7 authorized")]
- public GeoCli(IGeoReverseLookup geoReverseLookup,
- IGeoLocationWrite geoLocationWrite, ISelectorStorage selectorStorage, AppSettings appSettings, IConsole console,
+ public GeoCli(IGeoReverseLookup geoReverseLookup,
+ IGeoLocationWrite geoLocationWrite, ISelectorStorage selectorStorage, AppSettings appSettings, IConsole console,
IGeoFileDownload geoFileDownload, IExifToolDownload exifToolDownload, IWebLogger logger)
{
_geoReverseLookup = geoReverseLookup;
@@ -51,7 +51,7 @@ public GeoCli(IGeoReverseLookup geoReverseLookup,
_geoFileDownload = geoFileDownload;
_logger = logger;
}
-
+
///
/// Command line importer to Database and update disk
///
@@ -62,25 +62,25 @@ public async Task CommandLineAsync(string[] args)
_appSettings.Verbose = ArgsHelper.NeedVerbose(args);
// Set type of GeoReverseLookup
_appSettings.ApplicationType = AppSettings.StarskyAppType.Geo;
-
+
// Download ExifTool
await _exifToolDownload.DownloadExifTool(_appSettings.IsWindows);
-
+
// Geo cities1000 download
if ( _appSettings.GeoFilesSkipDownloadOnStartup != true )
{
await _geoFileDownload.DownloadAsync();
}
-
+
if ( ArgsHelper.NeedHelp(args) ||
- ( new ArgsHelper(_appSettings).GetPathFormArgs(args, false).Length <= 1
- && ArgsHelper.GetSubPathFormArgs(args).Length <= 1
- && new ArgsHelper(_appSettings).GetRelativeValue(args) == null ) )
+ ( new ArgsHelper(_appSettings).GetPathFormArgs(args, false).Length <= 1
+ && ArgsHelper.GetSubPathFormArgs(args).Length <= 1
+ && new ArgsHelper(_appSettings).GetRelativeValue(args) == null ) )
{
new ArgsHelper(_appSettings, _console).NeedHelpShowDialog();
return;
}
-
+
// Using both options
string inputPath;
// -s = if subPath || -p is path
@@ -95,36 +95,36 @@ public async Task CommandLineAsync(string[] args)
{
inputPath = new ArgsHelper(_appSettings).GetPathFormArgs(args, false);
}
-
+
// overwrite subPath with relative days
// use -g or --SubPathRelative to use it.
// envs are not supported
var getSubPathRelative = new ArgsHelper(_appSettings).GetRelativeValue(args);
- if (getSubPathRelative != null)
+ if ( getSubPathRelative != null )
{
- var dateTime = DateTime.Now.AddDays(( double ) getSubPathRelative);
+ var dateTime = DateTime.Now.AddDays(( double )getSubPathRelative);
var path = _appSettings.DatabasePathToFilePath(
new StructureService(_iStorage, _appSettings.Structure)
.ParseSubfolders(dateTime));
inputPath = !string.IsNullOrEmpty(path) ? path : string.Empty;
}
-
+
// used in this session to find the files back
_appSettings.StorageFolder = inputPath;
-
+
if ( inputPath == null || _iStorage.IsFolderOrFile("/") == FolderOrFileModel.FolderOrFileTypeList.Deleted )
{
_console.WriteLine("Folder location is not found \n" +
- $"Please try the `-h` command to get help \nDid search for: {inputPath}");
+ $"Please try the `-h` command to get help \nDid search for: {inputPath}");
return;
}
-
+
// use relative to StorageFolder
var listOfFiles = _iStorage.GetAllFilesInDirectory("/")
.Where(ExtensionRolesHelper.IsExtensionSyncSupported).ToList();
-
+
var fileIndexList = await _readMeta.ReadExifAndXmpFromFileAddFilePathHashAsync(listOfFiles);
-
+
var toMetaFilesUpdate = new List();
if ( ArgsHelper.GetIndexMode(args) )
{
@@ -133,25 +133,25 @@ public async Task CommandLineAsync(string[] args)
var geoIndexGpx = new GeoIndexGpx(_appSettings, _iStorage, _logger);
toMetaFilesUpdate = await geoIndexGpx.LoopFolderAsync(fileIndexList);
-
+
_console.Write("¬");
await _geoLocationWrite.LoopFolderAsync(toMetaFilesUpdate, false);
_console.Write("(gps added)");
}
-
+
fileIndexList = await _geoReverseLookup.LoopFolderLookup(fileIndexList,
ArgsHelper.GetAll(args));
-
+
if ( fileIndexList.Count >= 1 )
{
_console.Write("~ Add city, state and country info ~");
-
+
await _geoLocationWrite.LoopFolderAsync(fileIndexList, true);
}
-
+
_console.Write("^\n");
_console.Write("~ Rename thumbnails ~");
-
+
// Loop though all options
fileIndexList.AddRange(toMetaFilesUpdate);
@@ -164,10 +164,10 @@ private async Task RenameFileHash(IEnumerable fileIndexList)
{
// update thumbs to avoid unnecessary re-generation
foreach ( var item in fileIndexList.GroupBy(i => i.FilePath).
- Select(g => g.First())
- .ToList() )
+ Select(g => g.First())
+ .ToList() )
{
- var newThumb = (await new FileHash(_iStorage).GetHashCodeAsync(item.FilePath!)).Key;
+ var newThumb = ( await new FileHash(_iStorage).GetHashCodeAsync(item.FilePath!) ).Key;
if ( item.FileHash == newThumb ) continue;
new ThumbnailFileMoveAllSizes(_thumbnailStorage).FileMove(
item.FileHash!, newThumb);
diff --git a/starsky/starsky.feature.geolookup/Services/GeoFileDownload.cs b/starsky/starsky.feature.geolookup/Services/GeoFileDownload.cs
index 84d5690a69..c32da8c5bc 100644
--- a/starsky/starsky.feature.geolookup/Services/GeoFileDownload.cs
+++ b/starsky/starsky.feature.geolookup/Services/GeoFileDownload.cs
@@ -20,7 +20,7 @@ public sealed class GeoFileDownload : IGeoFileDownload
public const string CountryName = "cities1000";
internal long MinimumSizeInBytes { get; set; } = 7000000; // 7 MB
-
+
public GeoFileDownload(AppSettings appSettings, IHttpClientHelper httpClientHelper, ISelectorStorage selectorStorage)
{
_appSettings = appSettings;
@@ -31,38 +31,38 @@ public GeoFileDownload(AppSettings appSettings, IHttpClientHelper httpClientHelp
internal const string BaseUrl =
"download.geonames.org/export/dump/";
internal const string MirrorUrl = "qdraw.nl/special/mirror/geonames/";
-
+
public async Task DownloadAsync()
{
RemoveFailedDownload();
CreateDependenciesFolder();
-
- if(!_hostStorage.ExistFile(
- Path.Combine(_appSettings.DependenciesFolder,CountryName + ".txt")) )
+
+ if ( !_hostStorage.ExistFile(
+ Path.Combine(_appSettings.DependenciesFolder, CountryName + ".txt")) )
{
var outputZip = Path.Combine(_appSettings.DependenciesFolder,
CountryName + ".zip");
- var baseResult = await _httpClientHelper.Download( "https://" + BaseUrl + CountryName + ".zip",outputZip);
+ var baseResult = await _httpClientHelper.Download("https://" + BaseUrl + CountryName + ".zip", outputZip);
if ( !baseResult )
{
- await _httpClientHelper.Download("https://" + MirrorUrl + CountryName + ".zip",outputZip);
+ await _httpClientHelper.Download("https://" + MirrorUrl + CountryName + ".zip", outputZip);
}
new Zipper().ExtractZip(outputZip, _appSettings.DependenciesFolder);
}
- if(!_hostStorage.ExistFile(
- Path.Combine(_appSettings.DependenciesFolder,"admin1CodesASCII.txt")))
+ if ( !_hostStorage.ExistFile(
+ Path.Combine(_appSettings.DependenciesFolder, "admin1CodesASCII.txt")) )
{
// code for the second administrative division,
// a county in the US, see file admin2Codes.txt; varchar(80)
var outputFile = Path.Combine(_appSettings.DependenciesFolder,
"admin1CodesASCII.txt");
var baseResult = await _httpClientHelper.Download("https://" +
- BaseUrl + "admin1CodesASCII.txt",outputFile);
+ BaseUrl + "admin1CodesASCII.txt", outputFile);
if ( !baseResult )
{
await _httpClientHelper.Download("https://" +
- MirrorUrl + "admin1CodesASCII.txt",outputFile);
+ MirrorUrl + "admin1CodesASCII.txt", outputFile);
}
}
}
@@ -81,12 +81,12 @@ internal void CreateDependenciesFolder()
internal void RemoveFailedDownload()
{
if ( !_hostStorage.ExistFile(Path.Combine(
- _appSettings.DependenciesFolder,
- CountryName + ".zip")) )
+ _appSettings.DependenciesFolder,
+ CountryName + ".zip")) )
{
return;
}
-
+
// When trying to download a file
var zipLength = _hostStorage
.ReadStream(Path.Combine(_appSettings.DependenciesFolder, CountryName + ".zip"))
diff --git a/starsky/starsky.feature.geolookup/Services/GeoFileDownloadBackgroundService.cs b/starsky/starsky.feature.geolookup/Services/GeoFileDownloadBackgroundService.cs
index 60c8762a0d..bc408dcafe 100644
--- a/starsky/starsky.feature.geolookup/Services/GeoFileDownloadBackgroundService.cs
+++ b/starsky/starsky.feature.geolookup/Services/GeoFileDownloadBackgroundService.cs
@@ -31,17 +31,17 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await DownloadAsync(stoppingToken);
}
-
+
internal async Task DownloadAsync(CancellationToken _ = default)
{
- using (var scope = _serviceScopeFactory.CreateScope())
+ using ( var scope = _serviceScopeFactory.CreateScope() )
{
var appSettings = scope.ServiceProvider.GetRequiredService();
var logger = scope.ServiceProvider.GetRequiredService();
// Geo Helper has a direct need of this, other are downloaded when needed
// This Background service is for running offline
- if ( appSettings.ApplicationType == AppSettings.StarskyAppType.Geo) return;
+ if ( appSettings.ApplicationType == AppSettings.StarskyAppType.Geo ) return;
if ( appSettings.GeoFilesSkipDownloadOnStartup == true )
{
logger.LogInformation("GeoFilesSkipDownloadOnStartup is true, skip download");
diff --git a/starsky/starsky.feature.geolookup/Services/GeoIndexGpx.cs b/starsky/starsky.feature.geolookup/Services/GeoIndexGpx.cs
index d621fb467b..6efdc25b20 100644
--- a/starsky/starsky.feature.geolookup/Services/GeoIndexGpx.cs
+++ b/starsky/starsky.feature.geolookup/Services/GeoIndexGpx.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -17,118 +17,118 @@
[assembly: InternalsVisibleTo("starskytest")]
namespace starsky.feature.geolookup.Services
{
- public class GeoIndexGpx : IGeoIndexGpx
- {
- private readonly AppSettings _appSettings;
- private readonly IStorage _iStorage;
- private readonly IMemoryCache? _cache;
- private readonly IWebLogger _logger;
-
- public GeoIndexGpx(AppSettings appSettings, IStorage iStorage,
- IWebLogger logger, IMemoryCache? memoryCache = null )
- {
- _appSettings = appSettings;
- _iStorage = iStorage;
- _cache = memoryCache;
- _logger = logger;
- }
-
- private static List GetNoLocationItems(IEnumerable metaFilesInDirectory)
- {
- return metaFilesInDirectory.Where(
- metaFileItem =>
- (Math.Abs(metaFileItem.Latitude) < 0.001 && Math.Abs(metaFileItem.Longitude) < 0.001)
- && metaFileItem.DateTime.Year > 2) // ignore files without a date
- .ToList();
- }
-
- private async Task> GetGpxFileAsync(List metaFilesInDirectory)
- {
- var geoList = new List();
- foreach (var metaFileItem in metaFilesInDirectory)
- {
-
- if( !ExtensionRolesHelper.IsExtensionForceGpx(metaFileItem.FileName) ) continue;
-
- if ( !_iStorage.ExistFile(metaFileItem.FilePath!) ) continue;
-
- using ( var stream = _iStorage.ReadStream(metaFileItem.FilePath!) )
- {
- geoList.AddRange(await new ReadMetaGpx(_logger).ReadGpxFileAsync(stream, geoList));
- }
- }
- return geoList;
- }
-
- ///
- /// Convert to the appSettings timezone setting
- ///
- /// current DateTime
- /// optional only to display errors
- /// The time in the specified timezone
- /// DateTime Kind should not be Local
- internal DateTime ConvertTimeZone(DateTime valueDateTime, string subPath = "")
- {
- if ( valueDateTime.Kind == DateTimeKind.Utc )
- {
- return valueDateTime;
- }
-
- // Not supported by TimeZoneInfo convert
- if ( valueDateTime.Kind != DateTimeKind.Unspecified || _appSettings.CameraTimeZoneInfo == null )
- {
- throw new ArgumentException($"valueDateTime DateTime-Kind '{valueDateTime.Kind}' " +
- $"'{subPath}' should be Unspecified", nameof(valueDateTime));
- }
-
- return TimeZoneInfo.ConvertTime(valueDateTime,
- _appSettings.CameraTimeZoneInfo, TimeZoneInfo.Utc);
- }
-
- public async Task> LoopFolderAsync(List metaFilesInDirectory)
- {
- var toUpdateMetaFiles = new List();
-
- var gpxList = await GetGpxFileAsync(metaFilesInDirectory);
- if ( gpxList.Count == 0 )
- {
- return toUpdateMetaFiles;
- }
-
- metaFilesInDirectory = GetNoLocationItems(metaFilesInDirectory);
-
- var subPath = metaFilesInDirectory.FirstOrDefault()?.ParentDirectory!;
- new GeoCacheStatusService(_cache).StatusUpdate(subPath,
- metaFilesInDirectory.Count, StatusType.Total);
-
- foreach (var metaFileItem in metaFilesInDirectory.Select(
- (value, index) => new { value, index }))
- {
- var dateTimeCameraUtc = ConvertTimeZone(metaFileItem.value.DateTime,
- metaFileItem.value.FilePath!);
-
- var fileGeoData = gpxList.MinBy(p => Math.Abs((p.DateTime - dateTimeCameraUtc).Ticks));
- if(fileGeoData == null) continue;
-
- var minutesDifference = (dateTimeCameraUtc - fileGeoData.DateTime).TotalMinutes;
- if(minutesDifference < -5 || minutesDifference > 5) continue;
-
- metaFileItem.value.Latitude = fileGeoData.Latitude;
- metaFileItem.value.Longitude = fileGeoData.Longitude;
- metaFileItem.value.LocationAltitude = fileGeoData.Altitude;
-
- toUpdateMetaFiles.Add(metaFileItem.value);
-
- // status update
- new GeoCacheStatusService(_cache).StatusUpdate(metaFileItem.value.ParentDirectory!,
- metaFileItem.index, StatusType.Current);
- }
-
- // Ready signal
- new GeoCacheStatusService(_cache).StatusUpdate(subPath,
- metaFilesInDirectory.Count, StatusType.Current);
-
- return toUpdateMetaFiles;
- }
- }
+ public class GeoIndexGpx : IGeoIndexGpx
+ {
+ private readonly AppSettings _appSettings;
+ private readonly IStorage _iStorage;
+ private readonly IMemoryCache? _cache;
+ private readonly IWebLogger _logger;
+
+ public GeoIndexGpx(AppSettings appSettings, IStorage iStorage,
+ IWebLogger logger, IMemoryCache? memoryCache = null)
+ {
+ _appSettings = appSettings;
+ _iStorage = iStorage;
+ _cache = memoryCache;
+ _logger = logger;
+ }
+
+ private static List GetNoLocationItems(IEnumerable metaFilesInDirectory)
+ {
+ return metaFilesInDirectory.Where(
+ metaFileItem =>
+ ( Math.Abs(metaFileItem.Latitude) < 0.001 && Math.Abs(metaFileItem.Longitude) < 0.001 )
+ && metaFileItem.DateTime.Year > 2) // ignore files without a date
+ .ToList();
+ }
+
+ private async Task> GetGpxFileAsync(List metaFilesInDirectory)
+ {
+ var geoList = new List();
+ foreach ( var metaFileItem in metaFilesInDirectory )
+ {
+
+ if ( !ExtensionRolesHelper.IsExtensionForceGpx(metaFileItem.FileName) ) continue;
+
+ if ( !_iStorage.ExistFile(metaFileItem.FilePath!) ) continue;
+
+ using ( var stream = _iStorage.ReadStream(metaFileItem.FilePath!) )
+ {
+ geoList.AddRange(await new ReadMetaGpx(_logger).ReadGpxFileAsync(stream, geoList));
+ }
+ }
+ return geoList;
+ }
+
+ ///
+ /// Convert to the appSettings timezone setting
+ ///
+ /// current DateTime
+ /// optional only to display errors
+ /// The time in the specified timezone
+ /// DateTime Kind should not be Local
+ internal DateTime ConvertTimeZone(DateTime valueDateTime, string subPath = "")
+ {
+ if ( valueDateTime.Kind == DateTimeKind.Utc )
+ {
+ return valueDateTime;
+ }
+
+ // Not supported by TimeZoneInfo convert
+ if ( valueDateTime.Kind != DateTimeKind.Unspecified || _appSettings.CameraTimeZoneInfo == null )
+ {
+ throw new ArgumentException($"valueDateTime DateTime-Kind '{valueDateTime.Kind}' " +
+ $"'{subPath}' should be Unspecified", nameof(valueDateTime));
+ }
+
+ return TimeZoneInfo.ConvertTime(valueDateTime,
+ _appSettings.CameraTimeZoneInfo, TimeZoneInfo.Utc);
+ }
+
+ public async Task> LoopFolderAsync(List metaFilesInDirectory)
+ {
+ var toUpdateMetaFiles = new List();
+
+ var gpxList = await GetGpxFileAsync(metaFilesInDirectory);
+ if ( gpxList.Count == 0 )
+ {
+ return toUpdateMetaFiles;
+ }
+
+ metaFilesInDirectory = GetNoLocationItems(metaFilesInDirectory);
+
+ var subPath = metaFilesInDirectory.FirstOrDefault()?.ParentDirectory!;
+ new GeoCacheStatusService(_cache).StatusUpdate(subPath,
+ metaFilesInDirectory.Count, StatusType.Total);
+
+ foreach ( var metaFileItem in metaFilesInDirectory.Select(
+ (value, index) => new { value, index }) )
+ {
+ var dateTimeCameraUtc = ConvertTimeZone(metaFileItem.value.DateTime,
+ metaFileItem.value.FilePath!);
+
+ var fileGeoData = gpxList.MinBy(p => Math.Abs(( p.DateTime - dateTimeCameraUtc ).Ticks));
+ if ( fileGeoData == null ) continue;
+
+ var minutesDifference = ( dateTimeCameraUtc - fileGeoData.DateTime ).TotalMinutes;
+ if ( minutesDifference < -5 || minutesDifference > 5 ) continue;
+
+ metaFileItem.value.Latitude = fileGeoData.Latitude;
+ metaFileItem.value.Longitude = fileGeoData.Longitude;
+ metaFileItem.value.LocationAltitude = fileGeoData.Altitude;
+
+ toUpdateMetaFiles.Add(metaFileItem.value);
+
+ // status update
+ new GeoCacheStatusService(_cache).StatusUpdate(metaFileItem.value.ParentDirectory!,
+ metaFileItem.index, StatusType.Current);
+ }
+
+ // Ready signal
+ new GeoCacheStatusService(_cache).StatusUpdate(subPath,
+ metaFilesInDirectory.Count, StatusType.Current);
+
+ return toUpdateMetaFiles;
+ }
+ }
}
diff --git a/starsky/starsky.feature.geolookup/Services/GeoReverseLookup.cs b/starsky/starsky.feature.geolookup/Services/GeoReverseLookup.cs
index 03b80e95f9..b2a792f4dc 100644
--- a/starsky/starsky.feature.geolookup/Services/GeoReverseLookup.cs
+++ b/starsky/starsky.feature.geolookup/Services/GeoReverseLookup.cs
@@ -1,4 +1,3 @@
-#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -24,211 +23,212 @@ namespace starsky.feature.geolookup.Services
[Service(typeof(IGeoReverseLookup), InjectionLifetime = InjectionLifetime.Singleton)]
[SuppressMessage("Performance", "CA1822:Mark members as static")]
public sealed class GeoReverseLookup : IGeoReverseLookup
- {
- private ReverseGeoCode? _reverseGeoCode;
- private IEnumerable? _admin1CodesAscii;
- private readonly IMemoryCache? _cache;
- private readonly AppSettings _appSettings;
- private readonly IWebLogger _logger;
- private readonly IGeoFileDownload _geoFileDownload;
-
- ///
- /// Getting GeoData
- ///
- /// to know where to store the deps files
- /// used to get IGeoFileDownload - Abstraction to download Geo Data
- /// for keeping status
- /// debug logger
- public GeoReverseLookup(AppSettings appSettings,
- IServiceScopeFactory serviceScopeFactory, IWebLogger logger,
- IMemoryCache? memoryCache = null)
- {
- _appSettings = appSettings;
- _logger = logger;
- _geoFileDownload = serviceScopeFactory.CreateScope().ServiceProvider.GetRequiredService();
- _reverseGeoCode = null;
- _admin1CodesAscii = null;
- _cache = memoryCache;
- }
-
- ///
- /// Internal API - Getting GeoData
- ///
- /// to know where to store the deps files
- /// Abstraction to download Geo Data
- /// for keeping status
- /// debug logger
- internal GeoReverseLookup(AppSettings appSettings, IGeoFileDownload geoFileDownload, IWebLogger logger, IMemoryCache? memoryCache = null)
- {
- _appSettings = appSettings;
- _logger = logger;
- // Get the IGeoFileDownload from the service scope due different injection lifetime (singleton vs scoped)
- _geoFileDownload = geoFileDownload;
- _reverseGeoCode = null;
- _admin1CodesAscii = null;
- _cache = memoryCache;
- }
-
- internal async Task<(IEnumerable, ReverseGeoCode)> SetupAsync()
- {
- await _geoFileDownload.DownloadAsync();
-
+ {
+ private ReverseGeoCode? _reverseGeoCode;
+ private IEnumerable? _admin1CodesAscii;
+ private readonly IMemoryCache? _cache;
+ private readonly AppSettings _appSettings;
+ private readonly IWebLogger _logger;
+ private readonly IGeoFileDownload _geoFileDownload;
+
+ ///
+ /// Getting GeoData
+ ///
+ /// to know where to store the deps files
+ /// used to get IGeoFileDownload - Abstraction to download Geo Data
+ /// for keeping status
+ /// debug logger
+ public GeoReverseLookup(AppSettings appSettings,
+ IServiceScopeFactory serviceScopeFactory, IWebLogger logger,
+ IMemoryCache? memoryCache = null)
+ {
+ _appSettings = appSettings;
+ _logger = logger;
+ _geoFileDownload = serviceScopeFactory.CreateScope().ServiceProvider.GetRequiredService();
+ _reverseGeoCode = null;
+ _admin1CodesAscii = null;
+ _cache = memoryCache;
+ }
+
+ ///
+ /// Internal API - Getting GeoData
+ ///
+ /// to know where to store the deps files
+ /// Abstraction to download Geo Data
+ /// for keeping status
+ /// debug logger
+ internal GeoReverseLookup(AppSettings appSettings, IGeoFileDownload geoFileDownload, IWebLogger logger, IMemoryCache? memoryCache = null)
+ {
+ _appSettings = appSettings;
+ _logger = logger;
+ // Get the IGeoFileDownload from the service scope due different injection lifetime (singleton vs scoped)
+ _geoFileDownload = geoFileDownload;
+ _reverseGeoCode = null;
+ _admin1CodesAscii = null;
+ _cache = memoryCache;
+ }
+
+ internal async Task<(IEnumerable, ReverseGeoCode)> SetupAsync()
+ {
+ await _geoFileDownload.DownloadAsync();
+
_admin1CodesAscii = GeoFileReader.ReadAdmin1Codes(
Path.Combine(_appSettings.DependenciesFolder, "admin1CodesASCII.txt"));
-
+
_reverseGeoCode = new ReverseGeoCode(
GeoFileReader.ReadExtendedGeoNames(
Path.Combine(_appSettings.DependenciesFolder, GeoFileDownload.CountryName + ".txt")));
- return (_admin1CodesAscii, _reverseGeoCode );
- }
-
- internal string? GetAdmin1Name(string countryCode, string[] adminCodes)
- {
- if (_admin1CodesAscii == null || adminCodes.Length != 4) return null;
-
- var admin1Code = countryCode + "." + adminCodes[0];
-
- var admin2Object = _admin1CodesAscii.FirstOrDefault(p => p.Code == admin1Code);
- return admin2Object?.NameASCII;
- }
-
- ///
- /// Checks for files that already done
- /// if latitude is not location 0,0, That's default
- /// If one of the meta items are missing, keep in list
- /// If extension in exifTool supported, so no gpx
- ///
- /// List of files with metadata
- /// true = overwrite the location names, that have a gps location
- /// list that can be updated
- public List RemoveNoUpdateItems(IEnumerable metaFilesInDirectory,
- bool overwriteLocationNames)
- {
- // this will overwrite the location names, that have a gps location
- if (overwriteLocationNames)
- return metaFilesInDirectory.Where(
- metaFileItem =>
- Math.Abs(metaFileItem.Latitude) > 0.001 && Math.Abs(metaFileItem.Longitude) > 0.001)
- .ToList();
-
- // the default situation
- return metaFilesInDirectory.Where(
- metaFileItem =>
- ((Math.Abs(metaFileItem.Latitude) > 0.001 && Math.Abs(metaFileItem.Longitude) > 0.001)
- && (string.IsNullOrEmpty(metaFileItem.LocationCity)
- || string.IsNullOrEmpty(metaFileItem.LocationState)
- || string.IsNullOrEmpty(metaFileItem.LocationCountry)
- )) // for now NO check on: metaFileItem.LocationCountryCode
- && ExtensionRolesHelper.IsExtensionExifToolSupported(metaFileItem.FileName)
- ).ToList();
- }
-
- ///
- /// Reverse Geo Syncing for a folder
- ///
- /// list of files to lookup
- /// true = overwrite the location names, that have a gps location
- ///
- public async Task> LoopFolderLookup(List metaFilesInDirectory,
- bool overwriteLocationNames)
- {
- metaFilesInDirectory = RemoveNoUpdateItems(metaFilesInDirectory,overwriteLocationNames);
-
- var subPath = metaFilesInDirectory.FirstOrDefault()?.ParentDirectory;
- if ( subPath == null ) return metaFilesInDirectory;
-
- new GeoCacheStatusService(_cache).StatusUpdate(subPath, metaFilesInDirectory.Count*2, StatusType.Total);
-
- foreach (var metaFileItem in metaFilesInDirectory.Select(
- (value, index) => new { value, index }))
- {
-
- var result = await GetLocation(metaFileItem.value.Latitude, metaFileItem.value.Longitude);
- new GeoCacheStatusService(_cache).StatusUpdate(metaFileItem.value.ParentDirectory!,
- metaFileItem.index, StatusType.Current);
- if ( !result.IsSuccess )
- {
- continue;
- }
- metaFileItem.value.LocationCity = result.LocationCity;
- metaFileItem.value.LocationState = result.LocationState;
- metaFileItem.value.LocationCountry = result.LocationCountry;
- metaFileItem.value.LocationCountryCode = result.LocationCountryCode;
- }
-
- // Ready signal
- new GeoCacheStatusService(_cache).StatusUpdate(subPath,
- metaFilesInDirectory.Count, StatusType.Current);
-
- return metaFilesInDirectory;
- }
-
- public async Task GetLocation(double latitude, double longitude)
- {
- if ( _reverseGeoCode == null )
- {
- (_, _reverseGeoCode) = await SetupAsync();
- }
-
- var status = new GeoLocationModel
- {
- Longitude = longitude,
- Latitude = latitude,
- IsSuccess = false,
- ErrorReason = "Unknown"
- };
-
- if ( !ValidateLocation.ValidateLatitudeLongitude(latitude,longitude) )
- {
- status.ErrorReason = "Non-valid location";
- return status;
- }
-
- // Create a point from a lat/long pair from which we want to conduct our search(es) (center)
- var place = _reverseGeoCode.CreateFromLatLong(
- status.Latitude, status.Longitude);
-
- // Find nearest
- var nearestPlace = _reverseGeoCode.NearestNeighbourSearch(place, 1).FirstOrDefault();
-
- if ( nearestPlace == null ) {
- status.ErrorReason = "No nearest place found";
- return status;
- }
-
- // Distance to avoid non logic locations
- var distanceTo = GeoDistanceTo.GetDistance(
- nearestPlace.Latitude,
- nearestPlace.Longitude,
- status.Latitude,
- status.Longitude);
-
- if ( distanceTo > 35 )
- {
+ return (_admin1CodesAscii, _reverseGeoCode);
+ }
+
+ internal string? GetAdmin1Name(string countryCode, string[] adminCodes)
+ {
+ if ( _admin1CodesAscii == null || adminCodes.Length != 4 ) return null;
+
+ var admin1Code = countryCode + "." + adminCodes[0];
+
+ var admin2Object = _admin1CodesAscii.FirstOrDefault(p => p.Code == admin1Code);
+ return admin2Object?.NameASCII;
+ }
+
+ ///
+ /// Checks for files that already done
+ /// if latitude is not location 0,0, That's default
+ /// If one of the meta items are missing, keep in list
+ /// If extension in exifTool supported, so no gpx
+ ///
+ /// List of files with metadata
+ /// true = overwrite the location names, that have a gps location
+ /// list that can be updated
+ public List RemoveNoUpdateItems(IEnumerable metaFilesInDirectory,
+ bool overwriteLocationNames)
+ {
+ // this will overwrite the location names, that have a gps location
+ if ( overwriteLocationNames )
+ return metaFilesInDirectory.Where(
+ metaFileItem =>
+ Math.Abs(metaFileItem.Latitude) > 0.001 && Math.Abs(metaFileItem.Longitude) > 0.001)
+ .ToList();
+
+ // the default situation
+ return metaFilesInDirectory.Where(
+ metaFileItem =>
+ ( ( Math.Abs(metaFileItem.Latitude) > 0.001 && Math.Abs(metaFileItem.Longitude) > 0.001 )
+ && ( string.IsNullOrEmpty(metaFileItem.LocationCity)
+ || string.IsNullOrEmpty(metaFileItem.LocationState)
+ || string.IsNullOrEmpty(metaFileItem.LocationCountry)
+ ) ) // for now NO check on: metaFileItem.LocationCountryCode
+ && ExtensionRolesHelper.IsExtensionExifToolSupported(metaFileItem.FileName)
+ ).ToList();
+ }
+
+ ///
+ /// Reverse Geo Syncing for a folder
+ ///
+ /// list of files to lookup
+ /// true = overwrite the location names, that have a gps location
+ ///
+ public async Task> LoopFolderLookup(List metaFilesInDirectory,
+ bool overwriteLocationNames)
+ {
+ metaFilesInDirectory = RemoveNoUpdateItems(metaFilesInDirectory, overwriteLocationNames);
+
+ var subPath = metaFilesInDirectory.FirstOrDefault()?.ParentDirectory;
+ if ( subPath == null ) return metaFilesInDirectory;
+
+ new GeoCacheStatusService(_cache).StatusUpdate(subPath, metaFilesInDirectory.Count * 2, StatusType.Total);
+
+ foreach ( var metaFileItem in metaFilesInDirectory.Select(
+ (value, index) => new { value, index }) )
+ {
+
+ var result = await GetLocation(metaFileItem.value.Latitude, metaFileItem.value.Longitude);
+ new GeoCacheStatusService(_cache).StatusUpdate(metaFileItem.value.ParentDirectory!,
+ metaFileItem.index, StatusType.Current);
+ if ( !result.IsSuccess )
+ {
+ continue;
+ }
+ metaFileItem.value.LocationCity = result.LocationCity;
+ metaFileItem.value.LocationState = result.LocationState;
+ metaFileItem.value.LocationCountry = result.LocationCountry;
+ metaFileItem.value.LocationCountryCode = result.LocationCountryCode;
+ }
+
+ // Ready signal
+ new GeoCacheStatusService(_cache).StatusUpdate(subPath,
+ metaFilesInDirectory.Count, StatusType.Current);
+
+ return metaFilesInDirectory;
+ }
+
+ public async Task GetLocation(double latitude, double longitude)
+ {
+ if ( _reverseGeoCode == null )
+ {
+ (_, _reverseGeoCode) = await SetupAsync();
+ }
+
+ var status = new GeoLocationModel
+ {
+ Longitude = longitude,
+ Latitude = latitude,
+ IsSuccess = false,
+ ErrorReason = "Unknown"
+ };
+
+ if ( !ValidateLocation.ValidateLatitudeLongitude(latitude, longitude) )
+ {
+ status.ErrorReason = "Non-valid location";
+ return status;
+ }
+
+ // Create a point from a lat/long pair from which we want to conduct our search(es) (center)
+ var place = _reverseGeoCode.CreateFromLatLong(
+ status.Latitude, status.Longitude);
+
+ // Find nearest
+ var nearestPlace = _reverseGeoCode.NearestNeighbourSearch(place, 1).FirstOrDefault();
+
+ if ( nearestPlace == null )
+ {
+ status.ErrorReason = "No nearest place found";
+ return status;
+ }
+
+ // Distance to avoid non logic locations
+ var distanceTo = GeoDistanceTo.GetDistance(
+ nearestPlace.Latitude,
+ nearestPlace.Longitude,
+ status.Latitude,
+ status.Longitude);
+
+ if ( distanceTo > 35 )
+ {
status.ErrorReason = "Distance to nearest place is too far";
- return status;
- }
-
- status.ErrorReason = "Success";
- status.IsSuccess = true;
- status.LocationCity = nearestPlace.NameASCII;
-
- // Catch is used for example the region VA (Vatican City)
- try
- {
- var region = new RegionInfo(nearestPlace.CountryCode);
- status.LocationCountry = region.NativeName;
- status.LocationCountryCode = region.ThreeLetterISORegionName;
- }
- catch ( ArgumentException e )
- {
- _logger.LogInformation("[GeoReverseLookup] " + e.Message);
- }
-
- status.LocationState = GetAdmin1Name(nearestPlace.CountryCode, nearestPlace.Admincodes);
-
- return status;
- }
- }
+ return status;
+ }
+
+ status.ErrorReason = "Success";
+ status.IsSuccess = true;
+ status.LocationCity = nearestPlace.NameASCII;
+
+ // Catch is used for example the region VA (Vatican City)
+ try
+ {
+ var region = new RegionInfo(nearestPlace.CountryCode);
+ status.LocationCountry = region.NativeName;
+ status.LocationCountryCode = region.ThreeLetterISORegionName;
+ }
+ catch ( ArgumentException e )
+ {
+ _logger.LogInformation("[GeoReverseLookup] " + e.Message);
+ }
+
+ status.LocationState = GetAdmin1Name(nearestPlace.CountryCode, nearestPlace.Admincodes);
+
+ return status;
+ }
+ }
}
diff --git a/starsky/starsky.feature.health/HealthCheck/DateAssemblyHealthCheck.cs b/starsky/starsky.feature.health/HealthCheck/DateAssemblyHealthCheck.cs
index 39de0a879b..9ddecc0061 100644
--- a/starsky/starsky.feature.health/HealthCheck/DateAssemblyHealthCheck.cs
+++ b/starsky/starsky.feature.health/HealthCheck/DateAssemblyHealthCheck.cs
@@ -26,9 +26,9 @@ public Task CheckHealthAsync(
CancellationToken cancellationToken = default(CancellationToken))
{
var assemblyDate = DateAssembly.GetBuildDate(Assembly.GetExecutingAssembly());
- return Task.FromResult(assemblyDate.AddDays(-2) > DateTime.UtcNow ?
+ return Task.FromResult(assemblyDate.AddDays(-2) > DateTime.UtcNow ?
HealthCheckResult.Unhealthy($"Current Date {assemblyDate.AddDays(-2)}>" +
- $"{DateTime.UtcNow} is earlier then the Assembly is build") :
+ $"{DateTime.UtcNow} is earlier then the Assembly is build") :
HealthCheckResult.Healthy("Current Date is after the Assembly is build :)"));
}
diff --git a/starsky/starsky.feature.health/HealthCheck/DiskOptionsPercentageSetup.cs b/starsky/starsky.feature.health/HealthCheck/DiskOptionsPercentageSetup.cs
index bfa82b68fb..ff0f4893aa 100644
--- a/starsky/starsky.feature.health/HealthCheck/DiskOptionsPercentageSetup.cs
+++ b/starsky/starsky.feature.health/HealthCheck/DiskOptionsPercentageSetup.cs
@@ -15,9 +15,9 @@ public static void Setup(string fullFilePath, DiskStorageOptions diskOptions, fl
{
var directoryInfo = new FileInfo(fullFilePath).Directory;
if ( directoryInfo == null ) return;
-
+
var tenPercentInBytes = Convert.ToInt64(( ( new DriveInfo(directoryInfo.Root.FullName).TotalFreeSpace /
- 1024f ) / 1024f ) * percentage );
+ 1024f ) / 1024f ) * percentage);
if ( tenPercentInBytes < 100 ) return;
diskOptions.AddDrive(
diff --git a/starsky/starsky.feature.health/HealthCheck/DiskStorageHealthCheck.cs b/starsky/starsky.feature.health/HealthCheck/DiskStorageHealthCheck.cs
index bb2dba5e4b..edd72524df 100644
--- a/starsky/starsky.feature.health/HealthCheck/DiskStorageHealthCheck.cs
+++ b/starsky/starsky.feature.health/HealthCheck/DiskStorageHealthCheck.cs
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
@@ -19,7 +18,7 @@ public class DiskStorageHealthCheck : IHealthCheck
public DiskStorageHealthCheck(DiskStorageOptions options)
{
var diskStorageOptions = options;
- _options = diskStorageOptions ?? throw new ArgumentNullException(nameof (options));
+ _options = diskStorageOptions ?? throw new ArgumentNullException(nameof(options));
}
public Task CheckHealthAsync(
@@ -28,22 +27,26 @@ public Task CheckHealthAsync(
{
try
{
- foreach (var (driveName, num) in _options.ConfiguredDrives.Values)
+ foreach ( var (driveName, num) in _options.ConfiguredDrives.Values )
{
var (exists4, actualFreeMegabytes4) = GetSystemDriveInfo(driveName);
- if (!exists4)
- return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus,
+ if ( !exists4 )
+ return Task.FromResult(new HealthCheckResult(
+ context.Registration.FailureStatus,
"Configured drive " + driveName + " is not present on system"));
- if (actualFreeMegabytes4 < num)
- return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus,
+ if ( actualFreeMegabytes4 < num )
+ return Task.FromResult(new HealthCheckResult(
+ context.Registration.FailureStatus,
$"Minimum configured megabytes for disk {driveName} is {num} " +
$"but actual free space are {actualFreeMegabytes4} megabytes"));
}
+
return Task.FromResult(HealthCheckResult.Healthy());
}
- catch (Exception ex)
+ catch ( Exception ex )
{
- return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, null, ex));
+ return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus,
+ null, ex));
}
}
@@ -54,15 +57,17 @@ private static (bool Exists, long ActualFreeMegabytes) GetSystemDriveInfo(string
{
drivesList = DriveInfo.GetDrives();
}
- catch (Exception)
+ catch ( Exception )
{
- return ( false, 0L );
+ return (false, 0L);
}
var driveInfo = Array.Find(drivesList,
drive => string.Equals(drive.Name, driveName,
StringComparison.InvariantCultureIgnoreCase));
- return driveInfo?.AvailableFreeSpace != null ? (true, driveInfo.AvailableFreeSpace / 1024L / 1024L) : (false, 0L);
+ return driveInfo?.AvailableFreeSpace != null
+ ? (true, driveInfo.AvailableFreeSpace / 1024L / 1024L)
+ : (false, 0L);
}
}
}
diff --git a/starsky/starsky.feature.health/HealthCheck/DiskStorageHealthCheckExtensions.cs b/starsky/starsky.feature.health/HealthCheck/DiskStorageHealthCheckExtensions.cs
index dc0869dba6..9c8b978500 100644
--- a/starsky/starsky.feature.health/HealthCheck/DiskStorageHealthCheckExtensions.cs
+++ b/starsky/starsky.feature.health/HealthCheck/DiskStorageHealthCheckExtensions.cs
@@ -9,15 +9,15 @@ public static class DiskStorageHealthCheckExtensions
{
public static IHealthChecksBuilder AddDiskStorageHealthCheck(
this IHealthChecksBuilder builder,
- Action setup,
- string name = null,
+ Action? setup,
+ string? name = null,
HealthStatus? failureStatus = null,
- IEnumerable tags = null,
+ IEnumerable? tags = null,
TimeSpan? timeout = null)
{
var options = new DiskStorageOptions();
setup?.Invoke(options);
- return builder.Add(new HealthCheckRegistration(name ?? "diskstorage", sp =>
+ return builder.Add(new HealthCheckRegistration(name ?? "diskstorage", sp =>
new DiskStorageHealthCheck(options), failureStatus, tags, timeout));
}
}
diff --git a/starsky/starsky.feature.health/HealthCheck/DiskStorageOptions.cs b/starsky/starsky.feature.health/HealthCheck/DiskStorageOptions.cs
index ea65eefe82..72537d6a1f 100644
--- a/starsky/starsky.feature.health/HealthCheck/DiskStorageOptions.cs
+++ b/starsky/starsky.feature.health/HealthCheck/DiskStorageOptions.cs
@@ -4,7 +4,7 @@ namespace starsky.feature.health.HealthCheck
{
public class DiskStorageOptions
{
- internal Dictionary ConfiguredDrives { get; } =
+ internal Dictionary ConfiguredDrives { get; } =
new Dictionary();
public void AddDrive(string driveName,
diff --git a/starsky/starsky.feature.health/HealthCheck/PathExistHealthCheck.cs b/starsky/starsky.feature.health/HealthCheck/PathExistHealthCheck.cs
index 5b18244a64..d4ffe993d2 100644
--- a/starsky/starsky.feature.health/HealthCheck/PathExistHealthCheck.cs
+++ b/starsky/starsky.feature.health/HealthCheck/PathExistHealthCheck.cs
@@ -17,26 +17,26 @@ public class PathExistHealthCheck : IHealthCheck
public PathExistHealthCheck(PathExistOptions options)
{
- var diskStorageOptions = options;
- _options = diskStorageOptions ?? throw new ArgumentNullException(nameof(options));
+ _options = options ?? throw new ArgumentNullException(nameof(options));
}
public Task CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
- var resultsList = _options.ConfiguredPaths.Select(path => new StorageHostFullPathFilesystem()
- .IsFolderOrFile(path)).ToList();
+ var resultsList = _options.ConfiguredPaths.Select(path =>
+ new StorageHostFullPathFilesystem()
+ .IsFolderOrFile(path)).ToList();
if ( resultsList.Count == 0 )
return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus,
$"Not configured"));
return Task.FromResult(
- resultsList.Exists(p => p == FolderOrFileModel.FolderOrFileTypeList.Deleted) ?
- new HealthCheckResult(context.Registration.FailureStatus, $"Configured path is not present on system") :
- HealthCheckResult.Healthy("Configured path is present"));
+ resultsList.Exists(p => p == FolderOrFileModel.FolderOrFileTypeList.Deleted)
+ ? new HealthCheckResult(context.Registration.FailureStatus,
+ $"Configured path is not present on system")
+ : HealthCheckResult.Healthy("Configured path is present"));
}
-
}
}
diff --git a/starsky/starsky.feature.health/HealthCheck/PathExistHealthCheckExtensions.cs b/starsky/starsky.feature.health/HealthCheck/PathExistHealthCheckExtensions.cs
index 109147ecf2..c4a5d33f92 100644
--- a/starsky/starsky.feature.health/HealthCheck/PathExistHealthCheckExtensions.cs
+++ b/starsky/starsky.feature.health/HealthCheck/PathExistHealthCheckExtensions.cs
@@ -9,16 +9,16 @@ public static class PathExistHealthCheckExtensions
{
public static IHealthChecksBuilder AddPathExistHealthCheck(
this IHealthChecksBuilder builder,
- Action setup,
- string name = null,
+ Action? setup,
+ string? name = null,
HealthStatus? failureStatus = null,
- IEnumerable tags = null,
+ IEnumerable? tags = null,
TimeSpan? timeout = null)
{
var options = new PathExistOptions();
setup?.Invoke(options);
- return builder.Add(new HealthCheckRegistration(name ?? "pathexist", sp =>
- (IHealthCheck) new PathExistHealthCheck(options), failureStatus, tags, timeout));
+ return builder.Add(new HealthCheckRegistration(name ?? "pathexist", sp =>
+ new PathExistHealthCheck(options), failureStatus, tags, timeout));
}
}
}
diff --git a/starsky/starsky.feature.health/HealthCheck/SetupHealthCheck.cs b/starsky/starsky.feature.health/HealthCheck/SetupHealthCheck.cs
index 63d34395b3..da4a004892 100644
--- a/starsky/starsky.feature.health/HealthCheck/SetupHealthCheck.cs
+++ b/starsky/starsky.feature.health/HealthCheck/SetupHealthCheck.cs
@@ -23,58 +23,58 @@ public SetupHealthCheck(AppSettings appSettings, IServiceCollection services)
public void BuilderHealth()
{
_services.AddHealthChecks()
- .AddDbContextCheck()
- .AddDiskStorageHealthCheck(
- setup: diskOptions =>
- {
- DiskOptionsPercentageSetup.Setup(_appSettings.StorageFolder,
- diskOptions);
- },
- name: "Storage_StorageFolder")
- .AddDiskStorageHealthCheck(
- setup: diskOptions =>
- {
- DiskOptionsPercentageSetup.Setup(_appSettings.ThumbnailTempFolder,
- diskOptions);
- },
- name: "Storage_ThumbnailTempFolder")
- .AddDiskStorageHealthCheck(
- setup: diskOptions =>
- {
- DiskOptionsPercentageSetup.Setup(_appSettings.TempFolder,
- diskOptions);
- },
- name: "Storage_TempFolder")
- .AddPathExistHealthCheck(
- setup: pathOptions => pathOptions.AddPath(_appSettings.StorageFolder),
- name: "Exist_StorageFolder")
- .AddPathExistHealthCheck(
- setup: pathOptions => pathOptions.AddPath(_appSettings.TempFolder),
- name: "Exist_TempFolder")
- .AddPathExistHealthCheck(
- setup: pathOptions => pathOptions.AddPath(_appSettings.ExifToolPath),
- name: "Exist_ExifToolPath")
- .AddPathExistHealthCheck(
- setup: pathOptions => pathOptions.AddPath(_appSettings.ThumbnailTempFolder),
- name: "Exist_ThumbnailTempFolder")
- .AddCheck("DateAssemblyHealthCheck");
-
- var healthSqlQuery = "SELECT * FROM `__EFMigrationsHistory` WHERE ProductVersion > 9";
+ .AddDbContextCheck()
+ .AddDiskStorageHealthCheck(
+ setup: diskOptions =>
+ {
+ DiskOptionsPercentageSetup.Setup(_appSettings.StorageFolder,
+ diskOptions);
+ },
+ name: "Storage_StorageFolder")
+ .AddDiskStorageHealthCheck(
+ setup: diskOptions =>
+ {
+ DiskOptionsPercentageSetup.Setup(_appSettings.ThumbnailTempFolder,
+ diskOptions);
+ },
+ name: "Storage_ThumbnailTempFolder")
+ .AddDiskStorageHealthCheck(
+ setup: diskOptions =>
+ {
+ DiskOptionsPercentageSetup.Setup(_appSettings.TempFolder,
+ diskOptions);
+ },
+ name: "Storage_TempFolder")
+ .AddPathExistHealthCheck(
+ setup: pathOptions => pathOptions.AddPath(_appSettings.StorageFolder),
+ name: "Exist_StorageFolder")
+ .AddPathExistHealthCheck(
+ setup: pathOptions => pathOptions.AddPath(_appSettings.TempFolder),
+ name: "Exist_TempFolder")
+ .AddPathExistHealthCheck(
+ setup: pathOptions => pathOptions.AddPath(_appSettings.ExifToolPath),
+ name: "Exist_ExifToolPath")
+ .AddPathExistHealthCheck(
+ setup: pathOptions => pathOptions.AddPath(_appSettings.ThumbnailTempFolder),
+ name: "Exist_ThumbnailTempFolder")
+ .AddCheck("DateAssemblyHealthCheck");
+
+ var healthSqlQuery = "SELECT * FROM `__EFMigrationsHistory` WHERE ProductVersion > 9";
+
+ switch ( _appSettings.DatabaseType )
+ {
+ case ( AppSettings.DatabaseTypeList.Mysql ):
+ _services.AddHealthChecks().AddMySql(_appSettings.DatabaseConnection);
+ break;
+ case AppSettings.DatabaseTypeList.Sqlite:
+ _services.AddHealthChecks().AddSqlite(_appSettings.DatabaseConnection, healthSqlQuery);
+ break;
+ case AppSettings.DatabaseTypeList.InMemoryDatabase:
+ break;
+ default:
+ throw new AggregateException("database type does not exist");
+ }
- switch (_appSettings.DatabaseType)
- {
- case (AppSettings.DatabaseTypeList.Mysql):
- _services.AddHealthChecks().AddMySql(_appSettings.DatabaseConnection);
- break;
- case AppSettings.DatabaseTypeList.Sqlite:
- _services.AddHealthChecks().AddSqlite(_appSettings.DatabaseConnection, healthSqlQuery);
- break;
- case AppSettings.DatabaseTypeList.InMemoryDatabase:
- break;
- default:
- throw new AggregateException("database type does not exist");
- }
-
}
}
}
diff --git a/starsky/starsky.feature.health/UpdateCheck/Models/ReleaseModel.cs b/starsky/starsky.feature.health/UpdateCheck/Models/ReleaseModel.cs
index 2f3655cd86..0632161320 100644
--- a/starsky/starsky.feature.health/UpdateCheck/Models/ReleaseModel.cs
+++ b/starsky/starsky.feature.health/UpdateCheck/Models/ReleaseModel.cs
@@ -19,19 +19,20 @@ public class ReleaseModel
public bool Draft { get; set; }
private string _tagName = string.Empty;
-
+
///
/// Should start with v
///
[JsonPropertyName("tag_name")]
- public string TagName {
+ public string TagName
+ {
get => _tagName;
set
{
- if ( string.IsNullOrWhiteSpace(value)) return;
+ if ( string.IsNullOrWhiteSpace(value) ) return;
if ( !value.StartsWith('v') ) Console.WriteLine($"{_tagName} Should start with v");
_tagName = value;
- }
+ }
}
}
}
diff --git a/starsky/starsky.feature.health/UpdateCheck/Services/CheckForUpdates.cs b/starsky/starsky.feature.health/UpdateCheck/Services/CheckForUpdates.cs
index 2687c7de3f..824313e846 100644
--- a/starsky/starsky.feature.health/UpdateCheck/Services/CheckForUpdates.cs
+++ b/starsky/starsky.feature.health/UpdateCheck/Services/CheckForUpdates.cs
@@ -1,4 +1,3 @@
-#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -22,7 +21,7 @@ namespace starsky.feature.health.UpdateCheck.Services
public class CheckForUpdates : ICheckForUpdates
{
internal const string GithubStarskyReleaseApi = "https://api.github.com/repos/qdraw/starsky/releases";
-
+
private readonly AppSettings? _appSettings;
private readonly IMemoryCache? _cache;
private readonly IHttpClientHelper _httpClientHelper;
@@ -33,7 +32,7 @@ public CheckForUpdates(IHttpClientHelper httpClientHelper, AppSettings? appSetti
_appSettings = appSettings;
_cache = cache;
}
-
+
internal const string QueryCheckForUpdatesCacheName = "CheckForUpdates";
///
@@ -44,53 +43,53 @@ public CheckForUpdates(IHttpClientHelper httpClientHelper, AppSettings? appSetti
[SuppressMessage("Usage", "S2589:cache & appSettings null")]
public async Task> IsUpdateNeeded(string currentVersion = "")
{
- if (_appSettings == null || _appSettings.CheckForUpdates == false )
- return new KeyValuePair(UpdateStatus.Disabled,"");
+ if ( _appSettings == null || _appSettings.CheckForUpdates == false )
+ return new KeyValuePair(UpdateStatus.Disabled, "");
currentVersion = string.IsNullOrWhiteSpace(currentVersion)
- ? _appSettings.AppVersion : currentVersion;
-
+ ? _appSettings.AppVersion : currentVersion;
+
// The CLI programs uses no cache
if ( _cache == null || _appSettings?.AddMemoryCache != true )
{
- return Parse(await QueryIsUpdateNeededAsync(),currentVersion);
+ return Parse(await QueryIsUpdateNeededAsync(), currentVersion);
}
- if ( _cache.TryGetValue(QueryCheckForUpdatesCacheName,
- out var cacheResult) && cacheResult != null )
+ if ( _cache.TryGetValue(QueryCheckForUpdatesCacheName,
+ out var cacheResult) && cacheResult != null )
{
- return Parse(( List ) cacheResult, currentVersion);
+ return Parse(( List )cacheResult, currentVersion);
}
cacheResult = await QueryIsUpdateNeededAsync();
- _cache.Set(QueryCheckForUpdatesCacheName, cacheResult,
- new TimeSpan(48,0,0));
+ _cache.Set(QueryCheckForUpdatesCacheName, cacheResult,
+ new TimeSpan(48, 0, 0));
- return Parse(( List? ) cacheResult,currentVersion);
+ return Parse(( List? )cacheResult, currentVersion);
}
internal async Task?> QueryIsUpdateNeededAsync()
{
// argument check is done in QueryIsUpdateNeeded
var (key, value) = await _httpClientHelper.ReadString(GithubStarskyReleaseApi);
- return !key ? new List() :
+ return !key ? new List() :
JsonSerializer.Deserialize>(value, DefaultJsonSerializer.CamelCase);
}
-
- internal static KeyValuePair Parse(IEnumerable? releaseModelList,
- string currentVersion )
+
+ internal static KeyValuePair Parse(IEnumerable? releaseModelList,
+ string currentVersion)
{
- var orderedReleaseModelList =
+ var orderedReleaseModelList =
releaseModelList?.OrderByDescending(p => p.TagName);
-
+
var tagName = orderedReleaseModelList?
.FirstOrDefault(p => p is { Draft: false, PreRelease: false })?.TagName;
-
+
if ( string.IsNullOrWhiteSpace(tagName) ||
- !tagName.StartsWith('v') )
+ !tagName.StartsWith('v') )
{
- return new KeyValuePair(UpdateStatus.NoReleasesFound,string.Empty);
+ return new KeyValuePair(UpdateStatus.NoReleasesFound, string.Empty);
}
try
@@ -101,7 +100,7 @@ internal static KeyValuePair Parse(IEnumerable(status, latestVersion.ToString());
}
- catch ( ArgumentException)
+ catch ( ArgumentException )
{
return new KeyValuePair(UpdateStatus.InputNotValid, string.Empty);
}
diff --git a/starsky/starsky.feature.health/starsky.feature.health.csproj b/starsky/starsky.feature.health/starsky.feature.health.csproj
index c45bb8f514..434c13c2c7 100644
--- a/starsky/starsky.feature.health/starsky.feature.health.csproj
+++ b/starsky/starsky.feature.health/starsky.feature.health.csproj
@@ -5,21 +5,22 @@
{d9c8e6e0-2526-4978-ad8c-b4e74993cfd8}
starsky.feature.health
0.6.0-beta.0
+ enable
-
+
-
-
+
+
-
+
-
-
+
+
-
+
build$([System.DateTime]::UtcNow.ToString("yyyyMMddHHmmss"))
diff --git a/starsky/starsky.feature.import/Helpers/UpdateImportTransformations.cs b/starsky/starsky.feature.import/Helpers/UpdateImportTransformations.cs
index 007f4dfd88..713d2afa12 100644
--- a/starsky/starsky.feature.import/Helpers/UpdateImportTransformations.cs
+++ b/starsky/starsky.feature.import/Helpers/UpdateImportTransformations.cs
@@ -1,4 +1,3 @@
-#nullable enable
using System.Collections.Generic;
using System.Threading.Tasks;
using starsky.foundation.database.Interfaces;
@@ -24,8 +23,9 @@ public class UpdateImportTransformations
private readonly AppSettings _appSettings;
private readonly IThumbnailQuery _thumbnailQuery;
- public UpdateImportTransformations(IWebLogger logger,
- IExifTool exifTool, ISelectorStorage selectorStorage, AppSettings appSettings, IThumbnailQuery thumbnailQuery)
+ public UpdateImportTransformations(IWebLogger logger,
+ IExifTool exifTool, ISelectorStorage selectorStorage, AppSettings appSettings,
+ IThumbnailQuery thumbnailQuery)
{
_logger = logger;
_exifTool = exifTool;
@@ -35,12 +35,14 @@ public UpdateImportTransformations(IWebLogger logger,
_thumbnailQuery = thumbnailQuery;
}
-
+
public delegate Task QueryUpdateDelegate(FileIndexItem fileIndexItem);
- public delegate Task?> QueryThumbnailUpdateDelegate(List thumbnailItems);
+
+ public delegate Task?> QueryThumbnailUpdateDelegate(
+ List thumbnailItems);
///
- /// Run Transformation on Import to the files in the database && Update fileHash in database
+ /// Run Transformation on Import to the files in the database and Update fileHash in database
///
///
/// information
@@ -53,18 +55,21 @@ internal async Task UpdateTransformations(
int colorClassTransformation, bool dateTimeParsedFromFileName,
bool indexMode)
{
- if ( !ExtensionRolesHelper.IsExtensionExifToolSupported(fileIndexItem.FileName) ) return fileIndexItem;
+ if ( !ExtensionRolesHelper.IsExtensionExifToolSupported(fileIndexItem.FileName) )
+ return fileIndexItem;
var comparedNamesList = new List();
if ( dateTimeParsedFromFileName )
{
- _logger.LogInformation($"[Import] DateTimeParsedFromFileName ExifTool Sync {fileIndexItem.FilePath}");
+ _logger.LogInformation(
+ $"[Import] DateTimeParsedFromFileName ExifTool Sync {fileIndexItem.FilePath}");
comparedNamesList = DateTimeParsedComparedNamesList();
}
if ( colorClassTransformation >= 0 )
{
- _logger.LogInformation($"[Import] ColorClassComparedNamesList ExifTool Sync {fileIndexItem.FilePath}");
+ _logger.LogInformation(
+ $"[Import] ColorClassComparedNamesList ExifTool Sync {fileIndexItem.FilePath}");
comparedNamesList = ColorClassComparedNamesList(comparedNamesList);
}
@@ -84,15 +89,17 @@ internal async Task UpdateTransformations(
{
return fileIndexItem;
}
-
+
// Hash is changed after transformation
- fileIndexItem.FileHash = (await new FileHash(_subPathStorage).GetHashCodeAsync(fileIndexItem.FilePath!)).Key;
+ fileIndexItem.FileHash =
+ ( await new FileHash(_subPathStorage).GetHashCodeAsync(fileIndexItem.FilePath!) )
+ .Key;
await queryUpdateDelegate(fileIndexItem);
return fileIndexItem.Clone();
}
-
+
internal static List DateTimeParsedComparedNamesList()
{
return new List
@@ -101,13 +108,11 @@ internal static List DateTimeParsedComparedNamesList()
nameof(FileIndexItem.DateTime).ToLowerInvariant(),
};
}
-
+
internal static List ColorClassComparedNamesList(List list)
{
list.Add(nameof(FileIndexItem.ColorClass).ToLowerInvariant());
return list;
}
-
}
}
-
diff --git a/starsky/starsky.feature.import/Interfaces/IImport.cs b/starsky/starsky.feature.import/Interfaces/IImport.cs
index b9032008c6..4f0fc94acb 100644
--- a/starsky/starsky.feature.import/Interfaces/IImport.cs
+++ b/starsky/starsky.feature.import/Interfaces/IImport.cs
@@ -1,29 +1,29 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Threading.Tasks;
using starsky.feature.import.Models;
using starsky.foundation.database.Models;
namespace starsky.feature.import.Interfaces
{
- public interface IImport
- {
- ///
- /// Test if file can be imported
- ///
- /// list of paths
- /// settings
- ///
- Task> Preflight(List fullFilePathsList,
- ImportSettingsModel importSettings);
+ public interface IImport
+ {
+ ///
+ /// Test if file can be imported
+ ///
+ /// list of paths
+ /// settings
+ ///
+ Task> Preflight(List fullFilePathsList,
+ ImportSettingsModel importSettings);
- ///
- /// Run Import
- ///
- /// list of paths
- /// settings
- ///
- Task> Importer(IEnumerable inputFullPathList,
- ImportSettingsModel importSettings);
+ ///
+ /// Run Import
+ ///
+ /// list of paths
+ /// settings
+ ///
+ Task> Importer(IEnumerable inputFullPathList,
+ ImportSettingsModel importSettings);
- }
+ }
}
diff --git a/starsky/starsky.feature.import/Models/ImportFileSettingsModel.cs b/starsky/starsky.feature.import/Models/ImportFileSettingsModel.cs
index 27cba262b0..0119b4a631 100644
--- a/starsky/starsky.feature.import/Models/ImportFileSettingsModel.cs
+++ b/starsky/starsky.feature.import/Models/ImportFileSettingsModel.cs
@@ -1,101 +1,106 @@
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http;
using starsky.foundation.platform.Models;
namespace starsky.feature.import.Models
{
- public class ImportSettingsModel
- {
- // Default constructor
- public ImportSettingsModel()
- {
- DeleteAfter = false;
- RecursiveDirectory = false;
- IndexMode = true;
- // ColorClass defaults in prop
- // Structure defaults in appSettings
- }
-
- ///
- /// Construct model using a request
- ///
- ///
- public ImportSettingsModel(HttpRequest request)
- {
- // the header defaults to zero, and that's not the correct default value
- if ( !string.IsNullOrWhiteSpace(request.Headers["ColorClass"]) &&
- int.TryParse(request.Headers["ColorClass"], out var colorClassNumber))
- {
+ public class ImportSettingsModel
+ {
+ // Default constructor
+ public ImportSettingsModel()
+ {
+ DeleteAfter = false;
+ RecursiveDirectory = false;
+ IndexMode = true;
+ // ColorClass defaults in prop
+ // Structure defaults in appSettings
+ }
+
+ ///
+ /// Construct model using a request
+ ///
+ ///
+ public ImportSettingsModel(HttpRequest request)
+ {
+ // the header defaults to zero, and that's not the correct default value
+ if ( !string.IsNullOrWhiteSpace(request.Headers["ColorClass"]) &&
+ int.TryParse(request.Headers["ColorClass"], out var colorClassNumber) )
+ {
ColorClass = colorClassNumber;
- }
-
- Structure = request.Headers["Structure"].ToString();
-
- // Always when importing using a request
- // otherwise it will stick in the temp folder
- DeleteAfter = true;
-
- // For the index Mode, false is always copy, true is check if exist in db, default true
- IndexMode = true;
-
- if ( request.Headers["IndexMode"].ToString().Equals("false",
- System.StringComparison.CurrentCultureIgnoreCase) )
- {
- IndexMode = false;
- }
-
- }
-
-
- // This is optional, when not in use ignore this setting
- private string _structure;
- public string Structure
- {
- get => string.IsNullOrEmpty(_structure) ? string.Empty : _structure; // if null>stringEmpty
- set
- {
- // Changed this => value used te be without check
- if (string.IsNullOrEmpty(value)) return;
- AppSettings.StructureCheck(value);
- _structure = value;
- }
- }
-
- public bool DeleteAfter { get; set; }
-
- public bool RecursiveDirectory { get; set; }
-
- ///
- /// -1 is ignore
- ///
- private int _colorClass = -1;
-
- ///
- /// Overwrite ColorClass settings
- /// Int value between 0 and 8
- ///
- public int ColorClass {
- get => _colorClass;
- set {
- if (value is >= 0 and <= 8) // hardcoded in FileIndexModel
- {
- _colorClass = value;
- return;
- }
- _colorClass = -1;
- }
- }
-
- ///
- /// indexing, false is always copy, true is check if exist in db,
- /// default true
- ///
- public bool IndexMode { get; set; }
-
- public ConsoleOutputMode ConsoleOutputMode { get; set; }
-
- public bool IsConsoleOutputModeDefault()
- {
- return ConsoleOutputMode.Default == ConsoleOutputMode;
- }
- }
+ }
+
+ Structure = request.Headers["Structure"].ToString();
+
+ // Always when importing using a request
+ // otherwise it will stick in the temp folder
+ DeleteAfter = true;
+
+ // For the index Mode, false is always copy, true is check if exist in db, default true
+ IndexMode = true;
+
+ if ( request.Headers["IndexMode"].ToString().Equals("false",
+ System.StringComparison.CurrentCultureIgnoreCase) )
+ {
+ IndexMode = false;
+ }
+ }
+
+
+ // This is optional, when not in use ignore this setting
+ private string _structure = string.Empty;
+
+ public string Structure
+ {
+ get => string.IsNullOrEmpty(_structure)
+ ? string.Empty
+ : _structure; // if null>stringEmpty
+ set
+ {
+ // Changed this => value used te be without check
+ if ( string.IsNullOrEmpty(value) ) return;
+ AppSettings.StructureCheck(value);
+ _structure = value;
+ }
+ }
+
+ public bool DeleteAfter { get; set; }
+
+ public bool RecursiveDirectory { get; set; }
+
+ ///
+ /// -1 is ignore
+ ///
+ private int _colorClass = -1;
+
+ ///
+ /// Overwrite ColorClass settings
+ /// Int value between 0 and 8
+ ///
+ public int ColorClass
+ {
+ get => _colorClass;
+ set
+ {
+ if ( value is >= 0 and <= 8 ) // hardcoded in FileIndexModel
+ {
+ _colorClass = value;
+ return;
+ }
+
+ _colorClass = -1;
+ }
+ }
+
+ ///
+ /// indexing, false is always copy, true is check if exist in db,
+ /// default true
+ ///
+ public bool IndexMode { get; set; }
+
+ public ConsoleOutputMode ConsoleOutputMode { get; set; }
+
+ public bool IsConsoleOutputModeDefault()
+ {
+ return ConsoleOutputMode.Default == ConsoleOutputMode;
+ }
+ }
}
diff --git a/starsky/starsky.feature.import/Services/Import.cs b/starsky/starsky.feature.import/Services/Import.cs
index 918302ff19..a6a2d6f8f5 100644
--- a/starsky/starsky.feature.import/Services/Import.cs
+++ b/starsky/starsky.feature.import/Services/Import.cs
@@ -1,4 +1,3 @@
-#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -34,6 +33,7 @@
using starsky.foundation.writemeta.Services;
[assembly: InternalsVisibleTo("starskytest")]
+
namespace starsky.feature.import.Services
{
///
@@ -43,7 +43,7 @@ namespace starsky.feature.import.Services
public class Import : IImport
{
private readonly IImportQuery? _importQuery;
-
+
// storage providers
private readonly IStorage _filesystemStorage;
private readonly IStorage _subPathStorage;
@@ -54,7 +54,7 @@ public class Import : IImport
private readonly ReadMeta _readMetaHost;
private readonly IExifTool _exifTool;
private readonly IQuery _query;
-
+
private readonly IConsole _console;
private readonly IMetaExifThumbnailService _metaExifThumbnailService;
@@ -69,7 +69,8 @@ public class Import : IImport
///
internal const string MessageDateTimeBasedOnFilename = "Date and Time based on filename";
- [SuppressMessage("Usage", "S107: Constructor has 8 parameters, which is greater than the 7 authorized")]
+ [SuppressMessage("Usage",
+ "S107: Constructor has 8 parameters, which is greater than the 7 authorized")]
public Import(
ISelectorStorage selectorStorage,
AppSettings appSettings,
@@ -84,22 +85,24 @@ public Import(
IServiceScopeFactory? serviceScopeFactory = null)
{
_importQuery = importQuery;
-
- _filesystemStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
- _subPathStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
- _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
-
- _appSettings = appSettings;
- _readMetaHost = new ReadMeta(_filesystemStorage, appSettings, null!, logger);
- _exifTool = exifTool;
- _query = query;
- _console = console;
- _metaExifThumbnailService = metaExifThumbnailService;
- _memoryCache = memoryCache;
- _serviceScopeFactory = serviceScopeFactory;
- _logger = logger;
- _updateImportTransformations = new UpdateImportTransformations(logger, _exifTool, selectorStorage, appSettings, thumbnailQuery);
- _thumbnailQuery = thumbnailQuery;
+
+ _filesystemStorage =
+ selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
+ _subPathStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
+ _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
+
+ _appSettings = appSettings;
+ _readMetaHost = new ReadMeta(_filesystemStorage, appSettings, null!, logger);
+ _exifTool = exifTool;
+ _query = query;
+ _console = console;
+ _metaExifThumbnailService = metaExifThumbnailService;
+ _memoryCache = memoryCache;
+ _serviceScopeFactory = serviceScopeFactory;
+ _logger = logger;
+ _updateImportTransformations = new UpdateImportTransformations(logger, _exifTool,
+ selectorStorage, appSettings, thumbnailQuery);
+ _thumbnailQuery = thumbnailQuery;
}
///
@@ -109,49 +112,53 @@ public Import(
/// paths
/// settings
///
- public async Task> Preflight(List fullFilePathsList,
+ public async Task> Preflight(List fullFilePathsList,
ImportSettingsModel importSettings)
{
var includedDirectoryFilePaths = AppendDirectoryFilePaths(
- fullFilePathsList,
+ fullFilePathsList,
importSettings).ToList();
-
+
// When Directory is Empty
if ( includedDirectoryFilePaths.Count == 0 )
{
return new List();
}
-
- var importIndexItemsList = (await includedDirectoryFilePaths
+
+ var importIndexItemsList = ( await includedDirectoryFilePaths
.ForEachAsync(
- async (includedFilePath)
+ async (includedFilePath)
=> await PreflightPerFile(includedFilePath, importSettings),
- _appSettings.MaxDegreesOfParallelism))!.ToList();
-
+ _appSettings.MaxDegreesOfParallelism) )!.ToList();
+
var directoriesContent = ParentFoldersDictionary(importIndexItemsList);
- importIndexItemsList = CheckForDuplicateNaming(importIndexItemsList.ToList(), directoriesContent);
+ importIndexItemsList =
+ CheckForDuplicateNaming(importIndexItemsList.ToList(), directoriesContent);
CheckForReadOnlyFileSystems(importIndexItemsList, importSettings.DeleteAfter);
return importIndexItemsList;
}
- internal List>> CheckForReadOnlyFileSystems( List importIndexItemsList, bool deleteAfter = true)
+ internal List>> CheckForReadOnlyFileSystems(
+ List importIndexItemsList, bool deleteAfter = true)
{
if ( !deleteAfter )
{
return new List>>();
}
-
+
var parentFolders = new List>>();
- foreach ( var itemSourceFullFilePath in importIndexItemsList.Select(item => item.SourceFullFilePath) )
+ foreach ( var itemSourceFullFilePath in importIndexItemsList.Select(item =>
+ item.SourceFullFilePath) )
{
var parentFolder = Directory.GetParent(itemSourceFullFilePath)
?.FullName;
if ( parentFolders.TrueForAll(p => p.Item1 != parentFolder) )
{
- parentFolders.Add(new Tuple>(parentFolder, new List{itemSourceFullFilePath}));
+ parentFolders.Add(new Tuple>(parentFolder,
+ new List { itemSourceFullFilePath }));
continue;
}
@@ -163,24 +170,25 @@ public async Task> Preflight(List fullFilePathsLis
{
var fileStorageInfo = _filesystemStorage.Info(parentFolder.Item1!);
if ( fileStorageInfo.IsFolderOrFile !=
- FolderOrFileModel.FolderOrFileTypeList.Folder ||
- fileStorageInfo.IsFileSystemReadOnly != true )
+ FolderOrFileModel.FolderOrFileTypeList.Folder ||
+ fileStorageInfo.IsFileSystemReadOnly != true )
{
continue;
}
- var items = parentFolder.Item2.Select(parentItem =>
- Array.Find(importIndexItemsList.ToArray(),
- p => p.SourceFullFilePath == parentItem)).Cast();
-
+ var items = parentFolder.Item2.Select(parentItem =>
+ Array.Find(importIndexItemsList.ToArray(),
+ p => p.SourceFullFilePath == parentItem)).Cast();
+
foreach ( var item in items )
{
importIndexItemsList[importIndexItemsList.IndexOf(item)]
.Status = ImportStatus.ReadOnlyFileSystem;
-
+
if ( _appSettings.IsVerbose() )
{
- _console.WriteLine($"🤷🗜️ Ignored, source file system is readonly try without move to copy {item.SourceFullFilePath}");
+ _console.WriteLine(
+ $"🤷🗜️ Ignored, source file system is readonly try without move to copy {item.SourceFullFilePath}");
}
}
}
@@ -194,18 +202,25 @@ public async Task> Preflight(List fullFilePathsLis
///
/// files to import, use FileIndexItem.ParentDirectory and status Ok
/// All parent folders with content
- internal Dictionary> ParentFoldersDictionary(List importIndexItemsList)
+ internal Dictionary> ParentFoldersDictionary(
+ List importIndexItemsList)
{
- var directoriesContent = new Dictionary>();
- foreach ( var importIndexItemFileIndexItemParentDirectory in importIndexItemsList.Where(p =>
- p.Status == ImportStatus.Ok).Select(p => p.FileIndexItem?.ParentDirectory) )
+ var directoriesContent = new Dictionary>();
+ foreach ( var importIndexItemFileIndexItemParentDirectory in importIndexItemsList.Where(
+ p =>
+ p.Status == ImportStatus.Ok)
+ .Select(p => p.FileIndexItem?.ParentDirectory) )
{
- if ( importIndexItemFileIndexItemParentDirectory == null || directoriesContent.ContainsKey(importIndexItemFileIndexItemParentDirectory) )
+ if ( importIndexItemFileIndexItemParentDirectory == null ||
+ directoriesContent.ContainsKey(importIndexItemFileIndexItemParentDirectory) )
continue;
-
+
var parentDirectoryList =
- _subPathStorage.GetAllFilesInDirectory(importIndexItemFileIndexItemParentDirectory).ToList();
- directoriesContent.Add(importIndexItemFileIndexItemParentDirectory, parentDirectoryList);
+ _subPathStorage
+ .GetAllFilesInDirectory(importIndexItemFileIndexItemParentDirectory)
+ .ToList();
+ directoriesContent.Add(importIndexItemFileIndexItemParentDirectory,
+ parentDirectoryList);
}
return directoriesContent;
@@ -218,108 +233,120 @@ internal Dictionary> ParentFoldersDictionary(ListDictionary of all parent folders
/// updated ImportIndexItem list
/// when there are to many files with the same name
- internal List CheckForDuplicateNaming(List importIndexItemsList,
- Dictionary> directoriesContent)
+ internal List CheckForDuplicateNaming(
+ List importIndexItemsList,
+ Dictionary> directoriesContent)
{
- foreach ( var importIndexItem in importIndexItemsList.Where(p =>
- p.Status == ImportStatus.Ok ) )
+ foreach ( var importIndexItem in importIndexItemsList.Where(p =>
+ p.Status == ImportStatus.Ok) )
{
if ( importIndexItem.FileIndexItem == null )
{
_logger.LogInformation("[CheckForDuplicateNaming] FileIndexItem is missing");
continue;
}
-
+
// Try again until the max
var updatedFilePath = "";
var indexer = 0;
for ( var i = 0; i < MaxTryGetDestinationPath; i++ )
{
updatedFilePath = AppendIndexerToFilePath(
- importIndexItem.FileIndexItem!.ParentDirectory!,
+ importIndexItem.FileIndexItem!.ParentDirectory!,
importIndexItem.FileIndexItem!.FileName!, indexer);
-
+
var currentDirectoryContent =
directoriesContent[importIndexItem.FileIndexItem.ParentDirectory!];
-
- if ( currentDirectoryContent.Contains(updatedFilePath) )
+
+ if ( currentDirectoryContent.Contains(updatedFilePath) )
{
indexer++;
continue;
}
+
currentDirectoryContent.Add(updatedFilePath);
break;
}
-
+
if ( indexer >= MaxTryGetDestinationPath || string.IsNullOrEmpty(updatedFilePath) )
{
throw new AggregateException($"tried after {MaxTryGetDestinationPath} times");
}
-
+
importIndexItem.FileIndexItem!.FilePath = updatedFilePath;
importIndexItem.FileIndexItem.FileName = PathHelper.GetFileName(updatedFilePath);
importIndexItem.FilePath = updatedFilePath;
}
+
return importIndexItemsList;
}
-
+
///
/// To Add files form directory to list
///
/// full file Path
/// settings to add recursive
- private List> AppendDirectoryFilePaths(List fullFilePathsList,
+ private List> AppendDirectoryFilePaths(
+ List fullFilePathsList,
ImportSettingsModel importSettings)
{
- var includedDirectoryFilePaths = new List>();
+ var includedDirectoryFilePaths = new List>();
foreach ( var fullFilePath in fullFilePathsList )
{
- if ( _filesystemStorage.ExistFolder(fullFilePath) && importSettings.RecursiveDirectory)
+ if ( _filesystemStorage.ExistFolder(fullFilePath) &&
+ importSettings.RecursiveDirectory )
{
// recursive
- includedDirectoryFilePaths.AddRange(_filesystemStorage.
- GetAllFilesInDirectoryRecursive(fullFilePath)
+ includedDirectoryFilePaths.AddRange(_filesystemStorage
+ .GetAllFilesInDirectoryRecursive(fullFilePath)
.Where(ExtensionRolesHelper.IsExtensionSyncSupported)
.Select(syncedFiles => new KeyValuePair(syncedFiles, true)));
continue;
}
- if ( _filesystemStorage.ExistFolder(fullFilePath) && !importSettings.RecursiveDirectory)
+
+ if ( _filesystemStorage.ExistFolder(fullFilePath) &&
+ !importSettings.RecursiveDirectory )
{
// non-recursive
- includedDirectoryFilePaths.AddRange(_filesystemStorage.GetAllFilesInDirectory(fullFilePath)
+ includedDirectoryFilePaths.AddRange(_filesystemStorage
+ .GetAllFilesInDirectory(fullFilePath)
.Where(ExtensionRolesHelper.IsExtensionSyncSupported)
.Select(syncedFiles => new KeyValuePair(syncedFiles, true)));
continue;
}
-
+
includedDirectoryFilePaths.Add(
- new KeyValuePair(fullFilePath,_filesystemStorage.ExistFile(fullFilePath))
+ new KeyValuePair(fullFilePath,
+ _filesystemStorage.ExistFile(fullFilePath))
);
}
return includedDirectoryFilePaths;
}
- internal async Task PreflightPerFile(KeyValuePair inputFileFullPath,
+ internal async Task PreflightPerFile(
+ KeyValuePair inputFileFullPath,
ImportSettingsModel importSettings)
{
if ( _appSettings.ImportIgnore.Exists(p => inputFileFullPath.Key.Contains(p)) )
{
ConsoleIfVerbose($"❌ skip due rules: {inputFileFullPath.Key} ");
- return new ImportIndexItem{
- Status = ImportStatus.Ignore,
+ return new ImportIndexItem
+ {
+ Status = ImportStatus.Ignore,
FilePath = inputFileFullPath.Key,
SourceFullFilePath = inputFileFullPath.Key,
AddToDatabase = DateTime.UtcNow
};
}
-
+
if ( !inputFileFullPath.Value || !_filesystemStorage.ExistFile(inputFileFullPath.Key) )
{
ConsoleIfVerbose($"❌ not found: {inputFileFullPath.Key}");
- return new ImportIndexItem{
- Status = ImportStatus.NotFound,
+ return new ImportIndexItem
+ {
+ Status = ImportStatus.NotFound,
FilePath = inputFileFullPath.Key,
SourceFullFilePath = inputFileFullPath.Key,
AddToDatabase = DateTime.UtcNow
@@ -327,63 +354,65 @@ internal async Task PreflightPerFile(KeyValuePair
}
var imageFormat = ExtensionRolesHelper.GetImageFormat(
- _filesystemStorage.ReadStream(inputFileFullPath.Key,
- 160));
-
+ _filesystemStorage.ReadStream(inputFileFullPath.Key,
+ 160));
+
// Check if extension is correct && Check if the file is correct
if ( !ExtensionRolesHelper.IsExtensionSyncSupported(inputFileFullPath.Key) ||
- !ExtensionRolesHelper.IsExtensionSyncSupported($".{imageFormat}") )
+ !ExtensionRolesHelper.IsExtensionSyncSupported($".{imageFormat}") )
{
ConsoleIfVerbose($"❌ extension not supported: {inputFileFullPath.Key}");
return new ImportIndexItem
{
- Status = ImportStatus.FileError,
- FilePath = inputFileFullPath.Key,
+ Status = ImportStatus.FileError,
+ FilePath = inputFileFullPath.Key,
SourceFullFilePath = inputFileFullPath.Key
};
}
-
- var hashList = await
+
+ var hashList = await
new FileHash(_filesystemStorage).GetHashCodeAsync(inputFileFullPath.Key);
if ( !hashList.Value )
{
ConsoleIfVerbose($"❌ FileHash error {inputFileFullPath.Key}");
return new ImportIndexItem
{
- Status = ImportStatus.FileError,
+ Status = ImportStatus.FileError,
FilePath = inputFileFullPath.Key,
SourceFullFilePath = inputFileFullPath.Key
};
}
-
- if (importSettings.IndexMode && await _importQuery!.IsHashInImportDbAsync(hashList.Key) )
+
+ if ( importSettings.IndexMode &&
+ await _importQuery!.IsHashInImportDbAsync(hashList.Key) )
{
ConsoleIfVerbose($"🤷 Ignored, exist already {inputFileFullPath.Key}");
return new ImportIndexItem
{
- Status = ImportStatus.IgnoredAlreadyImported,
+ Status = ImportStatus.IgnoredAlreadyImported,
FilePath = inputFileFullPath.Key,
FileHash = hashList.Key,
AddToDatabase = DateTime.UtcNow,
SourceFullFilePath = inputFileFullPath.Key
};
- }
-
+ }
+
// Only accept files with correct meta data
// Check if there is a xmp file that contains data
- var fileIndexItem = await _readMetaHost.ReadExifAndXmpFromFileAsync(inputFileFullPath.Key);
-
+ var fileIndexItem =
+ await _readMetaHost.ReadExifAndXmpFromFileAsync(inputFileFullPath.Key);
+
// Parse the filename and create a new importIndexItem object
- var importIndexItem = ObjectCreateIndexItem(inputFileFullPath.Key, imageFormat,
+ var importIndexItem = ObjectCreateIndexItem(inputFileFullPath.Key, imageFormat,
hashList.Key, fileIndexItem!, importSettings.ColorClass,
_filesystemStorage.Info(inputFileFullPath.Key).Size);
-
+
// Update the parent and filenames
importIndexItem = ApplyStructure(importIndexItem, importSettings.Structure);
-
+
return importIndexItem;
}
-
+
private void ConsoleIfVerbose(string message)
{
if ( _appSettings.IsVerbose() )
@@ -404,12 +433,12 @@ private void ConsoleIfVerbose(string message)
/// Add filesize in bytes
///
private ImportIndexItem ObjectCreateIndexItem(
- string inputFileFullPath,
- ExtensionRolesHelper.ImageFormat imageFormat,
- string fileHashCode,
- FileIndexItem fileIndexItem,
- int colorClassTransformation,
- long size)
+ string inputFileFullPath,
+ ExtensionRolesHelper.ImageFormat imageFormat,
+ string fileHashCode,
+ FileIndexItem fileIndexItem,
+ int colorClassTransformation,
+ long size)
{
var importIndexItem = new ImportIndexItem(_appSettings)
{
@@ -421,11 +450,12 @@ private ImportIndexItem ObjectCreateIndexItem(
FilePath = fileIndexItem.FilePath,
ColorClass = fileIndexItem.ColorClass
};
-
+
// used for files without a Exif Date for example WhatsApp images
if ( fileIndexItem.DateTime.Year == 1 )
{
- importIndexItem.FileIndexItem.DateTime = importIndexItem.ParseDateTimeFromFileName();
+ importIndexItem.FileIndexItem.DateTime =
+ importIndexItem.ParseDateTimeFromFileName();
// used to sync exifTool and to let the user know that the transformation has been applied
importIndexItem.FileIndexItem.Description = MessageDateTimeBasedOnFilename;
// only set when date is parsed if not ignore update
@@ -436,12 +466,12 @@ private ImportIndexItem ObjectCreateIndexItem(
}
// Also add Camera brand to list
- importIndexItem.MakeModel = importIndexItem.FileIndexItem.MakeModel;
-
+ importIndexItem.MakeModel = importIndexItem.FileIndexItem.MakeModel;
+
// AddToDatabase is Used by the importer History agent
importIndexItem.FileIndexItem.AddToDatabase = DateTime.UtcNow;
importIndexItem.AddToDatabase = DateTime.UtcNow;
-
+
importIndexItem.FileIndexItem.Size = size;
importIndexItem.FileIndexItem.FileHash = fileHashCode;
importIndexItem.FileIndexItem.ImageFormat = imageFormat;
@@ -449,7 +479,7 @@ private ImportIndexItem ObjectCreateIndexItem(
if ( colorClassTransformation < 0 ) return importIndexItem;
// only when set in ImportSettingsModel
- var colorClass = ( ColorClassParser.Color ) colorClassTransformation;
+ var colorClass = ( ColorClassParser.Color )colorClassTransformation;
importIndexItem.FileIndexItem.ColorClass = colorClass;
importIndexItem.ColorClass = colorClass;
return importIndexItem;
@@ -461,42 +491,47 @@ private ImportIndexItem ObjectCreateIndexItem(
///
/// to overwrite, keep empty to ignore
/// Names applied to FileIndexItem
- private ImportIndexItem ApplyStructure(ImportIndexItem importIndexItem, string overwriteStructure)
+ private ImportIndexItem ApplyStructure(ImportIndexItem importIndexItem,
+ string overwriteStructure)
{
importIndexItem.Structure = _appSettings.Structure;
-
+
// Feature to overwrite structures when importing using a header
// Overwrite the structure in the ImportIndexItem
- if (!string.IsNullOrWhiteSpace(overwriteStructure))
+ if ( !string.IsNullOrWhiteSpace(overwriteStructure) )
{
importIndexItem.Structure = overwriteStructure;
}
-
+
var structureService = new StructureService(_subPathStorage, importIndexItem.Structure);
-
+
importIndexItem.FileIndexItem!.ParentDirectory = structureService.ParseSubfolders(
- importIndexItem.FileIndexItem.DateTime, importIndexItem.FileIndexItem.FileCollectionName!,
- FilenamesHelper.GetFileExtensionWithoutDot(importIndexItem.FileIndexItem.FileName!));
-
+ importIndexItem.FileIndexItem.DateTime,
+ importIndexItem.FileIndexItem.FileCollectionName!,
+ FilenamesHelper.GetFileExtensionWithoutDot(importIndexItem.FileIndexItem
+ .FileName!));
+
importIndexItem.FileIndexItem.FileName = structureService.ParseFileName(
- importIndexItem.FileIndexItem.DateTime, importIndexItem.FileIndexItem.FileCollectionName!,
- FilenamesHelper.GetFileExtensionWithoutDot(importIndexItem.FileIndexItem.FileName!));
+ importIndexItem.FileIndexItem.DateTime,
+ importIndexItem.FileIndexItem.FileCollectionName!,
+ FilenamesHelper.GetFileExtensionWithoutDot(importIndexItem.FileIndexItem
+ .FileName!));
importIndexItem.FilePath = importIndexItem.FileIndexItem.FilePath;
-
+
return importIndexItem;
}
-
+
///
/// Run import on list of files and folders (full path style)
///
/// list of files and folders (full path style)
/// settings
/// status object
- public async Task> Importer(IEnumerable inputFullPathList,
+ public async Task> Importer(IEnumerable inputFullPathList,
ImportSettingsModel importSettings)
{
var preflightItemList = await Preflight(inputFullPathList.ToList(), importSettings);
-
+
// When directory is empty
if ( preflightItemList.Count == 0 )
{
@@ -506,12 +541,12 @@ public async Task> Importer(IEnumerable inputFullP
var directoriesContent = ParentFoldersDictionary(preflightItemList);
if ( importSettings.IndexMode ) await CreateParentFolders(directoriesContent);
- var importIndexItemsList = (await preflightItemList.AsEnumerable()
+ var importIndexItemsList = ( await preflightItemList.AsEnumerable()
.ForEachAsync(
- async (preflightItem)
+ async (preflightItem)
=> await Importer(preflightItem, importSettings),
- _appSettings.MaxDegreesOfParallelism))!.ToList();
-
+ _appSettings.MaxDegreesOfParallelism) )!.ToList();
+
return importIndexItemsList;
}
@@ -521,23 +556,26 @@ public async Task> Importer(IEnumerable inputFullP
///
///
///
- internal async Task> CreateMataThumbnail(IEnumerable
- importIndexItemsList, ImportSettingsModel importSettings)
+ internal async Task> CreateMataThumbnail(
+ IEnumerable
+ importIndexItemsList, ImportSettingsModel importSettings)
{
if ( _appSettings.MetaThumbnailOnImport == false ||
- !importSettings.IndexMode )
+ !importSettings.IndexMode )
{
return new List<(bool, bool, string, string?)>();
}
-
+
var items = importIndexItemsList
.Where(p => p.Status == ImportStatus.Ok)
- .Select(p => (p.FilePath, p.FileIndexItem!.FileHash)).Cast<(string,string)>().ToList();
-
+ .Select(p => (p.FilePath, p.FileIndexItem!.FileHash)).Cast<(string, string)>()
+ .ToList();
+
if ( items.Count == 0 )
{
return new List<(bool, bool, string, string?)>();
}
+
return await _metaExifThumbnailService.AddMetaThumbnail(items);
}
@@ -548,81 +586,94 @@ public async Task> Importer(IEnumerable inputFullP
/// config file
/// optional settings
/// status
- internal async Task Importer(ImportIndexItem? importIndexItem,
+ internal async Task Importer(ImportIndexItem? importIndexItem,
ImportSettingsModel importSettings)
{
if ( importIndexItem is not { Status: ImportStatus.Ok } ) return importIndexItem!;
// True when exist and file type is raw
var xmpExistForThisFileType = ExistXmpSidecarForThisFileType(importIndexItem);
-
- if ( xmpExistForThisFileType || (_appSettings.ExifToolImportXmpCreate
- && ExtensionRolesHelper.IsExtensionForceXmp(importIndexItem.FilePath)))
+
+ if ( xmpExistForThisFileType || ( _appSettings.ExifToolImportXmpCreate
+ && ExtensionRolesHelper.IsExtensionForceXmp(
+ importIndexItem.FilePath) ) )
{
// When a xmp file already exist (only for raws)
// AND when this created afterwards with the ExifToolImportXmpCreate setting (only for raws)
importIndexItem.FileIndexItem!.AddSidecarExtension("xmp");
}
-
+
// Add item to database
await AddToQueryAndImportDatabaseAsync(importIndexItem, importSettings);
-
+
// Copy
- if ( _appSettings.IsVerbose() ) _logger.LogInformation("[Import] Next Action = Copy" +
- $" {importIndexItem.SourceFullFilePath} {importIndexItem.FilePath}");
- using (var sourceStream = _filesystemStorage.ReadStream(importIndexItem.SourceFullFilePath))
+ if ( _appSettings.IsVerbose() )
+ _logger.LogInformation("[Import] Next Action = Copy" +
+ $" {importIndexItem.SourceFullFilePath} {importIndexItem.FilePath}");
+ using ( var sourceStream =
+ _filesystemStorage.ReadStream(importIndexItem.SourceFullFilePath) )
await _subPathStorage.WriteStreamAsync(sourceStream, importIndexItem.FilePath!);
-
+
// Copy the sidecar file
- if ( xmpExistForThisFileType)
- {
- var xmpSourceFullFilePath = ExtensionRolesHelper.ReplaceExtensionWithXmp(importIndexItem.SourceFullFilePath);
- var destinationXmpFullPath = ExtensionRolesHelper.ReplaceExtensionWithXmp(importIndexItem.FilePath);
- _filesystemStorage.FileCopy(xmpSourceFullFilePath, destinationXmpFullPath);
- }
-
- await CreateSideCarFile(importIndexItem, xmpExistForThisFileType);
-
- // Run Exiftool to Update for example colorClass
- UpdateImportTransformations.QueryUpdateDelegate? updateItemAsync = null;
- UpdateImportTransformations.QueryThumbnailUpdateDelegate? queryThumbnailUpdateDelegate = null;
-
- if ( importSettings.IndexMode )
- {
- var queryFactory = new QueryFactory(
- new SetupDatabaseTypes(_appSettings), _query,
- _memoryCache, _appSettings, _serviceScopeFactory, _logger);
- updateItemAsync = queryFactory.Query()!.UpdateItemAsync;
- queryThumbnailUpdateDelegate = (thumbnailItems) => new ThumbnailQueryFactory(
- new SetupDatabaseTypes(_appSettings), _serviceScopeFactory,
- _thumbnailQuery, _logger).ThumbnailQuery()!.AddThumbnailRangeAsync(thumbnailItems);
- }
-
- await CreateMataThumbnail(new List{importIndexItem}, importSettings);
-
- // next: and save the database item
- importIndexItem.FileIndexItem = await _updateImportTransformations
- .UpdateTransformations(updateItemAsync, importIndexItem.FileIndexItem!,
- importSettings.ColorClass, importIndexItem.DateTimeFromFileName, importSettings.IndexMode);
-
- await UpdateCreateMetaThumbnail(queryThumbnailUpdateDelegate, importIndexItem.FileIndexItem?.FileHash, importSettings.IndexMode);
-
- DeleteFileAfter(importSettings, importIndexItem);
-
- if ( _appSettings.IsVerbose() ) _console.Write("+");
- return importIndexItem;
+ if ( xmpExistForThisFileType )
+ {
+ var xmpSourceFullFilePath =
+ ExtensionRolesHelper.ReplaceExtensionWithXmp(importIndexItem
+ .SourceFullFilePath);
+ var destinationXmpFullPath =
+ ExtensionRolesHelper.ReplaceExtensionWithXmp(importIndexItem.FilePath);
+ _filesystemStorage.FileCopy(xmpSourceFullFilePath, destinationXmpFullPath);
+ }
+
+ await CreateSideCarFile(importIndexItem, xmpExistForThisFileType);
+
+ // Run Exiftool to Update for example colorClass
+ UpdateImportTransformations.QueryUpdateDelegate? updateItemAsync = null;
+ UpdateImportTransformations.QueryThumbnailUpdateDelegate? queryThumbnailUpdateDelegate =
+ null;
+
+ if ( importSettings.IndexMode )
+ {
+ var queryFactory = new QueryFactory(
+ new SetupDatabaseTypes(_appSettings), _query,
+ _memoryCache, _appSettings, _serviceScopeFactory, _logger);
+ updateItemAsync = queryFactory.Query()!.UpdateItemAsync;
+ queryThumbnailUpdateDelegate = (thumbnailItems) => new ThumbnailQueryFactory(
+ new SetupDatabaseTypes(_appSettings), _serviceScopeFactory,
+ _thumbnailQuery, _logger).ThumbnailQuery()!
+ .AddThumbnailRangeAsync(thumbnailItems);
+ }
+
+ await CreateMataThumbnail(new List { importIndexItem },
+ importSettings);
+
+ // next: and save the database item
+ importIndexItem.FileIndexItem = await _updateImportTransformations
+ .UpdateTransformations(updateItemAsync, importIndexItem.FileIndexItem!,
+ importSettings.ColorClass, importIndexItem.DateTimeFromFileName,
+ importSettings.IndexMode);
+
+ await UpdateCreateMetaThumbnail(queryThumbnailUpdateDelegate,
+ importIndexItem.FileIndexItem?.FileHash, importSettings.IndexMode);
+
+ DeleteFileAfter(importSettings, importIndexItem);
+
+ if ( _appSettings.IsVerbose() ) _console.Write("+");
+ return importIndexItem;
}
- private async Task UpdateCreateMetaThumbnail( UpdateImportTransformations.QueryThumbnailUpdateDelegate? queryThumbnailUpdateDelegate,
+ private async Task UpdateCreateMetaThumbnail(
+ UpdateImportTransformations.QueryThumbnailUpdateDelegate? queryThumbnailUpdateDelegate,
string? fileHash, bool indexMode)
{
- if ( fileHash == null || _appSettings.MetaThumbnailOnImport == false || !indexMode || queryThumbnailUpdateDelegate == null) return;
+ if ( fileHash == null || _appSettings.MetaThumbnailOnImport == false || !indexMode ||
+ queryThumbnailUpdateDelegate == null ) return;
// Check if fastest version is available to show
var setStatus = _thumbnailStorage.ExistFile(
ThumbnailNameHelper.Combine(fileHash, ThumbnailSize.TinyMeta));
await queryThumbnailUpdateDelegate(new List
{
- new ThumbnailResultDataTransferModel(fileHash,setStatus)
+ new ThumbnailResultDataTransferModel(fileHash, setStatus)
});
}
@@ -640,6 +691,7 @@ private void DeleteFileAfter(ImportSettingsModel importSettings,
{
_console.WriteLine($"🚮 Delete file: {importIndexItem.SourceFullFilePath}");
}
+
_filesystemStorage.FileDelete(importIndexItem.SourceFullFilePath);
}
@@ -649,38 +701,39 @@ private void DeleteFileAfter(ImportSettingsModel importSettings,
///
///
///
- private async Task CreateSideCarFile(ImportIndexItem importIndexItem, bool xmpExistForThisFileType)
+ private async Task CreateSideCarFile(ImportIndexItem importIndexItem,
+ bool xmpExistForThisFileType)
{
- if ( _appSettings.ExifToolImportXmpCreate && !xmpExistForThisFileType)
+ if ( _appSettings.ExifToolImportXmpCreate && !xmpExistForThisFileType )
{
- var exifCopy = new ExifCopy(_subPathStorage,
- _thumbnailStorage, _exifTool, new ReadMeta(_subPathStorage,
- _appSettings, null!, _logger),_thumbnailQuery);
+ var exifCopy = new ExifCopy(_subPathStorage,
+ _thumbnailStorage, _exifTool, new ReadMeta(_subPathStorage,
+ _appSettings, null!, _logger), _thumbnailQuery);
await exifCopy.XmpSync(importIndexItem.FileIndexItem!.FilePath!);
}
}
///
- /// Support for include sidecar files - True when exist && current filetype is raw
+ /// Support for include sidecar files - True when exist and current filetype is raw
///
/// to get the SourceFullFilePath
- /// True when exist && current filetype is raw
+ /// True when exist and current filetype is raw
internal bool ExistXmpSidecarForThisFileType(ImportIndexItem importIndexItem)
{
if ( string.IsNullOrEmpty(importIndexItem.SourceFullFilePath) )
{
return false;
}
-
+
// Support for include sidecar files
var xmpSourceFullFilePath =
ExtensionRolesHelper.ReplaceExtensionWithXmp(importIndexItem
.SourceFullFilePath);
return ExtensionRolesHelper.IsExtensionForceXmp(importIndexItem
- .SourceFullFilePath) &&
- _filesystemStorage.ExistFile(xmpSourceFullFilePath);
+ .SourceFullFilePath) &&
+ _filesystemStorage.ExistFile(xmpSourceFullFilePath);
}
-
+
///
/// Add item to database
///
@@ -690,9 +743,10 @@ internal async Task AddToQueryAndImportDatabaseAsync(
{
if ( !importSettings.IndexMode || _importQuery?.TestConnection() != true )
{
- if ( _appSettings.IsVerbose() ) _logger.LogInformation(" AddToQueryAndImportDatabaseAsync Ignored - " +
- $"IndexMode {importSettings.IndexMode} " +
- $"TestConnection {_importQuery?.TestConnection()}");
+ if ( _appSettings.IsVerbose() )
+ _logger.LogInformation(" AddToQueryAndImportDatabaseAsync Ignored - " +
+ $"IndexMode {importSettings.IndexMode} " +
+ $"TestConnection {_importQuery?.TestConnection()}");
return;
}
@@ -702,11 +756,13 @@ internal async Task AddToQueryAndImportDatabaseAsync(
_memoryCache, _appSettings, _serviceScopeFactory, _logger);
var query = queryFactory.Query();
await query!.AddItemAsync(importIndexItem.FileIndexItem!);
-
+
// Add to check db, to avoid duplicate input
- var importQuery = new ImportQueryFactory(new SetupDatabaseTypes(_appSettings), _importQuery,_console, _logger).ImportQuery();
- await importQuery!.AddAsync(importIndexItem, importSettings.IsConsoleOutputModeDefault() );
-
+ var importQuery = new ImportQueryFactory(new SetupDatabaseTypes(_appSettings),
+ _importQuery, _console, _logger).ImportQuery();
+ await importQuery!.AddAsync(importIndexItem,
+ importSettings.IsConsoleOutputModeDefault());
+
await query.DisposeAsync();
}
@@ -723,7 +779,8 @@ internal async Task AddToQueryAndImportDatabaseAsync(
/// number
/// subPath style
/// test_1.jpg with complete filePath
- internal static string AppendIndexerToFilePath(string parentDirectory, string fileName , int index)
+ internal static string AppendIndexerToFilePath(string parentDirectory, string fileName,
+ int index)
{
if ( index >= 1 )
{
@@ -733,14 +790,15 @@ internal static string AppendIndexerToFilePath(string parentDirectory, string fi
FilenamesHelper.GetFileExtensionWithoutDot(fileName)
);
}
+
return PathHelper.AddSlash(parentDirectory) + PathHelper.RemovePrefixDbSlash(fileName);
}
-
+
///
/// Create parent folders if the folder does not exist on disk
///
/// List of all ParentFolders
- private async Task CreateParentFolders(Dictionary> directoriesContent)
+ private async Task CreateParentFolders(Dictionary> directoriesContent)
{
foreach ( var parentDirectoryPath in directoriesContent )
{
@@ -756,15 +814,16 @@ private async Task CreateParentFolders(Dictionary> directori
await CreateNewDatabaseDirectory(parentPath.ToString());
- if ( _subPathStorage.ExistFolder(parentPath.ToString()))
+ if ( _subPathStorage.ExistFolder(parentPath.ToString()) )
{
continue;
}
+
_subPathStorage.CreateDirectory(parentPath.ToString());
}
}
}
-
+
///
/// Temp place to store parent Directories to avoid lots of Database requests
///
@@ -777,16 +836,16 @@ private async Task CreateParentFolders(Dictionary> directori
/// async task
private async Task CreateNewDatabaseDirectory(string parentPath)
{
- if ( AddedParentDirectories.Contains(parentPath) ||
- _query.SingleItem(parentPath) != null ) return;
-
+ if ( AddedParentDirectories.Contains(parentPath) ||
+ _query.SingleItem(parentPath) != null ) return;
+
var item = new FileIndexItem(PathHelper.RemoveLatestSlash(parentPath))
{
AddToDatabase = DateTime.UtcNow,
IsDirectory = true,
ColorClass = ColorClassParser.Color.None
};
-
+
await _query.AddItemAsync(item);
AddedParentDirectories.Add(parentPath);
}
diff --git a/starsky/starsky.feature.import/Services/ImportCli.cs b/starsky/starsky.feature.import/Services/ImportCli.cs
index 91f50c5862..4d2e5c6789 100644
--- a/starsky/starsky.feature.import/Services/ImportCli.cs
+++ b/starsky/starsky.feature.import/Services/ImportCli.cs
@@ -27,7 +27,7 @@ public ImportCli(IImport importService, AppSettings appSettings, IConsole consol
_console = console;
_exifToolDownload = exifToolDownload;
}
-
+
///
/// Command line importer to Database and update disk
///
@@ -41,21 +41,22 @@ public async Task Importer(string[] args)
await _exifToolDownload.DownloadExifTool(_appSettings.IsWindows);
_appSettings.ApplicationType = AppSettings.StarskyAppType.Importer;
- if (ArgsHelper.NeedHelp(args) || new ArgsHelper(_appSettings)
- .GetPathFormArgs(args,false).Length <= 1)
+ if ( ArgsHelper.NeedHelp(args) || new ArgsHelper(_appSettings)
+ .GetPathFormArgs(args, false).Length <= 1 )
{
new ArgsHelper(_appSettings, _console).NeedHelpShowDialog();
return;
}
-
+
var inputPathListFormArgs = new ArgsHelper(_appSettings).GetPathListFormArgs(args);
-
+
if ( _appSettings.IsVerbose() ) foreach ( var inputPath in inputPathListFormArgs )
+ {
+ _console.WriteLine($">> import: {inputPath}");
+ }
+
+ var importSettings = new ImportSettingsModel
{
- _console.WriteLine($">> import: {inputPath}");
- }
-
- var importSettings = new ImportSettingsModel {
DeleteAfter = ArgsHelper.GetMove(args),
RecursiveDirectory = ArgsHelper.NeedRecursive(args),
IndexMode = ArgsHelper.GetIndexMode(args),
@@ -63,15 +64,15 @@ public async Task Importer(string[] args)
ConsoleOutputMode = ArgsHelper.GetConsoleOutputMode(args)
};
- if ( _appSettings.IsVerbose() )
+ if ( _appSettings.IsVerbose() )
{
_console.WriteLine($"Options: DeleteAfter: {importSettings.DeleteAfter}, " +
- $"RecursiveDirectory {importSettings.RecursiveDirectory}, " +
- $"ColorClass (overwrite) {importSettings.ColorClass}, " +
- $"Structure {_appSettings.Structure}, " +
- $"IndexMode {importSettings.IndexMode}");
+ $"RecursiveDirectory {importSettings.RecursiveDirectory}, " +
+ $"ColorClass (overwrite) {importSettings.ColorClass}, " +
+ $"Structure {_appSettings.Structure}, " +
+ $"IndexMode {importSettings.IndexMode}");
}
-
+
var stopWatch = Stopwatch.StartNew();
var result = await _importService.Importer(inputPathListFormArgs, importSettings);
@@ -86,9 +87,10 @@ private void WriteOutputStatus(ImportSettingsModel importSettings, List p.Status == ImportStatus.Ok);
_console.WriteLine($"\nDone Importing {okCount}");
- if ( okCount != 0 ) {
+ if ( okCount != 0 )
+ {
_console.WriteLine($"Time: {Math.Round(stopWatch.Elapsed.TotalSeconds, 1)} " +
- $"sec. or {Math.Round(stopWatch.Elapsed.TotalMinutes, 1)} min.");
+ $"sec. or {Math.Round(stopWatch.Elapsed.TotalMinutes, 1)} min.");
}
_console.WriteLine($"Failed: {result.Count(p => p.Status != ImportStatus.Ok)}");
}
@@ -97,16 +99,16 @@ private void WriteOutputStatus(ImportSettingsModel importSettings, List
{e9c60bf0-09b6-40c9-95b5-25c7a185365e}
0.6.0-beta.0
+ enable
-
+
-
-
-
+
+
+
-
+
diff --git a/starsky/starsky.feature.metaupdate/Helpers/AddNotFoundInIndexStatus.cs b/starsky/starsky.feature.metaupdate/Helpers/AddNotFoundInIndexStatus.cs
index 65124442d0..4512d17d50 100644
--- a/starsky/starsky.feature.metaupdate/Helpers/AddNotFoundInIndexStatus.cs
+++ b/starsky/starsky.feature.metaupdate/Helpers/AddNotFoundInIndexStatus.cs
@@ -1,6 +1,4 @@
using System.Collections.Generic;
-using System.Linq;
-using Microsoft.EntityFrameworkCore;
using starsky.foundation.database.Helpers;
using starsky.foundation.database.Models;
@@ -10,14 +8,14 @@ public static class AddNotFoundInIndexStatus
{
internal static void Update(IEnumerable inputFilePaths, List fileIndexResultsList)
{
- foreach (var subPath in inputFilePaths)
+ foreach ( var subPath in inputFilePaths )
{
// when item is not in the database
if ( fileIndexResultsList.Exists(p => p.FilePath == subPath) )
{
continue;
}
- StatusCodesHelper.ReturnExifStatusError(new FileIndexItem(subPath),
+ StatusCodesHelper.ReturnExifStatusError(new FileIndexItem(subPath),
FileIndexItem.ExifStatus.NotFoundNotInIndex,
fileIndexResultsList);
}
diff --git a/starsky/starsky.feature.metaupdate/Helpers/AddParentCacheIfNotExist.cs b/starsky/starsky.feature.metaupdate/Helpers/AddParentCacheIfNotExist.cs
index 9b6af936e2..cfdb76191d 100644
--- a/starsky/starsky.feature.metaupdate/Helpers/AddParentCacheIfNotExist.cs
+++ b/starsky/starsky.feature.metaupdate/Helpers/AddParentCacheIfNotExist.cs
@@ -17,7 +17,7 @@ public AddParentCacheIfNotExist(IQuery query, IWebLogger logger)
_query = query;
_logger = logger;
}
-
+
internal async Task> AddParentCacheIfNotExistAsync(IEnumerable updatedPaths)
{
var parentDirectoryList = new HashSet();
@@ -27,25 +27,25 @@ internal async Task> AddParentCacheIfNotExistAsync(IEnumerable
+ var shouldAddParentDirectoriesToCache = parentDirectoryList.Where(parentDirectory =>
!_query.CacheGetParentFolder(parentDirectory).Item1).ToList();
-
+
if ( shouldAddParentDirectoriesToCache.Count == 0 )
{
return new List();
}
var databaseQueryResult = await _query.GetAllObjectsAsync(shouldAddParentDirectoriesToCache);
-
- _logger.LogInformation("[AddParentCacheIfNotExist] files added to cache " +
- string.Join(",", shouldAddParentDirectoriesToCache));
-
+
+ _logger.LogInformation("[AddParentCacheIfNotExist] files added to cache " +
+ string.Join(",", shouldAddParentDirectoriesToCache));
+
foreach ( var directory in shouldAddParentDirectoriesToCache )
{
var byDirectory = databaseQueryResult.Where(p => p.ParentDirectory == directory).ToList();
_query.AddCacheParentItem(directory, byDirectory);
}
- return shouldAddParentDirectoriesToCache;
+ return shouldAddParentDirectoriesToCache;
}
}
}
diff --git a/starsky/starsky.feature.metaupdate/Helpers/AppendXmpPathsWhenCollectionsFalse.cs b/starsky/starsky.feature.metaupdate/Helpers/AppendXmpPathsWhenCollectionsFalse.cs
index 922af793a6..75a47697d6 100644
--- a/starsky/starsky.feature.metaupdate/Helpers/AppendXmpPathsWhenCollectionsFalse.cs
+++ b/starsky/starsky.feature.metaupdate/Helpers/AppendXmpPathsWhenCollectionsFalse.cs
@@ -12,7 +12,7 @@ public static List AppendXmpPathsWhenCollectionsFalse(bool collections,
{
return inputFilePaths;
}
-
+
var inputFilePathsWithXmpFiles = new List();
// append xmp files to list (does not need to exist on disk)
@@ -23,7 +23,7 @@ public static List AppendXmpPathsWhenCollectionsFalse(bool collections,
ExtensionRolesHelper.ReplaceExtensionWithXmp(
inputFilePath));
}
-
+
inputFilePaths.AddRange(inputFilePathsWithXmpFiles);
return inputFilePaths;
}
diff --git a/starsky/starsky.feature.metaupdate/Interfaces/IMetaPreflight.cs b/starsky/starsky.feature.metaupdate/Interfaces/IMetaPreflight.cs
index 96c9a6988f..3b5db0d34b 100644
--- a/starsky/starsky.feature.metaupdate/Interfaces/IMetaPreflight.cs
+++ b/starsky/starsky.feature.metaupdate/Interfaces/IMetaPreflight.cs
@@ -7,7 +7,7 @@ namespace starsky.feature.metaupdate.Interfaces
public interface IMetaPreflight
{
Task<(List fileIndexResultsList, Dictionary> changedFileIndexItemName)>
- PreflightAsync(FileIndexItem inputModel, List inputFilePaths,
+ PreflightAsync(FileIndexItem? inputModel, List inputFilePaths,
bool append,
bool collections, int rotateClock);
}
diff --git a/starsky/starsky.feature.metaupdate/Interfaces/IMetaReplaceService.cs b/starsky/starsky.feature.metaupdate/Interfaces/IMetaReplaceService.cs
index d680707141..eee22f8b78 100644
--- a/starsky/starsky.feature.metaupdate/Interfaces/IMetaReplaceService.cs
+++ b/starsky/starsky.feature.metaupdate/Interfaces/IMetaReplaceService.cs
@@ -6,7 +6,7 @@ namespace starsky.feature.metaupdate.Interfaces
{
public interface IMetaReplaceService
{
- Task> Replace(string f, string fieldName,
+ Task> Replace(string f, string fieldName,
string search, string replace, bool collections);
}
}
diff --git a/starsky/starsky.feature.metaupdate/Interfaces/IMetaUpdateService.cs b/starsky/starsky.feature.metaupdate/Interfaces/IMetaUpdateService.cs
index 235cce73e8..aa8d0f20df 100644
--- a/starsky/starsky.feature.metaupdate/Interfaces/IMetaUpdateService.cs
+++ b/starsky/starsky.feature.metaupdate/Interfaces/IMetaUpdateService.cs
@@ -17,9 +17,9 @@ public interface IMetaUpdateService
///
///
Task> UpdateAsync(
- Dictionary> changedFileIndexItemName,
+ Dictionary> changedFileIndexItemName,
List fileIndexResultsList,
- FileIndexItem inputModel, // only when changedFileIndexItemName = null
+ FileIndexItem? inputModel, // only when changedFileIndexItemName = null
bool collections, bool append, // only when changedFileIndexItemName = null
int rotateClock);
diff --git a/starsky/starsky.feature.metaupdate/Services/DeleteItem.cs b/starsky/starsky.feature.metaupdate/Services/DeleteItem.cs
index b7266765fd..e488fe1c21 100644
--- a/starsky/starsky.feature.metaupdate/Services/DeleteItem.cs
+++ b/starsky/starsky.feature.metaupdate/Services/DeleteItem.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using starsky.feature.metaupdate.Interfaces;
@@ -29,50 +30,60 @@ public DeleteItem(IQuery query, AppSettings appSettings, ISelectorStorage select
_thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
_statusCodeHelper = new StatusCodesHelper(appSettings);
}
-
+
public async Task> DeleteAsync(string filePath, bool collections)
{
var inputFilePaths = PathHelper.SplitInputFilePaths(filePath);
var fileIndexResultsList = new List();
var collectionAndInsideDirectoryList = new List();
- foreach (var subPath in inputFilePaths)
+ foreach ( var subPath in inputFilePaths )
{
var detailView = _query.SingleItem(subPath, null, collections, false);
- if (detailView?.FileIndexItem?.FilePath == null)
+ if ( detailView?.FileIndexItem?.FilePath == null )
{
HandleNotFoundStatus(subPath, fileIndexResultsList);
continue;
}
// Status should be deleted before you can delete the item
- if (_iStorage.IsFolderOrFile(detailView.FileIndexItem.FilePath) == FolderOrFileModel.FolderOrFileTypeList.Deleted)
+ if ( _iStorage.IsFolderOrFile(detailView.FileIndexItem.FilePath) ==
+ FolderOrFileModel.FolderOrFileTypeList.Deleted )
{
- HandleNotFoundSourceMissingStatus(detailView.FileIndexItem, fileIndexResultsList);
+ HandleNotFoundSourceMissingStatus(detailView.FileIndexItem,
+ fileIndexResultsList);
continue;
}
// Dir is readonly / don't delete
- if (_statusCodeHelper.IsReadOnlyStatus(detailView) == FileIndexItem.ExifStatus.ReadOnly)
+ if ( _statusCodeHelper.IsReadOnlyStatus(detailView) ==
+ FileIndexItem.ExifStatus.ReadOnly )
{
HandleReadOnlyStatus(detailView.FileIndexItem, fileIndexResultsList);
continue;
}
- if (StatusCodesHelper.IsDeletedStatus(detailView) != FileIndexItem.ExifStatus.Deleted)
+ if ( StatusCodesHelper.IsDeletedStatus(detailView) !=
+ FileIndexItem.ExifStatus.Deleted )
{
- HandleOperationNotSupportedStatus(detailView.FileIndexItem, fileIndexResultsList);
+ HandleOperationNotSupportedStatus(detailView.FileIndexItem,
+ fileIndexResultsList);
continue;
}
- collectionAndInsideDirectoryList.AddRange(DetailView.GetCollectionSubPathList(detailView.FileIndexItem, collections, subPath));
+ collectionAndInsideDirectoryList.AddRange(
+ DetailView.GetCollectionSubPathList(detailView.FileIndexItem, collections,
+ subPath));
// For deleting content of an entire directory
if ( detailView.FileIndexItem.IsDirectory != true ) continue;
// when deleting a folder the collections setting does nothing
- collectionAndInsideDirectoryList.AddRange((await _query.GetAllFilesAsync(detailView.FileIndexItem.FilePath)).Select(itemInDirectory => itemInDirectory.FilePath));
+ var items =
+ ( await _query.GetAllFilesAsync(detailView.FileIndexItem.FilePath) ).Select(
+ itemInDirectory => itemInDirectory.FilePath!);
+ collectionAndInsideDirectoryList.AddRange(items);
}
await HandleCollectionDeletion(collectionAndInsideDirectoryList, fileIndexResultsList);
@@ -80,21 +91,24 @@ public async Task> DeleteAsync(string filePath, bool collect
return fileIndexResultsList;
}
- private async Task HandleCollectionDeletion(List collectionAndInsideDirectoryList, List fileIndexResultsList)
+ [SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")]
+ private async Task HandleCollectionDeletion(List collectionAndInsideDirectoryList,
+ List fileIndexResultsList)
{
// collectionAndInsideDirectoryList should not have duplicate items
- foreach (var collectionSubPath in new HashSet(collectionAndInsideDirectoryList))
+ foreach ( var collectionSubPath in
+ new HashSet(collectionAndInsideDirectoryList) )
{
var detailViewItem = _query.SingleItem(collectionSubPath, null, false, false);
// null only happens when some other process also delete this item
- if (detailViewItem == null) continue;
+ if ( detailViewItem == null ) continue;
// return a Ok, which means the file is deleted
- detailViewItem.FileIndexItem.Status = FileIndexItem.ExifStatus.Ok;
-
+ detailViewItem.FileIndexItem!.Status = FileIndexItem.ExifStatus.Ok;
+
// remove thumbnail from disk
- _thumbnailStorage.FileDelete(detailViewItem.FileIndexItem.FileHash);
+ _thumbnailStorage.FileDelete(detailViewItem.FileIndexItem!.FileHash!);
fileIndexResultsList.Add(detailViewItem.FileIndexItem.Clone());
// remove item from db
@@ -104,9 +118,11 @@ private async Task HandleCollectionDeletion(List collectionAndInsideDire
RemoveFileOrFolderFromDisk(detailViewItem);
// the child directories are still stored in the database
- if (detailViewItem.FileIndexItem.IsDirectory != true) continue;
+ if ( detailViewItem.FileIndexItem.IsDirectory != true ) continue;
- foreach (var item in (await _query.GetAllRecursiveAsync(collectionSubPath)).Where(p => p.IsDirectory == true))
+ foreach ( var item in
+ ( await _query.GetAllRecursiveAsync(collectionSubPath) ).Where(p =>
+ p.IsDirectory == true) )
{
item.Status = FileIndexItem.ExifStatus.Deleted;
fileIndexResultsList.Add(item.Clone());
@@ -115,30 +131,38 @@ private async Task HandleCollectionDeletion(List collectionAndInsideDire
}
}
- private static void HandleNotFoundStatus(string subPath, List fileIndexResultsList)
+ private static void HandleNotFoundStatus(string subPath,
+ List fileIndexResultsList)
{
- StatusCodesHelper.ReturnExifStatusError(new FileIndexItem(subPath), FileIndexItem.ExifStatus.NotFoundNotInIndex, fileIndexResultsList);
+ StatusCodesHelper.ReturnExifStatusError(new FileIndexItem(subPath),
+ FileIndexItem.ExifStatus.NotFoundNotInIndex, fileIndexResultsList);
}
- private static void HandleNotFoundSourceMissingStatus(FileIndexItem fileIndexItem, List fileIndexResultsList)
+ private static void HandleNotFoundSourceMissingStatus(FileIndexItem fileIndexItem,
+ List fileIndexResultsList)
{
- StatusCodesHelper.ReturnExifStatusError(fileIndexItem, FileIndexItem.ExifStatus.NotFoundSourceMissing, fileIndexResultsList);
+ StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
+ FileIndexItem.ExifStatus.NotFoundSourceMissing, fileIndexResultsList);
}
- private static void HandleReadOnlyStatus(FileIndexItem fileIndexItem, List fileIndexResultsList)
+ private static void HandleReadOnlyStatus(FileIndexItem fileIndexItem,
+ List fileIndexResultsList)
{
- StatusCodesHelper.ReturnExifStatusError(fileIndexItem, FileIndexItem.ExifStatus.ReadOnly, fileIndexResultsList);
+ StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
+ FileIndexItem.ExifStatus.ReadOnly, fileIndexResultsList);
}
- private static void HandleOperationNotSupportedStatus(FileIndexItem fileIndexItem, List fileIndexResultsList)
+ private static void HandleOperationNotSupportedStatus(FileIndexItem fileIndexItem,
+ List fileIndexResultsList)
{
- StatusCodesHelper.ReturnExifStatusError(fileIndexItem, FileIndexItem.ExifStatus.OperationNotSupported, fileIndexResultsList);
+ StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
+ FileIndexItem.ExifStatus.OperationNotSupported, fileIndexResultsList);
}
private void RemoveXmpSideCarFile(DetailView detailViewItem)
{
// remove the sidecar file (if exist)
- if ( ExtensionRolesHelper.IsExtensionForceXmp(detailViewItem.FileIndexItem
+ if ( ExtensionRolesHelper.IsExtensionForceXmp(detailViewItem.FileIndexItem!
.FileName) )
{
_iStorage.FileDelete(
@@ -151,21 +175,21 @@ private void RemoveJsonSideCarFile(DetailView detailViewItem)
{
// remove the json sidecar file (if exist)
var jsonSubPath = JsonSidecarLocation.JsonLocation(detailViewItem
- .FileIndexItem.ParentDirectory, detailViewItem
- .FileIndexItem.FileName);
+ .FileIndexItem!.ParentDirectory!, detailViewItem
+ .FileIndexItem.FileName!);
_iStorage.FileDelete(jsonSubPath);
}
private void RemoveFileOrFolderFromDisk(DetailView detailViewItem)
{
- if ( detailViewItem.FileIndexItem.IsDirectory == true )
+ if ( detailViewItem.FileIndexItem!.IsDirectory == true )
{
- _iStorage.FolderDelete(detailViewItem.FileIndexItem.FilePath);
+ _iStorage.FolderDelete(detailViewItem.FileIndexItem!.FilePath!);
return;
}
-
+
// and remove the actual file
- _iStorage.FileDelete(detailViewItem.FileIndexItem.FilePath);
+ _iStorage.FileDelete(detailViewItem.FileIndexItem.FilePath!);
}
}
}
diff --git a/starsky/starsky.feature.metaupdate/Services/MetaInfo.cs b/starsky/starsky.feature.metaupdate/Services/MetaInfo.cs
index 35039e278b..017c726d8f 100644
--- a/starsky/starsky.feature.metaupdate/Services/MetaInfo.cs
+++ b/starsky/starsky.feature.metaupdate/Services/MetaInfo.cs
@@ -28,47 +28,47 @@ public MetaInfo(IQuery query, AppSettings appSettings, ISelectorStorage selector
{
_query = query;
_iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
- _readMeta = new ReadMeta(_iStorage,appSettings, memoryCache, logger);
+ _readMeta = new ReadMeta(_iStorage, appSettings, memoryCache, logger);
_statusCodeHelper = new StatusCodesHelper(appSettings);
}
-
+
public async Task> GetInfoAsync(List inputFilePaths, bool collections)
{
// the result list
var fileIndexResultsList = new List();
-
- foreach (var subPath in inputFilePaths)
+
+ foreach ( var subPath in inputFilePaths )
{
var detailView = _query.SingleItem(subPath, null, collections, false);
-
+
if ( detailView?.FileIndexItem == null )
{
- StatusCodesHelper.ReturnExifStatusError(new FileIndexItem(subPath),
+ StatusCodesHelper.ReturnExifStatusError(new FileIndexItem(subPath),
FileIndexItem.ExifStatus.NotFoundNotInIndex,
fileIndexResultsList);
continue;
}
-
+
if ( !_iStorage.ExistFile(detailView.FileIndexItem.FilePath!) )
{
- StatusCodesHelper.ReturnExifStatusError(detailView.FileIndexItem!,
+ StatusCodesHelper.ReturnExifStatusError(detailView.FileIndexItem!,
FileIndexItem.ExifStatus.NotFoundSourceMissing,
fileIndexResultsList);
- continue;
+ continue;
}
-
+
// Check if extension is supported for ExtensionExifToolSupportedList
// Not all files are able to write with exifTool
- if(!ExtensionRolesHelper.IsExtensionExifToolSupported(detailView.FileIndexItem!.FileName)
- && !ExtensionRolesHelper.IsExtensionSidecar(detailView.FileIndexItem!.FileName))
+ if ( !ExtensionRolesHelper.IsExtensionExifToolSupported(detailView.FileIndexItem!.FileName)
+ && !ExtensionRolesHelper.IsExtensionSidecar(detailView.FileIndexItem!.FileName) )
{
StatusCodesHelper.ReturnExifStatusError(
- await new FileIndexItemJsonParser(_iStorage).ReadAsync(detailView.FileIndexItem),
+ await new FileIndexItemJsonParser(_iStorage).ReadAsync(detailView.FileIndexItem),
FileIndexItem.ExifStatus.ExifWriteNotSupported,
fileIndexResultsList);
continue;
}
-
+
var statusResults = StatusCodesHelper.IsDeletedStatus(detailView);
// only when default status to avoid unneeded checks
if ( statusResults == FileIndexItem.ExifStatus.Default ) statusResults = _statusCodeHelper.IsReadOnlyStatus(detailView);
@@ -76,11 +76,11 @@ public async Task> GetInfoAsync(List inputFilePaths,
if ( statusResults == FileIndexItem.ExifStatus.Default ) statusResults = FileIndexItem.ExifStatus.Ok;
var collectionSubPathList = DetailView.GetCollectionSubPathList(detailView.FileIndexItem, collections, subPath);
-
+
foreach ( var collectionSubPath in collectionSubPathList )
{
var collectionItem = await _readMeta.ReadExifAndXmpFromFileAsync(collectionSubPath);
-
+
collectionItem!.Status = statusResults;
collectionItem.CollectionPaths = collectionSubPathList;
collectionItem.ImageFormat =
diff --git a/starsky/starsky.feature.metaupdate/Services/MetaPreflight.cs b/starsky/starsky.feature.metaupdate/Services/MetaPreflight.cs
index 393ead7620..3d3372dcba 100644
--- a/starsky/starsky.feature.metaupdate/Services/MetaPreflight.cs
+++ b/starsky/starsky.feature.metaupdate/Services/MetaPreflight.cs
@@ -13,7 +13,6 @@
using starsky.foundation.storage.Interfaces;
using starsky.foundation.storage.Models;
using starsky.foundation.storage.Storage;
-#nullable enable
namespace starsky.feature.metaupdate.Services
{
@@ -30,7 +29,8 @@ public MetaPreflight(IQuery query, AppSettings appSettings, ISelectorStorage? se
_query = query;
_appSettings = appSettings;
_logger = logger;
- if ( selectorStorage != null ){
+ if ( selectorStorage != null )
+ {
_iStorage = selectorStorage.Get(
SelectorStorage.StorageServices.SubPath);
}
@@ -38,44 +38,44 @@ public MetaPreflight(IQuery query, AppSettings appSettings, ISelectorStorage? se
public async Task<(List fileIndexResultsList,
Dictionary> changedFileIndexItemName)>
- PreflightAsync(FileIndexItem inputModel, List inputFilePaths,
+ PreflightAsync(FileIndexItem? inputModel, List inputFilePaths,
bool append, bool collections, int rotateClock)
{
// the result list
var fileIndexUpdateList = new List();
-
+
// Per file stored key = string[fileHash] item => List
// FileIndexItem.name (e.g. Tags) that are changed
var changedFileIndexItemName = new Dictionary>();
-
+
// Prefill cache to avoid fast updating issues
- await new AddParentCacheIfNotExist(_query,_logger).AddParentCacheIfNotExistAsync(inputFilePaths);
+ await new AddParentCacheIfNotExist(_query, _logger).AddParentCacheIfNotExistAsync(inputFilePaths);
// add xmp files to the list
inputFilePaths = AppendXmpPathsWhenCollectionsFalseHelper.AppendXmpPathsWhenCollectionsFalse(collections, inputFilePaths);
-
+
var resultFileIndexItemsList = await _query.GetObjectsByFilePathAsync(
inputFilePaths, collections);
foreach ( var fileIndexItem in resultFileIndexItemsList )
{
// Files that are not on disk
- if ( _iStorage!.IsFolderOrFile(fileIndexItem.FilePath!) ==
- FolderOrFileModel.FolderOrFileTypeList.Deleted )
+ if ( _iStorage!.IsFolderOrFile(fileIndexItem.FilePath!) ==
+ FolderOrFileModel.FolderOrFileTypeList.Deleted )
{
- StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
+ StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
FileIndexItem.ExifStatus.NotFoundSourceMissing,
fileIndexUpdateList);
- continue;
+ continue;
}
-
+
// Dir is readonly / don't edit
- if ( new StatusCodesHelper(_appSettings).IsReadOnlyStatus(fileIndexItem)
- == FileIndexItem.ExifStatus.ReadOnly)
+ if ( new StatusCodesHelper(_appSettings).IsReadOnlyStatus(fileIndexItem)
+ == FileIndexItem.ExifStatus.ReadOnly )
{
- StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
+ StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
FileIndexItem.ExifStatus.ReadOnly,
fileIndexUpdateList);
- continue;
+ continue;
}
CompareAllLabelsAndRotation(changedFileIndexItemName,
@@ -85,18 +85,18 @@ public MetaPreflight(IQuery query, AppSettings appSettings, ISelectorStorage? se
CheckGeoLocationStatus(fileIndexItem);
// this one is good :)
- if ( fileIndexItem.Status is FileIndexItem.ExifStatus.Default or FileIndexItem.ExifStatus.OkAndSame)
+ if ( fileIndexItem.Status is FileIndexItem.ExifStatus.Default or FileIndexItem.ExifStatus.OkAndSame )
{
fileIndexItem.Status = FileIndexItem.ExifStatus.Ok;
}
-
+
// Deleted is allowed but the status need be updated
- if (( StatusCodesHelper.IsDeletedStatus(fileIndexItem)
- == FileIndexItem.ExifStatus.Deleted) )
+ if ( ( StatusCodesHelper.IsDeletedStatus(fileIndexItem)
+ == FileIndexItem.ExifStatus.Deleted ) )
{
fileIndexItem.Status = FileIndexItem.ExifStatus.Deleted;
}
-
+
// The hash in FileIndexItem is not correct
// Clone to not change after update
fileIndexUpdateList.Add(fileIndexItem);
@@ -119,7 +119,7 @@ private static void CheckGeoLocationStatus(FileIndexItem fileIndexItem)
{
if ( fileIndexItem.Latitude == 0 || fileIndexItem.Longitude == 0 )
return;
-
+
var result = ValidateLocation.ValidateLatitudeLongitude(
fileIndexItem.Latitude, fileIndexItem.Longitude);
if ( !result )
@@ -138,18 +138,18 @@ private static void CheckGeoLocationStatus(FileIndexItem fileIndexItem)
/// object that include the changes
/// true= for tags to add
/// rotation value 1 left, -1 right, 0 nothing
- public static void CompareAllLabelsAndRotation( Dictionary> changedFileIndexItemName,
- FileIndexItem collectionsFileIndexItem, FileIndexItem statusModel, bool append, int rotateClock)
+ public static void CompareAllLabelsAndRotation(Dictionary> changedFileIndexItemName,
+ FileIndexItem collectionsFileIndexItem, FileIndexItem? statusModel, bool append, int rotateClock)
{
if ( changedFileIndexItemName == null || string.IsNullOrEmpty(collectionsFileIndexItem.FilePath) )
throw new MissingFieldException(nameof(changedFileIndexItemName));
-
+
// compare and add changes to collectionsDetailView
- var comparedNamesList = FileIndexCompareHelper.Compare(collectionsFileIndexItem,
+ var comparedNamesList = FileIndexCompareHelper.Compare(collectionsFileIndexItem,
statusModel, append);
// if requested, add changes to rotation
- collectionsFileIndexItem =
+ collectionsFileIndexItem =
RotationCompare(rotateClock, collectionsFileIndexItem, comparedNamesList);
collectionsFileIndexItem.LastChanged = comparedNamesList;
@@ -162,7 +162,7 @@ public static void CompareAllLabelsAndRotation( Dictionary>
// overwrite list if already exist
changedFileIndexItemName[collectionsFileIndexItem.FilePath!] = comparedNamesList;
}
-
+
///
/// Add to comparedNames list and add to detail view
///
@@ -170,14 +170,14 @@ public static void CompareAllLabelsAndRotation( Dictionary>
/// main db object
/// list of types that are changes
/// updated image
- public static FileIndexItem RotationCompare(int rotateClock, FileIndexItem fileIndexItem,
+ public static FileIndexItem RotationCompare(int rotateClock, FileIndexItem fileIndexItem,
ICollection comparedNamesList)
{
// Do orientation / Rotate if needed (after compare)
- if (!FileIndexItem.IsRelativeOrientation(rotateClock)) return fileIndexItem;
+ if ( !FileIndexItem.IsRelativeOrientation(rotateClock) ) return fileIndexItem;
// run this on detail view => statusModel is always default
fileIndexItem.SetRelativeOrientation(rotateClock);
-
+
// list of exifTool to update this field
if ( !comparedNamesList.Contains(nameof(fileIndexItem.Orientation).ToLowerInvariant()) )
{
diff --git a/starsky/starsky.feature.metaupdate/Services/MetaReplaceService.cs b/starsky/starsky.feature.metaupdate/Services/MetaReplaceService.cs
index 2fe49e59da..702a606f02 100644
--- a/starsky/starsky.feature.metaupdate/Services/MetaReplaceService.cs
+++ b/starsky/starsky.feature.metaupdate/Services/MetaReplaceService.cs
@@ -24,7 +24,7 @@ public class MetaReplaceService : IMetaReplaceService
{
private readonly IQuery _query;
private readonly AppSettings _appSettings;
- private readonly IStorage _iStorage;
+ private readonly IStorage _iStorage = new StorageHostFullPathFilesystem();
private readonly IWebLogger _logger;
/// Replace meta content
@@ -32,11 +32,16 @@ public class MetaReplaceService : IMetaReplaceService
/// Settings of the application
/// storage abstraction
/// web logger
- public MetaReplaceService(IQuery query, AppSettings appSettings, ISelectorStorage selectorStorage, IWebLogger logger)
+ public MetaReplaceService(IQuery query, AppSettings appSettings,
+ ISelectorStorage? selectorStorage, IWebLogger logger)
{
_query = query;
_appSettings = appSettings;
- if ( selectorStorage != null ) _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
+ if ( selectorStorage != null )
+ {
+ _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
+ }
+
_logger = logger;
}
@@ -48,60 +53,74 @@ public MetaReplaceService(IQuery query, AppSettings appSettings, ISelectorStorag
/// from
/// to
/// stack collections
- public async Task> Replace(string f, string fieldName, string search, string replace, bool collections)
+ public async Task> Replace(string f, string fieldName, string search,
+ string replace, bool collections)
{
// when you search for nothing, your fast done
- if ( string.IsNullOrEmpty(search) ) return new List
+ if ( string.IsNullOrEmpty(search) )
{
- new FileIndexItem{Status = FileIndexItem.ExifStatus.OperationNotSupported}
- };
+ return
+ [
+ new FileIndexItem { Status = FileIndexItem.ExifStatus.OperationNotSupported }
+ ];
+ }
// escaping null values
if ( string.IsNullOrEmpty(replace) ) replace = string.Empty;
- if ( ! FileIndexCompareHelper.CheckIfPropertyExist(fieldName) ) return new List
- {
- new FileIndexItem{Status = FileIndexItem.ExifStatus.OperationNotSupported}
- };
+ if ( !FileIndexCompareHelper.CheckIfPropertyExist(fieldName) )
+ return new List
+ {
+ new FileIndexItem
+ {
+ Status = FileIndexItem.ExifStatus.OperationNotSupported
+ }
+ };
var inputFilePaths = PathHelper.SplitInputFilePaths(f).ToList();
- inputFilePaths = AppendXmpPathsWhenCollectionsFalseHelper.AppendXmpPathsWhenCollectionsFalse(collections, inputFilePaths);
+ inputFilePaths =
+ AppendXmpPathsWhenCollectionsFalseHelper.AppendXmpPathsWhenCollectionsFalse(
+ collections, inputFilePaths);
// the result list
var fileIndexUpdatedList = new List();
// Prefill cache to avoid fast updating issues
- await new AddParentCacheIfNotExist(_query,_logger).AddParentCacheIfNotExistAsync(inputFilePaths);
-
+ await new AddParentCacheIfNotExist(_query, _logger).AddParentCacheIfNotExistAsync(
+ inputFilePaths);
+
// Assumes that this give status Ok back by default
var queryFileIndexItemsList = await _query.GetObjectsByFilePathAsync(
inputFilePaths, collections);
-
+
// to collect
foreach ( var fileIndexItem in queryFileIndexItemsList )
{
// if folder is deleted
- if ( _iStorage.IsFolderOrFile(fileIndexItem.FilePath!) == FolderOrFileModel.FolderOrFileTypeList.Deleted )
+ if ( _iStorage.IsFolderOrFile(fileIndexItem.FilePath!) ==
+ FolderOrFileModel.FolderOrFileTypeList.Deleted )
{
- StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
+ StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
FileIndexItem.ExifStatus.NotFoundSourceMissing,
fileIndexUpdatedList);
- continue;
+ continue;
}
-
+
// Dir is readonly / don't edit
- if ( new StatusCodesHelper(_appSettings).IsReadOnlyStatus(fileIndexItem)
- == FileIndexItem.ExifStatus.ReadOnly)
+ if ( new StatusCodesHelper(_appSettings).IsReadOnlyStatus(fileIndexItem)
+ == FileIndexItem.ExifStatus.ReadOnly )
{
- StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
+ StatusCodesHelper.ReturnExifStatusError(fileIndexItem,
FileIndexItem.ExifStatus.ReadOnly,
fileIndexUpdatedList);
- continue;
+ continue;
}
+
fileIndexUpdatedList.Add(fileIndexItem);
}
- fileIndexUpdatedList = SearchAndReplace(fileIndexUpdatedList, fieldName, search, replace);
+ fileIndexUpdatedList =
+ SearchAndReplace(fileIndexUpdatedList, fieldName, search, replace);
AddNotFoundInIndexStatus.Update(inputFilePaths, fileIndexUpdatedList);
@@ -109,34 +128,34 @@ public async Task> Replace(string f, string fieldName, strin
foreach ( var fileIndexItem in fileIndexUpdatedList )
{
// Status Ok is already set
-
+
// Deleted is allowed but the status need be updated
- if ((fileIndexItem.Status == FileIndexItem.ExifStatus.Ok) &&
- StatusCodesHelper.IsDeletedStatus(fileIndexItem) ==
- FileIndexItem.ExifStatus.Deleted)
+ if ( ( fileIndexItem.Status == FileIndexItem.ExifStatus.Ok ) &&
+ StatusCodesHelper.IsDeletedStatus(fileIndexItem) ==
+ FileIndexItem.ExifStatus.Deleted )
{
fileIndexItem.Status = FileIndexItem.ExifStatus.Deleted;
}
-
+
fileIndexResultList.Add(fileIndexItem);
}
-
+
return await new Duplicate(_query).RemoveDuplicateAsync(fileIndexResultList);
}
- public static List SearchAndReplace(List fileIndexResultsList,
+ public static List SearchAndReplace(List fileIndexResultsList,
string fieldName, string search, string replace)
{
- foreach ( var fileIndexItem in fileIndexResultsList.Where(
- p => p.Status
- is FileIndexItem.ExifStatus.Ok
- or FileIndexItem.ExifStatus.OkAndSame
- or FileIndexItem.ExifStatus.Deleted
- or FileIndexItem.ExifStatus.DeletedAndSame) )
+ foreach ( var fileIndexItem in fileIndexResultsList.Where(
+ p => p.Status
+ is FileIndexItem.ExifStatus.Ok
+ or FileIndexItem.ExifStatus.OkAndSame
+ or FileIndexItem.ExifStatus.Deleted
+ or FileIndexItem.ExifStatus.DeletedAndSame) )
{
var searchInObject = FileIndexCompareHelper.Get(fileIndexItem, fieldName);
var replacedToObject = new object();
-
+
var propertiesA = new FileIndexItem().GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance);
@@ -144,15 +163,15 @@ or FileIndexItem.ExifStatus.Deleted
p.Name, fieldName,
StringComparison.InvariantCultureIgnoreCase));
- if ( property?.PropertyType == typeof(string))
+ if ( property?.PropertyType == typeof(string) )
{
- var searchIn = searchInObject != null ? ( string ) searchInObject : string.Empty;
-
+ var searchIn = searchInObject != null ? ( string )searchInObject : string.Empty;
+
// Replace Ignore Case
replacedToObject = Regex.Replace(
searchIn,
- Regex.Escape(search),
- replace.Replace("$","$$"),
+ Regex.Escape(search),
+ replace.Replace("$", "$$"),
RegexOptions.IgnoreCase,
TimeSpan.FromMilliseconds(100)
);
@@ -164,7 +183,5 @@ or FileIndexItem.ExifStatus.Deleted
return fileIndexResultsList;
}
-
-
}
}
diff --git a/starsky/starsky.feature.metaupdate/Services/MetaUpdateService.cs b/starsky/starsky.feature.metaupdate/Services/MetaUpdateService.cs
index 4fcf0027d4..da631430ad 100644
--- a/starsky/starsky.feature.metaupdate/Services/MetaUpdateService.cs
+++ b/starsky/starsky.feature.metaupdate/Services/MetaUpdateService.cs
@@ -21,188 +21,193 @@
using ExifToolCmdHelper = starsky.foundation.writemeta.Helpers.ExifToolCmdHelper;
[assembly: InternalsVisibleTo("starskytest")]
-namespace starsky.feature.metaupdate.Services
+
+namespace starsky.feature.metaupdate.Services;
+
+[Service(typeof(IMetaUpdateService), InjectionLifetime = InjectionLifetime.Scoped)]
+public class MetaUpdateService : IMetaUpdateService
{
- [Service(typeof(IMetaUpdateService), InjectionLifetime = InjectionLifetime.Scoped)]
- public class MetaUpdateService : IMetaUpdateService
- {
- private readonly IQuery _query;
- private readonly IExifTool _exifTool;
- private readonly IReadMetaSubPathStorage _readMeta;
- private readonly IStorage _iStorage;
- private readonly IStorage _thumbnailStorage;
- private readonly IMetaPreflight _metaPreflight;
- private readonly IWebLogger _logger;
- private readonly IThumbnailService _thumbnailService;
- private readonly IThumbnailQuery _thumbnailQuery;
-
- [SuppressMessage("Usage", "S107: Constructor has 8 parameters, which is greater than the 7 authorized")]
- public MetaUpdateService(
- IQuery query,
- IExifTool exifTool,
- ISelectorStorage selectorStorage,
- IMetaPreflight metaPreflight,
- IWebLogger logger,
- IReadMetaSubPathStorage readMetaSubPathStorage,
- IThumbnailService thumbnailService,
- IThumbnailQuery thumbnailQuery)
+ private readonly IQuery _query;
+ private readonly IExifTool _exifTool;
+ private readonly IReadMetaSubPathStorage _readMeta;
+ private readonly IStorage _iStorage;
+ private readonly IStorage _thumbnailStorage;
+ private readonly IMetaPreflight _metaPreflight;
+ private readonly IWebLogger _logger;
+ private readonly IThumbnailService _thumbnailService;
+ private readonly IThumbnailQuery _thumbnailQuery;
+
+ [SuppressMessage("Usage",
+ "S107: Constructor has 8 parameters, which is greater than the 7 authorized")]
+ public MetaUpdateService(
+ IQuery query,
+ IExifTool exifTool,
+ ISelectorStorage selectorStorage,
+ IMetaPreflight metaPreflight,
+ IWebLogger logger,
+ IReadMetaSubPathStorage readMetaSubPathStorage,
+ IThumbnailService thumbnailService,
+ IThumbnailQuery thumbnailQuery)
+ {
+ _query = query;
+ _exifTool = exifTool;
+ _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
+ _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
+ _readMeta = readMetaSubPathStorage;
+ _metaPreflight = metaPreflight;
+ _logger = logger;
+ _thumbnailService = thumbnailService;
+ _thumbnailQuery = thumbnailQuery;
+ }
+
+ ///
+ /// Run Update
+ ///
+ /// Per file stored string{fileHash},
+ /// List*string*{FileIndexItem.name (e.g. Tags) that are changed}
+ /// items stored in the database
+ /// (only used when cache is disabled)
+ /// This model is overwritten in the database and ExifTool
+ /// enable or disable this feature
+ /// only for disabled cache or changedFileIndexItemName=null
+ /// rotation value 1 left, -1 right, 0 nothing
+ public async Task> UpdateAsync(
+ Dictionary>? changedFileIndexItemName,
+ List fileIndexResultsList,
+ FileIndexItem? inputModel, // only when changedFileIndexItemName = null
+ bool collections, bool append, // only when changedFileIndexItemName = null
+ int rotateClock) // <- this one is needed
+ {
+ if ( changedFileIndexItemName == null )
{
- _query = query;
- _exifTool = exifTool;
- _iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
- _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
- _readMeta = readMetaSubPathStorage;
- _metaPreflight = metaPreflight;
- _logger = logger;
- _thumbnailService = thumbnailService;
- _thumbnailQuery = thumbnailQuery;
+ changedFileIndexItemName = ( await _metaPreflight.PreflightAsync(inputModel,
+ fileIndexResultsList.Select(p => p.FilePath!).ToList(), append, collections,
+ rotateClock) ).changedFileIndexItemName;
}
- ///
- /// Run Update
- ///
- /// Per file stored string{fileHash},
- /// List*string*{FileIndexItem.name (e.g. Tags) that are changed}
- /// items stored in the database
- /// (only used when cache is disabled)
- /// This model is overwritten in the database and ExifTool
- /// enable or disable this feature
- /// only for disabled cache or changedFileIndexItemName=null
- /// rotation value 1 left, -1 right, 0 nothing
- public async Task> UpdateAsync(
- Dictionary> changedFileIndexItemName,
- List fileIndexResultsList,
- FileIndexItem inputModel, // only when changedFileIndexItemName = null
- bool collections, bool append, // only when changedFileIndexItemName = null
- int rotateClock) // <- this one is needed
+ var updatedItems = new List();
+ var fileIndexItemList = fileIndexResultsList
+ .Where(p => p.Status is FileIndexItem.ExifStatus.Ok
+ or FileIndexItem.ExifStatus.Deleted).ToList();
+
+ foreach ( var fileIndexItem in fileIndexItemList )
{
- if ( changedFileIndexItemName == null )
- {
- changedFileIndexItemName = (await _metaPreflight.PreflightAsync(inputModel,
- fileIndexResultsList.Select(p => p.FilePath).ToList(), append, collections,
- rotateClock)).changedFileIndexItemName;
- }
-
- var updatedItems = new List();
- var fileIndexItemList = fileIndexResultsList
- .Where(p => p.Status is FileIndexItem.ExifStatus.Ok
- or FileIndexItem.ExifStatus.Deleted).ToList();
-
- foreach ( var fileIndexItem in fileIndexItemList )
+ if ( changedFileIndexItemName.TryGetValue(fileIndexItem.FilePath!, out var value) )
{
- if (changedFileIndexItemName.TryGetValue(fileIndexItem.FilePath!, out List value) )
- {
- // used for tracking differences, in the database/ExifTool compared to the inputModel
-
- await UpdateWriteDiskDatabase(fileIndexItem, value, rotateClock);
- updatedItems.Add(fileIndexItem);
- continue;
- }
-
- _logger.LogError($"Missing in key: {fileIndexItem.FilePath}",
- new InvalidDataException($"changedFileIndexItemName: " +
- $"{string.Join(",",changedFileIndexItemName)}"));
- throw new ArgumentException($"Missing in key: {fileIndexItem.FilePath}",
- nameof(changedFileIndexItemName));
+ // used for tracking differences, in the database/ExifTool compared to the inputModel
+
+ await UpdateWriteDiskDatabase(fileIndexItem, value, rotateClock);
+ updatedItems.Add(fileIndexItem);
+ continue;
}
- return updatedItems;
+ _logger.LogError($"Missing in key: {fileIndexItem.FilePath}",
+ new InvalidDataException($"changedFileIndexItemName: " +
+ $"{string.Join(",", changedFileIndexItemName)}"));
+ throw new ArgumentException($"Missing in key: {fileIndexItem.FilePath}",
+ nameof(changedFileIndexItemName));
}
- public void UpdateReadMetaCache(IEnumerable returnNewResultList)
- {
- _readMeta.UpdateReadMetaCache(returnNewResultList);
- }
+ return updatedItems;
+ }
+
+ public void UpdateReadMetaCache(IEnumerable returnNewResultList)
+ {
+ _readMeta.UpdateReadMetaCache(returnNewResultList);
+ }
+
+ ///
+ /// Update ExifTool, Thumbnail, Database and if needed rotateClock
+ ///
+ /// output database object
+ /// name of fields updated by exifTool
+ /// rotation value (if needed)
+ private async Task UpdateWriteDiskDatabase(FileIndexItem fileIndexItem,
+ List comparedNamesList, int rotateClock = 0)
+ {
+ // do rotation on thumbs
+ await RotationThumbnailExecute(rotateClock, fileIndexItem);
- ///
- /// Update ExifTool, Thumbnail, Database and if needed rotateClock
- ///
- /// output database object
- /// name of fields updated by exifTool
- /// rotation value (if needed)
- private async Task UpdateWriteDiskDatabase(FileIndexItem fileIndexItem, List comparedNamesList, int rotateClock = 0)
+ if ( fileIndexItem.IsDirectory != true
+ && ExtensionRolesHelper.IsExtensionExifToolSupported(fileIndexItem.FileName) )
{
- // do rotation on thumbs
- await RotationThumbnailExecute(rotateClock, fileIndexItem);
+ // feature to exif update
+ var exifUpdateFilePaths = new List { fileIndexItem.FilePath! };
+ var exifTool = new ExifToolCmdHelper(_exifTool, _iStorage, _thumbnailStorage,
+ _readMeta, _thumbnailQuery);
- if ( fileIndexItem.IsDirectory != true
- && ExtensionRolesHelper.IsExtensionExifToolSupported(fileIndexItem.FileName) )
- {
- // feature to exif update
- var exifUpdateFilePaths = new List
- {
- fileIndexItem.FilePath
- };
- var exifTool = new ExifToolCmdHelper(_exifTool,_iStorage, _thumbnailStorage,_readMeta, _thumbnailQuery);
-
- // to avoid diskWatcher catch up
- _query.SetGetObjectByFilePathCache(fileIndexItem.FilePath!, fileIndexItem, TimeSpan.FromSeconds(5));
-
- // Do an Exif Sync for all files, including thumbnails
- var (exifResult,newFileHashes) = await exifTool.UpdateAsync(fileIndexItem,
- exifUpdateFilePaths, comparedNamesList,true, true);
-
- await ApplyOrGenerateUpdatedFileHash(newFileHashes, fileIndexItem);
-
- _logger.LogInformation(string.IsNullOrEmpty(exifResult)
- ? $"[UpdateWriteDiskDatabase] ExifTool result is Nothing or " +
- $"Null for: path:{fileIndexItem.FilePath} {DateTime.UtcNow.ToShortTimeString()}"
- : $"[UpdateWriteDiskDatabase] ExifTool result: {exifResult} path:{fileIndexItem.FilePath}");
- }
- else if ( fileIndexItem.ImageFormat != ExtensionRolesHelper.ImageFormat.xmp &&
- fileIndexItem.ImageFormat != ExtensionRolesHelper.ImageFormat.meta_json )
- {
- await new FileIndexItemJsonParser(_iStorage).WriteAsync(fileIndexItem);
- }
-
- // set last edited
- fileIndexItem.LastEdited = _iStorage.Info(fileIndexItem.FilePath!).LastWriteTime;
-
- // Do a database sync + cache sync
- await _query.UpdateItemAsync(fileIndexItem);
-
- // to avoid diskWatcher catch up (and updates the last edited dateTime)
- _query.SetGetObjectByFilePathCache(fileIndexItem.FilePath!, fileIndexItem, TimeSpan.FromSeconds(5));
-
- // > async > force you to read the file again
- // do not include thumbs in MetaCache
- // only the full path url of the source image
- _readMeta.RemoveReadMetaCache(fileIndexItem.FilePath);
+ // to avoid diskWatcher catch up
+ _query.SetGetObjectByFilePathCache(fileIndexItem.FilePath!, fileIndexItem,
+ TimeSpan.FromSeconds(5));
+
+ // Do an Exif Sync for all files, including thumbnails
+ var (exifResult, newFileHashes) = await exifTool.UpdateAsync(fileIndexItem,
+ exifUpdateFilePaths, comparedNamesList, true, true);
+
+ await ApplyOrGenerateUpdatedFileHash(newFileHashes, fileIndexItem);
+
+ _logger.LogInformation(string.IsNullOrEmpty(exifResult)
+ ? $"[UpdateWriteDiskDatabase] ExifTool result is Nothing or " +
+ $"Null for: path:{fileIndexItem.FilePath} {DateTime.UtcNow.ToShortTimeString()}"
+ : $"[UpdateWriteDiskDatabase] ExifTool result: {exifResult} path:{fileIndexItem.FilePath}");
+ }
+ else if ( fileIndexItem.ImageFormat != ExtensionRolesHelper.ImageFormat.xmp &&
+ fileIndexItem.ImageFormat != ExtensionRolesHelper.ImageFormat.meta_json )
+ {
+ await new FileIndexItemJsonParser(_iStorage).WriteAsync(fileIndexItem);
}
- internal async Task ApplyOrGenerateUpdatedFileHash(List newFileHashes, FileIndexItem fileIndexItem)
+ // set last edited
+ fileIndexItem.LastEdited = _iStorage.Info(fileIndexItem.FilePath!).LastWriteTime;
+
+ // Do a database sync + cache sync
+ await _query.UpdateItemAsync(fileIndexItem);
+
+ // to avoid diskWatcher catch up (and updates the last edited dateTime)
+ _query.SetGetObjectByFilePathCache(fileIndexItem.FilePath!, fileIndexItem,
+ TimeSpan.FromSeconds(5));
+
+ // > async > force you to read the file again
+ // do not include thumbs in MetaCache
+ // only the full path url of the source image
+ _readMeta.RemoveReadMetaCache(fileIndexItem.FilePath!);
+ }
+
+ internal async Task ApplyOrGenerateUpdatedFileHash(List newFileHashes,
+ FileIndexItem fileIndexItem)
+ {
+ if ( !string.IsNullOrWhiteSpace(newFileHashes.FirstOrDefault()) )
{
- if ( !string.IsNullOrWhiteSpace(newFileHashes.FirstOrDefault()))
- {
- fileIndexItem.FileHash = newFileHashes.FirstOrDefault();
- _logger.LogInformation($"use fileHash from exiftool {fileIndexItem.FileHash}");
- return;
- }
- // when newFileHashes is null or string.empty
- // rename is done in the exiftool helper
- var newFileHash = (await new FileHash(_iStorage).GetHashCodeAsync(fileIndexItem.FilePath!)).Key;
- _thumbnailStorage.FileMove(fileIndexItem.FileHash!, newFileHash);
- fileIndexItem.FileHash = newFileHash;
+ fileIndexItem.FileHash = newFileHashes.FirstOrDefault();
+ _logger.LogInformation($"use fileHash from exiftool {fileIndexItem.FileHash}");
+ return;
}
- ///
- /// Run the Orientation changes on the thumbnail (only relative)
- ///
- /// -1 or 1
- /// object contains fileHash
- /// updated image
- internal async Task RotationThumbnailExecute(int rotateClock, FileIndexItem fileIndexItem)
+ // when newFileHashes is null or string.empty
+ // rename is done in the exiftool helper
+ var newFileHash =
+ ( await new FileHash(_iStorage).GetHashCodeAsync(fileIndexItem.FilePath!) ).Key;
+ _thumbnailStorage.FileMove(fileIndexItem.FileHash!, newFileHash);
+ fileIndexItem.FileHash = newFileHash;
+ }
+
+ ///
+ /// Run the Orientation changes on the thumbnail (only relative)
+ ///
+ /// -1 or 1
+ /// object contains fileHash
+ /// updated image
+ internal async Task RotationThumbnailExecute(int rotateClock, FileIndexItem fileIndexItem)
+ {
+ // Do orientation
+ if ( FileIndexItem.IsRelativeOrientation(rotateClock) )
{
- // Do orientation
- if ( FileIndexItem.IsRelativeOrientation(rotateClock) )
+ foreach ( var size in ThumbnailNameHelper.AllThumbnailSizes )
{
- foreach ( var size in ThumbnailNameHelper.AllThumbnailSizes )
- {
- var fileHash = ThumbnailNameHelper.Combine(
- fileIndexItem.FileHash!, size);
- await _thumbnailService.RotateThumbnail(fileHash,rotateClock,
- ThumbnailNameHelper.GetSize(size));
- }
+ var fileHash = ThumbnailNameHelper.Combine(
+ fileIndexItem.FileHash!, size);
+ await _thumbnailService.RotateThumbnail(fileHash, rotateClock,
+ ThumbnailNameHelper.GetSize(size));
}
}
}
diff --git a/starsky/starsky.feature.metaupdate/starsky.feature.metaupdate.csproj b/starsky/starsky.feature.metaupdate/starsky.feature.metaupdate.csproj
index 3d2507251e..2d410cf14b 100644
--- a/starsky/starsky.feature.metaupdate/starsky.feature.metaupdate.csproj
+++ b/starsky/starsky.feature.metaupdate/starsky.feature.metaupdate.csproj
@@ -5,15 +5,16 @@
starsky.feature.metaupdate
{9567d576-4dee-481b-b316-c55d493416f4}
0.6.0-beta.0
+ enable
-
+
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/starsky/starsky.feature.packagetelemetry/Services/DeviceIdService.cs b/starsky/starsky.feature.packagetelemetry/Services/DeviceIdService.cs
index 9814d6ab4c..ba0f26f9ea 100644
--- a/starsky/starsky.feature.packagetelemetry/Services/DeviceIdService.cs
+++ b/starsky/starsky.feature.packagetelemetry/Services/DeviceIdService.cs
@@ -28,34 +28,34 @@ public DeviceIdService(ISelectorStorage selectorStorage, ISettingsService settin
_settingsService = settingsService;
_hostStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
}
-
+
public string IoReg { get; set; } = "ioreg";
public string DbusMachineIdPath { get; set; } = "/var/lib/dbus/machine-id";
public string MachineIdPath2 { get; set; } = "/etc/machine-id";
public string BsdHostIdPath { get; set; } = "/etc/hostid";
- public async Task DeviceId(OSPlatform? currentPlatform )
+ public async Task DeviceId(OSPlatform? currentPlatform)
{
var id = string.Empty;
if ( currentPlatform == OSPlatform.OSX )
{
id = await DeviceIdOsX();
}
-
+
if ( currentPlatform == OSPlatform.Windows )
{
id = DeviceIdWindows(currentPlatform);
}
-
- if ( currentPlatform == OSPlatform.Linux || currentPlatform == OSPlatform.FreeBSD)
+
+ if ( currentPlatform == OSPlatform.Linux || currentPlatform == OSPlatform.FreeBSD )
{
id = await DeviceIdLinuxBsdAsync();
}
// For privacy reason this content of this id will be anonymous
id = Sha256.ComputeSha256(id);
-
+
if ( string.IsNullOrEmpty(id) )
{
id = await DeviceIdDatabaseId();
@@ -80,13 +80,13 @@ private async Task DeviceIdLinuxBsdAsync()
var stream = _hostStorage.ReadStream(DbusMachineIdPath);
return await StreamToStringHelper.StreamToStringAsync(stream);
}
-
+
if ( _hostStorage.ExistFile(MachineIdPath2) )
{
var stream = _hostStorage.ReadStream(MachineIdPath2);
return await StreamToStringHelper.StreamToStringAsync(stream);
}
-
+
if ( !_hostStorage.ExistFile(BsdHostIdPath) ) return string.Empty;
var streamBsd = _hostStorage.ReadStream(BsdHostIdPath);
return await StreamToStringHelper.StreamToStringAsync(streamBsd);
@@ -101,8 +101,8 @@ internal async Task DeviceIdOsX()
// ioreg -rd1 -c IOPlatformExpertDevice
var result = await Command.Run(IoReg, "-rd1", "-c", "IOPlatformExpertDevice").Task;
if ( !result.Success ) return string.Empty;
-
- var match = Regex.Match(result.StandardOutput,"\"IOPlatformUUID\" = \"[\\d+\\w+-]+\"",
+
+ var match = Regex.Match(result.StandardOutput, "\"IOPlatformUUID\" = \"[\\d+\\w+-]+\"",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
var id = match.Value.Replace("\"IOPlatformUUID\" = \"", "").Replace('\"', ' ').Trim();
return id;
@@ -111,7 +111,7 @@ internal async Task DeviceIdOsX()
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
internal static string DeviceIdWindows(OSPlatform? currentPlatform)
{
- if (currentPlatform != OSPlatform.Windows)
+ if ( currentPlatform != OSPlatform.Windows )
{
return string.Empty;
}
diff --git a/starsky/starsky.feature.packagetelemetry/Services/PackageTelemetry.cs b/starsky/starsky.feature.packagetelemetry/Services/PackageTelemetry.cs
index e4675c7a0a..db76e87d42 100644
--- a/starsky/starsky.feature.packagetelemetry/Services/PackageTelemetry.cs
+++ b/starsky/starsky.feature.packagetelemetry/Services/PackageTelemetry.cs
@@ -19,7 +19,7 @@
[assembly: InternalsVisibleTo("starskytest")]
namespace starsky.feature.packagetelemetry.Services
{
-
+
[Service(typeof(IPackageTelemetry), InjectionLifetime = InjectionLifetime.Scoped)]
public class PackageTelemetry : IPackageTelemetry
{
@@ -48,9 +48,9 @@ public PackageTelemetry(IHttpClientHelper httpClientHelper, AppSettings appSetti
internal static OSPlatform? GetCurrentOsPlatform()
{
OSPlatform? currentPlatform = null;
- foreach ( var platform in new List{OSPlatform.Linux,
- OSPlatform.Windows, OSPlatform.OSX,
- OSPlatform.FreeBSD}.Where(RuntimeInformation.IsOSPlatform) )
+ foreach ( var platform in new List{OSPlatform.Linux,
+ OSPlatform.Windows, OSPlatform.OSX,
+ OSPlatform.FreeBSD}.Where(RuntimeInformation.IsOSPlatform) )
{
currentPlatform = platform;
}
@@ -63,9 +63,9 @@ internal List> GetSystemData(OSPlatform? currentPla
currentPlatform ??= GetCurrentOsPlatform();
var dockerContainer = currentPlatform == OSPlatform.Linux &&
- Environment.GetEnvironmentVariable(
- "DOTNET_RUNNING_IN_CONTAINER") == "true";
-
+ Environment.GetEnvironmentVariable(
+ "DOTNET_RUNNING_IN_CONTAINER") == "true";
+
var data = new List>
{
new KeyValuePair("UTCTime", DateTime.UtcNow.ToString(CultureInfo.InvariantCulture)),
@@ -79,7 +79,7 @@ internal List> GetSystemData(OSPlatform? currentPla
new KeyValuePair("DockerContainer", dockerContainer.ToString()),
new KeyValuePair("CurrentCulture", CultureInfo.CurrentCulture.ThreeLetterISOLanguageName),
new KeyValuePair("AspNetCoreEnvironment", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Not set"),
- new KeyValuePair("WebsiteName", GetEncryptedSiteName()),
+ new KeyValuePair("WebsiteName", GetEncryptedSiteName()),
new KeyValuePair("DeviceId", deviceId ?? "Not set"),
};
return data;
@@ -98,7 +98,7 @@ internal async Task>> AddDatabaseData(List>> AddDatabaseData(List("FileIndexItemDirectoryCount",fileIndexItemDirectoryCount.ToString()),
new KeyValuePair("FileIndexItemCount",fileIndexItemCount.ToString())
});
-
+
return data;
}
- internal List> AddAppSettingsData( List> data)
+ internal List> AddAppSettingsData(List> data)
{
var type = typeof(AppSettings);
var properties = type.GetProperties();
// ReSharper disable once LoopCanBeConvertedToQuery
- foreach (var property in properties)
+ foreach ( var property in properties )
{
var someAttribute = Array.Find(Attribute.GetCustomAttributes(property), x => x is PackageTelemetryAttribute);
if ( someAttribute == null )
@@ -137,13 +137,13 @@ internal List> AddAppSettingsData( List) ||
- propValue?.GetType() == typeof(Dictionary>) )
+
+ if ( propValue?.GetType() == typeof(List) ||
+ propValue?.GetType() == typeof(Dictionary>) )
{
value = ParseContent(propValue);
}
@@ -160,17 +160,17 @@ internal static string ParseContent(object propValue)
private async Task PostData(HttpContent formUrlEncodedContent)
{
- return (await _httpClientHelper.PostString("https://" + PackageTelemetryUrl,formUrlEncodedContent,
- _appSettings.EnablePackageTelemetryDebug == true)).Key;
+ return ( await _httpClientHelper.PostString("https://" + PackageTelemetryUrl, formUrlEncodedContent,
+ _appSettings.EnablePackageTelemetryDebug == true) ).Key;
}
-
+
public async Task PackageTelemetrySend()
{
if ( _appSettings.EnablePackageTelemetry == false )
{
return null;
}
-
+
var currentOsPlatform = GetCurrentOsPlatform();
var deviceId = await _deviceIdService.DeviceId(currentOsPlatform);
@@ -184,12 +184,12 @@ private async Task PostData(HttpContent formUrlEncodedContent)
{
return await PostData(formEncodedData);
}
-
+
foreach ( var (key, value) in telemetryDataItems )
{
_logger.LogInformation($"[EnablePackageTelemetryDebug] {key} - {value}");
}
-
+
return null;
}
}
diff --git a/starsky/starsky.feature.packagetelemetry/Services/PackageTelemetryBackgroundService.cs b/starsky/starsky.feature.packagetelemetry/Services/PackageTelemetryBackgroundService.cs
index 21c251f857..6708a2199e 100644
--- a/starsky/starsky.feature.packagetelemetry/Services/PackageTelemetryBackgroundService.cs
+++ b/starsky/starsky.feature.packagetelemetry/Services/PackageTelemetryBackgroundService.cs
@@ -32,7 +32,7 @@ public PackageTelemetryBackgroundService(IServiceScopeFactory serviceScopeFactor
/// CompletedTask
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
- using (var scope = _serviceScopeFactory.CreateScope())
+ using ( var scope = _serviceScopeFactory.CreateScope() )
{
var appSettings = scope.ServiceProvider.GetRequiredService();
var httpClientHelper = scope.ServiceProvider.GetRequiredService();
@@ -42,7 +42,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
if ( appSettings.ApplicationType == AppSettings.StarskyAppType.WebController )
{
- await new PackageTelemetry(httpClientHelper, appSettings,logger,query,deviceIdService).PackageTelemetrySend();
+ await new PackageTelemetry(httpClientHelper, appSettings, logger, query, deviceIdService).PackageTelemetrySend();
}
}
}
diff --git a/starsky/starsky.feature.packagetelemetry/starsky.feature.packagetelemetry.csproj b/starsky/starsky.feature.packagetelemetry/starsky.feature.packagetelemetry.csproj
index ee0eacabde..1ad8f11aaa 100644
--- a/starsky/starsky.feature.packagetelemetry/starsky.feature.packagetelemetry.csproj
+++ b/starsky/starsky.feature.packagetelemetry/starsky.feature.packagetelemetry.csproj
@@ -1,24 +1,24 @@
-
-
- net8.0
- starsky.feature.packagetelemetry
- {6fbad8a6-53fa-41d2-98a4-61eb46d70794}
- 0.6.0-beta.0
- enable
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
+
+
+ net8.0
+ starsky.feature.packagetelemetry
+ {6fbad8a6-53fa-41d2-98a4-61eb46d70794}
+ 0.6.0-beta.0
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
diff --git a/starsky/starsky.feature.realtime/Services/CleanUpConnectionBackgroundService.cs b/starsky/starsky.feature.realtime/Services/CleanUpConnectionBackgroundService.cs
index 3d13f3a71b..4afac55d52 100644
--- a/starsky/starsky.feature.realtime/Services/CleanUpConnectionBackgroundService.cs
+++ b/starsky/starsky.feature.realtime/Services/CleanUpConnectionBackgroundService.cs
@@ -26,10 +26,10 @@ public CleanUpConnectionBackgroundService(IServiceScopeFactory serviceScopeFacto
/// CompletedTask
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
- using (var scope = _serviceScopeFactory.CreateScope())
+ using ( var scope = _serviceScopeFactory.CreateScope() )
{
var connectionsService = scope.ServiceProvider.GetRequiredService();
-
+
//exception is already catch-ed in the service
await connectionsService.CleanOldMessagesAsync();
}
diff --git a/starsky/starsky.feature.realtime/Services/RealtimeConnectionsService.cs b/starsky/starsky.feature.realtime/Services/RealtimeConnectionsService.cs
index 2a67aa5dcc..0ca40f8180 100644
--- a/starsky/starsky.feature.realtime/Services/RealtimeConnectionsService.cs
+++ b/starsky/starsky.feature.realtime/Services/RealtimeConnectionsService.cs
@@ -40,7 +40,7 @@ public async Task CleanOldMessagesAsync()
}
catch ( Exception exception )
{
- if (! exception.Message.Contains("Notifications' doesn't exist") )
+ if ( !exception.Message.Contains("Notifications' doesn't exist") )
{
_logger.LogError(exception, "[CleanOldMessagesAsync] catch-ed exception");
}
diff --git a/starsky/starsky.feature.realtime/starsky.feature.realtime.csproj b/starsky/starsky.feature.realtime/starsky.feature.realtime.csproj
index 15168efe44..86a630eb34 100644
--- a/starsky/starsky.feature.realtime/starsky.feature.realtime.csproj
+++ b/starsky/starsky.feature.realtime/starsky.feature.realtime.csproj
@@ -1,19 +1,19 @@
-
- net8.0
- starsky.feature.realtime
- {4a749ec1-4e6d-4c68-b69c-00c5c80f5660}
- 0.6.0-beta.0
- enable
-
+
+ net8.0
+ starsky.feature.realtime
+ {4a749ec1-4e6d-4c68-b69c-00c5c80f5660}
+ 0.6.0-beta.0
+ enable
+
-
-
-
-
+
+
+
+
-
- true
-
+
+ true
+
diff --git a/starsky/starsky.feature.rename/Services/RenameService.cs b/starsky/starsky.feature.rename/Services/RenameService.cs
index da9e2e9017..7dfbf89244 100644
--- a/starsky/starsky.feature.rename/Services/RenameService.cs
+++ b/starsky/starsky.feature.rename/Services/RenameService.cs
@@ -11,505 +11,545 @@
using starsky.foundation.storage.Models;
[assembly: InternalsVisibleTo("starskytest")]
-namespace starsky.feature.rename.Services
+
+namespace starsky.feature.rename.Services;
+
+public class RenameService
{
- public class RenameService
- {
- private readonly IQuery _query;
- private readonly IStorage _iStorage;
+ private readonly IQuery _query;
+ private readonly IStorage _iStorage;
+
+ public RenameService(IQuery query, IStorage iStorage)
+ {
+ _query = query;
+ _iStorage = iStorage;
+ }
+
+ /// Move or rename files and update the database.
+ /// The services also returns the source folders/files as NotFoundSourceMissing
+ /// subPath to file or folder
+ /// subPath location to move
+ /// true = copy files with the same name
+ public async Task> Rename(string f, string to, bool collections = true)
+ {
+ // -- param name="addDirectoryIfNotExist">true = create an directory if an parent directory is missing
+
+ var ((inputFileSubPaths, toFileSubPaths), fileIndexResultsList) =
+ InputOutputSubPathsPreflight(f, to, collections);
- public RenameService(IQuery query, IStorage iStorage)
+ for ( var i = 0; i < toFileSubPaths.Length; i++ )
{
- _query = query;
- _iStorage = iStorage;
+ // options
+ // 1. FromFolderToDeleted:
+ // folder rename
+ // 2. FromFolderToFolder:
+ // folder with child folders to folder
+ // 3. Not named
+ // file to file
+ // - overwrite a file is not supported
+ // 4. FromFileToDeleted:
+ // rename a file to new location
+ // 5. FromFileToFolder:
+ // file to direct folder file.jpg -> /folder/
+ // 6. folder merge parent folder with current folder (not covered), /test/ => /test/test/
+
+ var inputFileSubPath = inputFileSubPaths[i];
+ var toFileSubPath = toFileSubPaths[i];
+ var detailView = _query.SingleItem(inputFileSubPath, null, collections, false);
+
+ // The To location must be
+ var inputFileFolderStatus = _iStorage.IsFolderOrFile(inputFileSubPath);
+ var toFileFolderStatus = _iStorage.IsFolderOrFile(toFileSubPath);
+
+ var fileIndexItems = new List();
+ switch ( inputFileFolderStatus )
+ {
+ case FolderOrFileModel.FolderOrFileTypeList.Folder when toFileFolderStatus ==
+ FolderOrFileModel.FolderOrFileTypeList.Deleted:
+ await FromFolderToDeleted(inputFileSubPath, toFileSubPath,
+ fileIndexResultsList, detailView);
+ break;
+ case FolderOrFileModel.FolderOrFileTypeList.Folder when toFileFolderStatus ==
+ FolderOrFileModel.FolderOrFileTypeList.Folder:
+ await FromFolderToFolder(inputFileSubPath, toFileSubPath,
+ fileIndexResultsList, detailView!);
+ break;
+ case FolderOrFileModel.FolderOrFileTypeList.File when toFileFolderStatus ==
+ FolderOrFileModel.FolderOrFileTypeList.File:
+ // overwrite a file is not supported
+ fileIndexResultsList.Add(new FileIndexItem
+ {
+ Status = FileIndexItem.ExifStatus.OperationNotSupported
+ });
+ break;
+ case FolderOrFileModel.FolderOrFileTypeList.File when toFileFolderStatus ==
+ FolderOrFileModel.FolderOrFileTypeList.Deleted:
+ // toFileSubPath should contain the full subPath
+ await FromFileToDeleted(inputFileSubPath, toFileSubPath,
+ fileIndexResultsList, fileIndexItems, detailView!);
+ break;
+ case FolderOrFileModel.FolderOrFileTypeList.File when toFileFolderStatus ==
+ FolderOrFileModel.FolderOrFileTypeList.Folder:
+ toFileSubPath = GetFileName(toFileSubPath, inputFileSubPath);
+ // toFileSubPath must be the to copy directory, the filename is kept the same
+ await FromFileToFolder(inputFileSubPath, toFileSubPath,
+ fileIndexResultsList, fileIndexItems, detailView!);
+ break;
+ }
}
- /// Move or rename files and update the database.
- /// The services also returns the source folders/files as NotFoundSourceMissing
- /// subPath to file or folder
- /// subPath location to move
- /// true = copy files with the same name
- public async Task> Rename(string f, string to, bool collections = true)
- {
- // -- param name="addDirectoryIfNotExist">true = create an directory if an parent directory is missing
+ return fileIndexResultsList;
+ }
+
+ private async Task SaveToDatabaseAsync(List fileIndexItems,
+ List fileIndexResultsList, DetailView detailView, string toFileSubPath)
+ {
+ // Rename parent item >eg the folder or file
+ detailView.FileIndexItem!.SetFilePath(toFileSubPath);
+ detailView.FileIndexItem.Status = FileIndexItem.ExifStatus.Ok;
- var ((inputFileSubPaths, toFileSubPaths), fileIndexResultsList) =
- InputOutputSubPathsPreflight(f, to, collections);
+ fileIndexItems.Add(detailView.FileIndexItem);
- for (var i = 0; i < toFileSubPaths.Length; i++)
+ // To update the file that is changed
+ await _query.UpdateItemAsync(fileIndexItems);
+
+ fileIndexResultsList.AddRange(fileIndexItems);
+ }
+
+ private async Task FromFolderToDeleted(string inputFileSubPath,
+ string toFileSubPath, List fileIndexResultsList,
+ DetailView? detailView)
+ {
+ // clean from cache
+ _query.RemoveCacheParentItem(inputFileSubPath);
+
+ var fileIndexItems = await _query.GetAllRecursiveAsync(inputFileSubPath);
+ // Rename child items
+ fileIndexItems.ForEach(p =>
{
- // options
- // 1. FromFolderToDeleted:
- // folder rename
- // 2. FromFolderToFolder:
- // folder with child folders to folder
- // 3. Not named
- // file to file
- // - overwrite a file is not supported
- // 4. FromFileToDeleted:
- // rename a file to new location
- // 5. FromFileToFolder:
- // file to direct folder file.jpg -> /folder/
- // 6. folder merge parent folder with current folder (not covered), /test/ => /test/test/
-
- var inputFileSubPath = inputFileSubPaths[i];
- var toFileSubPath = toFileSubPaths[i];
- var detailView = _query.SingleItem(inputFileSubPath, null, collections, false);
-
- // The To location must be
- var inputFileFolderStatus = _iStorage.IsFolderOrFile(inputFileSubPath);
- var toFileFolderStatus = _iStorage.IsFolderOrFile(toFileSubPath);
-
- var fileIndexItems = new List();
- switch ( inputFileFolderStatus )
- {
- case FolderOrFileModel.FolderOrFileTypeList.Folder when toFileFolderStatus == FolderOrFileModel.FolderOrFileTypeList.Deleted:
- await FromFolderToDeleted(inputFileSubPath, toFileSubPath, fileIndexResultsList, detailView);
- break;
- case FolderOrFileModel.FolderOrFileTypeList.Folder when toFileFolderStatus == FolderOrFileModel.FolderOrFileTypeList.Folder:
- await FromFolderToFolder(inputFileSubPath, toFileSubPath, fileIndexResultsList, detailView);
- break;
- case FolderOrFileModel.FolderOrFileTypeList.File when toFileFolderStatus == FolderOrFileModel.FolderOrFileTypeList.File:
- // overwrite a file is not supported
- fileIndexResultsList.Add(new FileIndexItem
- {
- Status = FileIndexItem.ExifStatus.OperationNotSupported
- });
- break;
- case FolderOrFileModel.FolderOrFileTypeList.File when toFileFolderStatus == FolderOrFileModel.FolderOrFileTypeList.Deleted:
- // toFileSubPath should contain the full subPath
- await FromFileToDeleted(inputFileSubPath, toFileSubPath,
- fileIndexResultsList, fileIndexItems, detailView);
- break;
- case FolderOrFileModel.FolderOrFileTypeList.File when toFileFolderStatus == FolderOrFileModel.FolderOrFileTypeList.Folder:
- toFileSubPath = GetFileName(toFileSubPath, inputFileSubPath);
- // toFileSubPath must be the to copy directory, the filename is kept the same
- await FromFileToFolder(inputFileSubPath, toFileSubPath, fileIndexResultsList, fileIndexItems, detailView);
- break;
- }
+ var parentDirectory = p.ParentDirectory!
+ .Replace(inputFileSubPath, toFileSubPath);
+ p.ParentDirectory = parentDirectory;
+ p.Status = FileIndexItem.ExifStatus.Ok;
+ p.Tags = p.Tags!.Replace(TrashKeyword.TrashKeywordString, string.Empty);
}
- return fileIndexResultsList;
- }
+ );
- private async Task SaveToDatabaseAsync(List fileIndexItems,
- List fileIndexResultsList, DetailView detailView, string toFileSubPath)
+ // when there is already a database item in the output folder, but not on disk
+ // in the final step we going to update the database item to the new name
+ var toCheckList = fileIndexItems.Select(p => p.FilePath).Cast().ToList();
+ toCheckList.Add(toFileSubPath);
+ var checkOutput = await _query.GetObjectsByFilePathQueryAsync(toCheckList);
+ foreach ( var item in checkOutput )
{
- // Rename parent item >eg the folder or file
- detailView.FileIndexItem!.SetFilePath(toFileSubPath);
- detailView.FileIndexItem.Status = FileIndexItem.ExifStatus.Ok;
-
- fileIndexItems.Add(detailView.FileIndexItem);
-
- // To update the file that is changed
- await _query.UpdateItemAsync(fileIndexItems);
-
- fileIndexResultsList.AddRange(fileIndexItems);
+ await _query.RemoveItemAsync(item);
}
- private async Task FromFolderToDeleted(string inputFileSubPath,
- string toFileSubPath, List fileIndexResultsList,
- DetailView detailView)
+ // save before changing on disk
+ await SaveToDatabaseAsync(fileIndexItems, fileIndexResultsList,
+ detailView!, toFileSubPath);
+
+ // move entire folder
+ _iStorage.FolderMove(inputFileSubPath, toFileSubPath);
+
+ fileIndexResultsList.Add(new FileIndexItem(inputFileSubPath)
{
- // clean from cache
- _query.RemoveCacheParentItem(inputFileSubPath);
-
- var fileIndexItems = await _query.GetAllRecursiveAsync(inputFileSubPath);
- // Rename child items
- fileIndexItems.ForEach(p =>
+ Status = FileIndexItem.ExifStatus.NotFoundSourceMissing
+ });
+ }
+
+ private static string GetFileName(string toFileSubPath, string inputFileSubPath)
+ {
+ // Needed to create SetFilePath() for item that is copied, not the folder
+ // no double slash when moving to root folder
+ return toFileSubPath == "/"
+ ? $"/{FilenamesHelper.GetFileName(inputFileSubPath)}"
+ : $"{toFileSubPath}/{FilenamesHelper.GetFileName(inputFileSubPath)}";
+ }
+
+ ///
+ /// Checks for inputs that denied the request
+ ///
+ /// list of filePaths in string format (dot comma separated)
+ /// list of filePaths in string format (dot comma separated)
+ /// is Collections enabled
+ /// Tuple that contains two items:
+ /// item1) Tuple of the input output string - when fails this two array's has no items
+ /// item2) the list of fileIndex Items.
+ /// This contains only values when something is wrong and the request is denied
+ internal Tuple, List> InputOutputSubPathsPreflight
+ (string f, string to, bool collections)
+ {
+ var inputFileSubPaths = PathHelper.SplitInputFilePaths(f).Cast().ToList();
+ var toFileSubPaths = PathHelper.SplitInputFilePaths(to).Cast().ToList();
+
+ // check for the same input
+ if ( inputFileSubPaths.SequenceEqual(toFileSubPaths) )
+ {
+ return new Tuple, List>(
+ new Tuple(Array.Empty(), Array.Empty()),
+ new List
{
- var parentDirectory = p.ParentDirectory!
- .Replace(inputFileSubPath, toFileSubPath);
- p.ParentDirectory = parentDirectory;
- p.Status = FileIndexItem.ExifStatus.Ok;
- p.Tags = p.Tags!.Replace(TrashKeyword.TrashKeywordString, string.Empty);
+ new FileIndexItem
+ {
+ Status = FileIndexItem.ExifStatus.OperationNotSupported
+ }
}
);
+ }
+
+ // the result list
+ var fileIndexResultsList = new List();
+
+ for ( var i = 0; i < inputFileSubPaths.Count; i++ )
+ {
+ var inputFileSubPath = PathHelper.RemoveLatestSlash(inputFileSubPaths[i]!);
+ inputFileSubPaths[i] =
+ PathHelper.PrefixDbSlash(PathHelper.RemovePrefixDbSlash(inputFileSubPath));
- // when there is already a database item in the output folder, but not on disk
- // in the final step we going to update the database item to the new name
- var toCheckList = fileIndexItems.Select(p => p.FilePath).ToList();
- toCheckList.Add(toFileSubPath);
- var checkOutput = await _query.GetObjectsByFilePathQueryAsync(toCheckList);
- foreach ( var item in checkOutput )
+ var detailView = _query.SingleItem(inputFileSubPaths[i]!, null, collections, false);
+ if ( detailView == null )
{
- await _query.RemoveItemAsync(item);
+ inputFileSubPaths[i] = null;
}
-
- // save before changing on disk
- await SaveToDatabaseAsync(fileIndexItems, fileIndexResultsList,
- detailView, toFileSubPath);
-
- // move entire folder
- _iStorage.FolderMove(inputFileSubPath,toFileSubPath);
-
- fileIndexResultsList.Add(new FileIndexItem(inputFileSubPath){Status = FileIndexItem.ExifStatus.NotFoundSourceMissing});
}
- private static string GetFileName(string toFileSubPath, string inputFileSubPath)
- {
- // Needed to create SetFilePath() for item that is copied, not the folder
- // no double slash when moving to root folder
- return toFileSubPath == "/" ? $"/{FilenamesHelper.GetFileName(inputFileSubPath)}"
- : $"{toFileSubPath}/{FilenamesHelper.GetFileName(inputFileSubPath)}";
- }
-
- ///
- /// Checks for inputs that denied the request
- ///
- /// list of filePaths in string format (dot comma separated)
- /// list of filePaths in string format (dot comma separated)
- /// is Collections enabled
- /// Tuple that contains two items:
- /// item1) Tuple of the input output string - when fails this two array's has no items
- /// item2) the list of fileIndex Items.
- /// This contains only values when something is wrong and the request is denied
- internal Tuple,List> InputOutputSubPathsPreflight
- (string f, string to, bool collections)
+ // To check if the file/or folder has a unique name (in database)
+ for ( var i = 0; i < toFileSubPaths.Count; i++ )
{
- var inputFileSubPaths = PathHelper.SplitInputFilePaths(f).ToList();
- var toFileSubPaths = PathHelper.SplitInputFilePaths(to).ToList();
+ var toFileSubPath = PathHelper.RemoveLatestSlash(toFileSubPaths[i]!);
+ toFileSubPaths[i] = PathHelper.PrefixDbSlash(toFileSubPath);
+
+ // to move
+ var detailView = _query.SingleItem(toFileSubPaths[i]!, null, collections, false);
- // check for the same input
- if ( inputFileSubPaths.SequenceEqual(toFileSubPaths) )
+ // skip for files
+ if ( detailView?.FileIndexItem == null )
{
- return new Tuple, List>(
- new Tuple(Array.Empty(), Array.Empty()),
- new List
- {
- new FileIndexItem
- {
- Status = FileIndexItem.ExifStatus.OperationNotSupported
- }
- }
- );
+ // do NOT set null because you move to location that currently doesn't exist
+ // and avoid mixing up the order of files
+ continue;
}
-
- // the result list
- var fileIndexResultsList = new List();
-
- for (var i = 0; i < inputFileSubPaths.Count; i++)
- {
- var inputFileSubPath = PathHelper.RemoveLatestSlash(inputFileSubPaths[i]);
- inputFileSubPaths[i] = PathHelper.PrefixDbSlash(PathHelper.RemovePrefixDbSlash(inputFileSubPath));
- var detailView = _query.SingleItem(inputFileSubPaths[i], null, collections, false);
- if ( detailView == null )
- {
- inputFileSubPaths[i] = null;
- }
- }
-
- // To check if the file/or folder has a unique name (in database)
- for (var i = 0; i < toFileSubPaths.Count; i++)
+ // dirs are mergeable, when it isn't a directory
+ if ( detailView.FileIndexItem.IsDirectory == false )
{
- var toFileSubPath = PathHelper.RemoveLatestSlash(toFileSubPaths[i]);
- toFileSubPaths[i] = PathHelper.PrefixDbSlash(toFileSubPath);
-
- // to move
- var detailView = _query.SingleItem(toFileSubPaths[i], null, collections, false);
-
- // skip for files
- if ( detailView?.FileIndexItem == null )
- {
- // do NOT set null because you move to location that currently doesn't exist
- // and avoid mixing up the order of files
- continue;
- }
- // dirs are mergeable, when it isn't a directory
- if ( detailView.FileIndexItem.IsDirectory == false )
- {
- toFileSubPaths[i] = null;
- }
+ toFileSubPaths[i] = null;
}
-
- // // Remove null from list
- // remove both values when ONE OF those two values are null
- for ( var i = 0; i < toFileSubPaths.Count; i++ )
+ }
+
+ // // Remove null from list
+ // remove both values when ONE OF those two values are null
+ for ( var i = 0; i < toFileSubPaths.Count; i++ )
+ {
+ if ( toFileSubPaths[i] != null && inputFileSubPaths[i] != null ) continue;
+ toFileSubPaths.RemoveAt(i);
+ inputFileSubPaths.RemoveAt(i);
+ fileIndexResultsList.Add(new FileIndexItem
{
- if ( toFileSubPaths[i] != null && inputFileSubPaths[i] != null ) continue;
- toFileSubPaths.RemoveAt(i);
- inputFileSubPaths.RemoveAt(i);
- fileIndexResultsList.Add(new FileIndexItem
- {
- Status = FileIndexItem.ExifStatus.NotFoundNotInIndex
- });
- }
-
- // Check if two list are the same Length - Change this in the future BadRequest("f != to")
- // when moving a file that does not exist (/non-exist.jpg to /non-exist2.jpg)
- if (toFileSubPaths.Count != inputFileSubPaths.Count ||
- toFileSubPaths.Count == 0 || inputFileSubPaths.Count == 0)
- {
- // files that not exist
- fileIndexResultsList.Add(new FileIndexItem
- {
- Status = FileIndexItem.ExifStatus.NotFoundNotInIndex
- });
- return new Tuple, List>(
- new Tuple(Array.Empty(), Array.Empty()),
- fileIndexResultsList
- );
- }
- return CollectionAddPreflight(inputFileSubPaths, toFileSubPaths, fileIndexResultsList, collections);
+ Status = FileIndexItem.ExifStatus.NotFoundNotInIndex
+ });
}
- ///
- /// Get the collections items when preflighting
- /// Returns as Tuple
- /// item1: inputFileSubPaths, toFileSubPaths
- /// item2: list of fileIndex Results (which contains only error cases)
- ///
- /// from where to copy (file or folder)
- /// copy to (file or folder)
- /// results list
- /// enable file collections
- /// inputFileSubPaths list, toFileSubPaths list and fileIndexResultsList
- private Tuple, List> CollectionAddPreflight(
- IReadOnlyList inputFileSubPaths, IReadOnlyList toFileSubPaths,
- List fileIndexResultsList, bool collections)
+ // Check if two list are the same Length - Change this in the future BadRequest("f != to")
+ // when moving a file that does not exist (/non-exist.jpg to /non-exist2.jpg)
+ if ( toFileSubPaths.Count != inputFileSubPaths.Count ||
+ toFileSubPaths.Count == 0 || inputFileSubPaths.Count == 0 )
{
- if ( !collections )
+ // files that not exist
+ fileIndexResultsList.Add(new FileIndexItem
{
- return new Tuple, List>(
- new Tuple(inputFileSubPaths.ToArray(), toFileSubPaths.ToArray()),
- fileIndexResultsList
- );
+ Status = FileIndexItem.ExifStatus.NotFoundNotInIndex
+ });
+ return new Tuple, List>(
+ new Tuple(Array.Empty(), Array.Empty()),
+ fileIndexResultsList
+ );
+ }
+
+ return CollectionAddPreflight(inputFileSubPaths!, toFileSubPaths!, fileIndexResultsList,
+ collections);
+ }
+
+ ///
+ /// Get the collections items when preflighting
+ /// Returns as Tuple
+ /// item1: inputFileSubPaths, toFileSubPaths
+ /// item2: list of fileIndex Results (which contains only error cases)
+ ///
+ /// from where to copy (file or folder)
+ /// copy to (file or folder)
+ /// results list
+ /// enable file collections
+ /// inputFileSubPaths list, toFileSubPaths list and fileIndexResultsList
+ private Tuple, List> CollectionAddPreflight(
+ IReadOnlyList inputFileSubPaths, IReadOnlyList toFileSubPaths,
+ List fileIndexResultsList, bool collections)
+ {
+ if ( !collections )
+ {
+ return new Tuple, List>(
+ new Tuple(inputFileSubPaths.ToArray(),
+ toFileSubPaths.ToArray()),
+ fileIndexResultsList
+ );
+ }
+
+ var inputCollectionFileSubPaths = new List();
+ var toCollectionFileSubPaths = new List();
+
+ for ( var i = 0; i < inputFileSubPaths.Count; i++ )
+ {
+ // When the input is a folder, just copy the array
+ if ( _iStorage.ExistFolder(inputFileSubPaths[i]) )
+ {
+ inputCollectionFileSubPaths.Add(inputFileSubPaths[i]);
+ toCollectionFileSubPaths.Add(toFileSubPaths[i]);
+ continue;
}
-
- var inputCollectionFileSubPaths = new List();
- var toCollectionFileSubPaths = new List();
- for (var i = 0; i < inputFileSubPaths.Count; i++)
+ // when it is a file update the 'to paths'
+ var querySingleItemCollections = _query.SingleItem(inputFileSubPaths[i],
+ null, true, false);
+ var collectionPaths = querySingleItemCollections!.FileIndexItem!.CollectionPaths;
+
+ inputCollectionFileSubPaths.AddRange(collectionPaths);
+
+ for ( var j = 0; j < collectionPaths.Count; j++ )
{
- // When the input is a folder, just copy the array
- if ( _iStorage.ExistFolder(inputFileSubPaths[i]) )
+ var collectionItem = collectionPaths[j];
+ // When moving to a folder
+ if ( _iStorage.ExistFolder(toFileSubPaths[i]) )
{
- inputCollectionFileSubPaths.Add(inputFileSubPaths[i]);
toCollectionFileSubPaths.Add(toFileSubPaths[i]);
continue;
}
-
- // when it is a file update the 'to paths'
- var querySingleItemCollections = _query.SingleItem(inputFileSubPaths[i],
- null, true, false);
- var collectionPaths = querySingleItemCollections!.FileIndexItem!.CollectionPaths;
-
- inputCollectionFileSubPaths.AddRange(collectionPaths);
-
- for ( var j = 0; j < collectionPaths.Count; j++ )
+
+ var extensionWithoutDot =
+ FilenamesHelper.GetFileExtensionWithoutDot(collectionItem);
+ // when rename-ing the current file, but the other ones are implicit copied
+ if ( j == 0 )
{
- var collectionItem = collectionPaths[j];
- // When moving to a folder
- if ( _iStorage.ExistFolder(toFileSubPaths[i]) )
- {
- toCollectionFileSubPaths.Add(toFileSubPaths[i]);
- continue;
- }
-
- var extensionWithoutDot = FilenamesHelper.GetFileExtensionWithoutDot(collectionItem);
- // when rename-ing the current file, but the other ones are implicit copied
- if ( j == 0 )
- {
- extensionWithoutDot = FilenamesHelper.GetFileExtensionWithoutDot(toFileSubPaths[i]);
- }
-
- // Rename other sidecar files
- // From file to Deleted
- var parentFolder = PathHelper.AddSlash(FilenamesHelper.GetParentPath(toFileSubPaths[i]));
- var baseName = FilenamesHelper.GetFileNameWithoutExtension(toFileSubPaths[i]);
- toCollectionFileSubPaths.Add($"{parentFolder}{baseName}.{extensionWithoutDot}");
+ extensionWithoutDot =
+ FilenamesHelper.GetFileExtensionWithoutDot(toFileSubPaths[i]);
}
- }
- return new Tuple, List>(
- new Tuple(inputCollectionFileSubPaths.ToArray(), toCollectionFileSubPaths.ToArray()),
- fileIndexResultsList
- );
+ // Rename other sidecar files
+ // From file to Deleted
+ var parentFolder =
+ PathHelper.AddSlash(FilenamesHelper.GetParentPath(toFileSubPaths[i]));
+ var baseName = FilenamesHelper.GetFileNameWithoutExtension(toFileSubPaths[i]);
+ toCollectionFileSubPaths.Add($"{parentFolder}{baseName}.{extensionWithoutDot}");
+ }
}
- ///
- /// Move sidecar files when those exist
- ///
- /// from path
- /// to path
- private void MoveSidecarFile(string inputFileSubPath, string toFileSubPath)
+ return new Tuple, List>(
+ new Tuple(inputCollectionFileSubPaths.ToArray(),
+ toCollectionFileSubPaths.ToArray()),
+ fileIndexResultsList
+ );
+ }
+
+ ///
+ /// Move sidecar files when those exist
+ ///
+ /// from path
+ /// to path
+ private void MoveSidecarFile(string inputFileSubPath, string toFileSubPath)
+ {
+ // json sidecar move
+ var jsonInputFileSubPathSidecarFile = JsonSidecarLocation
+ .JsonLocation(inputFileSubPath);
+ var jsonSidecarFile = JsonSidecarLocation.JsonLocation(toFileSubPath);
+
+ if ( _iStorage.ExistFile(jsonInputFileSubPathSidecarFile) )
{
- // json sidecar move
- var jsonInputFileSubPathSidecarFile = JsonSidecarLocation
- .JsonLocation(inputFileSubPath);
- var jsonSidecarFile = JsonSidecarLocation.JsonLocation(toFileSubPath);
-
- if ( _iStorage.ExistFile(jsonInputFileSubPathSidecarFile) )
- {
- _iStorage.FileMove(jsonInputFileSubPathSidecarFile,jsonSidecarFile);
- }
+ _iStorage.FileMove(jsonInputFileSubPathSidecarFile, jsonSidecarFile);
+ }
- // xmp sidecar file move
- if ( !ExtensionRolesHelper.IsExtensionForceXmp(inputFileSubPath) )
- {
- return;
- }
- var xmpInputFileSubPathSidecarFile = ExtensionRolesHelper
- .ReplaceExtensionWithXmp(inputFileSubPath);
- var xmpSidecarFile = ExtensionRolesHelper
- .ReplaceExtensionWithXmp(toFileSubPath);
- if ( _iStorage.ExistFile(xmpInputFileSubPathSidecarFile) )
- {
- _iStorage.FileMove(xmpInputFileSubPathSidecarFile, xmpSidecarFile);
- }
+ // xmp sidecar file move
+ if ( !ExtensionRolesHelper.IsExtensionForceXmp(inputFileSubPath) )
+ {
+ return;
}
-
- internal Task FromFolderToFolder(string inputFileSubPath,
- string toFileSubPath, List fileIndexResultsList, DetailView detailView)
+ var xmpInputFileSubPathSidecarFile = ExtensionRolesHelper
+ .ReplaceExtensionWithXmp(inputFileSubPath);
+ var xmpSidecarFile = ExtensionRolesHelper
+ .ReplaceExtensionWithXmp(toFileSubPath);
+ if ( _iStorage.ExistFile(xmpInputFileSubPathSidecarFile) )
{
- if ( fileIndexResultsList == null )
- {
- throw new ArgumentNullException(nameof(fileIndexResultsList), "Should contain value");
- }
- return FromFolderToFolderAsync(inputFileSubPath, toFileSubPath, fileIndexResultsList,detailView);
+ _iStorage.FileMove(xmpInputFileSubPathSidecarFile, xmpSidecarFile);
}
-
- ///
- /// Copy from a folder to a folder
- ///
- /// from path
- /// to path
- ///
- ///
- /// fileIndexItems is null
- private async Task FromFolderToFolderAsync(string inputFileSubPath,
- string toFileSubPath, List fileIndexResultsList, DetailView detailView)
+ }
+
+
+ internal Task FromFolderToFolder(string inputFileSubPath,
+ string toFileSubPath, List fileIndexResultsList, DetailView detailView)
+ {
+ if ( fileIndexResultsList == null )
{
- // 1. Get Direct child files
- // 2. Get Direct folder and child folders
- // 3. move child files
- // 4. remove old folder
-
- // Store Child folders
- var directChildFolders = new List();
- directChildFolders.AddRange(_iStorage.GetDirectoryRecursive(inputFileSubPath).Select(p => p.Key));
-
- // Store direct files
- var directChildItems = new List();
- directChildItems.AddRange(_iStorage.GetAllFilesInDirectory(inputFileSubPath));
-
- // Replace all Recursive items in Query
- // Does only replace in existing database items
- var fileIndexItems = await _query.GetAllRecursiveAsync(inputFileSubPath);
-
- // Rename child items
- fileIndexItems.ForEach(p =>
- {
- p.ParentDirectory = p.ParentDirectory!
- .Replace(inputFileSubPath, toFileSubPath);
- p.Status = FileIndexItem.ExifStatus.Ok;
- }
- );
-
- // save before changing on disk
- await SaveToDatabaseAsync(fileIndexItems, fileIndexResultsList,
- detailView, toFileSubPath);
-
- // rename child folders
- foreach ( var inputChildFolder in directChildFolders )
+ throw new ArgumentNullException(nameof(fileIndexResultsList),
+ "Should contain value");
+ }
+
+ return FromFolderToFolderAsync(inputFileSubPath, toFileSubPath, fileIndexResultsList,
+ detailView);
+ }
+
+ ///
+ /// Copy from a folder to a folder
+ ///
+ /// from path
+ /// to path
+ ///
+ ///
+ /// fileIndexItems is null
+ private async Task FromFolderToFolderAsync(string inputFileSubPath,
+ string toFileSubPath, List fileIndexResultsList, DetailView detailView)
+ {
+ // 1. Get Direct child files
+ // 2. Get Direct folder and child folders
+ // 3. move child files
+ // 4. remove old folder
+
+ // Store Child folders
+ var directChildFolders = new List();
+ directChildFolders.AddRange(_iStorage.GetDirectoryRecursive(inputFileSubPath)
+ .Select(p => p.Key));
+
+ // Store direct files
+ var directChildItems = new List();
+ directChildItems.AddRange(_iStorage.GetAllFilesInDirectory(inputFileSubPath));
+
+ // Replace all Recursive items in Query
+ // Does only replace in existing database items
+ var fileIndexItems = await _query.GetAllRecursiveAsync(inputFileSubPath);
+
+ // Rename child items
+ fileIndexItems.ForEach(p =>
{
- // First FileSys (with folders)
- var outputChildItem = inputChildFolder
+ p.ParentDirectory = p.ParentDirectory!
.Replace(inputFileSubPath, toFileSubPath);
- _iStorage.FolderMove(inputChildFolder,outputChildItem);
+ p.Status = FileIndexItem.ExifStatus.Ok;
}
+ );
- // rename child files
- foreach ( var inputChildItem in directChildItems )
- {
- // First FileSys
- var outputChildItem = inputChildItem.Replace(inputFileSubPath, toFileSubPath);
- _iStorage.FileMove(inputChildItem,outputChildItem);
- }
-
- // when renaming a folder it should warn the UI that it should remove the source item
- fileIndexResultsList.Add(new FileIndexItem(inputFileSubPath){Status = FileIndexItem.ExifStatus.NotFoundSourceMissing});
+ // save before changing on disk
+ await SaveToDatabaseAsync(fileIndexItems, fileIndexResultsList,
+ detailView, toFileSubPath);
+
+ // rename child folders
+ foreach ( var inputChildFolder in directChildFolders )
+ {
+ // First FileSys (with folders)
+ var outputChildItem = inputChildFolder
+ .Replace(inputFileSubPath, toFileSubPath);
+ _iStorage.FolderMove(inputChildFolder, outputChildItem);
}
- private async Task FromFileToDeleted(string inputFileSubPath, string toFileSubPath,
- List fileIndexResultsList, List fileIndexItems, DetailView detailView)
+ // rename child files
+ foreach ( var inputChildItem in directChildItems )
{
- // when trying to rename something wrongs
- var fileName = FilenamesHelper.GetFileName(toFileSubPath);
- if ( !FilenamesHelper.IsValidFileName(fileName) )
- {
- fileIndexResultsList.Add(new FileIndexItem
- {
- Status = FileIndexItem.ExifStatus.OperationNotSupported
- });
- return; //next
- }
-
- // from/input cache should be cleared
- var inputParentSubFolder = FilenamesHelper.GetParentPath(inputFileSubPath);
- _query.RemoveCacheParentItem(inputParentSubFolder);
-
- var toParentSubFolder = FilenamesHelper.GetParentPath(toFileSubPath);
- if ( string.IsNullOrEmpty(toParentSubFolder) ) toParentSubFolder = "/";
-
- // clear cache (to FileSubPath parents)
- _query.RemoveCacheParentItem(toParentSubFolder);
-
- // Check if the parent folder exist in the database
- await _query.AddParentItemsAsync(toParentSubFolder);
-
- // Save in database before change on disk
- await SaveToDatabaseAsync(fileIndexItems, fileIndexResultsList,
- detailView, toFileSubPath);
-
- // add folder to file system
- if ( !_iStorage.ExistFolder(toParentSubFolder) )
+ // First FileSys
+ var outputChildItem = inputChildItem.Replace(inputFileSubPath, toFileSubPath);
+ _iStorage.FileMove(inputChildItem, outputChildItem);
+ }
+
+ // when renaming a folder it should warn the UI that it should remove the source item
+ fileIndexResultsList.Add(new FileIndexItem(inputFileSubPath)
+ {
+ Status = FileIndexItem.ExifStatus.NotFoundSourceMissing
+ });
+ }
+
+ private async Task FromFileToDeleted(string inputFileSubPath, string toFileSubPath,
+ List fileIndexResultsList, List fileIndexItems,
+ DetailView detailView)
+ {
+ // when trying to rename something wrongs
+ var fileName = FilenamesHelper.GetFileName(toFileSubPath);
+ if ( !FilenamesHelper.IsValidFileName(fileName) )
+ {
+ fileIndexResultsList.Add(new FileIndexItem
{
- _iStorage.CreateDirectory(toParentSubFolder);
- fileIndexResultsList.Add(new FileIndexItem(toParentSubFolder){Status = FileIndexItem.ExifStatus.Ok});
- }
-
- _iStorage.FileMove(inputFileSubPath,toFileSubPath);
- MoveSidecarFile(inputFileSubPath, toFileSubPath);
-
- // when renaming a folder it should warn the UI that it should remove the source item
- fileIndexResultsList.Add(new FileIndexItem(inputFileSubPath){Status = FileIndexItem.ExifStatus.NotFoundSourceMissing});
+ Status = FileIndexItem.ExifStatus.OperationNotSupported
+ });
+ return; //next
}
- private async Task FromFileToFolder(string inputFileSubPath, string toFileSubPath,
- List fileIndexResultsList, List fileIndexItems, DetailView detailView)
+ // from/input cache should be cleared
+ var inputParentSubFolder = FilenamesHelper.GetParentPath(inputFileSubPath);
+ _query.RemoveCacheParentItem(inputParentSubFolder);
+
+ var toParentSubFolder = FilenamesHelper.GetParentPath(toFileSubPath);
+ if ( string.IsNullOrEmpty(toParentSubFolder) ) toParentSubFolder = "/";
+
+ // clear cache (to FileSubPath parents)
+ _query.RemoveCacheParentItem(toParentSubFolder);
+
+ // Check if the parent folder exist in the database
+ await _query.AddParentItemsAsync(toParentSubFolder);
+
+ // Save in database before change on disk
+ await SaveToDatabaseAsync(fileIndexItems, fileIndexResultsList,
+ detailView, toFileSubPath);
+
+ // add folder to file system
+ if ( !_iStorage.ExistFolder(toParentSubFolder) )
+ {
+ _iStorage.CreateDirectory(toParentSubFolder);
+ fileIndexResultsList.Add(
+ new FileIndexItem(toParentSubFolder) { Status = FileIndexItem.ExifStatus.Ok });
+ }
+
+ _iStorage.FileMove(inputFileSubPath, toFileSubPath);
+ MoveSidecarFile(inputFileSubPath, toFileSubPath);
+
+ // when renaming a folder it should warn the UI that it should remove the source item
+ fileIndexResultsList.Add(new FileIndexItem(inputFileSubPath)
+ {
+ Status = FileIndexItem.ExifStatus.NotFoundSourceMissing
+ });
+ }
+
+ private async Task FromFileToFolder(string inputFileSubPath, string toFileSubPath,
+ List fileIndexResultsList, List fileIndexItems,
+ DetailView detailView)
+ {
+ // you can't move the file to the same location
+ if ( inputFileSubPath == toFileSubPath )
{
- // you can't move the file to the same location
- if ( inputFileSubPath == toFileSubPath )
+ fileIndexResultsList.Add(new FileIndexItem
{
- fileIndexResultsList.Add(new FileIndexItem
- {
- Status = FileIndexItem.ExifStatus.OperationNotSupported
- });
- return; //next
- }
- // when renaming a folder it should warn the UI that it should remove the source item
- fileIndexResultsList.Add(new FileIndexItem(inputFileSubPath){Status = FileIndexItem.ExifStatus.NotFoundSourceMissing});
-
- // from/input cache should be cleared
- var inputParentSubFolder = Breadcrumbs.BreadcrumbHelper(inputFileSubPath).LastOrDefault();
- _query.RemoveCacheParentItem(inputParentSubFolder!);
-
- // clear cache // parentSubFolder (to FileSubPath parents)
- var toParentSubFolder = Breadcrumbs.BreadcrumbHelper(toFileSubPath).LastOrDefault();
- _query.RemoveCacheParentItem(toParentSubFolder!);
-
- // Check if the parent folder exist in the database // parentSubFolder
- await _query.AddParentItemsAsync(toParentSubFolder);
-
- await SaveToDatabaseAsync(fileIndexItems, fileIndexResultsList,
- detailView, toFileSubPath);
-
- // First update database and then update for disk watcher
- _iStorage.FileMove(inputFileSubPath, toFileSubPath);
- MoveSidecarFile(inputFileSubPath, toFileSubPath);
+ Status = FileIndexItem.ExifStatus.OperationNotSupported
+ });
+ return; //next
}
- }
+ // when renaming a folder it should warn the UI that it should remove the source item
+ fileIndexResultsList.Add(new FileIndexItem(inputFileSubPath)
+ {
+ Status = FileIndexItem.ExifStatus.NotFoundSourceMissing
+ });
+
+ // from/input cache should be cleared
+ var inputParentSubFolder =
+ Breadcrumbs.BreadcrumbHelper(inputFileSubPath).LastOrDefault();
+ _query.RemoveCacheParentItem(inputParentSubFolder!);
+
+ // clear cache // parentSubFolder (to FileSubPath parents)
+ var toParentSubFolder = Breadcrumbs.BreadcrumbHelper(toFileSubPath).LastOrDefault();
+ _query.RemoveCacheParentItem(toParentSubFolder!);
+
+ // Check if the parent folder exist in the database // parentSubFolder
+ await _query.AddParentItemsAsync(toParentSubFolder!);
+
+ await SaveToDatabaseAsync(fileIndexItems, fileIndexResultsList,
+ detailView, toFileSubPath);
+
+ // First update database and then update for disk watcher
+ _iStorage.FileMove(inputFileSubPath, toFileSubPath);
+ MoveSidecarFile(inputFileSubPath, toFileSubPath);
+ }
}
diff --git a/starsky/starsky.feature.rename/starsky.feature.rename.csproj b/starsky/starsky.feature.rename/starsky.feature.rename.csproj
index c48ddb77dc..ef635bdf64 100644
--- a/starsky/starsky.feature.rename/starsky.feature.rename.csproj
+++ b/starsky/starsky.feature.rename/starsky.feature.rename.csproj
@@ -4,11 +4,12 @@
net8.0
{a864f834-133f-4ea8-9a4d-53e5cad837ab}
0.6.0-beta.0
+ enable
-
+
-
-
+
+
diff --git a/starsky/starsky.feature.search/Interfaces/ISearch.cs b/starsky/starsky.feature.search/Interfaces/ISearch.cs
index 1ebe83d200..37b370558e 100644
--- a/starsky/starsky.feature.search/Interfaces/ISearch.cs
+++ b/starsky/starsky.feature.search/Interfaces/ISearch.cs
@@ -1,4 +1,4 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using starsky.feature.search.ViewModels;
namespace starsky.feature.search.Interfaces
diff --git a/starsky/starsky.feature.search/Interfaces/ISearchSuggest.cs b/starsky/starsky.feature.search/Interfaces/ISearchSuggest.cs
index 270305c239..e90f3c5d72 100644
--- a/starsky/starsky.feature.search/Interfaces/ISearchSuggest.cs
+++ b/starsky/starsky.feature.search/Interfaces/ISearchSuggest.cs
@@ -3,7 +3,7 @@
namespace starsky.feature.search.Interfaces
{
-
+
public interface ISearchSuggest
{
Task> SearchSuggest(string query);
diff --git a/starsky/starsky.feature.search/Services/SearchService.cs b/starsky/starsky.feature.search/Services/SearchService.cs
index e127697541..0be8a6d209 100644
--- a/starsky/starsky.feature.search/Services/SearchService.cs
+++ b/starsky/starsky.feature.search/Services/SearchService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
@@ -17,418 +17,417 @@
using starsky.foundation.platform.Models;
using starsky.feature.search.Interfaces;
using starsky.feature.search.ViewModels;
-#nullable enable
namespace starsky.feature.search.Services
{
[Service(typeof(ISearch), InjectionLifetime = InjectionLifetime.Scoped)]
public class SearchService : ISearch
- {
- private readonly ApplicationDbContext _context;
- private readonly IMemoryCache? _cache;
- private readonly AppSettings? _appSettings;
- private readonly IWebLogger _logger;
-
- public SearchService(
- ApplicationDbContext context,
- IWebLogger logger,
- IMemoryCache? memoryCache = null,
- AppSettings? appSettings = null)
- {
- _context = context;
- _cache = memoryCache;
- _appSettings = appSettings;
- _logger = logger;
- }
-
- ///
- /// Search in database
- ///
- /// where to search in
- /// current page (0 = page 1)
- /// enable searchcache (in trash this is disabled)
- ///
- public async Task Search(string query = "",
- int pageNumber = 0, bool enableCache = true)
- {
- if ( !string.IsNullOrEmpty(query) && query.Length >= 500 )
- {
- throw new ArgumentException("Search Input Query is longer then 500 chars");
- }
-
- if ( query == TrashKeyword.TrashKeywordString )
- {
- _logger.LogInformation("Skip cache for trash");
- enableCache = false;
- }
-
- if ( !enableCache ||
- _cache == null || _appSettings?.AddMemoryCache == false )
- {
- return SkipSearchItems(await SearchDirect(query),pageNumber);
- }
-
- // Return values from IMemoryCache
- var querySearchCacheName = "search-" + query;
-
- // Return Cached object if it exist
- if ( _cache.TryGetValue(querySearchCacheName,
- out var objectSearchModel) )
- {
- return SkipSearchItems(objectSearchModel, pageNumber);
- }
-
- // Try to catch a new object
- objectSearchModel = await SearchDirect(query);
- _cache.Set(querySearchCacheName, objectSearchModel, new TimeSpan(0,10,0));
- return SkipSearchItems(objectSearchModel, pageNumber);
- }
-
- public bool? RemoveCache(string query)
- {
- // Add protection for disabled caching
- if( _cache == null || _appSettings?.AddMemoryCache == false) return null;
-
- var queryCacheName = "search-" + query;
- if (!_cache.TryGetValue(queryCacheName, out _)) return false;
- _cache.Remove(queryCacheName);
- return true;
- }
-
- ///
- /// Skip un-needed items
- ///
- ///
- /// current page (0 = page 1)
- ///
- private static SearchViewModel SkipSearchItems(object? objectSearchModel, int pageNumber)
- {
- if ( objectSearchModel is not SearchViewModel searchModel)
- {
- return new SearchViewModel();
- }
-
- // Clone the item to avoid removing items from cache
- searchModel = searchModel.Clone();
- if ( searchModel.FileIndexItems == null )
- {
- return new SearchViewModel();
- }
-
- searchModel.PageNumber = pageNumber;
-
- var skipFirstNumber = pageNumber * NumberOfResultsInView;
- var skipLastNumber = searchModel.SearchCount - ( pageNumber * NumberOfResultsInView ) - NumberOfResultsInView;
-
-
- // Remove the last items
- var skippedLastList = searchModel.FileIndexItems
- .Skip(skipFirstNumber)
- .SkipLast(skipLastNumber);
-
- var skippedHashSet = new HashSet(skippedLastList);
- searchModel.FileIndexItems = skippedHashSet.ToList();
-
- return searchModel;
- }
-
- ///
- /// Return all results
- ///
- /// where to search on
- ///
- private async Task SearchDirect(string? query = "")
- {
- var stopWatch = Stopwatch.StartNew();
-
- // Create an view model
- var model = new SearchViewModel
- {
- SearchQuery = query ?? string.Empty,
- Breadcrumb = new List {"/", query ?? string.Empty }
- // Null check will safe you from error 500 with Empty request
- };
-
- if ( query == null || model.FileIndexItems == null )
- {
- return model;
- }
-
- _orginalSearchQuery = model.SearchQuery;
-
- model.SearchQuery = QuerySafe(model.SearchQuery);
- model.SearchQuery = QueryShortcuts(model.SearchQuery);
- model = MatchSearch(model);
-
- model = await WideSearch(_context.FileIndex.AsNoTracking(),model);
-
- model = SearchViewModel.NarrowSearch(model);
-
- // Remove duplicates from list
- model.FileIndexItems = model.FileIndexItems!
- .Where(p => p.FilePath != null)
- .GroupBy(s => s.FilePath)
- .Select(grp => grp.FirstOrDefault())
- .OrderBy(s => s!.FilePath)
- .ToList()!;
-
- model.SearchCount = model.FileIndexItems.Count;
-
- model.FileIndexItems = model.FileIndexItems
- .OrderByDescending(p => p.DateTime).ToList();
-
- model.LastPageNumber = GetLastPageNumber(model.SearchCount);
-
- model.ElapsedSeconds = stopWatch.Elapsed.TotalSeconds;
- return model;
- }
-
- ///
- /// Main method to query the database, in other function there is sorting needed
- ///
- /// IQueryable database
- /// temp output model
- /// search model with content
- [SuppressMessage("ReSharper", "AccessToModifiedClosure")]
- [SuppressMessage("Performance", "CA1862:Use the \'StringComparison\' " +
- "method overloads to perform case-insensitive string comparisons",
- Justification = "EF Core does not support this: System.InvalidOperationException: The LINQ expression 'DbSet()")]
- private async Task WideSearch(IQueryable sourceList,
- SearchViewModel model)
- {
- var predicates = new List>>();
-
- // .AsNoTracking() => never change data to update
- for ( var i = 0; i < model.SearchIn.Count; i++ )
- {
- Enum.TryParse(model.SearchIn[i].ToLowerInvariant(),
+ {
+ private readonly ApplicationDbContext _context;
+ private readonly IMemoryCache? _cache;
+ private readonly AppSettings? _appSettings;
+ private readonly IWebLogger _logger;
+
+ public SearchService(
+ ApplicationDbContext context,
+ IWebLogger logger,
+ IMemoryCache? memoryCache = null,
+ AppSettings? appSettings = null)
+ {
+ _context = context;
+ _cache = memoryCache;
+ _appSettings = appSettings;
+ _logger = logger;
+ }
+
+ ///
+ /// Search in database
+ ///
+ /// where to search in
+ /// current page (0 = page 1)
+ /// enable searchcache (in trash this is disabled)
+ ///
+ public async Task Search(string query = "",
+ int pageNumber = 0, bool enableCache = true)
+ {
+ if ( !string.IsNullOrEmpty(query) && query.Length >= 500 )
+ {
+ throw new ArgumentException("Search Input Query is longer then 500 chars");
+ }
+
+ if ( query == TrashKeyword.TrashKeywordString )
+ {
+ _logger.LogInformation("Skip cache for trash");
+ enableCache = false;
+ }
+
+ if ( !enableCache ||
+ _cache == null || _appSettings?.AddMemoryCache == false )
+ {
+ return SkipSearchItems(await SearchDirect(query), pageNumber);
+ }
+
+ // Return values from IMemoryCache
+ var querySearchCacheName = "search-" + query;
+
+ // Return Cached object if it exist
+ if ( _cache.TryGetValue(querySearchCacheName,
+ out var objectSearchModel) )
+ {
+ return SkipSearchItems(objectSearchModel, pageNumber);
+ }
+
+ // Try to catch a new object
+ objectSearchModel = await SearchDirect(query);
+ _cache.Set(querySearchCacheName, objectSearchModel, new TimeSpan(0, 10, 0));
+ return SkipSearchItems(objectSearchModel, pageNumber);
+ }
+
+ public bool? RemoveCache(string query)
+ {
+ // Add protection for disabled caching
+ if ( _cache == null || _appSettings?.AddMemoryCache == false ) return null;
+
+ var queryCacheName = "search-" + query;
+ if ( !_cache.TryGetValue(queryCacheName, out _) ) return false;
+ _cache.Remove(queryCacheName);
+ return true;
+ }
+
+ ///
+ /// Skip un-needed items
+ ///
+ ///
+ /// current page (0 = page 1)
+ ///
+ private static SearchViewModel SkipSearchItems(object? objectSearchModel, int pageNumber)
+ {
+ if ( objectSearchModel is not SearchViewModel searchModel )
+ {
+ return new SearchViewModel();
+ }
+
+ // Clone the item to avoid removing items from cache
+ searchModel = searchModel.Clone();
+ if ( searchModel.FileIndexItems == null )
+ {
+ return new SearchViewModel();
+ }
+
+ searchModel.PageNumber = pageNumber;
+
+ var skipFirstNumber = pageNumber * NumberOfResultsInView;
+ var skipLastNumber = searchModel.SearchCount - ( pageNumber * NumberOfResultsInView ) - NumberOfResultsInView;
+
+
+ // Remove the last items
+ var skippedLastList = searchModel.FileIndexItems
+ .Skip(skipFirstNumber)
+ .SkipLast(skipLastNumber);
+
+ var skippedHashSet = new HashSet(skippedLastList);
+ searchModel.FileIndexItems = skippedHashSet.ToList();
+
+ return searchModel;
+ }
+
+ ///
+ /// Return all results
+ ///
+ /// where to search on
+ ///
+ private async Task SearchDirect(string? query = "")
+ {
+ var stopWatch = Stopwatch.StartNew();
+
+ // Create an view model
+ var model = new SearchViewModel
+ {
+ SearchQuery = query ?? string.Empty,
+ Breadcrumb = new List { "/", query ?? string.Empty }
+ // Null check will safe you from error 500 with Empty request
+ };
+
+ if ( query == null || model.FileIndexItems == null )
+ {
+ return model;
+ }
+
+ _orginalSearchQuery = model.SearchQuery;
+
+ model.SearchQuery = QuerySafe(model.SearchQuery);
+ model.SearchQuery = QueryShortcuts(model.SearchQuery);
+ model = MatchSearch(model);
+
+ model = await WideSearch(_context.FileIndex.AsNoTracking(), model);
+
+ model = SearchViewModel.NarrowSearch(model);
+
+ // Remove duplicates from list
+ model.FileIndexItems = model.FileIndexItems!
+ .Where(p => p.FilePath != null)
+ .GroupBy(s => s.FilePath)
+ .Select(grp => grp.FirstOrDefault())
+ .OrderBy(s => s!.FilePath)
+ .ToList()!;
+
+ model.SearchCount = model.FileIndexItems.Count;
+
+ model.FileIndexItems = model.FileIndexItems
+ .OrderByDescending(p => p.DateTime).ToList();
+
+ model.LastPageNumber = GetLastPageNumber(model.SearchCount);
+
+ model.ElapsedSeconds = stopWatch.Elapsed.TotalSeconds;
+ return model;
+ }
+
+ ///
+ /// Main method to query the database, in other function there is sorting needed
+ ///
+ /// IQueryable database
+ /// temp output model
+ /// search model with content
+ [SuppressMessage("ReSharper", "AccessToModifiedClosure")]
+ [SuppressMessage("Performance", "CA1862:Use the \'StringComparison\' " +
+ "method overloads to perform case-insensitive string comparisons",
+ Justification = "EF Core does not support this: System.InvalidOperationException: The LINQ expression 'DbSet()")]
+ private async Task WideSearch(IQueryable sourceList,
+ SearchViewModel model)
+ {
+ var predicates = new List>>();
+
+ // .AsNoTracking() => never change data to update
+ for ( var i = 0; i < model.SearchIn.Count; i++ )
+ {
+ Enum.TryParse(model.SearchIn[i].ToLowerInvariant(),
true, out var searchInType);
- if ( model.SearchForOptions[i] == SearchViewModel.SearchForOptionType.Not )
- {
- continue;
- }
-
- switch ( searchInType )
- {
- case SearchViewModel.SearchInTypes.imageformat:
- if ( Enum.TryParse(
- model.SearchFor[i].ToLowerInvariant(), out var castImageFormat) )
- {
- var result = castImageFormat;
- predicates.Add(x => x.ImageFormat == result);
- }
- break;
- case SearchViewModel.SearchInTypes.description:
+ if ( model.SearchForOptions[i] == SearchViewModel.SearchForOptionType.Not )
+ {
+ continue;
+ }
+
+ switch ( searchInType )
+ {
+ case SearchViewModel.SearchInTypes.imageformat:
+ if ( Enum.TryParse(
+ model.SearchFor[i].ToLowerInvariant(), out var castImageFormat) )
+ {
+ var result = castImageFormat;
+ predicates.Add(x => x.ImageFormat == result);
+ }
+ break;
+ case SearchViewModel.SearchInTypes.description:
// need to have description out of the Func<>
// ToLowerInvariant.Contains(__description_1))' could not be translated.
- var description = model.SearchFor[i];
- predicates.Add(x => x.Description!.ToLower().Contains(description));
- break;
- case SearchViewModel.SearchInTypes.filename:
- var filename = model.SearchFor[i];
- predicates.Add(x => x.FileName!.ToLower().Contains(filename));
- break;
- case SearchViewModel.SearchInTypes.filepath:
- var filePath = model.SearchFor[i];
- predicates.Add(x => x.FilePath!.ToLower().Contains(filePath));
- break;
- case SearchViewModel.SearchInTypes.parentdirectory:
- var parentDirectory = model.SearchFor[i];
- predicates.Add(x => x.ParentDirectory!.ToLower().Contains(parentDirectory));
- break;
- case SearchViewModel.SearchInTypes.title:
- var title = model.SearchFor[i];
- predicates.Add(x => x.Title!.ToLower().Contains(title));
- break;
- case SearchViewModel.SearchInTypes.make:
- // is in the database one field => will be filtered in narrowSearch
- var make = model.SearchFor[i];
- predicates.Add(x => x.MakeModel!.ToLower().Contains(make));
- break;
- case SearchViewModel.SearchInTypes.model:
- // is in the database one field => will be filtered in narrowSearch
- var modelMake = model.SearchFor[i];
- predicates.Add(x => x.MakeModel!.ToLower().Contains(modelMake));
- break;
- case SearchViewModel.SearchInTypes.filehash:
- var fileHash = model.SearchFor[i];
- predicates.Add(x => x.FileHash != null && x.FileHash.ToLower().Contains(fileHash) );
- break;
- case SearchViewModel.SearchInTypes.software:
- var software = model.SearchFor[i];
- predicates.Add(x => x.Software!.ToLower().Contains(software));
- break;
- case SearchViewModel.SearchInTypes.isdirectory:
- if ( bool.TryParse(model.SearchFor[i].ToLowerInvariant(),
- out var boolIsDirectory) )
- {
- predicates.Add(x => x.IsDirectory == boolIsDirectory);
- model.SearchFor[i] = boolIsDirectory.ToString();
- }
- break;
- case SearchViewModel.SearchInTypes.lastedited:
- predicates.Add(SearchWideDateTime.WideSearchDateTimeGet(model,i,SearchWideDateTime.WideSearchDateTimeGetType.LastEdited));
- break;
- case SearchViewModel.SearchInTypes.addtodatabase:
- predicates.Add(SearchWideDateTime.WideSearchDateTimeGet(model,i,SearchWideDateTime.WideSearchDateTimeGetType.AddToDatabase));
- break;
- case SearchViewModel.SearchInTypes.datetime:
- predicates.Add(SearchWideDateTime.WideSearchDateTimeGet(model,i,SearchWideDateTime.WideSearchDateTimeGetType.DateTime));
- break;
- case SearchViewModel.SearchInTypes.colorclass:
- if ( Enum.TryParse(
- model.SearchFor[i].ToLowerInvariant(), out var castColorClass) )
- {
- predicates.Add(x => x.ColorClass == castColorClass);
- }
- break;
- default:
- var tags = model.SearchFor[i];
- predicates.Add(x => x.Tags!.ToLower().Contains(tags));
- break;
- }
- // Need to have the type registered in FileIndexPropList
- }
-
- _logger.LogInformation($"search --> {model.SearchQuery}");
-
- var predicate = PredicateExecution(predicates, model);
-
- model.FileIndexItems = await sourceList.Where(predicate).ToListAsync();
-
- return model;
- }
- private static Expression> PredicateExecution(
- IReadOnlyList>> predicates,
- SearchViewModel model)
- {
- var predicate = PredicateBuilder.False();
- for ( var i = 0; i < predicates.Count; i++ )
- {
- if ( i == 0 )
- {
- predicate = predicates[i];
- continue;
- }
-
- var item = predicates[i - 1];
- var item2 = predicates[i];
-
- // Search for OR
- if ( !model.SearchOperatorContinue(i, model.SearchIn.Count) )
- {
- predicate = item.Or(item2);
- continue;
- }
-
- predicate = item.AndAlso(item2);
- }
-
- return predicate;
- }
-
- ///
+ var description = model.SearchFor[i];
+ predicates.Add(x => x.Description!.ToLower().Contains(description));
+ break;
+ case SearchViewModel.SearchInTypes.filename:
+ var filename = model.SearchFor[i];
+ predicates.Add(x => x.FileName!.ToLower().Contains(filename));
+ break;
+ case SearchViewModel.SearchInTypes.filepath:
+ var filePath = model.SearchFor[i];
+ predicates.Add(x => x.FilePath!.ToLower().Contains(filePath));
+ break;
+ case SearchViewModel.SearchInTypes.parentdirectory:
+ var parentDirectory = model.SearchFor[i];
+ predicates.Add(x => x.ParentDirectory!.ToLower().Contains(parentDirectory));
+ break;
+ case SearchViewModel.SearchInTypes.title:
+ var title = model.SearchFor[i];
+ predicates.Add(x => x.Title!.ToLower().Contains(title));
+ break;
+ case SearchViewModel.SearchInTypes.make:
+ // is in the database one field => will be filtered in narrowSearch
+ var make = model.SearchFor[i];
+ predicates.Add(x => x.MakeModel!.ToLower().Contains(make));
+ break;
+ case SearchViewModel.SearchInTypes.model:
+ // is in the database one field => will be filtered in narrowSearch
+ var modelMake = model.SearchFor[i];
+ predicates.Add(x => x.MakeModel!.ToLower().Contains(modelMake));
+ break;
+ case SearchViewModel.SearchInTypes.filehash:
+ var fileHash = model.SearchFor[i];
+ predicates.Add(x => x.FileHash != null && x.FileHash.ToLower().Contains(fileHash));
+ break;
+ case SearchViewModel.SearchInTypes.software:
+ var software = model.SearchFor[i];
+ predicates.Add(x => x.Software!.ToLower().Contains(software));
+ break;
+ case SearchViewModel.SearchInTypes.isdirectory:
+ if ( bool.TryParse(model.SearchFor[i].ToLowerInvariant(),
+ out var boolIsDirectory) )
+ {
+ predicates.Add(x => x.IsDirectory == boolIsDirectory);
+ model.SearchFor[i] = boolIsDirectory.ToString();
+ }
+ break;
+ case SearchViewModel.SearchInTypes.lastedited:
+ predicates.Add(SearchWideDateTime.WideSearchDateTimeGet(model, i, SearchWideDateTime.WideSearchDateTimeGetType.LastEdited));
+ break;
+ case SearchViewModel.SearchInTypes.addtodatabase:
+ predicates.Add(SearchWideDateTime.WideSearchDateTimeGet(model, i, SearchWideDateTime.WideSearchDateTimeGetType.AddToDatabase));
+ break;
+ case SearchViewModel.SearchInTypes.datetime:
+ predicates.Add(SearchWideDateTime.WideSearchDateTimeGet(model, i, SearchWideDateTime.WideSearchDateTimeGetType.DateTime));
+ break;
+ case SearchViewModel.SearchInTypes.colorclass:
+ if ( Enum.TryParse(
+ model.SearchFor[i].ToLowerInvariant(), out var castColorClass) )
+ {
+ predicates.Add(x => x.ColorClass == castColorClass);
+ }
+ break;
+ default:
+ var tags = model.SearchFor[i];
+ predicates.Add(x => x.Tags!.ToLower().Contains(tags));
+ break;
+ }
+ // Need to have the type registered in FileIndexPropList
+ }
+
+ _logger.LogInformation($"search --> {model.SearchQuery}");
+
+ var predicate = PredicateExecution(predicates, model);
+
+ model.FileIndexItems = await sourceList.Where(predicate).ToListAsync();
+
+ return model;
+ }
+ private static Expression> PredicateExecution(
+ IReadOnlyList>> predicates,
+ SearchViewModel model)
+ {
+ var predicate = PredicateBuilder.False();
+ for ( var i = 0; i < predicates.Count; i++ )
+ {
+ if ( i == 0 )
+ {
+ predicate = predicates[i];
+ continue;
+ }
+
+ var item = predicates[i - 1];
+ var item2 = predicates[i];
+
+ // Search for OR
+ if ( !model.SearchOperatorContinue(i, model.SearchIn.Count) )
+ {
+ predicate = item.Or(item2);
+ continue;
+ }
+
+ predicate = item.AndAlso(item2);
+ }
+
+ return predicate;
+ }
+
+ ///
/// Store the query during search
///
- private string _defaultQuery = string.Empty;
-
- ///
- /// The orginal user search query
- ///
- private string _orginalSearchQuery = string.Empty;
-
- ///
- /// Parse search query for -Tags and default search queries e.g. "test"
- ///
- /// Search model
- /// filled fields in model
- public SearchViewModel MatchSearch(SearchViewModel model)
- {
- // return nulls to avoid errors
- if ( string.IsNullOrWhiteSpace(model.SearchQuery) )
- {
- return model;
- }
-
- _defaultQuery = model.SearchQuery;
-
- // Need to have the type registered in FileIndexPropList
-
- foreach (var itemName in FileIndexItem.FileIndexPropList())
- {
- SearchItemName(model, itemName);
- }
-
+ private string _defaultQuery = string.Empty;
+
+ ///
+ /// The orginal user search query
+ ///
+ private string _orginalSearchQuery = string.Empty;
+
+ ///
+ /// Parse search query for -Tags and default search queries e.g. "test"
+ ///
+ /// Search model
+ /// filled fields in model
+ public SearchViewModel MatchSearch(SearchViewModel model)
+ {
+ // return nulls to avoid errors
+ if ( string.IsNullOrWhiteSpace(model.SearchQuery) )
+ {
+ return model;
+ }
+
+ _defaultQuery = model.SearchQuery;
+
+ // Need to have the type registered in FileIndexPropList
+
+ foreach ( var itemName in FileIndexItem.FileIndexPropList() )
+ {
+ SearchItemName(model, itemName);
+ }
+
// handle keywords without for example -Tags, or -DateTime prefix
- model.ParseDefaultOption(_defaultQuery);
-
- model.SearchQuery = _orginalSearchQuery;
- return model;
- }
-
- ///
- /// Search for e.g. -Tags:"test"
- ///
- /// Model
- /// e.g. Tags or Description
- private void SearchItemName(SearchViewModel model, string itemName)
- {
- if ( model.SearchQuery == null ) return;
-
- // ignore double quotes
- model.SearchQuery = model.SearchQuery.Replace("\"\"", "\"");
-
- // Escape special quotes
- model.SearchQuery = Regex.Replace(model.SearchQuery, "[“”‘’]", "\"",
- RegexOptions.None, TimeSpan.FromMilliseconds(100));
-
- // new: unescaped
- // (:|=|;|>|<|-)((["'])(\\?.)*?\3|[\w\!\~\-_\.\/:,;]+)( \|\|| \&\&)?
- Regex inurlRegex = new Regex(
- "-" + itemName +
- "(:|=|;|>|<|-)(([\"\'])(\\\\?.)*?\\3|[\\w\\!\\~\\-_\\.\\/:,;]+)( \\|\\|| \\&\\&)?",
- RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
-
- _defaultQuery = inurlRegex.Replace(_defaultQuery,"");
- // the current query is removed from the list, so the next item will not search on it
-
- var regexInUrlMatches = inurlRegex.Matches(model.SearchQuery);
- if(regexInUrlMatches.Count == 0) return;
-
- foreach (var regexInUrlValue in regexInUrlMatches.Select(p => p.Value))
- {
- var itemQuery = regexInUrlValue;
-
- // ignore fake results
- if ( string.IsNullOrEmpty(itemQuery) ) continue;
-
- // put ||&& in operator field => next regex > removed
- var itemQueryWithOperator = itemQuery;
-
- Regex rgx = new Regex("-"+ itemName +"(:|=|;|>|<|-)",
- RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
-
- // To Search Type
- var itemNameSearch = rgx.Match(itemQuery).Value;
- if (itemNameSearch.Length == 0 ) continue;
-
- // replace
- itemQuery = rgx.Replace(itemQuery, string.Empty);
-
- // Option last of itemNameSearch (Get last option = ^1)
- var searchForOption = itemNameSearch[^1].ToString();
-
- // Remove parenthesis
- itemQuery = itemQuery.Replace("\"", string.Empty);
- itemQuery = itemQuery.Replace("'", string.Empty);
-
- // Remove || / && at the end of the string
- // (\|\||\&\&)$
- const string pattern = "(\\|\\||\\&\\&)$";
- itemQuery = Regex.Replace(itemQuery, pattern, string.Empty,
+ model.ParseDefaultOption(_defaultQuery);
+
+ model.SearchQuery = _orginalSearchQuery;
+ return model;
+ }
+
+ ///
+ /// Search for e.g. -Tags:"test"
+ ///
+ /// Model
+ /// e.g. Tags or Description
+ private void SearchItemName(SearchViewModel model, string itemName)
+ {
+ if ( model.SearchQuery == null ) return;
+
+ // ignore double quotes
+ model.SearchQuery = model.SearchQuery.Replace("\"\"", "\"");
+
+ // Escape special quotes
+ model.SearchQuery = Regex.Replace(model.SearchQuery, "[“”‘’]", "\"",
+ RegexOptions.None, TimeSpan.FromMilliseconds(100));
+
+ // new: unescaped
+ // (:|=|;|>|<|-)((["'])(\\?.)*?\3|[\w\!\~\-_\.\/:,;]+)( \|\|| \&\&)?
+ Regex inurlRegex = new Regex(
+ "-" + itemName +
+ "(:|=|;|>|<|-)(([\"\'])(\\\\?.)*?\\3|[\\w\\!\\~\\-_\\.\\/:,;]+)( \\|\\|| \\&\\&)?",
+ RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
+
+ _defaultQuery = inurlRegex.Replace(_defaultQuery, "");
+ // the current query is removed from the list, so the next item will not search on it
+
+ var regexInUrlMatches = inurlRegex.Matches(model.SearchQuery);
+ if ( regexInUrlMatches.Count == 0 ) return;
+
+ foreach ( var regexInUrlValue in regexInUrlMatches.Select(p => p.Value) )
+ {
+ var itemQuery = regexInUrlValue;
+
+ // ignore fake results
+ if ( string.IsNullOrEmpty(itemQuery) ) continue;
+
+ // put ||&& in operator field => next regex > removed
+ var itemQueryWithOperator = itemQuery;
+
+ Regex rgx = new Regex("-" + itemName + "(:|=|;|>|<|-)",
+ RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
+
+ // To Search Type
+ var itemNameSearch = rgx.Match(itemQuery).Value;
+ if ( itemNameSearch.Length == 0 ) continue;
+
+ // replace
+ itemQuery = rgx.Replace(itemQuery, string.Empty);
+
+ // Option last of itemNameSearch (Get last option = ^1)
+ var searchForOption = itemNameSearch[^1].ToString();
+
+ // Remove parenthesis
+ itemQuery = itemQuery.Replace("\"", string.Empty);
+ itemQuery = itemQuery.Replace("'", string.Empty);
+
+ // Remove || / && at the end of the string
+ // (\|\||\&\&)$
+ const string pattern = "(\\|\\||\\&\\&)$";
+ itemQuery = Regex.Replace(itemQuery, pattern, string.Empty,
RegexOptions.None, TimeSpan.FromMilliseconds(100));
-
+
// is | or &
var andOrChar = SearchViewModel.AndOrRegex(itemQueryWithOperator);
@@ -446,76 +445,76 @@ private void SearchItemName(SearchViewModel model, string itemName)
model.SetAddSearchFor(itemSingleQuery.Trim());
model.SetAddSearchInStringType(itemName);
}
- }
- }
-
- ///
- /// Trim value (remove spaces)
- ///
- /// searchQuery
- /// trimmed value
- public static string QuerySafe(string query)
- {
- query = query.Trim();
- return query;
- }
-
- ///
- /// Allow -inurl shortcut
- ///
- /// search Query
- /// replaced Url
- public static string QueryShortcuts(string query)
- {
- // should be ignoring case
- query = Regex.Replace(query, "-inurl", "-FilePath",
- RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
- return query;
- }
-
- ///
- /// The amount of search results on one single page
- /// Including the trash page
- ///
- private const int NumberOfResultsInView = 120;
-
- ///
- /// Get the last page number
- /// Roundup by NumberOfResultsInView
- ///
- /// number of search results
- /// last page number (0=index)
- private static int GetLastPageNumber(int fileIndexQueryCount)
- {
- var searchLastPageNumbers = (RoundUp(fileIndexQueryCount) / NumberOfResultsInView) - 1;
-
- if (fileIndexQueryCount <= NumberOfResultsInView)
- {
- searchLastPageNumbers = 0;
- }
- return searchLastPageNumbers;
- }
-
- ///
- /// Roundup
- ///
- /// to round e.g. 10
- /// roundup value
- public static int RoundUp(int toRound)
- {
- // 10 => ResultsInView
- if (toRound % NumberOfResultsInView == 0) return toRound;
- return (NumberOfResultsInView - toRound % NumberOfResultsInView) + toRound;
- }
-
- ///
- /// round number down
- ///
- /// to round
- /// round down value
- public static int RoundDown(int toRound)
- {
- return toRound - toRound % 10;
- }
- }
+ }
+ }
+
+ ///
+ /// Trim value (remove spaces)
+ ///
+ /// searchQuery
+ /// trimmed value
+ public static string QuerySafe(string query)
+ {
+ query = query.Trim();
+ return query;
+ }
+
+ ///
+ /// Allow -inurl shortcut
+ ///
+ /// search Query
+ /// replaced Url
+ public static string QueryShortcuts(string query)
+ {
+ // should be ignoring case
+ query = Regex.Replace(query, "-inurl", "-FilePath",
+ RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
+ return query;
+ }
+
+ ///
+ /// The amount of search results on one single page
+ /// Including the trash page
+ ///
+ private const int NumberOfResultsInView = 120;
+
+ ///
+ /// Get the last page number
+ /// Roundup by NumberOfResultsInView
+ ///
+ /// number of search results
+ /// last page number (0=index)
+ private static int GetLastPageNumber(int fileIndexQueryCount)
+ {
+ var searchLastPageNumbers = ( RoundUp(fileIndexQueryCount) / NumberOfResultsInView ) - 1;
+
+ if ( fileIndexQueryCount <= NumberOfResultsInView )
+ {
+ searchLastPageNumbers = 0;
+ }
+ return searchLastPageNumbers;
+ }
+
+ ///
+ /// Roundup
+ ///
+ /// to round e.g. 10
+ /// roundup value
+ public static int RoundUp(int toRound)
+ {
+ // 10 => ResultsInView
+ if ( toRound % NumberOfResultsInView == 0 ) return toRound;
+ return ( NumberOfResultsInView - toRound % NumberOfResultsInView ) + toRound;
+ }
+
+ ///
+ /// round number down
+ ///
+ /// to round
+ /// round down value
+ public static int RoundDown(int toRound)
+ {
+ return toRound - toRound % 10;
+ }
+ }
}
diff --git a/starsky/starsky.feature.search/Services/SearchSuggestionsInflateHostedService.cs b/starsky/starsky.feature.search/Services/SearchSuggestionsInflateHostedService.cs
index 3e8a1f68aa..f43d080c12 100644
--- a/starsky/starsky.feature.search/Services/SearchSuggestionsInflateHostedService.cs
+++ b/starsky/starsky.feature.search/Services/SearchSuggestionsInflateHostedService.cs
@@ -29,7 +29,7 @@ public SearchSuggestionsInflateHostedService(IServiceScopeFactory scopeFactory,
public async Task StartAsync(CancellationToken cancellationToken)
{
- using (var scope = _scopeFactory.CreateScope())
+ using ( var scope = _scopeFactory.CreateScope() )
{
var dbContext = scope.ServiceProvider.GetRequiredService();
await new SearchSuggestionsService(dbContext, _memoryCache, _logger, _appSettings).Inflate();
diff --git a/starsky/starsky.feature.search/Services/SearchSuggestionsService.cs b/starsky/starsky.feature.search/Services/SearchSuggestionsService.cs
index 4826070797..d4f4f38550 100644
--- a/starsky/starsky.feature.search/Services/SearchSuggestionsService.cs
+++ b/starsky/starsky.feature.search/Services/SearchSuggestionsService.cs
@@ -14,7 +14,7 @@
namespace starsky.feature.search.Services
{
-
+
[Service(typeof(ISearchSuggest), InjectionLifetime = InjectionLifetime.Scoped)]
public class SearchSuggestionsService : ISearchSuggest
{
@@ -24,9 +24,9 @@ public class SearchSuggestionsService : ISearchSuggest
private readonly IWebLogger _logger;
public SearchSuggestionsService(
- ApplicationDbContext context,
+ ApplicationDbContext context,
IMemoryCache? memoryCache,
- IWebLogger logger,
+ IWebLogger logger,
AppSettings appSettings)
{
_context = context;
@@ -45,23 +45,23 @@ public SearchSuggestionsService(
[SuppressMessage("Performance", "CA1827:Do not use Count() or LongCount() when Any() can be used")]
[SuppressMessage("Performance", "S1155:Do not use Count() or LongCount() when Any() can be used",
Justification = "ANY is not supported by EF Core")]
- public async Task>> Inflate()
+ public async Task>> Inflate()
{
- if ( _cache == null) return new List>();
-
- if (_cache.TryGetValue(nameof(SearchSuggestionsService), out _))
- return new Dictionary().ToList();
+ if ( _cache == null ) return new List>();
+
+ if ( _cache.TryGetValue(nameof(SearchSuggestionsService), out _) )
+ return new Dictionary().ToList();
var allFilesList = new List>();
try
{
allFilesList = await _context.FileIndex
- .Where(p => !string.IsNullOrEmpty(p.Tags) )
+ .Where(p => !string.IsNullOrEmpty(p.Tags))
.GroupBy(i => i.Tags)
// ReSharper disable once UseMethodAny.1
.Where(x => x.Count() >= 1) // .ANY is not supported by EF Core
.TagWith("Inflate SearchSuggestionsService")
- .Select(val =>
+ .Select(val =>
new KeyValuePair(val.Key!, val.Count())).ToListAsync();
}
catch ( Exception exception )
@@ -73,12 +73,12 @@ public async Task>> Inflate()
return allFilesList;
}
- var suggestions = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
+ var suggestions = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
foreach ( var tag in allFilesList )
{
if ( string.IsNullOrEmpty(tag.Key) ) continue;
-
+
var keywordsHashSet = HashSetHelper.StringToHashSet(tag.Key.Trim());
foreach ( var keyword in keywordsHashSet )
@@ -89,20 +89,20 @@ public async Task>> Inflate()
}
else
{
- suggestions.Add(keyword,tag.Value);
+ suggestions.Add(keyword, tag.Value);
}
}
}
-
+
var suggestionsFiltered = suggestions
.Where(p => p.Value >= 10)
.OrderByDescending(p => p.Value)
.ToList();
- var cacheExpire = suggestionsFiltered.Count != 0 ?
- new TimeSpan(120,0,0) : new TimeSpan(0, 1, 0);
-
- _cache.Set(nameof(SearchSuggestionsService), suggestionsFiltered,
+ var cacheExpire = suggestionsFiltered.Count != 0 ?
+ new TimeSpan(120, 0, 0) : new TimeSpan(0, 1, 0);
+
+ _cache.Set(nameof(SearchSuggestionsService), suggestionsFiltered,
cacheExpire);
return suggestionsFiltered;
@@ -114,15 +114,15 @@ public async Task>> Inflate()
/// Key/Value pared list
public async Task>> GetAllSuggestions()
{
- if( _cache == null || _appSettings.AddMemoryCache == false)
- return new Dictionary();
+ if ( _cache == null || _appSettings.AddMemoryCache == false )
+ return new Dictionary();
if ( _cache.TryGetValue(nameof(SearchSuggestionsService),
- out var objectFileFolders) )
+ out var objectFileFolders) )
{
- return objectFileFolders as List> ?? new List>();
+ return objectFileFolders as List> ?? new List>();
}
-
+
return await Inflate();
}
@@ -134,20 +134,20 @@ public async Task>> GetAllSuggestions()
public async Task> SearchSuggest(string query)
{
if ( string.IsNullOrEmpty(query) ) return new List();
- if( _cache == null || _appSettings.AddMemoryCache == false) return new List();
-
+ if ( _cache == null || _appSettings.AddMemoryCache == false ) return new List();
+
var allSuggestions = await GetAllSuggestions();
-
- var results = allSuggestions.Where(p =>
+
+ var results = allSuggestions.Where(p =>
p.Key.StartsWith(query, StringComparison.InvariantCultureIgnoreCase))
.Take(MaxResult)
.OrderByDescending(p => p.Value).Select(p => p.Key)
.ToList();
-
+
results.AddRange(SystemResults()
.Where(p => p.StartsWith(query, StringComparison.InvariantCultureIgnoreCase))
- .Take(MaxResult) );
-
+ .Take(MaxResult));
+
return results;
}
diff --git a/starsky/starsky.feature.search/Services/SearchWideDateTime.cs b/starsky/starsky.feature.search/Services/SearchWideDateTime.cs
index 8e0e0bd9f6..9a90e0beec 100644
--- a/starsky/starsky.feature.search/Services/SearchWideDateTime.cs
+++ b/starsky/starsky.feature.search/Services/SearchWideDateTime.cs
@@ -8,17 +8,16 @@ namespace starsky.feature.search.Services
{
public class SearchWideDateTime
{
-
///
- /// Query for DateTime: in between values, entire days, from, type of queries
- ///
- /// Query Source
- /// output
- /// number of search query (i)
- ///
- public static Expression> WideSearchDateTimeGet(SearchViewModel model, int indexer, WideSearchDateTimeGetType type)
- {
- SearchForEntireDay(model,indexer);
+ /// Query for DateTime: in between values, entire days, from, type of queries
+ ///
+ /// output
+ /// number of search query (i)
+ ///
+ public static Expression> WideSearchDateTimeGet(
+ SearchViewModel model, int indexer, WideSearchDateTimeGetType type)
+ {
+ SearchForEntireDay(model, indexer);
// faster search for searching within
// how ever this is still triggered multiple times
@@ -31,26 +30,28 @@ public static Expression> WideSearchDateTimeGet(SearchV
{
var beforeDateTime =
SearchViewModel.ParseDateTime(model.SearchFor[beforeIndexSearchForOptions]);
-
+
var afterDateTime =
SearchViewModel.ParseDateTime(model.SearchFor[afterIndexSearchForOptions]);
// We have now an extra query, and this is always AND
model.SetAndOrOperator('&', -2);
-
+
switch ( type )
{
case WideSearchDateTimeGetType.DateTime:
- return (p => p.DateTime >= beforeDateTime && p.DateTime <= afterDateTime);
+ return ( p => p.DateTime >= beforeDateTime && p.DateTime <= afterDateTime );
case WideSearchDateTimeGetType.LastEdited:
- return (p => p.LastEdited >= beforeDateTime && p.LastEdited <= afterDateTime);
+ return ( p =>
+ p.LastEdited >= beforeDateTime && p.LastEdited <= afterDateTime );
case WideSearchDateTimeGetType.AddToDatabase:
- return (p => p.AddToDatabase >= beforeDateTime && p.AddToDatabase <= afterDateTime);
+ return ( p =>
+ p.AddToDatabase >= beforeDateTime && p.AddToDatabase <= afterDateTime );
default:
throw new ArgumentException("enum incomplete", nameof(type));
}
}
-
+
var dateTime = SearchViewModel.ParseDateTime(model.SearchFor[indexer]);
// Normal search
@@ -61,11 +62,11 @@ public static Expression> WideSearchDateTimeGet(SearchV
switch ( type )
{
case WideSearchDateTimeGetType.DateTime:
- return (p => p.DateTime <= dateTime);
+ return ( p => p.DateTime <= dateTime );
case WideSearchDateTimeGetType.LastEdited:
- return (p => p.LastEdited <= dateTime);
+ return ( p => p.LastEdited <= dateTime );
case WideSearchDateTimeGetType.AddToDatabase:
- return (p => p.AddToDatabase <= dateTime);
+ return ( p => p.AddToDatabase <= dateTime );
default:
throw new ArgumentNullException(nameof(type));
}
@@ -73,11 +74,11 @@ public static Expression> WideSearchDateTimeGet(SearchV
switch ( type )
{
case WideSearchDateTimeGetType.DateTime:
- return (p => p.DateTime >= dateTime);
+ return ( p => p.DateTime >= dateTime );
case WideSearchDateTimeGetType.LastEdited:
- return (p => p.LastEdited >= dateTime);
+ return ( p => p.LastEdited >= dateTime );
case WideSearchDateTimeGetType.AddToDatabase:
- return (p => p.AddToDatabase >= dateTime);
+ return ( p => p.AddToDatabase >= dateTime );
default:
throw new ArgumentNullException(nameof(type));
}
@@ -85,17 +86,17 @@ public static Expression> WideSearchDateTimeGet(SearchV
switch ( type )
{
case WideSearchDateTimeGetType.DateTime:
- return (p => p.DateTime == dateTime);
+ return ( p => p.DateTime == dateTime );
case WideSearchDateTimeGetType.LastEdited:
- return (p => p.LastEdited == dateTime);
+ return ( p => p.LastEdited == dateTime );
case WideSearchDateTimeGetType.AddToDatabase:
- return (p => p.AddToDatabase == dateTime);
+ return ( p => p.AddToDatabase == dateTime );
default:
throw new ArgumentNullException(nameof(type));
}
}
- }
-
+ }
+
///
/// Convert 1 to today
///
@@ -104,15 +105,15 @@ public static Expression> WideSearchDateTimeGet(SearchV
private static void SearchForEntireDay(SearchViewModel model, int indexer)
{
var dateTime = SearchViewModel.ParseDateTime(model.SearchFor[indexer]);
-
+
model.SearchFor[indexer] = dateTime.ToString("dd-MM-yyyy HH:mm:ss",
CultureInfo.InvariantCulture);
-
+
// Searching for entire day
if ( model.SearchForOptions[indexer] != SearchViewModel.SearchForOptionType.Equal ||
- dateTime.Hour != 0 || dateTime.Minute != 0 || dateTime.Second != 0 ||
- dateTime.Millisecond != 0 ) return;
-
+ dateTime.Hour != 0 || dateTime.Minute != 0 || dateTime.Second != 0 ||
+ dateTime.Millisecond != 0 ) return;
+
model.SearchForOptions[indexer] = SearchViewModel.SearchForOptionType.GreaterThen;
model.SearchForOptions.Add(SearchViewModel.SearchForOptionType.LessThen);
@@ -122,7 +123,7 @@ private static void SearchForEntireDay(SearchViewModel model, int indexer)
model.SearchFor.Add(add24Hours);
model.SearchIn.Add("DateTime");
}
-
+
///
/// Static binded types that are supported
///
diff --git a/starsky/starsky.feature.search/ViewModels/SearchViewModel.cs b/starsky/starsky.feature.search/ViewModels/SearchViewModel.cs
index e5bb5ac66e..33c746b873 100644
--- a/starsky/starsky.feature.search/ViewModels/SearchViewModel.cs
+++ b/starsky/starsky.feature.search/ViewModels/SearchViewModel.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
@@ -34,12 +34,12 @@ public SearchViewModel()
/// Private field: Used to know how old the search query is
///
private readonly DateTime _dateTime;
-
+
///
/// Items on the page
///
public List? FileIndexItems { get; set; }
-
+
///
/// Full location specification
///
@@ -47,18 +47,18 @@ public SearchViewModel()
// ReSharper disable once CollectionNeverQueried.Global
// ReSharper disable once PropertyCanBeMadeInitOnly.Global
public List Breadcrumb { get; set; }
-
+
///
/// Where to search for
///
public string? SearchQuery { get; set; } = string.Empty;
-
+
///
/// Current page number (index=0)
///
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public int PageNumber { get; set; }
-
+
///
/// The last page (index=0)
///
@@ -120,28 +120,28 @@ public void SetAddSearchInStringType(string value)
{
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
SearchIn ??= new List();
-
+
// use ctor to have an empty list
var fileIndexPropList = FileIndexItem.FileIndexPropList();
var fileIndexPropListIndex = fileIndexPropList.FindIndex
(x => x.Equals(value, StringComparison.OrdinalIgnoreCase));
- if (fileIndexPropListIndex != -1 )
+ if ( fileIndexPropListIndex != -1 )
{
SearchIn.Add(fileIndexPropList[fileIndexPropListIndex]);
- }
+ }
}
-
-
+
+
///
/// Private field: Search for the following value in using SearchFor inside: _searchIn
///
internal List? SearchForInternal = new();
-
+
///
/// The values to search for, to know which field use the same indexer in _searchIn
///
public List SearchFor
- {
+ {
// don't change it to 'SearchFor => _searchFor'
get
{
@@ -158,7 +158,7 @@ public void SetAddSearchFor(string value)
SearchForInternal ??= new List();
SearchForInternal.Add(value.Trim().ToLowerInvariant());
}
-
+
///
/// The search for types
///
@@ -171,26 +171,26 @@ public enum SearchForOptionType
///
[Display(Name = ">")] // in json it is GreaterThen
GreaterThen,
-
+
///
/// <
///
[Display(Name = "<")]
LessThen,
-
+
///
/// =
///
[Display(Name = "=")]
Equal,
-
+
///
/// -
///
[Display(Name = "!-")]
Not
}
-
+
///
/// Private field: Search Options >, <,=. (greater than sign, less than sign, equal sign)
/// to know which field use the same indexer in _searchIn or _searchFor
@@ -202,7 +202,7 @@ public enum SearchForOptionType
/// to know which field use the same indexer in _searchIn or _searchFor
///
public List SearchForOptions
- {
+ {
get
{
return SearchForOptionsInternal ?? new List();
@@ -244,13 +244,14 @@ public SearchForOptionType SetAddSearchForOptions(string value)
///
/// The type of page returns, (Search or Trash)
///
- public string PageType {
+ public string PageType
+ {
get
{
- if (string.IsNullOrEmpty(SearchQuery) ) return PageViewType.PageType.Search.ToString();
- return SearchQuery == TrashKeyword.TrashKeywordString ? PageViewType.PageType.Trash.ToString()
+ if ( string.IsNullOrEmpty(SearchQuery) ) return PageViewType.PageType.Search.ToString();
+ return SearchQuery == TrashKeyword.TrashKeywordString ? PageViewType.PageType.Trash.ToString()
: PageViewType.PageType.Search.ToString();
- }
+ }
}
///
@@ -269,14 +270,14 @@ public double ElapsedSeconds
_elapsedSeconds = value - value % 0.001;
}
}
-
+
///
/// Used to know how old the search query is
/// Used to know if a page is cached
///
[SuppressMessage("Usage", "S6561: Avoid using DateTime.Now " +
- "for benchmarking or timespan calculation operations.")]
- public double Offset => Math.Round(Math.Abs((DateTime.UtcNow - _dateTime).TotalSeconds), 2);
+ "for benchmarking or timespan calculation operations.")]
+ public double Offset => Math.Round(Math.Abs(( DateTime.UtcNow - _dateTime ).TotalSeconds), 2);
///
@@ -300,32 +301,32 @@ public void SetAndOrOperator(char andOrChar, int relativeLocation = 0)
andOrBool = false;
}
- if (SearchOperatorOptionsInternal.Count == 0 && andOrChar == '|')
+ if ( SearchOperatorOptionsInternal.Count == 0 && andOrChar == '|' )
{
SearchOperatorOptionsInternal.Add(false);
}
-
+
// Store item on a different location in the List
if ( relativeLocation == 0 )
{
SearchOperatorOptionsInternal.Add(andOrBool);
}
- else if ( SearchOperatorOptionsInternal.Count+relativeLocation <= -1 )
+ else if ( SearchOperatorOptionsInternal.Count + relativeLocation <= -1 )
{
SearchOperatorOptionsInternal.Insert(0, andOrBool);
}
else
{
- SearchOperatorOptionsInternal.Insert(SearchOperatorOptionsInternal.Count+relativeLocation,andOrBool);
+ SearchOperatorOptionsInternal.Insert(SearchOperatorOptionsInternal.Count + relativeLocation, andOrBool);
}
-
+
}
-
+
///
/// Search Operator, eg. || &&
///
public List SearchOperatorOptions
- {
+ {
get
{
return SearchOperatorOptionsInternal ?? new List();
@@ -342,13 +343,13 @@ public List SearchOperatorOptions
public bool SearchOperatorContinue(int indexer, int max)
{
if ( SearchOperatorOptionsInternal == null ) return true;
- if ( indexer <= -1 || indexer > max) return true;
+ if ( indexer <= -1 || indexer > max ) return true;
// for -Datetime=1 (03-03-2019 00:00:00-03-03-2019 23:59:59), this are two queries >= fail!!
- if (indexer >= SearchOperatorOptionsInternal.Count ) return true; // used when general words without update
+ if ( indexer >= SearchOperatorOptionsInternal.Count ) return true; // used when general words without update
var returnResult = SearchOperatorOptionsInternal[indexer];
return returnResult;
}
-
+
///
/// ||[OR] = |, else = &, default = string.Emphy
///
@@ -362,23 +363,23 @@ public static char AndOrRegex(string item)
// To Search Type
var lastStringValue = rgx.Match(item).Value;
-
+
// set default
if ( string.IsNullOrEmpty(lastStringValue) ) lastStringValue = string.Empty;
-
+
if ( lastStringValue == "||" ) return '|';
return '&';
}
-
+
///
/// Copy the current object in memory
///
///
public SearchViewModel Clone()
{
- return (SearchViewModel) MemberwiseClone();
+ return ( SearchViewModel )MemberwiseClone();
}
-
+
///
/// For reparsing keywords to -Tags:"keyword"
/// handle keywords without for example -Tags, or -DateTime prefix
@@ -393,7 +394,7 @@ public string ParseDefaultOption(string defaultQuery)
// fallback situation
// search on for example: '%'
- if ( SearchFor.Count == 0 )
+ if ( SearchFor.Count == 0 )
{
SetAddSearchFor(defaultQuery);
SetAddSearchInStringType("tags");
@@ -405,7 +406,7 @@ public string ParseDefaultOption(string defaultQuery)
// // &&|\|\|
Regex andOrRegex = new Regex("&&|\\|\\|",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
-
+
var andOrRegexMatches = andOrRegex.Matches(defaultQuery);
foreach ( Match andOrValue in andOrRegexMatches )
@@ -421,26 +422,26 @@ public string ParseDefaultOption(string defaultQuery)
SetAndOrOperator(AndOrRegex("&&"));
}
}
-
+
return returnQueryBuilder.ToString();
}
- private (string defaultQuery, StringBuilder returnQueryBuilder)
+ private (string defaultQuery, StringBuilder returnQueryBuilder)
ParseQuotedValues(string defaultQuery, StringBuilder returnQueryBuilder)
{
// Get Quoted values
// (["'])(\\?.)*?\1
-
+
// Quoted or words
// [\w!]+|(["'])(\\?.)*?\1
-
+
Regex inUrlRegex = new Regex("[\\w!]+|([\"\'])(\\\\?.)*?\\1",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(100));
// Escape special quotes
- defaultQuery = Regex.Replace(defaultQuery, "[“”‘’]", "\"",
+ defaultQuery = Regex.Replace(defaultQuery, "[“”‘’]", "\"",
RegexOptions.None, TimeSpan.FromMilliseconds(100));
-
+
var regexInUrlMatches = inUrlRegex.Matches(defaultQuery);
foreach ( Match regexInUrl in regexInUrlMatches )
@@ -459,28 +460,28 @@ public string ParseDefaultOption(string defaultQuery)
var startIndexer = regexInUrl.Index;
var startLength = regexInUrl.Length;
- var lastChar = defaultQuery[startIndexer + regexInUrl.Length -1 ];
-
+ var lastChar = defaultQuery[startIndexer + regexInUrl.Length - 1];
+
if ( defaultQuery[regexInUrl.Index] == '"' &&
- lastChar == '"' ||
- defaultQuery[regexInUrl.Index] == '\'' &&
- lastChar == '\'' )
+ lastChar == '"' ||
+ defaultQuery[regexInUrl.Index] == '\'' &&
+ lastChar == '\'' )
{
startIndexer = regexInUrl.Index + 1;
startLength = regexInUrl.Length - 2;
}
-
+
// Get Value
var searchForQuery = defaultQuery.Substring(startIndexer, startLength);
-
+
returnQueryBuilder.Append($"-Tags:\"{searchForQuery}\" ");
-
+
SetAddSearchFor(searchForQuery);
SetAddSearchInStringType("tags");
// Detecting Not Queries (string must be at least 3 chars)
- if ( ( regexInUrl.Index - 1 >= 0 && defaultQuery[regexInUrl.Index - 1] == '-' )
- || ( regexInUrl.Index + 2 <= regexInUrl.Length && defaultQuery[regexInUrl.Index + 2] == '-' ) )
+ if ( ( regexInUrl.Index - 1 >= 0 && defaultQuery[regexInUrl.Index - 1] == '-' )
+ || ( regexInUrl.Index + 2 <= regexInUrl.Length && defaultQuery[regexInUrl.Index + 2] == '-' ) )
{
SetAddSearchForOptions("-");
continue;
@@ -488,9 +489,9 @@ public string ParseDefaultOption(string defaultQuery)
SetAddSearchForOptions("=");
}
- return ( defaultQuery, returnQueryBuilder );
+ return (defaultQuery, returnQueryBuilder);
}
-
+
///
/// Filter for WideSearch
/// Always after wideSearch
@@ -505,7 +506,7 @@ public static SearchViewModel NarrowSearch(SearchViewModel model)
for ( var i = 0; i < model.SearchIn.Count; i++ )
{
- var propertyStringName = FileIndexItem.FileIndexPropList().Find( p =>
+ var propertyStringName = FileIndexItem.FileIndexPropList().Find(p =>
string.Equals(p, model.SearchIn[i],
StringComparison.InvariantCultureIgnoreCase));
@@ -515,18 +516,18 @@ public static SearchViewModel NarrowSearch(SearchViewModel model)
}
var property = new FileIndexItem().GetType().GetProperty(propertyStringName)!;
-
+
// skip OR searches
if ( !model.SearchOperatorContinue(i, model.SearchIn.Count) )
{
continue;
}
- PropertySearch(model, property, model.SearchFor[i],model.SearchForOptions[i]);
+ PropertySearch(model, property, model.SearchFor[i], model.SearchForOptions[i]);
}
-
+
// hide xmp files in default view
- if ( model.SearchIn.TrueForAll(p => !string.Equals(p, nameof(SearchInTypes.imageformat),
- StringComparison.InvariantCultureIgnoreCase)))
+ if ( model.SearchIn.TrueForAll(p => !string.Equals(p, nameof(SearchInTypes.imageformat),
+ StringComparison.InvariantCultureIgnoreCase)) )
{
model.FileIndexItems = model.FileIndexItems!
.Where(p => p.ImageFormat != ExtensionRolesHelper.ImageFormat.xmp).ToList();
@@ -540,23 +541,23 @@ internal static SearchViewModel PropertySearchStringType(
PropertyInfo property, string searchForQuery,
SearchForOptionType searchType)
{
- switch (searchType)
+ switch ( searchType )
{
case SearchForOptionType.Not:
model.FileIndexItems = model.FileIndexItems!.Where(
- p => p.GetType().GetProperty(property.Name)?.Name == property.Name
- && ! // not
- p.GetType().GetProperty(property.Name)!.GetValue(p, null)?
- .ToString()?.ToLowerInvariant().Contains(searchForQuery,
- StringComparison.InvariantCultureIgnoreCase) == true
+ p => p.GetType().GetProperty(property.Name)?.Name == property.Name
+ && ! // not
+ p.GetType().GetProperty(property.Name)!.GetValue(p, null)?
+ .ToString()?.ToLowerInvariant().Contains(searchForQuery,
+ StringComparison.InvariantCultureIgnoreCase) == true
).ToList();
break;
default:
model.FileIndexItems = model.FileIndexItems?
- .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
- && p.GetType().GetProperty(property.Name)!.GetValue(p, null)?
- .ToString()?.ToLowerInvariant().Contains(searchForQuery,
- StringComparison.InvariantCultureIgnoreCase) == true
+ .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
+ && p.GetType().GetProperty(property.Name)!.GetValue(p, null)?
+ .ToString()?.ToLowerInvariant().Contains(searchForQuery,
+ StringComparison.InvariantCultureIgnoreCase) == true
).ToList();
break;
}
@@ -572,40 +573,40 @@ internal static SearchViewModel PropertySearchBoolType(
{
return new SearchViewModel();
}
-
- if ( property == null)
+
+ if ( property == null )
{
return model;
}
-
+
model.FileIndexItems = model.FileIndexItems?
- .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
- && (bool?) p.GetType().GetProperty(property.Name)?.GetValue(p, null) == boolIsValue
+ .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
+ && ( bool? )p.GetType().GetProperty(property.Name)?.GetValue(p, null) == boolIsValue
).ToList();
return model;
}
-
+
private static SearchViewModel PropertySearchImageFormatType(
SearchViewModel model,
PropertyInfo property, ExtensionRolesHelper.ImageFormat castImageFormat,
SearchForOptionType searchType)
{
- switch (searchType)
+ switch ( searchType )
{
case SearchForOptionType.Not:
model.FileIndexItems = model.FileIndexItems!
- .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
- && (ExtensionRolesHelper.ImageFormat) p.GetType().GetProperty(property.Name)?
- .GetValue(p, null)!
- != // not
- castImageFormat
+ .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
+ && ( ExtensionRolesHelper.ImageFormat )p.GetType().GetProperty(property.Name)?
+ .GetValue(p, null)!
+ != // not
+ castImageFormat
).ToList();
break;
default:
model.FileIndexItems = model.FileIndexItems!
- .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
- && (ExtensionRolesHelper.ImageFormat) p.GetType().GetProperty(property.Name)?
- .GetValue(p, null)! == castImageFormat
+ .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
+ && ( ExtensionRolesHelper.ImageFormat )p.GetType().GetProperty(property.Name)?
+ .GetValue(p, null)! == castImageFormat
).ToList();
break;
}
@@ -619,32 +620,32 @@ private static SearchViewModel PropertySearchDateTimeType(
SearchForOptionType searchType)
{
var parsedDateTime = ParseDateTime(searchForQuery);
-
- switch (searchType)
+
+ switch ( searchType )
{
case SearchForOptionType.LessThen:
model.FileIndexItems = model.FileIndexItems!
- .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
- && (DateTime) p.GetType().GetProperty(property.Name)?.GetValue(p, null)!
- <= parsedDateTime
+ .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
+ && ( DateTime )p.GetType().GetProperty(property.Name)?.GetValue(p, null)!
+ <= parsedDateTime
).ToList();
break;
case SearchForOptionType.GreaterThen:
model.FileIndexItems = model.FileIndexItems!
- .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
- && (DateTime) p.GetType().GetProperty(property.Name)?.GetValue(p, null)!
- >= parsedDateTime
+ .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
+ && ( DateTime )p.GetType().GetProperty(property.Name)?.GetValue(p, null)!
+ >= parsedDateTime
).ToList();
break;
default:
model.FileIndexItems = model.FileIndexItems!
- .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
- && (DateTime) p.GetType().GetProperty(property.Name)?.GetValue(p, null)!
- == parsedDateTime
+ .Where(p => p.GetType().GetProperty(property.Name)?.Name == property.Name
+ && ( DateTime )p.GetType().GetProperty(property.Name)?.GetValue(p, null)!
+ == parsedDateTime
).ToList();
break;
}
-
+
return model;
}
@@ -656,7 +657,7 @@ private static SearchViewModel PropertySearchDateTimeType(
/// the query to search for (always string)
/// greater then, equal
/// search values
- internal static SearchViewModel PropertySearch(SearchViewModel model,
+ internal static SearchViewModel PropertySearch(SearchViewModel model,
PropertyInfo property, string searchForQuery, SearchForOptionType searchType)
{
@@ -665,27 +666,27 @@ internal static SearchViewModel PropertySearch(SearchViewModel model,
return PropertySearchStringType(model, property, searchForQuery, searchType);
}
- if ( (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?)) &&
- bool.TryParse(searchForQuery, out var boolIsValue))
+ if ( ( property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?) ) &&
+ bool.TryParse(searchForQuery, out var boolIsValue) )
{
return PropertySearchBoolType(model, property, boolIsValue);
}
-
- if ( property.PropertyType == typeof(ExtensionRolesHelper.ImageFormat) &&
- Enum.TryParse(
- searchForQuery.ToLowerInvariant(), out var castImageFormat) )
+
+ if ( property.PropertyType == typeof(ExtensionRolesHelper.ImageFormat) &&
+ Enum.TryParse(
+ searchForQuery.ToLowerInvariant(), out var castImageFormat) )
{
- return PropertySearchImageFormatType(model, property, castImageFormat, searchType);
+ return PropertySearchImageFormatType(model, property, castImageFormat, searchType);
}
-
+
if ( property.PropertyType == typeof(DateTime) )
{
- return PropertySearchDateTimeType(model, property, searchForQuery, searchType);
+ return PropertySearchDateTimeType(model, property, searchForQuery, searchType);
}
return model;
}
-
+
///
/// Internal API: to parse datetime objects
///
@@ -695,38 +696,38 @@ internal static DateTime ParseDateTime(string input)
{
// For relative values
- if ( Regex.IsMatch(input, @"^\d+$",
- RegexOptions.None, TimeSpan.FromMilliseconds(100)) &&
- int.TryParse(input, out var relativeValue) )
+ if ( Regex.IsMatch(input, @"^\d+$",
+ RegexOptions.None, TimeSpan.FromMilliseconds(100)) &&
+ int.TryParse(input, out var relativeValue) )
{
- if(relativeValue >= 1) relativeValue *= -1; // always in the past
+ if ( relativeValue >= 1 ) relativeValue *= -1; // always in the past
if ( relativeValue > -60000 ) // 24-11-1854
{
return DateTime.Today.AddDays(relativeValue);
}
}
-
+
var patternLab = new List
{
"yyyy-MM-dd\\tHH:mm:ss", // < lowercase :)
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd-HH:mm:ss",
- "yyyy-MM-dd",
- "dd-MM-yyyy",
+ "yyyy-MM-dd",
+ "dd-MM-yyyy",
"dd-MM-yyyy HH:mm:ss",
"dd-MM-yyyy\\tHH:mm:ss",
"MM/dd/yyyy HH:mm:ss", // < used by the next string rule 01/30/2018 00:00:00
};
-
+
DateTime dateTime = DateTime.MinValue;
-
- foreach (var pattern in patternLab)
+
+ foreach ( var pattern in patternLab )
{
- DateTime.TryParseExact(input,
- pattern,
- CultureInfo.InvariantCulture,
+ DateTime.TryParseExact(input,
+ pattern,
+ CultureInfo.InvariantCulture,
DateTimeStyles.None, out dateTime);
- if(dateTime.Year > 2) return dateTime;
+ if ( dateTime.Year > 2 ) return dateTime;
}
return dateTime.Year > 2 ? dateTime : DateTime.Now;
}
diff --git a/starsky/starsky.feature.search/starsky.feature.search.csproj b/starsky/starsky.feature.search/starsky.feature.search.csproj
index 67eafe62da..6ba1351866 100644
--- a/starsky/starsky.feature.search/starsky.feature.search.csproj
+++ b/starsky/starsky.feature.search/starsky.feature.search.csproj
@@ -7,10 +7,14 @@
enable
starsky.feature.search
+
-
-
-
+
+
+
+
+ true
+
diff --git a/starsky/starsky.feature.settings/Services/UpdateAppSettingsByPath.cs b/starsky/starsky.feature.settings/Services/UpdateAppSettingsByPath.cs
index e249534447..c642eb38d8 100644
--- a/starsky/starsky.feature.settings/Services/UpdateAppSettingsByPath.cs
+++ b/starsky/starsky.feature.settings/Services/UpdateAppSettingsByPath.cs
@@ -23,10 +23,11 @@ public UpdateAppSettingsByPath(AppSettings appSettings, ISelectorStorage selecto
_hostStorage =
selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
}
-
- public async Task UpdateAppSettingsAsync(AppSettingsTransferObject appSettingTransferObject)
+
+ public async Task UpdateAppSettingsAsync(
+ AppSettingsTransferObject appSettingTransferObject)
{
- if ( !string.IsNullOrEmpty(appSettingTransferObject.StorageFolder))
+ if ( !string.IsNullOrEmpty(appSettingTransferObject.StorageFolder) )
{
if ( !_appSettings.StorageFolderAllowEdit )
{
@@ -37,7 +38,8 @@ public async Task UpdateAppSettingsAsync(AppSettin
"There is an Environment variable set so you can't update it here"
};
}
- if (!_hostStorage.ExistFolder(appSettingTransferObject.StorageFolder) )
+
+ if ( !_hostStorage.ExistFolder(appSettingTransferObject.StorageFolder) )
{
return new UpdateAppSettingsStatusModel
{
@@ -47,23 +49,18 @@ public async Task UpdateAppSettingsAsync(AppSettin
};
}
}
-
+
AppSettingsCompareHelper.Compare(_appSettings, appSettingTransferObject);
- var transfer = ( AppSettingsTransferObject ) _appSettings;
-
+ var transfer = ( AppSettingsTransferObject )_appSettings;
+
// should not forget app: prefix
- var jsonOutput = JsonSerializer.Serialize(new { app = transfer }, DefaultJsonSerializer.NoNamingPolicy);
+ var jsonOutput = JsonSerializer.Serialize(new { app = transfer },
+ DefaultJsonSerializer.NoNamingPolicyBoolAsString);
await _hostStorage.WriteStreamAsync(
StringToStreamHelper.StringToStream(jsonOutput),
_appSettings.AppSettingsPath);
-
- return new UpdateAppSettingsStatusModel
- {
- StatusCode = 200,
- Message = "Updated"
- };
- }
-
+ return new UpdateAppSettingsStatusModel { StatusCode = 200, Message = "Updated" };
+ }
}
diff --git a/starsky/starsky.feature.syncbackground/Helpers/OnStartupSync.cs b/starsky/starsky.feature.syncbackground/Helpers/OnStartupSync.cs
index e1c73fa57c..181939e14f 100644
--- a/starsky/starsky.feature.syncbackground/Helpers/OnStartupSync.cs
+++ b/starsky/starsky.feature.syncbackground/Helpers/OnStartupSync.cs
@@ -36,7 +36,7 @@ public class OnStartupSync
///
///
///
- public OnStartupSync(IServiceScopeFactory serviceScopeFactory, IDiskWatcherBackgroundTaskQueue backgroundTaskQueue, AppSettings appSettings,
+ public OnStartupSync(IServiceScopeFactory serviceScopeFactory, IDiskWatcherBackgroundTaskQueue backgroundTaskQueue, AppSettings appSettings,
ISynchronize synchronize, ISettingsService settingsService, IWebLogger logger)
{
_serviceScopeFactory = serviceScopeFactory;
@@ -57,22 +57,22 @@ await _backgroundTaskQueue.QueueBackgroundWorkItemAsync(async token =>
public async Task StartUpSyncTask()
{
- if ( _appSettings.SyncOnStartup != true )
+ if ( _appSettings.SyncOnStartup != true )
{
return;
}
var lastUpdatedValue = await _settingsService.GetSetting(
SettingsType.LastSyncBackgroundDateTime);
-
+
await _synchronize.Sync("/", PushToSockets, lastUpdatedValue.ToLocalTime());
await _settingsService.AddOrUpdateSetting(
SettingsType.LastSyncBackgroundDateTime,
DateTime.UtcNow.ToString(SettingsFormats.LastSyncBackgroundDateTime, CultureInfo.InvariantCulture));
-
+
_logger.LogInformation("Sync on startup done");
}
-
+
internal async Task PushToSockets(List updatedList)
{
using var scope = _serviceScopeFactory.CreateScope();
@@ -83,6 +83,6 @@ internal async Task PushToSockets(List updatedList)
await webSocketConnectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None);
await notificationQuery.AddNotification(webSocketResponse);
- }
+ }
}
diff --git a/starsky/starsky.feature.syncbackground/Services/OnStartupSyncBackgroundService.cs b/starsky/starsky.feature.syncbackground/Services/OnStartupSyncBackgroundService.cs
index ee137f731d..1d17cbf748 100644
--- a/starsky/starsky.feature.syncbackground/Services/OnStartupSyncBackgroundService.cs
+++ b/starsky/starsky.feature.syncbackground/Services/OnStartupSyncBackgroundService.cs
@@ -7,15 +7,14 @@
using starsky.foundation.injection;
using starsky.foundation.platform.Interfaces;
using starsky.foundation.platform.Models;
-using starsky.foundation.realtime.Interfaces;
using starsky.foundation.settings.Interfaces;
using starsky.foundation.sync.SyncInterfaces;
using starsky.foundation.sync.WatcherBackgroundService;
[assembly: InternalsVisibleTo("starskytest")]
+
namespace starsky.feature.syncbackground.Services
{
-
[Service(typeof(IHostedService), InjectionLifetime = InjectionLifetime.Singleton)]
public class OnStartupSyncBackgroundService : BackgroundService
{
@@ -35,13 +34,15 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var scope = _serviceScopeFactory.CreateScope();
var appSettings = scope.ServiceProvider.GetRequiredService();
- var diskWatcherBackgroundTaskQueue = scope.ServiceProvider.GetRequiredService();
+ var diskWatcherBackgroundTaskQueue = scope.ServiceProvider
+ .GetRequiredService();
var synchronize = scope.ServiceProvider.GetRequiredService();
var settingsService = scope.ServiceProvider.GetRequiredService();
var logger = scope.ServiceProvider.GetRequiredService();
- await new OnStartupSync(_serviceScopeFactory, diskWatcherBackgroundTaskQueue, appSettings, synchronize, settingsService, logger).StartUpSync();
- }
+ await new OnStartupSync(_serviceScopeFactory, diskWatcherBackgroundTaskQueue,
+ appSettings, synchronize, settingsService, logger).StartUpSync();
+ }
}
}
diff --git a/starsky/starsky.feature.syncbackground/starsky.feature.syncbackground.csproj b/starsky/starsky.feature.syncbackground/starsky.feature.syncbackground.csproj
index c011803818..2cbf838962 100644
--- a/starsky/starsky.feature.syncbackground/starsky.feature.syncbackground.csproj
+++ b/starsky/starsky.feature.syncbackground/starsky.feature.syncbackground.csproj
@@ -1,23 +1,23 @@
+
+ net8.0
+ starsky.feature.syncbackground
+ {15e1493e-6e79-4314-907f-b3ef18eb9046}
+ 0.6.0-beta.0
+ enable
+
-
- net8.0
- starsky.feature.syncbackground
- {15e1493e-6e79-4314-907f-b3ef18eb9046}
- 0.6.0-beta.0
- enable
-
+
+
+
-
- true
-
+
+
+
+
-
-
-
+
+ true
+
-
-
-
-
diff --git a/starsky/starsky.feature.thumbnail/Services/DatabaseThumbnailGenerationService.cs b/starsky/starsky.feature.thumbnail/Services/DatabaseThumbnailGenerationService.cs
index a3cc3287fa..88a7be0fe5 100644
--- a/starsky/starsky.feature.thumbnail/Services/DatabaseThumbnailGenerationService.cs
+++ b/starsky/starsky.feature.thumbnail/Services/DatabaseThumbnailGenerationService.cs
@@ -29,8 +29,8 @@ public class DatabaseThumbnailGenerationService : IDatabaseThumbnailGenerationSe
private readonly IUpdateStatusGeneratedThumbnailService _updateStatusGeneratedThumbnailService;
private readonly IThumbnailQuery _thumbnailQuery;
- public DatabaseThumbnailGenerationService(IQuery query, IWebLogger logger, IWebSocketConnectionsService connectionsService,
- IThumbnailService thumbnailService, IThumbnailQuery thumbnailQuery,
+ public DatabaseThumbnailGenerationService(IQuery query, IWebLogger logger, IWebSocketConnectionsService connectionsService,
+ IThumbnailService thumbnailService, IThumbnailQuery thumbnailQuery,
IThumbnailQueuedHostedService bgTaskQueue,
IUpdateStatusGeneratedThumbnailService updateStatusGeneratedThumbnailService)
{
@@ -42,12 +42,12 @@ public DatabaseThumbnailGenerationService(IQuery query, IWebLogger logger, IWebS
_bgTaskQueue = bgTaskQueue;
_updateStatusGeneratedThumbnailService = updateStatusGeneratedThumbnailService;
}
-
+
public async Task StartBackgroundQueue(DateTime endTime)
{
var thumbnailItems = await _thumbnailQuery.UnprocessedGeneratedThumbnails();
var queryItems = await _query.GetObjectsByFileHashAsync(thumbnailItems.Select(p => p.FileHash).ToList());
-
+
foreach ( var chuckedItems in thumbnailItems.ChunkyEnumerable(50) )
{
// When the CPU is to high its gives a Error 500
@@ -68,22 +68,22 @@ internal async Task> FilterAndWorkThumbnailGeneration
{
return await WorkThumbnailGeneration(chuckedItems, queryItems);
}
-
+
_logger.LogInformation("Cancel job due timeout");
return new List();
}
-
+
internal async Task> WorkThumbnailGeneration(
List chuckedItems,
List fileIndexItems)
{
var resultData = new List();
-
+
foreach ( var item in chuckedItems )
{
var fileIndexItem = fileIndexItems.Find(p => p.FileHash == item.FileHash);
- if ( fileIndexItem?.FilePath == null ||
- fileIndexItem.Status != FileIndexItem.ExifStatus.Ok )
+ if ( fileIndexItem?.FilePath == null ||
+ fileIndexItem.Status != FileIndexItem.ExifStatus.Ok )
{
// when null set to false
item.Small ??= false;
@@ -92,10 +92,10 @@ internal async Task> WorkThumbnailGeneration(
await _thumbnailQuery.UpdateAsync(item);
continue;
}
-
- var generationResultModels = (await _thumbnailService.CreateThumbAsync(fileIndexItem
- .FilePath!, fileIndexItem.FileHash!)).ToList();
-
+
+ var generationResultModels = ( await _thumbnailService.CreateThumbAsync(fileIndexItem
+ .FilePath!, fileIndexItem.FileHash!) ).ToList();
+
await _updateStatusGeneratedThumbnailService.AddOrUpdateStatusAsync(
generationResultModels);
var removedItems = await _updateStatusGeneratedThumbnailService
@@ -103,13 +103,13 @@ await _updateStatusGeneratedThumbnailService.AddOrUpdateStatusAsync(
if ( removedItems.Count != 0 )
{
_logger.LogInformation($"[DatabaseThumbnailGenerationService] removed items ({DateTime.UtcNow:HH:mm:ss})" +
- $" items: {string.Join(",",removedItems)}");
+ $" items: {string.Join(",", removedItems)}");
continue;
}
resultData.Add(fileIndexItem);
}
-
+
var filteredData = resultData
.Where(p => p.Status == FileIndexItem.ExifStatus.Ok).ToList();
@@ -118,10 +118,10 @@ await _updateStatusGeneratedThumbnailService.AddOrUpdateStatusAsync(
_logger.LogInformation($"[DatabaseThumbnailGenerationService] no items ({DateTime.UtcNow:HH:mm:ss})");
return chuckedItems;
}
-
+
_logger.LogInformation($"[DatabaseThumbnailGenerationService] done ({DateTime.UtcNow:HH:mm:ss})" +
- $" {filteredData.Count} items: " +
- $"{string.Join(",",filteredData.Select(p => p.FilePath).ToList())}");
+ $" {filteredData.Count} items: " +
+ $"{string.Join(",", filteredData.Select(p => p.FilePath).ToList())}");
var webSocketResponse =
new ApiNotificationResponseModel>(filteredData, ApiNotificationType.ThumbnailGeneration);
diff --git a/starsky/starsky.feature.thumbnail/Services/ManualThumbnailGenerationService.cs b/starsky/starsky.feature.thumbnail/Services/ManualThumbnailGenerationService.cs
index 8a2eb96ae8..826c71f6e5 100644
--- a/starsky/starsky.feature.thumbnail/Services/ManualThumbnailGenerationService.cs
+++ b/starsky/starsky.feature.thumbnail/Services/ManualThumbnailGenerationService.cs
@@ -28,16 +28,16 @@ public class ManualThumbnailGenerationService : IManualThumbnailGenerationServic
private readonly IWebSocketConnectionsService _connectionsService;
private readonly IThumbnailService _thumbnailService;
private readonly IThumbnailQueuedHostedService _bgTaskQueue;
-
- public ManualThumbnailGenerationService(IQuery query, IWebLogger logger, IWebSocketConnectionsService connectionsService,
- IThumbnailService thumbnailService,
+
+ public ManualThumbnailGenerationService(IQuery query, IWebLogger logger, IWebSocketConnectionsService connectionsService,
+ IThumbnailService thumbnailService,
IThumbnailQueuedHostedService bgTaskQueue)
{
_query = query;
_logger = logger;
_connectionsService = connectionsService;
_thumbnailService = thumbnailService;
- _bgTaskQueue = bgTaskQueue;
+ _bgTaskQueue = bgTaskQueue;
}
public async Task ManualBackgroundQueue(string subPath)
@@ -48,7 +48,7 @@ await _bgTaskQueue.QueueBackgroundWorkItemAsync(async _ =>
await WorkThumbnailGeneration(subPath);
}, subPath);
}
-
+
internal async Task WorkThumbnailGeneration(string subPath)
{
try
@@ -69,7 +69,7 @@ internal async Task WorkThumbnailGeneration(string subPath)
var webSocketResponse =
new ApiNotificationResponseModel>(result, ApiNotificationType.ThumbnailGeneration);
await _connectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None);
-
+
_logger.LogInformation($"[ThumbnailGenerationController] done {subPath}");
}
catch ( UnauthorizedAccessException e )
@@ -88,7 +88,7 @@ internal static List WhichFilesNeedToBePushedForUpdates(List {"LastEdited", "FileHash"};
+ item.LastChanged = new List { "LastEdited", "FileHash" };
result.Add(item);
}
diff --git a/starsky/starsky.feature.thumbnail/Services/PeriodicThumbnailScanHostedService.cs b/starsky/starsky.feature.thumbnail/Services/PeriodicThumbnailScanHostedService.cs
index 95e89c36ec..9165d6e0ac 100644
--- a/starsky/starsky.feature.thumbnail/Services/PeriodicThumbnailScanHostedService.cs
+++ b/starsky/starsky.feature.thumbnail/Services/PeriodicThumbnailScanHostedService.cs
@@ -6,7 +6,6 @@
using Microsoft.Extensions.Hosting;
using starsky.feature.thumbnail.Interfaces;
using starsky.foundation.injection;
-using starsky.foundation.platform.Extensions;
using starsky.foundation.platform.Interfaces;
using starsky.foundation.platform.Models;
@@ -27,16 +26,16 @@ public class PeriodicThumbnailScanHostedService : BackgroundService
internal TimeSpan Period { get; set; }
internal int MinimumIntervalInMinutes { get; set; } = 3;
-
+
internal bool IsEnabled { get; set; }
-
+
public PeriodicThumbnailScanHostedService(AppSettings appSettings,
- IWebLogger logger,
+ IWebLogger logger,
IServiceScopeFactory factory)
{
_logger = logger;
_factory = factory;
-
+
if ( appSettings.ThumbnailGenerationIntervalInMinutes >= MinimumIntervalInMinutes )
{
Period = TimeSpan.FromMinutes(appSettings
@@ -44,10 +43,10 @@ public PeriodicThumbnailScanHostedService(AppSettings appSettings,
IsEnabled = true;
return;
}
-
+
Period = TimeSpan.FromMinutes(60);
}
-
+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// why Task.Yield -> https://medium.com/@thepen0411/how-to-resolve-the-net-background-service-blocking-issue-c96086de8acd
@@ -61,8 +60,8 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await RunJob(cancellationToken);
}
-
- if ( !IsEnabled)
+
+ if ( !IsEnabled )
{
return false;
}
@@ -81,19 +80,19 @@ await timer.WaitForNextTickAsync(cancellationToken) )
{
_logger.LogError("[StartBackgroundAsync] catch-ed OperationCanceledException", exception);
}
-
+
return null;
}
internal async Task RunJob(CancellationToken cancellationToken = default)
{
- if (! IsEnabled )
+ if ( !IsEnabled )
{
_logger.LogInformation(
$"Skipped {nameof(PeriodicThumbnailScanHostedService)}");
return false;
}
-
+
cancellationToken.ThrowIfCancellationRequested();
try
@@ -108,7 +107,7 @@ await timer.WaitForNextTickAsync(cancellationToken) )
$" Count: {_executionCount} ({DateTime.UtcNow:HH:mm:ss})");
return true;
}
- catch (Exception ex)
+ catch ( Exception ex )
{
_logger.LogInformation(
$"Failed to execute {nameof(PeriodicThumbnailScanHostedService)} " +
diff --git a/starsky/starsky.feature.trash/Interfaces/IMoveToTrashService.cs b/starsky/starsky.feature.trash/Interfaces/IMoveToTrashService.cs
index ed47aaada7..469dcaf294 100644
--- a/starsky/starsky.feature.trash/Interfaces/IMoveToTrashService.cs
+++ b/starsky/starsky.feature.trash/Interfaces/IMoveToTrashService.cs
@@ -13,7 +13,7 @@ public interface IMoveToTrashService
/// list of files
Task> MoveToTrashAsync(List inputFilePaths,
bool collections);
-
+
///
/// Is it supported to use the system trash
/// But it does NOT check if the feature toggle is enabled
diff --git a/starsky/starsky.feature.trash/Services/MoveToTrashService.cs b/starsky/starsky.feature.trash/Services/MoveToTrashService.cs
index 457a417774..1f09bfc099 100644
--- a/starsky/starsky.feature.trash/Services/MoveToTrashService.cs
+++ b/starsky/starsky.feature.trash/Services/MoveToTrashService.cs
@@ -1,4 +1,4 @@
-using System.Runtime.CompilerServices;
+using System.Runtime.CompilerServices;
using System.Text;
using starsky.feature.metaupdate.Interfaces;
using starsky.feature.trash.Interfaces;
@@ -24,8 +24,8 @@ public class MoveToTrashService : IMoveToTrashService
private readonly ITrashConnectionService _connectionService;
public MoveToTrashService(AppSettings appSettings, IQuery query,
- IMetaPreflight metaPreflight,
- IUpdateBackgroundTaskQueue queue,
+ IMetaPreflight metaPreflight,
+ IUpdateBackgroundTaskQueue queue,
ITrashService systemTrashService, IMetaUpdateService metaUpdateService,
ITrashConnectionService connectionService
)
@@ -38,7 +38,7 @@ ITrashConnectionService connectionService
_metaUpdateService = metaUpdateService;
_connectionService = connectionService;
}
-
+
///
/// Is supported and enabled in the feature toggle
///
@@ -46,7 +46,7 @@ ITrashConnectionService connectionService
public bool IsEnabled()
{
return _appSettings.UseSystemTrash == true &&
- _systemTrashService.DetectToUseSystemTrash();
+ _systemTrashService.DetectToUseSystemTrash();
}
///
@@ -70,37 +70,37 @@ public async Task> MoveToTrashAsync(
List inputFilePaths, bool collections)
{
var inputModel = new FileIndexItem { Tags = TrashKeyword.TrashKeywordString };
- var (fileIndexResultsList, changedFileIndexItemName) =
+ var (fileIndexResultsList, changedFileIndexItemName) =
await _metaPreflight.PreflightAsync(inputModel, inputFilePaths,
false, collections, 0);
(fileIndexResultsList, changedFileIndexItemName) = await AppendChildItemsToTrashList(fileIndexResultsList, changedFileIndexItemName);
-
+
var moveToTrashList =
fileIndexResultsList.Where(p =>
p.Status is FileIndexItem.ExifStatus.Ok or FileIndexItem.ExifStatus.Deleted).ToList();
var isSystemTrashEnabled = IsEnabled();
-
+
await _queue.QueueBackgroundWorkItemAsync(async _ =>
{
await _connectionService.ConnectionServiceAsync(moveToTrashList, isSystemTrashEnabled);
-
+
if ( isSystemTrashEnabled )
{
await SystemTrashInQueue(moveToTrashList);
return;
}
-
- await MetaTrashInQueue(changedFileIndexItemName!,
+
+ await MetaTrashInQueue(changedFileIndexItemName!,
fileIndexResultsList, inputModel, collections);
-
+
}, "trash");
-
+
return TrashConnectionService.StatusUpdate(moveToTrashList, isSystemTrashEnabled);
}
- private async Task MetaTrashInQueue(Dictionary> changedFileIndexItemName,
+ private async Task MetaTrashInQueue(Dictionary> changedFileIndexItemName,
List fileIndexResultsList, FileIndexItem inputModel, bool collections)
{
await _metaUpdateService.UpdateAsync(changedFileIndexItemName,
@@ -112,8 +112,8 @@ await _metaUpdateService.UpdateAsync(changedFileIndexItemName,
///
///
///
- internal async Task<(List, Dictionary>?)> AppendChildItemsToTrashList(List moveToTrash,
- Dictionary> changedFileIndexItemName)
+ internal async Task<(List, Dictionary>?)> AppendChildItemsToTrashList(List moveToTrash,
+ Dictionary> changedFileIndexItemName)
{
var parentSubPaths = moveToTrash
.Where(p => !string.IsNullOrEmpty(p.FilePath) && p.IsDirectory == true)
@@ -122,24 +122,24 @@ await _metaUpdateService.UpdateAsync(changedFileIndexItemName,
if ( parentSubPaths.Count == 0 )
{
- return ( moveToTrash, changedFileIndexItemName );
+ return (moveToTrash, changedFileIndexItemName);
}
var childItems = ( await _query.GetAllObjectsAsync(parentSubPaths) )
.Where(p => p.FilePath != null).ToList();
-
+
moveToTrash.AddRange(childItems);
- foreach ( var childItem in childItems)
+ foreach ( var childItem in childItems )
{
var builder = new StringBuilder(childItem.Tags);
builder.Append(", ");
builder.Append(TrashKeyword.TrashKeywordString);
childItem.Tags = builder.ToString();
-
- changedFileIndexItemName.TryAdd(childItem.FilePath!, new List {"tags"});
+
+ changedFileIndexItemName.TryAdd(childItem.FilePath!, new List { "tags" });
}
- return (moveToTrash,changedFileIndexItemName);
+ return (moveToTrash, changedFileIndexItemName);
}
private async Task SystemTrashInQueue(List moveToTrash)
@@ -148,9 +148,9 @@ private async Task SystemTrashInQueue(List moveToTrash)
.Where(p => p.FilePath != null)
.Select(p => _appSettings.DatabasePathToFilePath(p.FilePath!))
.ToList();
-
+
_systemTrashService.Trash(fullFilePaths);
-
+
await _query.RemoveItemAsync(moveToTrash);
}
}
diff --git a/starsky/starsky.feature.trash/Services/TrashConnectionService.cs b/starsky/starsky.feature.trash/Services/TrashConnectionService.cs
index 420d369565..9e6c97e009 100644
--- a/starsky/starsky.feature.trash/Services/TrashConnectionService.cs
+++ b/starsky/starsky.feature.trash/Services/TrashConnectionService.cs
@@ -8,14 +8,14 @@
namespace starsky.feature.trash.Services;
-[Service(typeof(ITrashConnectionService),
+[Service(typeof(ITrashConnectionService),
InjectionLifetime = InjectionLifetime.Scoped)]
public class TrashConnectionService : ITrashConnectionService
{
private readonly IWebSocketConnectionsService _webSocketConnectionsService;
private readonly INotificationQuery _notificationQuery;
-
- public TrashConnectionService(IWebSocketConnectionsService webSocketConnectionsService,
+
+ public TrashConnectionService(IWebSocketConnectionsService webSocketConnectionsService,
INotificationQuery notificationQuery)
{
_webSocketConnectionsService = webSocketConnectionsService;
@@ -29,7 +29,7 @@ public static List StatusUpdate(
var status = isSystemTrash
? FileIndexItem.ExifStatus.NotFoundSourceMissing
: FileIndexItem.ExifStatus.Deleted;
-
+
foreach ( var item in moveToTrash )
{
item.Status = status;
@@ -37,13 +37,13 @@ public static List StatusUpdate(
return moveToTrash;
}
- public async Task> ConnectionServiceAsync( List moveToTrash,
+ public async Task> ConnectionServiceAsync(List moveToTrash,
bool isSystemTrash)
{
moveToTrash = StatusUpdate(moveToTrash, isSystemTrash);
-
+
var webSocketResponse = new ApiNotificationResponseModel>(
- moveToTrash,ApiNotificationType.MoveToTrash);
+ moveToTrash, ApiNotificationType.MoveToTrash);
await _webSocketConnectionsService.SendToAllAsync(webSocketResponse, CancellationToken.None);
await _notificationQuery.AddNotification(webSocketResponse);
return moveToTrash;
diff --git a/starsky/starsky.feature.trash/starsky.feature.trash.csproj b/starsky/starsky.feature.trash/starsky.feature.trash.csproj
index 4d34e42094..add9db259e 100644
--- a/starsky/starsky.feature.trash/starsky.feature.trash.csproj
+++ b/starsky/starsky.feature.trash/starsky.feature.trash.csproj
@@ -10,10 +10,13 @@
-
-
-
-
+
+
+
+
-
+
+
+ true
+
diff --git a/starsky/starsky.feature.webftppublish/FtpAbstractions/Helpers/WrapFtpWebRequest.cs b/starsky/starsky.feature.webftppublish/FtpAbstractions/Helpers/WrapFtpWebRequest.cs
index a285f21cbc..2ff756eb2b 100644
--- a/starsky/starsky.feature.webftppublish/FtpAbstractions/Helpers/WrapFtpWebRequest.cs
+++ b/starsky/starsky.feature.webftppublish/FtpAbstractions/Helpers/WrapFtpWebRequest.cs
@@ -12,7 +12,7 @@ public WrapFtpWebRequest(FtpWebRequest request)
{
_request = request;
}
-
+
///
///
/// Selects FTP command to use. WebRequestMethods.Ftp.DownloadFile is default.
@@ -30,24 +30,25 @@ public string Method
///
public NetworkCredential Credentials
{
- get => null;
+ get => null!;
set => _request.Credentials = value;
}
- public bool UsePassive
+ public bool UsePassive
{
get => _request.UsePassive;
set => _request.UsePassive = value;
}
-
+
///
/// True by default, false allows transmission using text mode
///
- public bool UseBinary
+ public bool UseBinary
{
get => _request.UseBinary;
set => _request.UseBinary = value;
}
+
public bool KeepAlive
{
get => _request.KeepAlive;
@@ -60,7 +61,7 @@ public bool KeepAlive
/// Wrapper response
public IFtpWebResponse GetResponse()
{
- return new WrapFtpWebResponse((FtpWebResponse)_request.GetResponse());
+ return new WrapFtpWebResponse(( FtpWebResponse )_request.GetResponse());
}
///
@@ -71,5 +72,4 @@ public Stream GetRequestStream()
return _request.GetRequestStream();
}
}
-
}
diff --git a/starsky/starsky.feature.webftppublish/FtpAbstractions/Helpers/WrapFtpWebResponse.cs b/starsky/starsky.feature.webftppublish/FtpAbstractions/Helpers/WrapFtpWebResponse.cs
index 7b34fc0f00..e08cf692cb 100644
--- a/starsky/starsky.feature.webftppublish/FtpAbstractions/Helpers/WrapFtpWebResponse.cs
+++ b/starsky/starsky.feature.webftppublish/FtpAbstractions/Helpers/WrapFtpWebResponse.cs
@@ -7,7 +7,7 @@ namespace starsky.feature.webftppublish.FtpAbstractions.Helpers
{
public class WrapFtpWebResponse : IFtpWebResponse
{
- private FtpWebResponse _response;
+ private FtpWebResponse? _response;
public WrapFtpWebResponse(FtpWebResponse response)
{
@@ -24,7 +24,7 @@ protected virtual void Dispose(bool disposing)
{
if ( !disposing ) return;
if ( _response == null ) return;
- ((IDisposable)_response).Dispose();
+ ( ( IDisposable )_response ).Dispose();
_response = null;
}
diff --git a/starsky/starsky.feature.webftppublish/FtpAbstractions/Services/FtpWebRequestFactory.cs b/starsky/starsky.feature.webftppublish/FtpAbstractions/Services/FtpWebRequestFactory.cs
index 0c80475f5b..23c5ecda6e 100644
--- a/starsky/starsky.feature.webftppublish/FtpAbstractions/Services/FtpWebRequestFactory.cs
+++ b/starsky/starsky.feature.webftppublish/FtpAbstractions/Services/FtpWebRequestFactory.cs
@@ -20,7 +20,7 @@ public class FtpWebRequestFactory : IFtpWebRequestFactory
/// new Requester
public IFtpWebRequest Create(string uri)
{
- return new WrapFtpWebRequest((FtpWebRequest)WebRequest.Create(uri));
+ return new WrapFtpWebRequest(( FtpWebRequest )WebRequest.Create(uri));
}
}
}
diff --git a/starsky/starsky.feature.webftppublish/Helpers/WebFtpCli.cs b/starsky/starsky.feature.webftppublish/Helpers/WebFtpCli.cs
index e6097c6f2b..033a3978a0 100644
--- a/starsky/starsky.feature.webftppublish/Helpers/WebFtpCli.cs
+++ b/starsky/starsky.feature.webftppublish/Helpers/WebFtpCli.cs
@@ -21,50 +21,52 @@ public class WebFtpCli
private readonly IStorage _hostStorageProvider;
private readonly IFtpWebRequestFactory _webRequestFactory;
- public WebFtpCli(AppSettings appSettings, ISelectorStorage selectorStorage, IConsole console,
+ public WebFtpCli(AppSettings appSettings, ISelectorStorage selectorStorage,
+ IConsole console,
IFtpWebRequestFactory webRequestFactory)
{
_appSettings = appSettings;
_console = console;
- _argsHelper = new ArgsHelper(_appSettings,console);
- _hostStorageProvider = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
+ _argsHelper = new ArgsHelper(_appSettings, console);
+ _hostStorageProvider =
+ selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
_webRequestFactory = webRequestFactory;
}
public async Task RunAsync(string[] args)
{
_appSettings.Verbose = ArgsHelper.NeedVerbose(args);
-
- if ( ArgsHelper.NeedHelp(args))
+
+ if ( ArgsHelper.NeedHelp(args) )
{
_appSettings.ApplicationType = AppSettings.StarskyAppType.WebFtp;
_argsHelper.NeedHelpShowDialog();
return;
}
-
+
var inputFullFileDirectory = new ArgsHelper(_appSettings)
- .GetPathFormArgs(args,false);
+ .GetPathFormArgs(args, false);
- if (string.IsNullOrWhiteSpace(inputFullFileDirectory))
+ if ( string.IsNullOrWhiteSpace(inputFullFileDirectory) )
{
_console.WriteLine("Please use the -p to add a path first");
return;
}
-
+
// used in this session to find the files back
- if ( _hostStorageProvider.IsFolderOrFile(inputFullFileDirectory)
- == FolderOrFileModel.FolderOrFileTypeList.Deleted )
+ if ( _hostStorageProvider.IsFolderOrFile(inputFullFileDirectory)
+ == FolderOrFileModel.FolderOrFileTypeList.Deleted )
{
_console.WriteLine($"Folder location {inputFullFileDirectory} " +
- $"is not found \nPlease try the `-h` command to get help ");
+ $"is not found \nPlease try the `-h` command to get help ");
return;
}
-
+
// check if settings is valid
if ( string.IsNullOrEmpty(_appSettings.WebFtp) )
{
- _console.WriteLine($"Please update the WebFtp settings in appsettings.json" );
+ _console.WriteLine($"Please update the WebFtp settings in appsettings.json");
return;
}
@@ -72,24 +74,24 @@ public async Task RunAsync(string[] args)
if ( !_hostStorageProvider.ExistFile(settingsFullFilePath) )
{
_console.WriteLine($"Please run 'starskywebhtmlcli' " +
- $"first to generate a settings file" );
+ $"first to generate a settings file");
return;
}
- var settings = await
+ var settings = await
new DeserializeJson(_hostStorageProvider).ReadAsync(
settingsFullFilePath);
- var ftpService = new FtpService(_appSettings,_hostStorageProvider,
+ var ftpService = new FtpService(_appSettings, _hostStorageProvider,
_console, _webRequestFactory)
- .Run(inputFullFileDirectory, settings.Slug, settings.Copy);
+ .Run(inputFullFileDirectory, settings!.Slug, settings.Copy);
if ( !ftpService )
{
_console.WriteLine("Ftp copy failed");
return;
}
-
+
_console.WriteLine($"Ftp copy successful done: {settings.Slug}");
}
}
diff --git a/starsky/starsky.feature.webftppublish/Models/FtpPublishManifestModel.cs b/starsky/starsky.feature.webftppublish/Models/FtpPublishManifestModel.cs
index 7fa331876b..8554ae1b97 100644
--- a/starsky/starsky.feature.webftppublish/Models/FtpPublishManifestModel.cs
+++ b/starsky/starsky.feature.webftppublish/Models/FtpPublishManifestModel.cs
@@ -7,8 +7,8 @@ public class FtpPublishManifestModel
///
/// Short for name, without spaces and non-ascii
///
- public string Slug { get; set; }
-
+ public string Slug { get; set; } = string.Empty;
+
///
/// List of files to Copy, string is relative path and bool is True for copy
///
diff --git a/starsky/starsky.feature.webftppublish/Services/FtpService.cs b/starsky/starsky.feature.webftppublish/Services/FtpService.cs
index 461c44254d..51c8a24700 100644
--- a/starsky/starsky.feature.webftppublish/Services/FtpService.cs
+++ b/starsky/starsky.feature.webftppublish/Services/FtpService.cs
@@ -72,7 +72,7 @@ public FtpService(AppSettings appSettings, IStorage storage, IConsole console,
public bool Run(string parentDirectory, string slug, Dictionary copyContent)
{
foreach ( var thisDirectory in
- CreateListOfRemoteDirectories(parentDirectory, slug, copyContent) )
+ CreateListOfRemoteDirectories(parentDirectory, slug, copyContent) )
{
_console.Write(",");
if ( DoesFtpDirectoryExist(thisDirectory) ) continue;
@@ -117,7 +117,7 @@ internal IEnumerable CreateListOfRemoteDirectories(string parentDirector
{
var parentItems = Breadcrumbs.BreadcrumbHelper(copyItem.Key);
foreach ( var item in parentItems.Where(p =>
- p != Path.DirectorySeparatorChar.ToString()) )
+ p != Path.DirectorySeparatorChar.ToString()) )
{
if ( _storage.ExistFolder(parentDirectory + item) )
{
@@ -157,8 +157,8 @@ internal bool MakeUpload(string parentDirectory, string slug,
{
const string pathDelimiter = "/";
var toFtpPath = PathHelper.RemoveLatestSlash(_webFtpNoLogin) + pathDelimiter +
- GenerateSlugHelper.GenerateSlug(slug, true) + pathDelimiter +
- item;
+ GenerateSlugHelper.GenerateSlug(slug, true) + pathDelimiter +
+ item;
_console.Write(".");
@@ -253,7 +253,7 @@ internal bool CreateFtpDirectory(string directory)
}
catch ( WebException ex )
{
- var ftpWebResponse = ( FtpWebResponse )ex.Response;
+ var ftpWebResponse = ( FtpWebResponse? )ex.Response;
ftpWebResponse?.Close();
return ftpWebResponse?.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable;
}
diff --git a/starsky/starsky.feature.webftppublish/starsky.feature.webftppublish.csproj b/starsky/starsky.feature.webftppublish/starsky.feature.webftppublish.csproj
index d66dec8926..33dd1688c0 100644
--- a/starsky/starsky.feature.webftppublish/starsky.feature.webftppublish.csproj
+++ b/starsky/starsky.feature.webftppublish/starsky.feature.webftppublish.csproj
@@ -5,12 +5,13 @@
{31df1419-6c81-4372-b7ae-a6ebb429e7e9}
0.6.0-beta.0
+ enable
-
+
-
-
-
+
+
+
diff --git a/starsky/starsky.feature.webhtmlpublish/Extensions/DictionaryExtensions.cs b/starsky/starsky.feature.webhtmlpublish/Extensions/DictionaryExtensions.cs
index a635863d44..fbf2227ed3 100644
--- a/starsky/starsky.feature.webhtmlpublish/Extensions/DictionaryExtensions.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Extensions/DictionaryExtensions.cs
@@ -15,7 +15,7 @@ public static void AddRangeOverride(this IDictionary
}
public static void ForEach(this IEnumerable source, Action action)
{
- foreach (var item in source)
+ foreach ( var item in source )
action(item);
}
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Helpers/CopyPublishedContent.cs b/starsky/starsky.feature.webhtmlpublish/Helpers/CopyPublishedContent.cs
index dc8296bf58..7b0c1abe20 100644
--- a/starsky/starsky.feature.webhtmlpublish/Helpers/CopyPublishedContent.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Helpers/CopyPublishedContent.cs
@@ -17,8 +17,9 @@ public sealed class CopyPublishedContent
public CopyPublishedContent(ToCreateSubfolder toCreateSubfolder,
ISelectorStorage selectorStorage)
{
+ ArgumentNullException.ThrowIfNull(selectorStorage);
+
_toCreateSubfolder = toCreateSubfolder;
- if ( selectorStorage == null ) return;
_hostStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
}
@@ -49,10 +50,10 @@ public Dictionary CopyContent(
internal static string GetContentFolder()
{
return PathHelper.RemoveLatestBackslash(AppDomain.CurrentDomain.BaseDirectory) +
- Path.DirectorySeparatorChar +
- "WebHtmlPublish" +
- Path.DirectorySeparatorChar +
- "PublishedContent";
+ Path.DirectorySeparatorChar +
+ "WebHtmlPublish" +
+ Path.DirectorySeparatorChar +
+ "PublishedContent";
}
}
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Helpers/EmbeddedViewsPath.cs b/starsky/starsky.feature.webhtmlpublish/Helpers/EmbeddedViewsPath.cs
index 4d6831fae1..e2d7cea315 100644
--- a/starsky/starsky.feature.webhtmlpublish/Helpers/EmbeddedViewsPath.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Helpers/EmbeddedViewsPath.cs
@@ -8,11 +8,11 @@ public static class EmbeddedViewsPath
public static string GetViewFullPath(string viewName)
{
return AppDomain.CurrentDomain.BaseDirectory +
- Path.DirectorySeparatorChar +
- "WebHtmlPublish" +
- Path.DirectorySeparatorChar +
- "EmbeddedViews" +
- Path.DirectorySeparatorChar + viewName;
+ Path.DirectorySeparatorChar +
+ "WebHtmlPublish" +
+ Path.DirectorySeparatorChar +
+ "EmbeddedViews" +
+ Path.DirectorySeparatorChar + viewName;
}
}
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Helpers/ParseRazor.cs b/starsky/starsky.feature.webhtmlpublish/Helpers/ParseRazor.cs
index 20301a86a5..90dab2cf73 100644
--- a/starsky/starsky.feature.webhtmlpublish/Helpers/ParseRazor.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Helpers/ParseRazor.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Threading.Tasks;
using RazorLight;
using starsky.feature.webhtmlpublish.ViewModels;
@@ -9,47 +9,47 @@
namespace starsky.feature.webhtmlpublish.Helpers
{
- public class ParseRazor
- {
- private readonly RazorLightEngine _engine;
- private readonly IStorage _hostFileSystemStorage;
- private readonly IWebLogger _logger;
+ public class ParseRazor
+ {
+ private readonly RazorLightEngine _engine;
+ private readonly IStorage _hostFileSystemStorage;
+ private readonly IWebLogger _logger;
- public ParseRazor(IStorage fileSystemStorage, IWebLogger logger)
- {
- _hostFileSystemStorage = fileSystemStorage;
- _logger = logger;
- _engine = new RazorLightEngineBuilder()
- .UseEmbeddedResourcesProject(typeof(PublishManifest))
+ public ParseRazor(IStorage fileSystemStorage, IWebLogger logger)
+ {
+ _hostFileSystemStorage = fileSystemStorage;
+ _logger = logger;
+ _engine = new RazorLightEngineBuilder()
+ .UseEmbeddedResourcesProject(typeof(PublishManifest))
.UseEmbeddedResourcesProject(typeof(WebHtmlViewModel))
.UseEmbeddedResourcesProject(typeof(AppSettings))
.UseEmbeddedResourcesProject(typeof(FileIndexItem))
- .UseEmbeddedResourcesProject(typeof(System.Linq.Enumerable))
- .UseEmbeddedResourcesProject(typeof(AppSettingsPublishProfiles))
- .UseFileSystemProject(AppDomain.CurrentDomain.BaseDirectory )
- // > starskywebhtmlcli/bin folder
- .UseMemoryCachingProvider()
- .Build();
- }
+ .UseEmbeddedResourcesProject(typeof(System.Linq.Enumerable))
+ .UseEmbeddedResourcesProject(typeof(AppSettingsPublishProfiles))
+ .UseFileSystemProject(AppDomain.CurrentDomain.BaseDirectory)
+ // > starskywebhtmlcli/bin folder
+ .UseMemoryCachingProvider()
+ .Build();
+ }
- public bool Exist(string viewName)
- {
- var path = EmbeddedViewsPath.GetViewFullPath(viewName);
- return _hostFileSystemStorage.ExistFile(path);
- }
-
- public async Task EmbeddedViews(string viewName, object viewModel)
- {
- if ( Exist(viewName) )
- {
- // has an dependency on the filesystem by _engine.CompileRenderAsync
- return await
- _engine.CompileRenderAsync(
- "WebHtmlPublish/EmbeddedViews/" + viewName, viewModel);
- }
-
- _logger.LogInformation("View Not Exist " + EmbeddedViewsPath.GetViewFullPath(viewName));
- return string.Empty;
- }
- }
+ public bool Exist(string viewName)
+ {
+ var path = EmbeddedViewsPath.GetViewFullPath(viewName);
+ return _hostFileSystemStorage.ExistFile(path);
+ }
+
+ public async Task EmbeddedViews(string viewName, object viewModel)
+ {
+ if ( Exist(viewName) )
+ {
+ // has an dependency on the filesystem by _engine.CompileRenderAsync
+ return await
+ _engine.CompileRenderAsync(
+ "WebHtmlPublish/EmbeddedViews/" + viewName, viewModel);
+ }
+
+ _logger.LogInformation("View Not Exist " + EmbeddedViewsPath.GetViewFullPath(viewName));
+ return string.Empty;
+ }
+ }
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Helpers/PublishCli.cs b/starsky/starsky.feature.webhtmlpublish/Helpers/PublishCli.cs
index d732fd8cb2..cbcb9b1cb2 100644
--- a/starsky/starsky.feature.webhtmlpublish/Helpers/PublishCli.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Helpers/PublishCli.cs
@@ -38,7 +38,7 @@ public PublishCli(ISelectorStorage storageSelector, IPublishPreflight publishPre
_storageSelector = storageSelector;
_logger = logger;
}
-
+
///
/// Command Line Helper to server WebHtml Content
///
@@ -47,38 +47,38 @@ public PublishCli(ISelectorStorage storageSelector, IPublishPreflight publishPre
public async Task Publisher(string[] args)
{
_appSettings.Verbose = ArgsHelper.NeedVerbose(args);
-
- if ( ArgsHelper.NeedHelp(args))
+
+ if ( ArgsHelper.NeedHelp(args) )
{
_appSettings.ApplicationType = AppSettings.StarskyAppType.WebHtml;
_argsHelper.NeedHelpShowDialog();
return;
}
-
- var inputFullPath = _argsHelper.GetPathFormArgs(args,false);
- if (string.IsNullOrWhiteSpace(inputFullPath))
+
+ var inputFullPath = _argsHelper.GetPathFormArgs(args, false);
+ if ( string.IsNullOrWhiteSpace(inputFullPath) )
{
_console.WriteLine("Please use the -p to add a path first");
return;
}
-
+
if ( _hostFileSystemStorage.IsFolderOrFile(inputFullPath) !=
- FolderOrFileModel.FolderOrFileTypeList.Folder )
+ FolderOrFileModel.FolderOrFileTypeList.Folder )
{
_console.WriteLine("Please add a valid folder: " + inputFullPath + ". " +
- "This folder is not found");
+ "This folder is not found");
return;
}
-
+
var settingsFullFilePath = Path.Combine(inputFullPath, "_settings.json");
if ( _hostFileSystemStorage.ExistFile(settingsFullFilePath) )
{
_console.WriteLine($"You have already run this program for this folder remove the " +
- $"_settings.json first and try it again" );
+ $"_settings.json first and try it again");
return;
}
- var itemName = _publishPreflight.GetNameConsole(inputFullPath,args);
+ var itemName = _publishPreflight.GetNameConsole(inputFullPath, args);
if ( _appSettings.IsVerbose() )
{
@@ -89,21 +89,21 @@ public async Task Publisher(string[] args)
// used in this session to find the photos back
// outside the webRoot of iStorage
_appSettings.StorageFolder = inputFullPath;
-
+
// use relative to StorageFolder
var listOfFiles = _subPathStorage.GetAllFilesInDirectory("/")
.Where(ExtensionRolesHelper.IsExtensionExifToolSupported).ToList();
-
+
var fileIndexList = await new ReadMeta(_subPathStorage, _appSettings, null!, _logger)
.ReadExifAndXmpFromFileAddFilePathHashAsync(listOfFiles);
-
+
// todo introduce selector
- var profileName = new PublishPreflight(_appSettings,_console, _storageSelector, _logger)
+ var profileName = new PublishPreflight(_appSettings, _console, _storageSelector, _logger)
.GetPublishProfileNameByIndex(0);
-
- await _publishService.RenderCopy(fileIndexList, profileName,
+
+ await _publishService.RenderCopy(fileIndexList, profileName,
itemName, inputFullPath, true);
-
+
_console.WriteLine("publish done");
}
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Helpers/PublishManifest.cs b/starsky/starsky.feature.webhtmlpublish/Helpers/PublishManifest.cs
index a171fe9721..428f9b3b81 100644
--- a/starsky/starsky.feature.webhtmlpublish/Helpers/PublishManifest.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Helpers/PublishManifest.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
-using System.Text.Json.Serialization;
using starsky.feature.webhtmlpublish.Models;
using starsky.foundation.platform.JsonConverter;
using starsky.foundation.storage.Helpers;
@@ -26,20 +25,17 @@ public PublishManifest(IStorage storage)
/// without ManifestName
///
///
- public PublishManifestModel ExportManifest( string parentFullFilePath, string itemName,
- Dictionary copyContent)
+ public PublishManifestModel ExportManifest(string parentFullFilePath, string itemName,
+ Dictionary? copyContent)
{
- var manifest = new PublishManifestModel
- {
- Name = itemName,
- Copy = copyContent
- };
+ copyContent ??= new Dictionary();
+ var manifest = new PublishManifestModel { Name = itemName, Copy = copyContent };
var output = JsonSerializer.Serialize(manifest, DefaultJsonSerializer.NoNamingPolicy);
var outputLocation = Path.Combine(parentFullFilePath, ManifestName);
-
+
_storage.FileDelete(outputLocation);
_storage.WriteStream(StringToStreamHelper.StringToStream(output), outputLocation);
-
+
return manifest;
}
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Helpers/ToBase64DataUriList.cs b/starsky/starsky.feature.webhtmlpublish/Helpers/ToBase64DataUriList.cs
index 405618d129..e54e570361 100644
--- a/starsky/starsky.feature.webhtmlpublish/Helpers/ToBase64DataUriList.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Helpers/ToBase64DataUriList.cs
@@ -17,40 +17,45 @@ public class ToBase64DataUriList
private readonly IWebLogger _logger;
private readonly AppSettings _appSettings;
- public ToBase64DataUriList(IStorage iStorage, IStorage thumbnailStorage, IWebLogger logger, AppSettings appSettings)
+ public ToBase64DataUriList(IStorage iStorage, IStorage thumbnailStorage, IWebLogger logger,
+ AppSettings appSettings)
{
_iStorage = iStorage;
_thumbnailStorage = thumbnailStorage;
_logger = logger;
_appSettings = appSettings;
}
-
+
[SuppressMessage("Usage", "S3966: Resource 'memoryStream' has " +
- "already been disposed explicitly or through a using statement implicitly. " +
- "Remove the redundant disposal.")]
+ "already been disposed explicitly or through a using statement implicitly. " +
+ "Remove the redundant disposal.")]
public async Task Create(List fileIndexList)
{
var base64ImageArray = new string[fileIndexList.Count];
- for (var i = 0; i
/// Create SubFolders by the profile.Folder setting
///
@@ -24,16 +24,16 @@ public void Create(AppSettingsPublishProfiles profile, string parentFolder)
// check if subfolder '1000' exist on disk
// used for moving subfolders first
var profileFolderStringBuilder = new StringBuilder();
- if (!string.IsNullOrEmpty(parentFolder))
+ if ( !string.IsNullOrEmpty(parentFolder) )
{
profileFolderStringBuilder.Append(parentFolder);
profileFolderStringBuilder.Append('/');
}
-
+
profileFolderStringBuilder.Append(profile.Folder);
- if ( _hostFileSystemStorage.IsFolderOrFile(profileFolderStringBuilder.ToString())
- == FolderOrFileModel.FolderOrFileTypeList.Deleted)
+ if ( _hostFileSystemStorage.IsFolderOrFile(profileFolderStringBuilder.ToString())
+ == FolderOrFileModel.FolderOrFileTypeList.Deleted )
{
_hostFileSystemStorage.CreateDirectory(profileFolderStringBuilder.ToString());
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Interfaces/IPublishPreflight.cs b/starsky/starsky.feature.webhtmlpublish/Interfaces/IPublishPreflight.cs
index 6c8bad6759..d80a743687 100644
--- a/starsky/starsky.feature.webhtmlpublish/Interfaces/IPublishPreflight.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Interfaces/IPublishPreflight.cs
@@ -10,15 +10,15 @@ public interface IPublishPreflight
/// Get all publish profile names
///
/// (string: name, bool: isValid)
- IEnumerable> GetAllPublishProfileNames();
-
+ IEnumerable> GetAllPublishProfileNames();
+
///
/// Check if the profile is valid
///
/// profile key
/// (bool and list of errors)
- Tuple> IsProfileValid(string publishProfileName);
-
+ Tuple> IsProfileValid(string publishProfileName);
+
string GetNameConsole(string inputPath, IReadOnlyList args);
List GetPublishProfileName(string publishProfileName);
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Interfaces/IWebHtmlPublishService.cs b/starsky/starsky.feature.webhtmlpublish/Interfaces/IWebHtmlPublishService.cs
index 056ea5c4bb..3e6d593eb9 100644
--- a/starsky/starsky.feature.webhtmlpublish/Interfaces/IWebHtmlPublishService.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Interfaces/IWebHtmlPublishService.cs
@@ -6,12 +6,12 @@ namespace starsky.feature.webhtmlpublish.Interfaces
{
public interface IWebHtmlPublishService
{
- Task> RenderCopy(List fileIndexItemsList,
+ Task?> RenderCopy(List fileIndexItemsList,
string publishProfileName, string itemName, string outputParentFullFilePathFolder,
bool moveSourceFiles = false);
Task GenerateZip(string fullFileParentFolderPath, string itemName,
- Dictionary renderCopyResult,
+ Dictionary? renderCopyResult,
bool deleteFolderAfterwards = false);
}
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Models/PublishManifestModel.cs b/starsky/starsky.feature.webhtmlpublish/Models/PublishManifestModel.cs
index dbb063b01a..e8370a6d50 100644
--- a/starsky/starsky.feature.webhtmlpublish/Models/PublishManifestModel.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Models/PublishManifestModel.cs
@@ -4,7 +4,6 @@
using System.Globalization;
using System.Reflection;
using starsky.foundation.platform.Helpers;
-using starsky.foundation.platform.Models;
namespace starsky.feature.webhtmlpublish.Models
{
@@ -14,20 +13,17 @@ public class PublishManifestModel
///
/// Display name
///
- public string Name { get; set; }
+ public string Name { get; set; } = string.Empty;
///
/// Which files or folders need to be copied
///
- public Dictionary Copy { get; set; } = new Dictionary();
+ public Dictionary Copy { get; set; } = new();
///
/// Slug, Name without spaces, but underscores are allowed
///
- public string Slug
- {
- get { return GenerateSlugHelper.GenerateSlug(Name, true); }
- }
+ public string Slug => GenerateSlugHelper.GenerateSlug(Name, true);
///
/// When did the export happen
@@ -38,6 +34,6 @@ public string Slug
///
/// Starsky Version
///
- public string Version => Assembly.GetExecutingAssembly().GetName().Version?.ToString();
+ public string? Version => Assembly.GetExecutingAssembly().GetName().Version?.ToString();
}
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Services/OverlayImage.cs b/starsky/starsky.feature.webhtmlpublish/Services/OverlayImage.cs
index ffcf16e23a..86c82b7c74 100644
--- a/starsky/starsky.feature.webhtmlpublish/Services/OverlayImage.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Services/OverlayImage.cs
@@ -22,38 +22,52 @@ public class OverlayImage : IOverlayImage
public OverlayImage(ISelectorStorage selectorStorage)
{
- if ( selectorStorage == null ) return;
_iStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
_thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
_hostFileSystem = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
}
- public string FilePathOverlayImage(string sourceFilePath, AppSettingsPublishProfiles profile)
+ public string FilePathOverlayImage(string sourceFilePath,
+ AppSettingsPublishProfiles profile)
{
var result = profile.Folder + GenerateSlugHelper.GenerateSlug(
- Path.GetFileNameWithoutExtension(sourceFilePath), true)
- + profile.Append + profile.GetExtensionWithDot(sourceFilePath);
+ Path.GetFileNameWithoutExtension(sourceFilePath), true)
+ + profile.Append +
+ profile.GetExtensionWithDot(sourceFilePath);
return result;
}
-
- public string FilePathOverlayImage(string outputParentFullFilePathFolder, string sourceFilePath,
+
+ public string FilePathOverlayImage(string outputParentFullFilePathFolder,
+ string sourceFilePath,
AppSettingsPublishProfiles profile)
{
var result = PathHelper.AddBackslash(outputParentFullFilePathFolder) +
- FilePathOverlayImage(sourceFilePath, profile);
+ FilePathOverlayImage(sourceFilePath, profile);
return result;
}
-
- public Task ResizeOverlayImageThumbnails(string itemFileHash, string outputFullFilePath, AppSettingsPublishProfiles profile)
+
+ public Task ResizeOverlayImageThumbnails(string itemFileHash,
+ string outputFullFilePath, AppSettingsPublishProfiles profile)
{
- if ( string.IsNullOrWhiteSpace(itemFileHash) ) throw new ArgumentNullException(nameof(itemFileHash));
- if ( !_thumbnailStorage.ExistFile(itemFileHash) ) throw new FileNotFoundException("fileHash " + itemFileHash);
+ if ( string.IsNullOrWhiteSpace(itemFileHash) )
+ throw new ArgumentNullException(nameof(itemFileHash));
+
+ if ( !_thumbnailStorage.ExistFile(itemFileHash) )
+ {
+ throw new FileNotFoundException("fileHash " + itemFileHash);
+ }
+
+ if ( _hostFileSystem.ExistFile(outputFullFilePath) )
+ {
+ return Task.FromResult(false);
+ }
- if ( _hostFileSystem.ExistFile(outputFullFilePath) ) return Task.FromResult(false);
if ( !_hostFileSystem.ExistFile(profile.Path) )
{
- throw new FileNotFoundException($"overlayImage is missing in profile.Path: {profile.Path}");
+ throw new FileNotFoundException(
+ $"overlayImage is missing in profile.Path: {profile.Path}");
}
+
return ResizeOverlayImageThumbnailsInternal(itemFileHash,
outputFullFilePath, profile);
}
@@ -69,13 +83,15 @@ private async Task ResizeOverlayImageThumbnailsInternal(
string itemFilePath,
string outputFullFilePath, AppSettingsPublishProfiles profile)
{
- using ( var sourceImageStream = _thumbnailStorage.ReadStream(itemFilePath))
+ using ( var sourceImageStream = _thumbnailStorage.ReadStream(itemFilePath) )
using ( var sourceImage = await Image.LoadAsync(sourceImageStream) )
- using ( var overlayImageStream = _hostFileSystem.ReadStream(profile.Path)) // for example a logo
+ using ( var overlayImageStream =
+ _hostFileSystem.ReadStream(profile.Path) ) // for example a logo
using ( var overlayImage = await Image.LoadAsync(overlayImageStream) )
- using ( var outputStream = new MemoryStream() )
+ using ( var outputStream = new MemoryStream() )
{
- return await ResizeOverlayImageShared(sourceImage, overlayImage, outputStream, profile,
+ return await ResizeOverlayImageShared(sourceImage, overlayImage, outputStream,
+ profile,
outputFullFilePath);
}
}
@@ -90,14 +106,17 @@ private async Task ResizeOverlayImageThumbnailsInternal(
public Task ResizeOverlayImageLarge(string itemFilePath,
string outputFullFilePath, AppSettingsPublishProfiles profile)
{
- if ( string.IsNullOrWhiteSpace(itemFilePath) ) throw new
- ArgumentNullException(nameof(itemFilePath));
- if ( !_iStorage.ExistFile(itemFilePath) ) throw new FileNotFoundException("subPath " + itemFilePath);
+ if ( string.IsNullOrWhiteSpace(itemFilePath) )
+ throw new
+ ArgumentNullException(nameof(itemFilePath));
+ if ( !_iStorage.ExistFile(itemFilePath) )
+ throw new FileNotFoundException("subPath " + itemFilePath);
- if ( _hostFileSystem.ExistFile(outputFullFilePath) ) return Task.FromResult(false);
+ if ( _hostFileSystem.ExistFile(outputFullFilePath) ) return Task.FromResult(false);
if ( !_hostFileSystem.ExistFile(profile.Path) )
{
- throw new FileNotFoundException($"overlayImage is missing in profile.Path: {profile.Path}");
+ throw new FileNotFoundException(
+ $"overlayImage is missing in profile.Path: {profile.Path}");
}
return ResizeOverlayImageLargeInternal(itemFilePath,
@@ -111,21 +130,23 @@ public Task ResizeOverlayImageLarge(string itemFilePath,
/// input Image
/// location where to store
/// image profile that contains sizes
- private async Task ResizeOverlayImageLargeInternal(string itemFilePath,
+ private async Task ResizeOverlayImageLargeInternal(string itemFilePath,
string outputFullFilePath, AppSettingsPublishProfiles profile)
{
- using ( var sourceImageStream = _iStorage.ReadStream(itemFilePath))
+ using ( var sourceImageStream = _iStorage.ReadStream(itemFilePath) )
using ( var sourceImage = await Image.LoadAsync(sourceImageStream) )
- using ( var overlayImageStream = _hostFileSystem.ReadStream(profile.Path))
+ using ( var overlayImageStream = _hostFileSystem.ReadStream(profile.Path) )
using ( var overlayImage = await Image.LoadAsync(overlayImageStream) )
- using ( var outputStream = new MemoryStream() )
+ using ( var outputStream = new MemoryStream() )
{
- return await ResizeOverlayImageShared(sourceImage, overlayImage, outputStream, profile,
+ return await ResizeOverlayImageShared(sourceImage, overlayImage, outputStream,
+ profile,
outputFullFilePath);
}
}
- [SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance")]
+ [SuppressMessage("Performance",
+ "CA1859:Use concrete types when possible for improved performance")]
private async Task ResizeOverlayImageShared(Image sourceImage, Image overlayImage,
Stream outputStream, AppSettingsPublishProfiles profile, string outputSubPath)
{
@@ -139,10 +160,10 @@ private async Task ResizeOverlayImageShared(Image sourceImage, Image overl
.Resize(profile.OverlayMaxWidth, 0, KnownResamplers.Lanczos3)
);
- int xPoint = sourceImage.Width - overlayImage.Width;
- int yPoint = sourceImage.Height - overlayImage.Height;
-
- sourceImage.Mutate(x => x.DrawImage(overlayImage,
+ var xPoint = sourceImage.Width - overlayImage.Width;
+ var yPoint = sourceImage.Height - overlayImage.Height;
+
+ sourceImage.Mutate(x => x.DrawImage(overlayImage,
new Point(xPoint, yPoint), 1F));
await sourceImage.SaveAsJpegAsync(outputStream);
diff --git a/starsky/starsky.feature.webhtmlpublish/Services/PublishPreflight.cs b/starsky/starsky.feature.webhtmlpublish/Services/PublishPreflight.cs
index 92dd1afbfd..ceea9598e3 100644
--- a/starsky/starsky.feature.webhtmlpublish/Services/PublishPreflight.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Services/PublishPreflight.cs
@@ -28,10 +28,10 @@ public PublishPreflight(AppSettings appSettings, IConsole console, ISelectorStor
_logger = logger;
_hostStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
}
-
- public List> GetPublishProfileNames()
+
+ public List> GetPublishProfileNames()
{
- var returnList = new List>();
+ var returnList = new List>();
if ( _appSettings.PublishProfiles == null || _appSettings.PublishProfiles.Count == 0 )
{
return returnList;
@@ -64,11 +64,11 @@ public Tuple> IsProfileValid(
///
/// profile object
/// (bool and list of errors)
- internal Tuple> IsProfileValid( KeyValuePair> profiles)
+ internal Tuple> IsProfileValid(KeyValuePair> profiles)
{
if ( profiles.Key == null || profiles.Value == null )
{
- return new Tuple>(false, new List {"Profile not found"});
+ return new Tuple>(false, new List { "Profile not found" });
}
var errors = new List();
@@ -79,21 +79,21 @@ internal Tuple> IsProfileValid( KeyValuePair>(errors.Count == 0, errors);
}
@@ -101,13 +101,13 @@ internal Tuple> IsProfileValid( KeyValuePair
/// (string: name, bool: isValid)
- public IEnumerable> GetAllPublishProfileNames()
+ public IEnumerable> GetAllPublishProfileNames()
{
return _appSettings.PublishProfiles!.Select(p =>
new KeyValuePair(
p.Key, IsProfileValid(p).Item1));
}
-
+
public List GetPublishProfileName(string publishProfileName)
{
return _appSettings.PublishProfiles!
@@ -131,13 +131,13 @@ public string GetNameConsole(string inputPath, IReadOnlyList args)
{
var name = ArgsHelper.GetName(args);
if ( !string.IsNullOrWhiteSpace(name) ) return name;
-
+
var suggestedInput = Path.GetFileName(inputPath);
-
- _console.WriteLine("\nWhat is the name of the item? (for: "+ suggestedInput +" press Enter)\n ");
+
+ _console.WriteLine("\nWhat is the name of the item? (for: " + suggestedInput + " press Enter)\n ");
name = _console.ReadLine();
-
- if (string.IsNullOrEmpty(name))
+
+ if ( string.IsNullOrEmpty(name) )
{
name = suggestedInput;
}
diff --git a/starsky/starsky.feature.webhtmlpublish/Services/WebHtmlPublishService.cs b/starsky/starsky.feature.webhtmlpublish/Services/WebHtmlPublishService.cs
index 0255760514..2787ad114a 100644
--- a/starsky/starsky.feature.webhtmlpublish/Services/WebHtmlPublishService.cs
+++ b/starsky/starsky.feature.webhtmlpublish/Services/WebHtmlPublishService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
@@ -22,401 +22,432 @@
using starsky.foundation.storage.Interfaces;
using starsky.foundation.storage.Services;
using starsky.foundation.storage.Storage;
-using starsky.foundation.thumbnailgeneration.Helpers;
using starsky.foundation.thumbnailgeneration.Interfaces;
using starsky.foundation.writemeta.Helpers;
using starsky.foundation.writemeta.Interfaces;
[assembly: InternalsVisibleTo("starskytest")]
+
namespace starsky.feature.webhtmlpublish.Services
{
[Service(typeof(IWebHtmlPublishService), InjectionLifetime = InjectionLifetime.Scoped)]
- public class WebHtmlPublishService : IWebHtmlPublishService
- {
- private readonly AppSettings _appSettings;
- private readonly IExifTool _exifTool;
- private readonly IStorage _subPathStorage;
- private readonly IStorage _thumbnailStorage;
- private readonly IStorage _hostFileSystemStorage;
- private readonly IConsole _console;
- private readonly IOverlayImage _overlayImage;
- private readonly PublishManifest _publishManifest;
- private readonly IPublishPreflight _publishPreflight;
- private readonly CopyPublishedContent _copyPublishedContent;
- private readonly ToCreateSubfolder _toCreateSubfolder;
- private readonly IThumbnailService _thumbnailService;
- private readonly IWebLogger _logger;
-
- [SuppressMessage("Usage", "S107: Constructor has 8 parameters, which is greater than the 7 authorized")]
- public WebHtmlPublishService(IPublishPreflight publishPreflight, ISelectorStorage
- selectorStorage, AppSettings appSettings, IExifToolHostStorage exifTool,
- IOverlayImage overlayImage, IConsole console, IWebLogger logger, IThumbnailService thumbnailService)
- {
- _publishPreflight = publishPreflight;
- _subPathStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
- _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
- _hostFileSystemStorage = selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
- _appSettings = appSettings;
- _exifTool = exifTool;
- _console = console;
- _overlayImage = overlayImage;
- _publishManifest = new PublishManifest(_hostFileSystemStorage);
- _toCreateSubfolder = new ToCreateSubfolder(_hostFileSystemStorage);
- _copyPublishedContent = new CopyPublishedContent(_toCreateSubfolder,
- selectorStorage);
- _logger = logger;
- _thumbnailService = thumbnailService;
- }
-
- public async Task> RenderCopy(List fileIndexItemsList,
- string publishProfileName, string itemName, string outputParentFullFilePathFolder,
- bool moveSourceFiles = false)
- {
- fileIndexItemsList = AddFileHashIfNotExist(fileIndexItemsList);
-
- await PreGenerateThumbnail(fileIndexItemsList, publishProfileName);
- var base64ImageArray = await Base64DataUriList(fileIndexItemsList);
-
- var copyContent = await Render(fileIndexItemsList, base64ImageArray,
- publishProfileName, itemName, outputParentFullFilePathFolder, moveSourceFiles);
-
- _publishManifest.ExportManifest(outputParentFullFilePathFolder, itemName, copyContent);
-
- return copyContent;
- }
-
- internal List AddFileHashIfNotExist(List fileIndexItemsList)
- {
- foreach ( var item in fileIndexItemsList.Where(item => string.IsNullOrEmpty(item.FileHash)) )
- {
- item.FileHash = new FileHash(_subPathStorage).GetHashCode(item.FilePath).Key;
- }
- return fileIndexItemsList;
- }
-
- internal bool ShouldSkipExtraLarge(string publishProfileName)
- {
- var skipExtraLarge = _publishPreflight?.GetPublishProfileName(publishProfileName)?
- .TrueForAll(p => p.SourceMaxWidth <= 1999);
- return skipExtraLarge == true;
-
- }
-
- internal async Task PreGenerateThumbnail(IEnumerable fileIndexItemsList, string publishProfileName)
- {
- var skipExtraLarge = ShouldSkipExtraLarge(publishProfileName);
- foreach ( var item in fileIndexItemsList )
- {
- await _thumbnailService.CreateThumbAsync(item.FilePath, item.FileHash, skipExtraLarge);
- }
- }
-
- ///
- /// Get base64 uri lists
- ///
- ///
- private Task Base64DataUriList(IEnumerable fileIndexItemsList)
- {
- return new ToBase64DataUriList(_subPathStorage,
- _thumbnailStorage,_logger, _appSettings).Create(fileIndexItemsList.ToList());
- }
-
- ///
- /// Render output of Publish action
- ///
- /// which items need to be published
- /// list of base64 hashes for html pages
- /// name of profile
- /// output publish item name
- /// path on host disk where to publish to
- /// include source files (false by default)
- ///
- private async Task> Render(List fileIndexItemsList,
- string[] base64ImageArray, string publishProfileName, string itemName,
- string outputParentFullFilePathFolder, bool moveSourceFiles = false)
- {
- if ( _appSettings.PublishProfiles.Count == 0 )
- {
- _console.WriteLine("There are no config items");
- return null;
- }
-
- if ( !_appSettings.PublishProfiles.ContainsKey(publishProfileName) )
- {
- _console.WriteLine("Key not found");
- return null;
- }
-
- if ( !_hostFileSystemStorage.ExistFolder(outputParentFullFilePathFolder) )
- {
- _hostFileSystemStorage.CreateDirectory(outputParentFullFilePathFolder);
- }
-
- base64ImageArray ??= new string[fileIndexItemsList.Count];
-
- // Order alphabetically
- // Ignore Items with Errors
- fileIndexItemsList = fileIndexItemsList
- .Where(p=> p.Status == FileIndexItem.ExifStatus.Ok ||
- p.Status == FileIndexItem.ExifStatus.ReadOnly)
- .OrderBy(p => p.FileName).ToList();
-
- var copyResult = new Dictionary();
-
- var profiles = _publishPreflight.GetPublishProfileName(publishProfileName);
- foreach (var currentProfile in profiles)
- {
- switch (currentProfile.ContentType)
- {
- case TemplateContentType.Html:
- copyResult.AddRangeOverride(await GenerateWebHtml(profiles, currentProfile, itemName,
- base64ImageArray, fileIndexItemsList, outputParentFullFilePathFolder));
- break;
- case TemplateContentType.Jpeg:
- copyResult.AddRangeOverride(await GenerateJpeg(currentProfile, fileIndexItemsList,
- outputParentFullFilePathFolder));
- break;
- case TemplateContentType.MoveSourceFiles:
- copyResult.AddRangeOverride(await GenerateMoveSourceFiles(currentProfile,fileIndexItemsList,
- outputParentFullFilePathFolder, moveSourceFiles));
- break;
- case TemplateContentType.PublishContent:
- // Copy all items in the subFolder content for example JavaScripts
- copyResult.AddRangeOverride(_copyPublishedContent.CopyContent(currentProfile, outputParentFullFilePathFolder));
- break;
- case TemplateContentType.PublishManifest:
- copyResult.Add(
- _overlayImage.FilePathOverlayImage("_settings.json", currentProfile)
- ,true);
- break;
- case TemplateContentType.OnlyFirstJpeg:
- if ( fileIndexItemsList.Count == 0 )
- {
- break;
- }
- var firstInList = new List{fileIndexItemsList.FirstOrDefault()};
- copyResult.AddRangeOverride(await GenerateJpeg(currentProfile, firstInList,
- outputParentFullFilePathFolder));
- break;
- }
- }
- return copyResult;
- }
-
- internal async Task> GenerateWebHtml(List profiles,
- AppSettingsPublishProfiles currentProfile, string itemName, string[] base64ImageArray,
- IEnumerable fileIndexItemsList, string outputParentFullFilePathFolder)
- {
- if ( string.IsNullOrEmpty(currentProfile.Template) )
- {
- _console.WriteLine("CurrentProfile Template not configured");
- return new Dictionary();
- }
-
- // Generates html by razorLight
- var viewModel = new WebHtmlViewModel
- {
- ItemName = itemName,
- Profiles = profiles,
- AppSettings = _appSettings,
- CurrentProfile = currentProfile,
- Base64ImageArray = base64ImageArray,
- // apply slug to items, but use it only in the copy
- FileIndexItems = fileIndexItemsList.Select(c => c.Clone()).ToList(),
- };
-
- // add to IClonable
- foreach (var item in viewModel.FileIndexItems)
- {
- item.FileName = GenerateSlugHelper.GenerateSlug(item.FileCollectionName!, true) +
- Path.GetExtension(item.FileName);
- }
-
- // has a direct dependency on the filesystem
- var embeddedResult = await new ParseRazor(_hostFileSystemStorage, _logger)
- .EmbeddedViews(currentProfile.Template, viewModel);
-
- var stream = StringToStreamHelper.StringToStream(embeddedResult);
- await _hostFileSystemStorage.WriteStreamAsync(stream,
- Path.Combine(outputParentFullFilePathFolder, currentProfile.Path));
-
- _console.Write(_appSettings.IsVerbose() ? embeddedResult +"\n" : "•");
-
- return new Dictionary
- {
- {
- currentProfile.Path.Replace(outputParentFullFilePathFolder, string.Empty),
- currentProfile.Copy
- }
- };
- }
-
- ///
- /// Generate loop of Jpeg images with overlay image
- /// With Retry included
- ///
- /// contains sizes
- /// list of items to generate jpeg for
- /// outputParentFullFilePathFolder
- /// when failed output, has default value
- ///
- internal async Task> GenerateJpeg(AppSettingsPublishProfiles profile,
- IReadOnlyCollection fileIndexItemsList, string outputParentFullFilePathFolder, int delay = 6)
- {
- _toCreateSubfolder.Create(profile,outputParentFullFilePathFolder);
-
- foreach (var item in fileIndexItemsList)
- {
- var outputPath = _overlayImage.FilePathOverlayImage(outputParentFullFilePathFolder,
- item.FilePath, profile);
-
- async Task ResizerLocal()
- {
- return await Resizer(outputPath, profile, item);
- }
-
- try
- {
- await RetryHelper.DoAsync(ResizerLocal, TimeSpan.FromSeconds(delay));
- }
- catch ( AggregateException e )
- {
- _logger.LogError($"[ResizerLocal] Skip due errors: (catch-ed exception) {item.FilePath} {item.FileHash}");
- foreach ( var exception in e.InnerExceptions )
- {
- _logger.LogError("[ResizerLocal] " + exception.Message, exception);
- }
- }
- }
-
- return fileIndexItemsList.ToDictionary(item =>
- _overlayImage.FilePathOverlayImage(item.FilePath, profile),
- item => profile.Copy);
- }
-
- ///
- /// Resize image with overlay
- ///
- /// absolute path of output on host disk
- /// size of output, overlay size, must contain metaData
- /// database item with filePath
- /// true when success
- /// when output is not valid
- private async Task Resizer(string outputPath, AppSettingsPublishProfiles profile,
- FileIndexItem item)
- {
- // for less than 1000px
- if (profile.SourceMaxWidth <= 1000 && _thumbnailStorage.ExistFile(ThumbnailNameHelper.
- Combine(item.FileHash, ThumbnailSize.Large)))
- {
- await _overlayImage.ResizeOverlayImageThumbnails(item.FileHash, outputPath, profile);
- }
- else if ( profile.SourceMaxWidth <= 2000 && _thumbnailStorage.ExistFile(ThumbnailNameHelper.
- Combine(item.FileHash, ThumbnailSize.ExtraLarge)) )
- {
- await _overlayImage.ResizeOverlayImageThumbnails(
- ThumbnailNameHelper.Combine(item.FileHash, ThumbnailSize.ExtraLarge), outputPath, profile);
- }
- else if ( _subPathStorage.ExistFile(item.FilePath))
- {
- // Thumbs are 2000 px (and larger)
- await _overlayImage.ResizeOverlayImageLarge(item.FilePath, outputPath, profile);
- }
-
- if ( profile.MetaData )
- {
- await MetaData(item, outputPath);
- }
-
- var imageFormat = ExtensionRolesHelper.GetImageFormat(_hostFileSystemStorage.ReadStream(outputPath,160));
- if ( imageFormat == ExtensionRolesHelper.ImageFormat.jpg )
- return true;
-
- _hostFileSystemStorage.FileDelete(outputPath);
-
- throw new DecodingException("[WebHtmlPublishService] image output is not valid");
- }
-
- ///
- /// Copy the metaData over the output path
- ///
- /// all the meta data
- /// absolute path on host disk
- private async Task MetaData(FileIndexItem item, string outputPath)
- {
- if ( !_subPathStorage.ExistFile(item.FilePath) ) return;
-
- // Write the metadata to the new created file
- var comparedNames = FileIndexCompareHelper.Compare(
- new FileIndexItem(), item);
-
- // Output has already rotated the image
- var rotation = nameof(FileIndexItem.Orientation).ToLowerInvariant();
-
- // should already check if it exists
+ public class WebHtmlPublishService : IWebHtmlPublishService
+ {
+ private readonly AppSettings _appSettings;
+ private readonly IExifTool _exifTool;
+ private readonly IStorage _subPathStorage;
+ private readonly IStorage _thumbnailStorage;
+ private readonly IStorage _hostFileSystemStorage;
+ private readonly IConsole _console;
+ private readonly IOverlayImage _overlayImage;
+ private readonly PublishManifest _publishManifest;
+ private readonly IPublishPreflight _publishPreflight;
+ private readonly CopyPublishedContent _copyPublishedContent;
+ private readonly ToCreateSubfolder _toCreateSubfolder;
+ private readonly IThumbnailService _thumbnailService;
+ private readonly IWebLogger _logger;
+
+ [SuppressMessage("Usage",
+ "S107: Constructor has 8 parameters, which is greater than the 7 authorized")]
+ public WebHtmlPublishService(IPublishPreflight publishPreflight, ISelectorStorage
+ selectorStorage, AppSettings appSettings, IExifToolHostStorage exifTool,
+ IOverlayImage overlayImage, IConsole console, IWebLogger logger,
+ IThumbnailService thumbnailService)
+ {
+ _publishPreflight = publishPreflight;
+ _subPathStorage = selectorStorage.Get(SelectorStorage.StorageServices.SubPath);
+ _thumbnailStorage = selectorStorage.Get(SelectorStorage.StorageServices.Thumbnail);
+ _hostFileSystemStorage =
+ selectorStorage.Get(SelectorStorage.StorageServices.HostFilesystem);
+ _appSettings = appSettings;
+ _exifTool = exifTool;
+ _console = console;
+ _overlayImage = overlayImage;
+ _publishManifest = new PublishManifest(_hostFileSystemStorage);
+ _toCreateSubfolder = new ToCreateSubfolder(_hostFileSystemStorage);
+ _copyPublishedContent = new CopyPublishedContent(_toCreateSubfolder,
+ selectorStorage);
+ _logger = logger;
+ _thumbnailService = thumbnailService;
+ }
+
+ public async Task?> RenderCopy(
+ List fileIndexItemsList,
+ string publishProfileName, string itemName, string outputParentFullFilePathFolder,
+ bool moveSourceFiles = false)
+ {
+ fileIndexItemsList = AddFileHashIfNotExist(fileIndexItemsList);
+
+ await PreGenerateThumbnail(fileIndexItemsList, publishProfileName);
+ var base64ImageArray = await Base64DataUriList(fileIndexItemsList);
+
+ var copyContent = await Render(fileIndexItemsList, base64ImageArray,
+ publishProfileName, itemName, outputParentFullFilePathFolder, moveSourceFiles);
+
+ _publishManifest.ExportManifest(outputParentFullFilePathFolder, itemName, copyContent);
+
+ return copyContent;
+ }
+
+ internal List AddFileHashIfNotExist(List fileIndexItemsList)
+ {
+ foreach ( var item in fileIndexItemsList.Where(item =>
+ string.IsNullOrEmpty(item.FileHash)) )
+ {
+ item.FileHash = new FileHash(_subPathStorage).GetHashCode(item.FilePath!).Key;
+ }
+
+ return fileIndexItemsList;
+ }
+
+ internal bool ShouldSkipExtraLarge(string publishProfileName)
+ {
+ var skipExtraLarge = _publishPreflight.GetPublishProfileName(publishProfileName)
+ .TrueForAll(p => p.SourceMaxWidth <= 1999);
+ return skipExtraLarge;
+ }
+
+ internal async Task PreGenerateThumbnail(IEnumerable fileIndexItemsList,
+ string publishProfileName)
+ {
+ var skipExtraLarge = ShouldSkipExtraLarge(publishProfileName);
+ foreach ( var item in fileIndexItemsList )
+ {
+ await _thumbnailService.CreateThumbAsync(item.FilePath, item.FileHash!,
+ skipExtraLarge);
+ }
+ }
+
+ ///
+ /// Get base64 uri lists
+ ///
+ ///
+ private Task Base64DataUriList(IEnumerable fileIndexItemsList)
+ {
+ return new ToBase64DataUriList(_subPathStorage,
+ _thumbnailStorage, _logger, _appSettings).Create(fileIndexItemsList.ToList());
+ }
+
+ ///
+ /// Render output of Publish action
+ ///
+ /// which items need to be published
+ /// list of base64 hashes for html pages
+ /// name of profile
+ /// output publish item name
+ /// path on host disk where to publish to
+ /// include source files (false by default)
+ ///
+ private async Task?> Render(List fileIndexItemsList,
+ string[]? base64ImageArray, string publishProfileName, string itemName,
+ string outputParentFullFilePathFolder, bool moveSourceFiles = false)
+ {
+ if ( _appSettings.PublishProfiles?.Count == 0 )
+ {
+ _console.WriteLine("There are no config items");
+ return null;
+ }
+
+ if ( _appSettings.PublishProfiles?.ContainsKey(publishProfileName) == false )
+ {
+ _console.WriteLine("Key not found");
+ return null;
+ }
+
+ if ( !_hostFileSystemStorage.ExistFolder(outputParentFullFilePathFolder) )
+ {
+ _hostFileSystemStorage.CreateDirectory(outputParentFullFilePathFolder);
+ }
+
+ base64ImageArray ??= new string[fileIndexItemsList.Count];
+
+ // Order alphabetically
+ // Ignore Items with Errors
+ fileIndexItemsList = fileIndexItemsList
+ .Where(p => p.Status is FileIndexItem.ExifStatus.Ok
+ or FileIndexItem.ExifStatus.ReadOnly)
+ .OrderBy(p => p.FileName).ToList();
+
+ var copyResult = new Dictionary();
+
+ var profiles = _publishPreflight.GetPublishProfileName(publishProfileName);
+ foreach ( var currentProfile in profiles )
+ {
+ switch ( currentProfile.ContentType )
+ {
+ case TemplateContentType.Html:
+ copyResult.AddRangeOverride(await GenerateWebHtml(profiles, currentProfile,
+ itemName,
+ base64ImageArray, fileIndexItemsList, outputParentFullFilePathFolder));
+ break;
+ case TemplateContentType.Jpeg:
+ copyResult.AddRangeOverride(await GenerateJpeg(currentProfile,
+ fileIndexItemsList,
+ outputParentFullFilePathFolder));
+ break;
+ case TemplateContentType.MoveSourceFiles:
+ copyResult.AddRangeOverride(await GenerateMoveSourceFiles(currentProfile,
+ fileIndexItemsList,
+ outputParentFullFilePathFolder, moveSourceFiles));
+ break;
+ case TemplateContentType.PublishContent:
+ // Copy all items in the subFolder content for example JavaScripts
+ copyResult.AddRangeOverride(
+ _copyPublishedContent.CopyContent(currentProfile,
+ outputParentFullFilePathFolder));
+ break;
+ case TemplateContentType.PublishManifest:
+ copyResult.Add(
+ _overlayImage.FilePathOverlayImage("_settings.json", currentProfile)
+ , true);
+ break;
+ case TemplateContentType.OnlyFirstJpeg:
+ var item = fileIndexItemsList.FirstOrDefault();
+ if ( item == null )
+ {
+ break;
+ }
+
+ var firstInList = new List { item };
+ copyResult.AddRangeOverride(await GenerateJpeg(currentProfile, firstInList,
+ outputParentFullFilePathFolder));
+ break;
+ }
+ }
+
+ return copyResult;
+ }
+
+ internal async Task> GenerateWebHtml(
+ List profiles,
+ AppSettingsPublishProfiles currentProfile, string itemName, string[] base64ImageArray,
+ IEnumerable fileIndexItemsList, string outputParentFullFilePathFolder)
+ {
+ if ( string.IsNullOrEmpty(currentProfile.Template) )
+ {
+ _console.WriteLine("CurrentProfile Template not configured");
+ return new Dictionary();
+ }
+
+ // Generates html by razorLight
+ var viewModel = new WebHtmlViewModel
+ {
+ ItemName = itemName,
+ Profiles = profiles,
+ AppSettings = _appSettings,
+ CurrentProfile = currentProfile,
+ Base64ImageArray = base64ImageArray,
+ // apply slug to items, but use it only in the copy
+ FileIndexItems = fileIndexItemsList.Select(c => c.Clone()).ToList(),
+ };
+
+ // add to IClonable
+ foreach ( var item in viewModel.FileIndexItems )
+ {
+ item.FileName = GenerateSlugHelper.GenerateSlug(item.FileCollectionName!, true) +
+ Path.GetExtension(item.FileName);
+ }
+
+ // has a direct dependency on the filesystem
+ var embeddedResult = await new ParseRazor(_hostFileSystemStorage, _logger)
+ .EmbeddedViews(currentProfile.Template, viewModel);
+
+ var stream = StringToStreamHelper.StringToStream(embeddedResult);
+ await _hostFileSystemStorage.WriteStreamAsync(stream,
+ Path.Combine(outputParentFullFilePathFolder, currentProfile.Path));
+
+ _console.Write(_appSettings.IsVerbose() ? embeddedResult + "\n" : "•");
+
+ return new Dictionary
+ {
+ {
+ currentProfile.Path.Replace(outputParentFullFilePathFolder, string.Empty),
+ currentProfile.Copy
+ }
+ };
+ }
+
+ ///
+ /// Generate loop of Jpeg images with overlay image
+ /// With Retry included
+ ///
+ /// contains sizes
+ /// list of items to generate jpeg for
+ /// outputParentFullFilePathFolder
+ /// when failed output, has default value
+ ///
+ internal async Task> GenerateJpeg(
+ AppSettingsPublishProfiles profile,
+ IReadOnlyCollection fileIndexItemsList,
+ string outputParentFullFilePathFolder, int delay = 6)
+ {
+ _toCreateSubfolder.Create(profile, outputParentFullFilePathFolder);
+
+ foreach ( var item in fileIndexItemsList )
+ {
+ var outputPath = _overlayImage.FilePathOverlayImage(outputParentFullFilePathFolder,
+ item.FilePath!, profile);
+
+ async Task ResizerLocal()
+ {
+ return await Resizer(outputPath, profile, item);
+ }
+
+ try
+ {
+ await RetryHelper.DoAsync(ResizerLocal, TimeSpan.FromSeconds(delay));
+ }
+ catch ( AggregateException e )
+ {
+ _logger.LogError(
+ $"[ResizerLocal] Skip due errors: (catch-ed exception) {item.FilePath} {item.FileHash}");
+ foreach ( var exception in e.InnerExceptions )
+ {
+ _logger.LogError("[ResizerLocal] " + exception.Message, exception);
+ }
+ }
+ }
+
+ return fileIndexItemsList.ToDictionary(item =>
+ _overlayImage.FilePathOverlayImage(item.FilePath!, profile),
+ _ => profile.Copy);
+ }
+
+ ///
+ /// Resize image with overlay
+ ///
+ /// absolute path of output on host disk
+ /// size of output, overlay size, must contain metaData
+ /// database item with filePath
+ /// true when success
+ /// when output is not valid
+ private async Task Resizer(string outputPath, AppSettingsPublishProfiles profile,
+ FileIndexItem item)
+ {
+ // for less than 1000px
+ if ( profile.SourceMaxWidth <= 1000 &&
+ _thumbnailStorage.ExistFile(
+ ThumbnailNameHelper.Combine(item.FileHash!, ThumbnailSize.Large)) )
+ {
+ await _overlayImage.ResizeOverlayImageThumbnails(item.FileHash!, outputPath,
+ profile);
+ }
+ else if ( profile.SourceMaxWidth <= 2000 &&
+ _thumbnailStorage.ExistFile(
+ ThumbnailNameHelper.Combine(item.FileHash!, ThumbnailSize.ExtraLarge)) )
+ {
+ await _overlayImage.ResizeOverlayImageThumbnails(
+ ThumbnailNameHelper.Combine(item.FileHash!, ThumbnailSize.ExtraLarge),
+ outputPath, profile);
+ }
+ else if ( _subPathStorage.ExistFile(item.FilePath!) )
+ {
+ // Thumbs are 2000 px (and larger)
+ await _overlayImage.ResizeOverlayImageLarge(item.FilePath!, outputPath, profile);
+ }
+
+ if ( profile.MetaData )
+ {
+ await MetaData(item, outputPath);
+ }
+
+ var imageFormat =
+ ExtensionRolesHelper.GetImageFormat(
+ _hostFileSystemStorage.ReadStream(outputPath, 160));
+ if ( imageFormat == ExtensionRolesHelper.ImageFormat.jpg )
+ return true;
+
+ _hostFileSystemStorage.FileDelete(outputPath);
+
+ throw new DecodingException("[WebHtmlPublishService] image output is not valid");
+ }
+
+ ///
+ /// Copy the metaData over the output path
+ ///
+ /// all the meta data
+ /// absolute path on host disk
+ private async Task MetaData(FileIndexItem item, string outputPath)
+ {
+ if ( !_subPathStorage.ExistFile(item.FilePath!) ) return;
+
+ // Write the metadata to the new created file
+ var comparedNames = FileIndexCompareHelper.Compare(
+ new FileIndexItem(), item);
+
+ // Output has already rotated the image
+ var rotation = nameof(FileIndexItem.Orientation).ToLowerInvariant();
+
+ // should already check if it exists
comparedNames.Remove(rotation);
// Write it back
await new ExifToolCmdHelper(_exifTool, _hostFileSystemStorage,
- _thumbnailStorage, null,null).UpdateAsync(item,
- new List {outputPath}, comparedNames,
- false, false);
- }
-
- internal async Task> GenerateMoveSourceFiles(
- AppSettingsPublishProfiles profile, IReadOnlyCollection fileIndexItemsList,
- string outputParentFullFilePathFolder, bool moveSourceFiles)
- {
- _toCreateSubfolder.Create(profile,outputParentFullFilePathFolder);
-
- foreach (var subPath in fileIndexItemsList.Select(p => p.FilePath))
- {
- // input: item.FilePath
- var outputPath = _overlayImage.FilePathOverlayImage(outputParentFullFilePathFolder,
- subPath, profile);
-
- await _hostFileSystemStorage.WriteStreamAsync(_subPathStorage.ReadStream(subPath),
- outputPath);
-
- // only delete when using in cli mode
- if ( moveSourceFiles )
- {
- _subPathStorage.FileDelete(subPath);
- }
- }
- return fileIndexItemsList.ToDictionary(item =>
- _overlayImage.FilePathOverlayImage(item.FilePath, profile),
- item => profile.Copy);
- }
-
- ///
- /// Generate Zip on Host
- ///
- /// One folder deeper than where the folder
- /// blog item name
- ///
- ///
- public async Task GenerateZip(string fullFileParentFolderPath, string itemName,
- Dictionary renderCopyResult, bool deleteFolderAfterwards = false)
- {
- var fileNames = renderCopyResult.Where(p => p.Value).Select(p => p.Key).ToList();
-
- var slugItemName = GenerateSlugHelper.GenerateSlug(itemName, true);
- var filePaths = fileNames.Select(p => Path.Combine(fullFileParentFolderPath, slugItemName, p)).ToList();
-
- new Zipper().CreateZip(fullFileParentFolderPath, filePaths, fileNames,
- slugItemName);
-
- // Write a single file to be sure that writing is ready
- var doneFileFullPath = Path.Combine(_appSettings.TempFolder, slugItemName) + ".done";
- await _hostFileSystemStorage.
- WriteStreamAsync(StringToStreamHelper.StringToStream("OK"), doneFileFullPath);
-
- if ( deleteFolderAfterwards )
- {
- _hostFileSystemStorage.FolderDelete(Path.Combine(_appSettings.TempFolder,
- slugItemName));
- }
- }
- }
+ _thumbnailStorage, null!, null!).UpdateAsync(item,
+ new List { outputPath }, comparedNames,
+ false, false);
+ }
+
+ internal async Task> GenerateMoveSourceFiles(
+ AppSettingsPublishProfiles profile,
+ IReadOnlyCollection fileIndexItemsList,
+ string outputParentFullFilePathFolder, bool moveSourceFiles)
+ {
+ _toCreateSubfolder.Create(profile, outputParentFullFilePathFolder);
+
+ foreach ( var subPath in fileIndexItemsList.Select(p => p.FilePath!) )
+ {
+ // input: item.FilePath
+ var outputPath = _overlayImage.FilePathOverlayImage(outputParentFullFilePathFolder,
+ subPath, profile);
+
+ await _hostFileSystemStorage.WriteStreamAsync(_subPathStorage.ReadStream(subPath),
+ outputPath);
+
+ // only delete when using in cli mode
+ if ( moveSourceFiles )
+ {
+ _subPathStorage.FileDelete(subPath);
+ }
+ }
+
+ return fileIndexItemsList.ToDictionary(item =>
+ _overlayImage.FilePathOverlayImage(item.FilePath!, profile),
+ _ => profile.Copy);
+ }
+
+ ///
+ /// Generate Zip on Host
+ ///
+ /// One folder deeper than where the folder
+ /// blog item name
+ ///
+ ///
+ public async Task GenerateZip(string fullFileParentFolderPath, string itemName,
+ Dictionary? renderCopyResult,
+ bool deleteFolderAfterwards = false)
+ {
+ ArgumentNullException.ThrowIfNull(renderCopyResult);
+
+ var fileNames = renderCopyResult.Where(p => p.Value).Select(p => p.Key).ToList();
+
+ var slugItemName = GenerateSlugHelper.GenerateSlug(itemName, true);
+ var filePaths = fileNames
+ .Select(p => Path.Combine(fullFileParentFolderPath, slugItemName, p)).ToList();
+
+ new Zipper().CreateZip(fullFileParentFolderPath, filePaths, fileNames,
+ slugItemName);
+
+ // Write a single file to be sure that writing is ready
+ var doneFileFullPath = Path.Combine(_appSettings.TempFolder, slugItemName) + ".done";
+ await _hostFileSystemStorage.WriteStreamAsync(StringToStreamHelper.StringToStream("OK"),
+ doneFileFullPath);
+
+ if ( deleteFolderAfterwards )
+ {
+ _hostFileSystemStorage.FolderDelete(Path.Combine(_appSettings.TempFolder,
+ slugItemName));
+ }
+ }
+ }
}
diff --git a/starsky/starsky.feature.webhtmlpublish/ViewModels/WebHtmlViewModel.cs b/starsky/starsky.feature.webhtmlpublish/ViewModels/WebHtmlViewModel.cs
index 566e29adbf..70c0717fdc 100644
--- a/starsky/starsky.feature.webhtmlpublish/ViewModels/WebHtmlViewModel.cs
+++ b/starsky/starsky.feature.webhtmlpublish/ViewModels/WebHtmlViewModel.cs
@@ -1,29 +1,29 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using starsky.foundation.database.Models;
using starsky.foundation.platform.Models;
-namespace starsky.feature.webhtmlpublish.ViewModels
+namespace starsky.feature.webhtmlpublish.ViewModels;
+
+public class WebHtmlViewModel
{
- public class WebHtmlViewModel
- {
- public string ItemName { get; set; }
+ public string ItemName { get; set; } = string.Empty;
+
+ ///
+ /// Used with helpers
+ ///
+ public AppSettings AppSettings { get; set; } = new();
+
+ ///
+ /// Current profile
+ ///
+ public AppSettingsPublishProfiles CurrentProfile { get; set; } = new();
+
+ ///
+ /// Other profiles within the same group
+ ///
+ public List Profiles { get; set; } = [];
- ///
- /// Used with helpers
- ///
- public AppSettings AppSettings { get; set; }
-
- ///
- /// Current profile
- ///
- public AppSettingsPublishProfiles CurrentProfile { get; set; }
-
- ///
- /// Other profiles within the same group
- ///
- public List Profiles { get; set; }
+ public string[] Base64ImageArray { get; set; } = [];
- public string[] Base64ImageArray { get; set; }
- public List FileIndexItems { get; set; }
- }
+ public List FileIndexItems { get; set; } = [];
}
diff --git a/starsky/starsky.feature.webhtmlpublish/starsky.feature.webhtmlpublish.csproj b/starsky/starsky.feature.webhtmlpublish/starsky.feature.webhtmlpublish.csproj
index cea89b3f9f..9a6da4a4f6 100644
--- a/starsky/starsky.feature.webhtmlpublish/starsky.feature.webhtmlpublish.csproj
+++ b/starsky/starsky.feature.webhtmlpublish/starsky.feature.webhtmlpublish.csproj
@@ -2,11 +2,12 @@
net8.0
- 8.0
+ 12.0
{7f7fe502-31a8-409b-bd0b-92d7d1bfeb31}
Full
0.6.0-beta.0
+ enable
@@ -14,25 +15,25 @@
true
-
+
-
-
-
+
+
+
-
+
-
-
-
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
diff --git a/starsky/starsky.foundation.accountmanagement/Extensions/UseBasicAuthenticationExtensions.cs b/starsky/starsky.foundation.accountmanagement/Extensions/UseBasicAuthenticationExtensions.cs
index e04aa74146..6cf55a6935 100644
--- a/starsky/starsky.foundation.accountmanagement/Extensions/UseBasicAuthenticationExtensions.cs
+++ b/starsky/starsky.foundation.accountmanagement/Extensions/UseBasicAuthenticationExtensions.cs
@@ -1,14 +1,14 @@
-using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Builder;
using starsky.foundation.accountmanagement.Middleware;
// ReSharper disable once IdentifierTypo
namespace starsky.foundation.accountmanagement.Extensions
{
- public static class UseBasicAuthenticationExtensions
- {
- public static IApplicationBuilder UseBasicAuthentication(this IApplicationBuilder builder)
- {
- return builder.UseMiddleware();
- }
- }
+ public static class UseBasicAuthenticationExtensions
+ {
+ public static IApplicationBuilder UseBasicAuthentication(this IApplicationBuilder builder)
+ {
+ return builder.UseMiddleware();
+ }
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Extensions/UseCheckIfAccountExistMiddlewareExtensions.cs b/starsky/starsky.foundation.accountmanagement/Extensions/UseCheckIfAccountExistMiddlewareExtensions.cs
index 2393915f30..8305bd4dd4 100644
--- a/starsky/starsky.foundation.accountmanagement/Extensions/UseCheckIfAccountExistMiddlewareExtensions.cs
+++ b/starsky/starsky.foundation.accountmanagement/Extensions/UseCheckIfAccountExistMiddlewareExtensions.cs
@@ -1,14 +1,14 @@
-using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Builder;
using starsky.foundation.accountmanagement.Middleware;
// ReSharper disable once IdentifierTypo
namespace starsky.foundation.accountmanagement.Extensions
{
- public static class UseCheckIfAccountExistMiddlewareExtensions
- {
- public static IApplicationBuilder UseCheckIfAccountExist(this IApplicationBuilder builder)
- {
- return builder.UseMiddleware();
- }
- }
+ public static class UseCheckIfAccountExistMiddlewareExtensions
+ {
+ public static IApplicationBuilder UseCheckIfAccountExist(this IApplicationBuilder builder)
+ {
+ return builder.UseMiddleware();
+ }
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Extensions/UseNoAccountLocalhostExtensions.cs b/starsky/starsky.foundation.accountmanagement/Extensions/UseNoAccountLocalhostExtensions.cs
index b4987c1c2a..a769829b14 100644
--- a/starsky/starsky.foundation.accountmanagement/Extensions/UseNoAccountLocalhostExtensions.cs
+++ b/starsky/starsky.foundation.accountmanagement/Extensions/UseNoAccountLocalhostExtensions.cs
@@ -1,14 +1,14 @@
-using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Builder;
using starsky.foundation.accountmanagement.Middleware;
// ReSharper disable once IdentifierTypo
namespace starsky.foundation.accountmanagement.Extensions
{
- public static class UseNoAccountLocalhostExtensions
- {
- public static IApplicationBuilder UseNoAccount(this IApplicationBuilder builder, bool enable)
- {
- return !enable ? builder : builder.UseMiddleware();
- }
- }
+ public static class UseNoAccountLocalhostExtensions
+ {
+ public static IApplicationBuilder UseNoAccount(this IApplicationBuilder builder, bool enable)
+ {
+ return !enable ? builder : builder.UseMiddleware();
+ }
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Helpers/IsLocalhost.cs b/starsky/starsky.foundation.accountmanagement/Helpers/IsLocalhost.cs
index 697819c383..feb7420917 100644
--- a/starsky/starsky.foundation.accountmanagement/Helpers/IsLocalhost.cs
+++ b/starsky/starsky.foundation.accountmanagement/Helpers/IsLocalhost.cs
@@ -1,4 +1,3 @@
-using System.Linq;
using System.Net;
namespace starsky.foundation.accountmanagement.Helpers
@@ -12,15 +11,17 @@ public static class IsLocalhost
/// context.Connection.LocalIpAddress
///
///
- public static bool IsHostLocalHost(IPAddress? connectionLocalIpAddress, IPAddress? connectionRemoteIpAddress)
+ public static bool IsHostLocalHost(IPAddress? connectionLocalIpAddress,
+ IPAddress? connectionRemoteIpAddress)
{
if ( connectionLocalIpAddress == null ||
- connectionRemoteIpAddress == null )
+ connectionRemoteIpAddress == null )
{
return false;
}
-
- return connectionRemoteIpAddress.Equals(connectionLocalIpAddress) || IPAddress.IsLoopback(connectionRemoteIpAddress);
+
+ return connectionRemoteIpAddress.Equals(connectionLocalIpAddress) ||
+ IPAddress.IsLoopback(connectionRemoteIpAddress);
}
}
}
diff --git a/starsky/starsky.foundation.accountmanagement/Helpers/Pbkdf2Hasher.cs b/starsky/starsky.foundation.accountmanagement/Helpers/Pbkdf2Hasher.cs
index 78b0d1539a..be57fb95f3 100644
--- a/starsky/starsky.foundation.accountmanagement/Helpers/Pbkdf2Hasher.cs
+++ b/starsky/starsky.foundation.accountmanagement/Helpers/Pbkdf2Hasher.cs
@@ -1,4 +1,4 @@
-// Copyright © 2018 Dmitry Sikorsky. All rights reserved.
+// Copyright © 2018 Dmitry Sikorsky. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@@ -7,39 +7,39 @@
namespace starsky.foundation.accountmanagement.Helpers
{
- public static class Pbkdf2Hasher
- {
- ///
- /// Get secured hash passwords based on a salt
- ///
- /// password
- /// to decrypt
- /// hased password
- public static string ComputeHash(string password, byte[] salt)
- {
- return Convert.ToBase64String(
- KeyDerivation.Pbkdf2(
- password: password,
- salt: salt,
- prf: KeyDerivationPrf.HMACSHA1,
- iterationCount: 10000,
- numBytesRequested: 256 / 8
- )
- );
- }
+ public static class Pbkdf2Hasher
+ {
+ ///
+ /// Get secured hash passwords based on a salt
+ ///
+ /// password
+ /// to decrypt
+ /// hased password
+ public static string ComputeHash(string password, byte[] salt)
+ {
+ return Convert.ToBase64String(
+ KeyDerivation.Pbkdf2(
+ password: password,
+ salt: salt,
+ prf: KeyDerivationPrf.HMACSHA1,
+ iterationCount: 10000,
+ numBytesRequested: 256 / 8
+ )
+ );
+ }
- ///
- /// Generate a random salt
- ///
- /// random salt
- public static byte[] GenerateRandomSalt()
- {
- byte[] salt = new byte[128 / 8];
+ ///
+ /// Generate a random salt
+ ///
+ /// random salt
+ public static byte[] GenerateRandomSalt()
+ {
+ byte[] salt = new byte[128 / 8];
- using (var rng = RandomNumberGenerator.Create())
- rng.GetBytes(salt);
+ using ( var rng = RandomNumberGenerator.Create() )
+ rng.GetBytes(salt);
- return salt;
- }
- }
+ return salt;
+ }
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Interfaces/IUserManager.cs b/starsky/starsky.foundation.accountmanagement/Interfaces/IUserManager.cs
index 99f7e4623a..dc6477e72c 100644
--- a/starsky/starsky.foundation.accountmanagement/Interfaces/IUserManager.cs
+++ b/starsky/starsky.foundation.accountmanagement/Interfaces/IUserManager.cs
@@ -1,94 +1,94 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using starsky.foundation.accountmanagement.Models;
using starsky.foundation.database.Models.Account;
namespace starsky.foundation.accountmanagement.Interfaces
{
- public enum SignUpResultError
- {
- CredentialTypeNotFound,
- NullString
- }
-
- public class SignUpResult
- {
- public User? User { get; }
- public bool Success { get; private set; }
- public SignUpResultError? Error { get; }
-
- public SignUpResult(User? user = null, bool success = false, SignUpResultError? error = null)
- {
- User = user;
- Success = success;
- Error = error;
- }
- }
-
- public enum ValidateResultError
- {
- CredentialTypeNotFound,
- CredentialNotFound,
- SecretNotValid,
- Lockout,
- UserNotFound
- }
+ public enum SignUpResultError
+ {
+ CredentialTypeNotFound,
+ NullString
+ }
- public enum ChangeSecretResultError
- {
- CredentialTypeNotFound,
- CredentialNotFound
- }
+ public class SignUpResult
+ {
+ public User? User { get; }
+ public bool Success { get; private set; }
+ public SignUpResultError? Error { get; }
- public class ChangeSecretResult
- {
- public bool Success { get; set; }
+ public SignUpResult(User? user = null, bool success = false, SignUpResultError? error = null)
+ {
+ User = user;
+ Success = success;
+ Error = error;
+ }
+ }
- public ChangeSecretResultError? Error { get; set; }
-
- public ChangeSecretResult(bool success = false, ChangeSecretResultError? error = null)
- {
- Success = success;
- Error = error;
- }
- }
+ public enum ValidateResultError
+ {
+ CredentialTypeNotFound,
+ CredentialNotFound,
+ SecretNotValid,
+ Lockout,
+ UserNotFound
+ }
- public interface IUserManager
- {
- Task AllUsersAsync();
-
- ///
- /// Add a new user, including Roles and UserRoles
- ///
- /// Nice Name, default string.Emthy
- /// default is: Email
- /// an email address, e.g. dont@mail.us
- /// Password
- /// result object
- Task SignUpAsync(string name, string credentialTypeCode,
- string? identifier, string? secret);
-
- void AddToRole(User user, string roleCode);
- void AddToRole(User user, Role role);
- void RemoveFromRole(User user, string roleCode);
- void RemoveFromRole(User user, Role role);
- ChangeSecretResult ChangeSecret(string credentialTypeCode, string? identifier, string secret);
- Task ValidateAsync(string credentialTypeCode,
- string? identifier, string secret);
+ public enum ChangeSecretResultError
+ {
+ CredentialTypeNotFound,
+ CredentialNotFound
+ }
- Task SignIn(HttpContext httpContext, User? user,
- bool isPersistent = false);
- void SignOut(HttpContext httpContext);
- int GetCurrentUserId(HttpContext httpContext);
- User? GetCurrentUser(HttpContext httpContext);
- User? GetUser(string credentialTypeCode, string identifier);
- Credential? GetCredentialsByUserId(int userId);
- Task RemoveUser(string credentialTypeCode,
- string identifier);
- User? Exist(string identifier);
+ public class ChangeSecretResult
+ {
+ public bool Success { get; set; }
- Task ExistAsync(int userTableId);
- Role? GetRole(string credentialTypeCode, string identifier);
- bool PreflightValidate(string userName, string password, string confirmPassword);
- }
+ public ChangeSecretResultError? Error { get; set; }
+
+ public ChangeSecretResult(bool success = false, ChangeSecretResultError? error = null)
+ {
+ Success = success;
+ Error = error;
+ }
+ }
+
+ public interface IUserManager
+ {
+ Task AllUsersAsync();
+
+ ///
+ /// Add a new user, including Roles and UserRoles
+ ///
+ /// Nice Name, default string.Emthy
+ /// default is: Email
+ /// an email address, e.g. dont@mail.us
+ /// Password
+ /// result object
+ Task SignUpAsync(string name, string credentialTypeCode,
+ string? identifier, string? secret);
+
+ void AddToRole(User user, string roleCode);
+ void AddToRole(User user, Role role);
+ void RemoveFromRole(User user, string roleCode);
+ void RemoveFromRole(User user, Role role);
+ ChangeSecretResult ChangeSecret(string credentialTypeCode, string? identifier, string secret);
+ Task ValidateAsync(string credentialTypeCode,
+ string? identifier, string secret);
+
+ Task SignIn(HttpContext httpContext, User? user,
+ bool isPersistent = false);
+ void SignOut(HttpContext httpContext);
+ int GetCurrentUserId(HttpContext httpContext);
+ User? GetCurrentUser(HttpContext httpContext);
+ User? GetUser(string credentialTypeCode, string identifier);
+ Credential? GetCredentialsByUserId(int userId);
+ Task RemoveUser(string credentialTypeCode,
+ string identifier);
+ User? Exist(string identifier);
+
+ Task ExistAsync(int userTableId);
+ Role? GetRole(string credentialTypeCode, string identifier);
+ bool PreflightValidate(string userName, string password, string confirmPassword);
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationHeaderValue.cs b/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationHeaderValue.cs
index af07c2d03d..48ff3a1579 100644
--- a/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationHeaderValue.cs
+++ b/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationHeaderValue.cs
@@ -1,60 +1,60 @@
-using System;
+using System;
// ReSharper disable once IdentifierTypo
namespace starsky.foundation.accountmanagement.Middleware
{
- public sealed class BasicAuthenticationHeaderValue
- {
- public BasicAuthenticationHeaderValue(string? authenticationHeaderValue)
- {
- if (!string.IsNullOrWhiteSpace(authenticationHeaderValue))
- {
- _authenticationHeaderValue = authenticationHeaderValue;
- if (TryDecodeHeaderValue())
- {
- ReadAuthenticationHeaderValue();
- }
- }
- }
+ public sealed class BasicAuthenticationHeaderValue
+ {
+ public BasicAuthenticationHeaderValue(string? authenticationHeaderValue)
+ {
+ if ( !string.IsNullOrWhiteSpace(authenticationHeaderValue) )
+ {
+ _authenticationHeaderValue = authenticationHeaderValue;
+ if ( TryDecodeHeaderValue() )
+ {
+ ReadAuthenticationHeaderValue();
+ }
+ }
+ }
- private readonly string _authenticationHeaderValue = string.Empty;
- private string[] _splitDecodedCredentials = Array.Empty();
+ private readonly string _authenticationHeaderValue = string.Empty;
+ private string[] _splitDecodedCredentials = Array.Empty();
- public bool IsValidBasicAuthenticationHeaderValue { get; private set; }
- public string UserIdentifier { get; private set; } = string.Empty;
- public string UserPassword { get; private set; } = string.Empty;
+ public bool IsValidBasicAuthenticationHeaderValue { get; private set; }
+ public string UserIdentifier { get; private set; } = string.Empty;
+ public string UserPassword { get; private set; } = string.Empty;
- private bool TryDecodeHeaderValue()
- {
- const int headerSchemeLength = 6;
- // The Length of the word "Basic "
- if (_authenticationHeaderValue.Length <= headerSchemeLength)
- {
- return false;
- }
- var encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength);
- try
- {
- var decodedCredentials = Convert.FromBase64String(encodedCredentials);
- _splitDecodedCredentials = System.Text.Encoding.ASCII.GetString(decodedCredentials).Split(':');
- return true;
- }
- catch (FormatException)
- {
- return false;
- }
- }
+ private bool TryDecodeHeaderValue()
+ {
+ const int headerSchemeLength = 6;
+ // The Length of the word "Basic "
+ if ( _authenticationHeaderValue.Length <= headerSchemeLength )
+ {
+ return false;
+ }
+ var encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength);
+ try
+ {
+ var decodedCredentials = Convert.FromBase64String(encodedCredentials);
+ _splitDecodedCredentials = System.Text.Encoding.ASCII.GetString(decodedCredentials).Split(':');
+ return true;
+ }
+ catch ( FormatException )
+ {
+ return false;
+ }
+ }
- private void ReadAuthenticationHeaderValue()
- {
- IsValidBasicAuthenticationHeaderValue = _splitDecodedCredentials.Length == 2
- && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[0])
- && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[1]);
- if (IsValidBasicAuthenticationHeaderValue)
- {
- UserIdentifier = _splitDecodedCredentials[0];
- UserPassword = _splitDecodedCredentials[1];
- }
- }
- }
+ private void ReadAuthenticationHeaderValue()
+ {
+ IsValidBasicAuthenticationHeaderValue = _splitDecodedCredentials.Length == 2
+ && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[0])
+ && !string.IsNullOrWhiteSpace(_splitDecodedCredentials[1]);
+ if ( IsValidBasicAuthenticationHeaderValue )
+ {
+ UserIdentifier = _splitDecodedCredentials[0];
+ UserPassword = _splitDecodedCredentials[1];
+ }
+ }
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationMiddleware.cs b/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationMiddleware.cs
index 347e213ef3..c879d8d6fe 100644
--- a/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationMiddleware.cs
+++ b/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationMiddleware.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
@@ -8,43 +8,43 @@
// ReSharper disable once IdentifierTypo
namespace starsky.foundation.accountmanagement.Middleware
{
- ///
- /// Accepts either username or email as user identifier for sign in with Http Basic authentication
- ///
- public sealed class BasicAuthenticationMiddleware
- {
-
- public BasicAuthenticationMiddleware(RequestDelegate next)
- {
- _next = next;
- }
+ ///
+ /// Accepts either username or email as user identifier for sign in with Http Basic authentication
+ ///
+ public sealed class BasicAuthenticationMiddleware
+ {
- private readonly RequestDelegate _next;
+ public BasicAuthenticationMiddleware(RequestDelegate next)
+ {
+ _next = next;
+ }
- public async Task Invoke(HttpContext context)
- {
- if (context.User.Identity?.IsAuthenticated == false)
- {
- var basicAuthenticationHeader = GetBasicAuthenticationHeaderValue(context);
- if (basicAuthenticationHeader.IsValidBasicAuthenticationHeaderValue)
- {
- var userManager = (IUserManager) context.RequestServices.GetRequiredService(typeof(IUserManager));
-
- var authenticationManager = new BasicAuthenticationSignInManager(
- context, basicAuthenticationHeader, userManager);
- await authenticationManager.TrySignInUser();
- }
- }
- await _next.Invoke(context);
- }
+ private readonly RequestDelegate _next;
- private static BasicAuthenticationHeaderValue GetBasicAuthenticationHeaderValue(HttpContext context)
- {
- var basicAuthenticationHeader = context.Request.Headers.Authorization.FirstOrDefault(header =>
- header!.StartsWith("Basic", StringComparison.OrdinalIgnoreCase));
-
- var decodedHeader = new BasicAuthenticationHeaderValue(basicAuthenticationHeader);
- return decodedHeader;
- }
- }
+ public async Task Invoke(HttpContext context)
+ {
+ if ( context.User.Identity?.IsAuthenticated == false )
+ {
+ var basicAuthenticationHeader = GetBasicAuthenticationHeaderValue(context);
+ if ( basicAuthenticationHeader.IsValidBasicAuthenticationHeaderValue )
+ {
+ var userManager = ( IUserManager )context.RequestServices.GetRequiredService(typeof(IUserManager));
+
+ var authenticationManager = new BasicAuthenticationSignInManager(
+ context, basicAuthenticationHeader, userManager);
+ await authenticationManager.TrySignInUser();
+ }
+ }
+ await _next.Invoke(context);
+ }
+
+ private static BasicAuthenticationHeaderValue GetBasicAuthenticationHeaderValue(HttpContext context)
+ {
+ var basicAuthenticationHeader = context.Request.Headers.Authorization.FirstOrDefault(header =>
+ header!.StartsWith("Basic", StringComparison.OrdinalIgnoreCase));
+
+ var decodedHeader = new BasicAuthenticationHeaderValue(basicAuthenticationHeader);
+ return decodedHeader;
+ }
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationSignInManager.cs b/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationSignInManager.cs
index f22889e1bd..dee236e1c3 100644
--- a/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationSignInManager.cs
+++ b/starsky/starsky.foundation.accountmanagement/Middleware/BasicAuthenticationSignInManager.cs
@@ -1,47 +1,47 @@
-using System.Threading.Tasks;
+using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using starsky.foundation.accountmanagement.Interfaces;
// ReSharper disable once IdentifierTypo
namespace starsky.foundation.accountmanagement.Middleware
{
- public sealed class BasicAuthenticationSignInManager
- {
- private readonly IUserManager _userManager;
+ public sealed class BasicAuthenticationSignInManager
+ {
+ private readonly IUserManager _userManager;
- public BasicAuthenticationSignInManager(
- HttpContext context,
- BasicAuthenticationHeaderValue authenticationHeaderValue,
- IUserManager userManager)
- {
- _context = context;
- _authenticationHeaderValue = authenticationHeaderValue;
- _userManager = userManager;
- }
-
- private readonly HttpContext _context;
- private readonly BasicAuthenticationHeaderValue _authenticationHeaderValue;
+ public BasicAuthenticationSignInManager(
+ HttpContext context,
+ BasicAuthenticationHeaderValue authenticationHeaderValue,
+ IUserManager userManager)
+ {
+ _context = context;
+ _authenticationHeaderValue = authenticationHeaderValue;
+ _userManager = userManager;
+ }
- public async Task TrySignInUser()
- {
- if (_authenticationHeaderValue.IsValidBasicAuthenticationHeaderValue)
- {
- var validateResult = await _userManager.ValidateAsync("email",
- _authenticationHeaderValue.UserIdentifier,
- _authenticationHeaderValue.UserPassword);
-
- if (!validateResult.Success)
- {
- _context.Response.StatusCode = 401;
- if(!_context.Response.Headers.ContainsKey("WWW-Authenticate") )
+ private readonly HttpContext _context;
+ private readonly BasicAuthenticationHeaderValue _authenticationHeaderValue;
+
+ public async Task TrySignInUser()
+ {
+ if ( _authenticationHeaderValue.IsValidBasicAuthenticationHeaderValue )
+ {
+ var validateResult = await _userManager.ValidateAsync("email",
+ _authenticationHeaderValue.UserIdentifier,
+ _authenticationHeaderValue.UserPassword);
+
+ if ( !validateResult.Success )
+ {
+ _context.Response.StatusCode = 401;
+ if ( !_context.Response.Headers.ContainsKey("WWW-Authenticate") )
{
_context.Response.Headers.Append("WWW-Authenticate", "Basic realm=\"Starsky " + validateResult.Error + " \"");
}
return;
- }
+ }
- await _userManager.SignIn(_context, validateResult.User);
- }
- }
- }
+ await _userManager.SignIn(_context, validateResult.User);
+ }
+ }
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Middleware/CheckIfAccountExistMiddleware.cs b/starsky/starsky.foundation.accountmanagement/Middleware/CheckIfAccountExistMiddleware.cs
index a20b2859c2..faa697bf09 100644
--- a/starsky/starsky.foundation.accountmanagement/Middleware/CheckIfAccountExistMiddleware.cs
+++ b/starsky/starsky.foundation.accountmanagement/Middleware/CheckIfAccountExistMiddleware.cs
@@ -13,7 +13,7 @@ namespace starsky.foundation.accountmanagement.Middleware
///
public sealed class CheckIfAccountExistMiddleware
{
-
+
public CheckIfAccountExistMiddleware(RequestDelegate next)
{
_next = next;
@@ -32,19 +32,19 @@ internal static int GetUserTableIdFromClaims(HttpContext httpContext)
public async Task Invoke(HttpContext context)
{
var isApiCall = context.Request.Path.HasValue && (
- context.Request.Path.Value.EndsWith("api/health/details") ||
+ context.Request.Path.Value.EndsWith("api/health/details") ||
context.Request.Path.Value.EndsWith("api/index") ||
context.Request.Path.Value.EndsWith("api/search") ||
context.Request.Path.Value.EndsWith("api/account/status") ||
- context.Request.Path.Value.EndsWith("api/env/"));
+ context.Request.Path.Value.EndsWith("api/env/") );
- if (context.User.Identity?.IsAuthenticated == true && isApiCall)
+ if ( context.User.Identity?.IsAuthenticated == true && isApiCall )
{
- var userManager = (IUserManager) context.RequestServices.GetRequiredService(typeof(IUserManager));
+ var userManager = ( IUserManager )context.RequestServices.GetRequiredService(typeof(IUserManager));
var id = GetUserTableIdFromClaims(context);
var result = await userManager.ExistAsync(id);
- if ( result == null)
+ if ( result == null )
{
userManager.SignOut(context);
context.Response.StatusCode = 401;
diff --git a/starsky/starsky.foundation.accountmanagement/Middleware/NoAccountLocalhostMiddleware.cs b/starsky/starsky.foundation.accountmanagement/Middleware/NoAccountLocalhostMiddleware.cs
index 970222b9c6..dcd898de4a 100644
--- a/starsky/starsky.foundation.accountmanagement/Middleware/NoAccountLocalhostMiddleware.cs
+++ b/starsky/starsky.foundation.accountmanagement/Middleware/NoAccountLocalhostMiddleware.cs
@@ -1,4 +1,3 @@
-#nullable enable
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
@@ -12,54 +11,54 @@
[assembly: InternalsVisibleTo("starskytest")]
namespace starsky.foundation.accountmanagement.Middleware
{
- ///
- /// Auto login when use is on localhost
- ///
- public sealed class NoAccountMiddleware
- {
- public NoAccountMiddleware(RequestDelegate next, AppSettings? appSettings = null)
- {
- _next = next;
- _appSettings = appSettings;
- }
+ ///
+ /// Auto login when use is on localhost
+ ///
+ public sealed class NoAccountMiddleware
+ {
+ public NoAccountMiddleware(RequestDelegate next, AppSettings? appSettings = null)
+ {
+ _next = next;
+ _appSettings = appSettings;
+ }
- private readonly RequestDelegate _next;
- private readonly AppSettings? _appSettings;
+ private readonly RequestDelegate _next;
+ private readonly AppSettings? _appSettings;
- internal const string Identifier = "mail@localhost";
+ internal const string Identifier = "mail@localhost";
- ///
- /// Enable: app__NoAccountLocalhost
- ///
- ///
- public async Task Invoke(HttpContext context)
- {
- var isHostAllowed = IsLocalhost.IsHostLocalHost(context.Connection.LocalIpAddress,
- context.Connection.RemoteIpAddress) || _appSettings?.DemoUnsafeDeleteStorageFolder == true;
+ ///
+ /// Enable: app__NoAccountLocalhost
+ ///
+ ///
+ public async Task Invoke(HttpContext context)
+ {
+ var isHostAllowed = IsLocalhost.IsHostLocalHost(context.Connection.LocalIpAddress,
+ context.Connection.RemoteIpAddress) || _appSettings?.DemoUnsafeDeleteStorageFolder == true;
- var isApiCall = context.Request.Path.HasValue && (context.Request.Path.Value.StartsWith("/api") ||
- context.Request.Path.Value.StartsWith("/realtime"));
-
- var isFromLogoutCall = context.Request.QueryString.HasValue &&
- context.Request.QueryString.Value!.Contains("fromLogout");
-
- if ( isHostAllowed && context.User.Identity?.IsAuthenticated == false && !isApiCall && !isFromLogoutCall)
- {
- var userManager = (IUserManager) context.RequestServices.GetRequiredService(typeof(IUserManager));
+ var isApiCall = context.Request.Path.HasValue && ( context.Request.Path.Value.StartsWith("/api") ||
+ context.Request.Path.Value.StartsWith("/realtime") );
- var user = userManager.GetUser("email", Identifier);
- if ( user == null )
- {
- var newPassword = Convert.ToBase64String(
- Pbkdf2Hasher.GenerateRandomSalt());
- await userManager.SignUpAsync(string.Empty,
- "email", Identifier, newPassword+newPassword);
- user = userManager.GetUser("email", Identifier);
- }
-
- await userManager.SignIn(context, user, true);
- }
- await _next.Invoke(context);
- }
- }
+ var isFromLogoutCall = context.Request.QueryString.HasValue &&
+ context.Request.QueryString.Value!.Contains("fromLogout");
+
+ if ( isHostAllowed && context.User.Identity?.IsAuthenticated == false && !isApiCall && !isFromLogoutCall )
+ {
+ var userManager = ( IUserManager )context.RequestServices.GetRequiredService(typeof(IUserManager));
+
+ var user = userManager.GetUser("email", Identifier);
+ if ( user == null )
+ {
+ var newPassword = Convert.ToBase64String(
+ Pbkdf2Hasher.GenerateRandomSalt());
+ await userManager.SignUpAsync(string.Empty,
+ "email", Identifier, newPassword + newPassword);
+ user = userManager.GetUser("email", Identifier);
+ }
+
+ await userManager.SignIn(context, user, true);
+ }
+ await _next.Invoke(context);
+ }
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Models/Account/ChangePasswordViewModel.cs b/starsky/starsky.foundation.accountmanagement/Models/Account/ChangePasswordViewModel.cs
index 91b2aa72f2..73998f03e7 100644
--- a/starsky/starsky.foundation.accountmanagement/Models/Account/ChangePasswordViewModel.cs
+++ b/starsky/starsky.foundation.accountmanagement/Models/Account/ChangePasswordViewModel.cs
@@ -1,32 +1,32 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
// ReSharper disable once IdentifierTypo
namespace starsky.foundation.accountmanagement.Models.Account
{
- public sealed class ChangePasswordViewModel
- {
- ///
- /// Password before change
- ///
- [StringLength(100, MinimumLength = 8)]
- [DataType(DataType.Password)]
- public string Password { get; set; } = string.Empty;
-
- ///
- /// The new password
- ///
- [Required]
- [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 8)]
- [DataType(DataType.Password)]
- [Display(Name = "ChangedPassword")]
- public string ChangedPassword { get; set; } = string.Empty;
+ public sealed class ChangePasswordViewModel
+ {
+ ///
+ /// Password before change
+ ///
+ [StringLength(100, MinimumLength = 8)]
+ [DataType(DataType.Password)]
+ public string Password { get; set; } = string.Empty;
- ///
- /// The new password confirmed
- ///
- [DataType(DataType.Password)]
- [Display(Name = "Confirm password")]
- [Compare("ChangedPassword", ErrorMessage = "The password and confirmation password do not match.")]
- public string ChangedConfirmPassword { get; set; } = string.Empty;
- }
+ ///
+ /// The new password
+ ///
+ [Required]
+ [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 8)]
+ [DataType(DataType.Password)]
+ [Display(Name = "ChangedPassword")]
+ public string ChangedPassword { get; set; } = string.Empty;
+
+ ///
+ /// The new password confirmed
+ ///
+ [DataType(DataType.Password)]
+ [Display(Name = "Confirm password")]
+ [Compare("ChangedPassword", ErrorMessage = "The password and confirmation password do not match.")]
+ public string ChangedConfirmPassword { get; set; } = string.Empty;
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Models/Account/LoginViewModel.cs b/starsky/starsky.foundation.accountmanagement/Models/Account/LoginViewModel.cs
index b1ef3d2d45..9752eeabab 100644
--- a/starsky/starsky.foundation.accountmanagement/Models/Account/LoginViewModel.cs
+++ b/starsky/starsky.foundation.accountmanagement/Models/Account/LoginViewModel.cs
@@ -1,18 +1,18 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
namespace starsky.foundation.accountmanagement.Models.Account
{
- public sealed class LoginViewModel
- {
- [Required]
- [EmailAddress]
- public string Email { get; set; } = string.Empty;
+ public sealed class LoginViewModel
+ {
+ [Required]
+ [EmailAddress]
+ public string Email { get; set; } = string.Empty;
- [Required]
- [DataType(DataType.Password)]
- public string Password { get; set; } = string.Empty;
+ [Required]
+ [DataType(DataType.Password)]
+ public string Password { get; set; } = string.Empty;
- [Display(Name = "Remember me?")]
- public bool RememberMe { get; set; } = true;
- }
+ [Display(Name = "Remember me?")]
+ public bool RememberMe { get; set; } = true;
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Models/Account/RegisterViewModel.cs b/starsky/starsky.foundation.accountmanagement/Models/Account/RegisterViewModel.cs
index cb194b8180..1471b2823c 100644
--- a/starsky/starsky.foundation.accountmanagement/Models/Account/RegisterViewModel.cs
+++ b/starsky/starsky.foundation.accountmanagement/Models/Account/RegisterViewModel.cs
@@ -1,28 +1,28 @@
-using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations;
namespace starsky.foundation.accountmanagement.Models.Account
{
- public sealed class RegisterViewModel
- {
- [Required]
- [EmailAddress]
- [Display(Name = "Email")]
- public string? Email { get; set; }
+ public sealed class RegisterViewModel
+ {
+ [Required]
+ [EmailAddress]
+ [Display(Name = "Email")]
+ public string? Email { get; set; }
- ///
- /// Name
- ///
- public string Name { get; set; } = "";
+ ///
+ /// Name
+ ///
+ public string Name { get; set; } = "";
- [Required]
- [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 8)]
- [DataType(DataType.Password)]
- [Display(Name = "Password")]
- public string? Password { get; set; }
+ [Required]
+ [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 8)]
+ [DataType(DataType.Password)]
+ [Display(Name = "Password")]
+ public string? Password { get; set; }
- [DataType(DataType.Password)]
- [Display(Name = "Confirm password")]
- [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
- public string? ConfirmPassword { get; set; }
- }
+ [DataType(DataType.Password)]
+ [Display(Name = "Confirm password")]
+ [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
+ public string? ConfirmPassword { get; set; }
+ }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Models/UserOverviewModel.cs b/starsky/starsky.foundation.accountmanagement/Models/UserOverviewModel.cs
index 3fec2954a7..652eb1191b 100644
--- a/starsky/starsky.foundation.accountmanagement/Models/UserOverviewModel.cs
+++ b/starsky/starsky.foundation.accountmanagement/Models/UserOverviewModel.cs
@@ -1,4 +1,3 @@
-#nullable enable
using System.Collections.Generic;
using starsky.foundation.database.Models.Account;
@@ -14,6 +13,6 @@ public UserOverviewModel(List? objectAllUsersResult = null)
}
public List Users { get; set; } = new List();
-
+
public bool IsSuccess { get; set; }
}
diff --git a/starsky/starsky.foundation.accountmanagement/Models/ValidateResult.cs b/starsky/starsky.foundation.accountmanagement/Models/ValidateResult.cs
index 3a488c378e..dcd8aed5d6 100644
--- a/starsky/starsky.foundation.accountmanagement/Models/ValidateResult.cs
+++ b/starsky/starsky.foundation.accountmanagement/Models/ValidateResult.cs
@@ -8,7 +8,7 @@ public sealed class ValidateResult
public User? User { get; set; }
public bool Success { get; set; }
public ValidateResultError? Error { get; set; }
-
+
public ValidateResult(User? user = null, bool success = false, ValidateResultError? error = null)
{
User = user;
diff --git a/starsky/starsky.foundation.accountmanagement/Services/UserManager.cs b/starsky/starsky.foundation.accountmanagement/Services/UserManager.cs
index 1b5999f74f..fac0da02e7 100644
--- a/starsky/starsky.foundation.accountmanagement/Services/UserManager.cs
+++ b/starsky/starsky.foundation.accountmanagement/Services/UserManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
@@ -37,14 +37,14 @@ public sealed class UserManager : IUserManager
private readonly IWebLogger _logger;
public UserManager(ApplicationDbContext dbContext, AppSettings appSettings, IWebLogger logger,
- IMemoryCache? memoryCache = null )
+ IMemoryCache? memoryCache = null)
{
_dbContext = dbContext;
_cache = memoryCache;
_appSettings = appSettings;
_logger = logger;
}
-
+
private bool IsCacheEnabled()
{
// || _appSettings?.AddMemoryCache == false > disabled
@@ -64,7 +64,7 @@ private List AddDefaultRoles()
AccountRoles.AppAccountRoles.User.ToString(),
AccountRoles.AppAccountRoles.Administrator.ToString(),
};
-
+
var roles = new List();
foreach ( var roleName in existingRoleNames )
{
@@ -85,10 +85,10 @@ private List AddDefaultRoles()
// Get the Int Ids from the database
role = _dbContext.Roles.FirstOrDefault(p => p.Code != null && p.Code.ToLower().Equals(roleName.ToLower()));
-
+
roles.Add(role!);
}
-
+
return roles;
}
@@ -105,7 +105,7 @@ private List AddDefaultRoles()
.Equals(credentialTypeCode.ToLower()));
// When not exist add it
- if (credentialType == null && credentialTypeCode.Equals("email", StringComparison.CurrentCultureIgnoreCase) )
+ if ( credentialType == null && credentialTypeCode.Equals("email", StringComparison.CurrentCultureIgnoreCase) )
{
credentialType = new CredentialType
{
@@ -123,16 +123,16 @@ private List AddDefaultRoles()
private const string AllUsersCacheKey = "UserManager_AllUsers";
-
+
///
/// Return the number of users in the database
///
///
public async Task AllUsersAsync()
{
- if (IsCacheEnabled() && _cache?.TryGetValue(AllUsersCacheKey, out var objectAllUsersResult) == true)
+ if ( IsCacheEnabled() && _cache?.TryGetValue(AllUsersCacheKey, out var objectAllUsersResult) == true )
{
- return new UserOverviewModel(( List? ) objectAllUsersResult);
+ return new UserOverviewModel(( List? )objectAllUsersResult);
}
try
@@ -140,12 +140,12 @@ public async Task AllUsersAsync()
var allUsers = await _dbContext.Users.TagWith("AllUsersAsync").ToListAsync();
if ( IsCacheEnabled() )
{
- _cache!.Set(AllUsersCacheKey, allUsers,
- new TimeSpan(99,0,0));
+ _cache!.Set(AllUsersCacheKey, allUsers,
+ new TimeSpan(99, 0, 0));
}
return new UserOverviewModel(allUsers);
}
- catch ( RetryLimitExceededException exception)
+ catch ( RetryLimitExceededException exception )
{
_logger.LogError(exception,
"[User Manager] RetryLimitExceededException [catch-ed]");
@@ -160,7 +160,7 @@ public async Task AllUsersAsync()
internal async Task AddUserToCache(User user)
{
if ( !IsCacheEnabled() ) return;
- var allUsers = (await AllUsersAsync()).Users;
+ var allUsers = ( await AllUsersAsync() ).Users;
var index = allUsers.Find(p => p.Id == user.Id);
if ( allUsers.Exists(p => p.Id == user.Id) && index != null )
{
@@ -171,20 +171,20 @@ internal async Task AddUserToCache(User user)
{
allUsers.Add(user);
}
- _cache!.Set(AllUsersCacheKey, allUsers,
- new TimeSpan(99,0,0));
+ _cache!.Set(AllUsersCacheKey, allUsers,
+ new TimeSpan(99, 0, 0));
}
-
+
///
/// Remove one user from cache
///
private async Task RemoveUserFromCacheAsync(User user)
{
if ( !IsCacheEnabled() ) return;
- var allUsers = (await AllUsersAsync()).Users;
+ var allUsers = ( await AllUsersAsync() ).Users;
allUsers.Remove(user);
- _cache!.Set(AllUsersCacheKey, allUsers,
- new TimeSpan(99,0,0));
+ _cache!.Set(AllUsersCacheKey, allUsers,
+ new TimeSpan(99, 0, 0));
}
///
@@ -207,8 +207,8 @@ private async Task RemoveUserFromCacheAsync(User user)
{
return await _dbContext.Users.FirstOrDefaultAsync(p => p.Id == userTableId);
}
-
- var users = (await AllUsersAsync()).Users;
+
+ var users = ( await AllUsersAsync() ).Users;
return users.Find(p => p.Id == userTableId);
}
@@ -222,16 +222,16 @@ private async Task RemoveUserFromCacheAsync(User user)
internal string GetRoleAddToUser(string identifier, User user)
{
var roleToAddToUser = _appSettings.AccountRegisterDefaultRole.ToString();
-
- if (_appSettings.AccountRegisterFirstRoleAdmin == true && !_dbContext.Users.Any(p => p != user) )
+
+ if ( _appSettings.AccountRegisterFirstRoleAdmin == true && !_dbContext.Users.Any(p => p != user) )
{
return AccountRoles.AppAccountRoles.Administrator.ToString();
}
- if (_appSettings.AccountRolesByEmailRegisterOverwrite != null
- && _appSettings.AccountRolesByEmailRegisterOverwrite
- .TryGetValue(identifier, out var emailsForConfig) &&
- AccountRoles.GetAllRoles().Contains(emailsForConfig) )
+ if ( _appSettings.AccountRolesByEmailRegisterOverwrite != null
+ && _appSettings.AccountRolesByEmailRegisterOverwrite
+ .TryGetValue(identifier, out var emailsForConfig) &&
+ AccountRoles.GetAllRoles().Contains(emailsForConfig) )
{
return emailsForConfig;
}
@@ -254,12 +254,12 @@ public async Task SignUpAsync(string name,
var roles = AddDefaultRoles();
AddDefaultPermissions();
AddDefaultRolePermissions();
-
- if ( string.IsNullOrEmpty(identifier) || string.IsNullOrEmpty(secret))
+
+ if ( string.IsNullOrEmpty(identifier) || string.IsNullOrEmpty(secret) )
{
return new SignUpResult(success: false, error: SignUpResultError.NullString);
}
-
+
// The email is stored in the Credentials database
var user = Exist(identifier);
if ( user == null )
@@ -271,22 +271,22 @@ public async Task SignUpAsync(string name,
Name = name,
Created = createdDate
};
-
+
await _dbContext.Users.AddAsync(user);
await _dbContext.SaveChangesAsync();
await AddUserToCache(user);
// to get the Id
user = await _dbContext.Users.FirstOrDefaultAsync(p => p.Created == createdDate);
-
+
if ( user == null ) throw new AggregateException("user should not be null");
}
// Add a user role based on a user id
- var roleToAdd = roles.Find(p => p.Code == GetRoleAddToUser(identifier,user));
+ var roleToAdd = roles.Find(p => p.Code == GetRoleAddToUser(identifier, user));
AddToRole(user, roleToAdd);
- if (credentialType == null)
+ if ( credentialType == null )
{
return new SignUpResult(success: false, error: SignUpResultError.CredentialTypeNotFound);
}
@@ -303,7 +303,7 @@ public async Task SignUpAsync(string name,
};
byte[] salt = Pbkdf2Hasher.GenerateRandomSalt();
string hash = Pbkdf2Hasher.ComputeHash(secret, salt);
-
+
credential.Secret = hash;
credential.Extra = Convert.ToBase64String(salt);
await _dbContext.Credentials.AddAsync(credential);
@@ -311,7 +311,7 @@ public async Task SignUpAsync(string name,
return new SignUpResult(user: user, success: true);
}
-
+
///
/// Add a link between the user and the role (for example Admin)
///
@@ -321,14 +321,14 @@ public void AddToRole(User user, string roleCode)
{
var role = _dbContext.Roles.TagWith("AddToRole").FirstOrDefault(r => r.Code == roleCode);
- if (role == null)
+ if ( role == null )
{
- return;
+ return;
}
-
+
AddToRole(user, role);
}
-
+
///
/// Add a link between the user and the role (for example Admin)
///
@@ -337,10 +337,10 @@ public void AddToRole(User user, string roleCode)
public void AddToRole(User user, Role? role)
{
var userRole = _dbContext.UserRoles.FirstOrDefault(p => p.User != null && p.User.Id == user.Id);
-
- if (userRole != null || role == null)
+
+ if ( userRole != null || role == null )
{
- return;
+ return;
}
// Add a user role based on a user id
@@ -356,60 +356,60 @@ public void RemoveFromRole(User user, string roleCode)
{
var role = _dbContext.Roles.TagWith("RemoveFromRole").FirstOrDefault(
r => string.Equals(r.Code, roleCode, StringComparison.OrdinalIgnoreCase));
-
- if (role == null)
+
+ if ( role == null )
{
- return;
+ return;
}
-
+
RemoveFromRole(user, role);
}
-
+
public void RemoveFromRole(User user, Role role)
{
var userRole = _dbContext.UserRoles.Find(user.Id, role.Id);
-
- if (userRole == null)
+
+ if ( userRole == null )
{
return;
}
-
+
_dbContext.UserRoles.Remove(userRole);
_dbContext.SaveChanges();
}
-
+
public ChangeSecretResult ChangeSecret(string credentialTypeCode, string? identifier, string secret)
{
var credentialType = _dbContext.CredentialTypes.FirstOrDefault(
ct => ct.Code != null && ct.Code.ToLower().Equals(credentialTypeCode.ToLower()));
-
- if (credentialType == null)
+
+ if ( credentialType == null )
{
return new ChangeSecretResult(success: false, error: ChangeSecretResultError.CredentialTypeNotFound);
}
-
+
var credential = _dbContext.Credentials.TagWith("ChangeSecret").FirstOrDefault(
c => c.CredentialTypeId == credentialType.Id && c.Identifier == identifier);
-
- if (credential == null || identifier == null)
+
+ if ( credential == null || identifier == null )
{
return new ChangeSecretResult(success: false, error: ChangeSecretResultError.CredentialNotFound);
}
-
+
var salt = Pbkdf2Hasher.GenerateRandomSalt();
var hash = Pbkdf2Hasher.ComputeHash(secret, salt);
-
+
credential.Secret = hash;
credential.Extra = Convert.ToBase64String(salt);
_dbContext.Credentials.Update(credential);
_dbContext.SaveChanges();
-
+
if ( IsCacheEnabled() )
{
- _cache!.Set(CredentialCacheKey(credentialType, identifier),
- credential,new TimeSpan(99,0,0));
+ _cache!.Set(CredentialCacheKey(credentialType, identifier),
+ credential, new TimeSpan(99, 0, 0));
}
-
+
return new ChangeSecretResult(success: true);
}
@@ -430,25 +430,25 @@ internal static string CredentialCacheKey(CredentialType credentialType, string?
{
return null;
}
-
+
var key = CredentialCacheKey(credentialType, identifier);
-
+
// Add caching for credentialType
- if (IsCacheEnabled() && _cache?.TryGetValue(key,
- out var objectCredentialTypeCode) == true)
+ if ( IsCacheEnabled() && _cache?.TryGetValue(key,
+ out var objectCredentialTypeCode) == true )
{
- return ( Credential? ) objectCredentialTypeCode;
+ return ( Credential? )objectCredentialTypeCode;
}
-
+
var credentialSelect = _dbContext.Credentials.AsNoTracking().TagWith("Credential").Where(
c => c.CredentialTypeId == credentialType.Id && c.Identifier == identifier).Select(x => new
- {
- x.Id,
- x.UserId,
- x.CredentialTypeId,
- x.Secret,
- x.Extra
- }).FirstOrDefault();
+ {
+ x.Id,
+ x.UserId,
+ x.CredentialTypeId,
+ x.Secret,
+ x.Extra
+ }).FirstOrDefault();
if ( credentialSelect == null )
{
@@ -464,14 +464,14 @@ internal static string CredentialCacheKey(CredentialType credentialType, string?
Extra = credentialSelect.Extra,
};
- if ( IsCacheEnabled())
+ if ( IsCacheEnabled() )
{
- _cache!.Set(key, credential,new TimeSpan(99,0,0));
+ _cache!.Set(key, credential, new TimeSpan(99, 0, 0));
}
return credential;
}
-
+
///
/// Get the CredentialType by the credentialTypeCode
@@ -482,20 +482,21 @@ internal static string CredentialCacheKey(CredentialType credentialType, string?
{
var cacheKey = "credentialTypeCode_" + credentialTypeCode;
// Add caching for credentialType
- if (IsCacheEnabled() && _cache?.TryGetValue(cacheKey,
- out var objectCredentialTypeCode) == true)
+ if ( IsCacheEnabled() && _cache?.TryGetValue(cacheKey,
+ out var objectCredentialTypeCode) == true )
{
- return ( CredentialType? ) objectCredentialTypeCode;
+ return ( CredentialType? )objectCredentialTypeCode;
}
-
+
var credentialTypeSelect = _dbContext.CredentialTypes.AsNoTracking().TagWith("CredentialType").Where(
- ct => ct.Code != null && ct.Code.ToLower().Equals(credentialTypeCode.ToLower())).Select(x => new {
- x.Id,
- x.Code,
- x.Name,
- x.Position
- }).FirstOrDefault();
-
+ ct => ct.Code != null && ct.Code.ToLower().Equals(credentialTypeCode.ToLower())).Select(x => new
+ {
+ x.Id,
+ x.Code,
+ x.Name,
+ x.Position
+ }).FirstOrDefault();
+
if ( credentialTypeSelect == null ) return null;
var credentialType = new CredentialType
@@ -508,8 +509,8 @@ internal static string CredentialCacheKey(CredentialType credentialType, string?
if ( IsCacheEnabled() )
{
- _cache!.Set(cacheKey, credentialType,
- new TimeSpan(99,0,0));
+ _cache!.Set(cacheKey, credentialType,
+ new TimeSpan(99, 0, 0));
}
return credentialType;
}
@@ -518,15 +519,15 @@ public bool PreflightValidate(string userName, string password, string confirmPa
{
var model = new RegisterViewModel
{
- Email = userName,
- Password = password,
+ Email = userName,
+ Password = password,
ConfirmPassword = confirmPassword
};
-
+
var context = new ValidationContext(model, null, null);
var results = new List();
return Validator.TryValidateObject(
- model, context, results,
+ model, context, results,
true
);
}
@@ -543,25 +544,25 @@ public async Task ValidateAsync(string credentialTypeCode,
{
var credentialType = CachedCredentialType(credentialTypeCode);
- if (credentialType == null)
+ if ( credentialType == null )
{
return new ValidateResult(success: false, error: ValidateResultError.CredentialTypeNotFound);
}
var credential = CachedCredential(credentialType, identifier);
-
- if (credential?.Extra == null)
+
+ if ( credential?.Extra == null )
{
return new ValidateResult(success: false, error: ValidateResultError.CredentialNotFound);
}
-
+
// No Password
if ( string.IsNullOrWhiteSpace(secret) )
{
return new ValidateResult(success: false, error: ValidateResultError.SecretNotValid);
}
-
- var userData = (await AllUsersAsync()).Users.Find(p => p.Id == credential.UserId);
+
+ var userData = ( await AllUsersAsync() ).Users.Find(p => p.Id == credential.UserId);
if ( userData == null )
{
return new ValidateResult(success: false, error: ValidateResultError.UserNotFound);
@@ -571,7 +572,7 @@ public async Task ValidateAsync(string credentialTypeCode,
{
return new ValidateResult(success: false, error: ValidateResultError.Lockout);
}
-
+
// To compare the secret
byte[] salt = Convert.FromBase64String(credential.Extra);
string hashedPassword = Pbkdf2Hasher.ComputeHash(secret, salt);
@@ -584,26 +585,26 @@ public async Task ValidateAsync(string credentialTypeCode,
return await SetLockIfFailedCountIsToHigh(credential.UserId);
}
- internal async Task ResetAndSuccess(int accessFailedCount, int userId, User? userData )
+ internal async Task ResetAndSuccess(int accessFailedCount, int userId, User? userData)
{
if ( accessFailedCount <= 0 )
{
return new ValidateResult(userData, true);
}
-
+
userData = await _dbContext.Users.FindAsync(userId);
if ( userData == null )
{
return new ValidateResult(success: false,
error: ValidateResultError.UserNotFound);
}
-
+
userData.LockoutEnabled = false;
userData.AccessFailedCount = 0;
userData.LockoutEnd = DateTime.MinValue;
await _dbContext.SaveChangesAsync();
await AddUserToCache(userData);
-
+
return new ValidateResult(userData, true);
}
@@ -628,7 +629,7 @@ internal async Task SetLockIfFailedCountIsToHigh(int userId)
await AddUserToCache(userData);
return new ValidateResult(success: false, error: errorReason);
}
-
+
public async Task SignIn(HttpContext httpContext, User? user, bool isPersistent = false)
{
if ( user == null )
@@ -640,16 +641,16 @@ public async Task SignIn(HttpContext httpContext, User? user, bool isPersi
{
return false;
}
-
+
ClaimsIdentity identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
-
+
await httpContext.SignInAsync(
- CookieAuthenticationDefaults.AuthenticationScheme,
- principal,
- new AuthenticationProperties() { IsPersistent = isPersistent}
+ CookieAuthenticationDefaults.AuthenticationScheme,
+ principal,
+ new AuthenticationProperties() { IsPersistent = isPersistent }
);
-
+
// Required in the direct context; when using a REST like call
httpContext.User = principal;
return true;
@@ -669,7 +670,7 @@ public async Task