diff --git a/.vscode/settings.json b/.vscode/settings.json index bfdac37..f5bb064 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,7 @@ "Exif", "Getα", "Greyscale", + "Hasher", "jfif", "mmod", "Nicéphore", diff --git a/BlurHash.Core/.vscode/format-report.json b/BlurHash.Core/.vscode/format-report.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/BlurHash.Core/.vscode/format-report.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/BlurHash.Core/AssemblyInfo.cs b/BlurHash.Core/AssemblyInfo.cs new file mode 100644 index 0000000..db02330 --- /dev/null +++ b/BlurHash.Core/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("BlurHash.Core.Test")] +[assembly: InternalsVisibleTo("Benchmarks")] \ No newline at end of file diff --git a/BlurHash.Core/Base83.cs b/BlurHash.Core/Base83.cs new file mode 100644 index 0000000..df8b728 --- /dev/null +++ b/BlurHash.Core/Base83.cs @@ -0,0 +1,64 @@ +namespace BlurHash; + +/// +/// Contains methods to encode or decode integers to Base83-Strings +/// +internal static class Base83 +{ + internal const string _Charset = @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"; + + private static readonly IReadOnlyDictionary _ReverseLookup; + + static Base83() + { + // Build inverse lookup table for fast decoding + Dictionary charIndices = new(); + int index = 0; + foreach (char c in _Charset) + { + charIndices[c] = index; + index++; + } + + _ReverseLookup = charIndices; + } + + /// + /// Encodes a number into its Base83-representation + /// + /// The number to encode + /// The data buffer to put the result data into + /// The Base83-representation of the number + public static void EncodeBase83(this int number, Span output) + { + int length = output.Length; + for (int i = 0; i < length; i++) + { + int digit = number % 83; + number /= 83; + output[length - i - 1] = _Charset[digit]; + } + } + + /// + /// Decodes an IEnumerable of Base83-characters into the integral value it represents + /// + /// The characters to decode + /// The decoded value as integer + public static int DecodeBase83(this ReadOnlySpan base83Data) + { + int result = 0; + foreach (char c in base83Data) + { + if (!_ReverseLookup.TryGetValue(c, out int digit)) + { + throw new ArgumentException("The given string contains invalid characters.", nameof(base83Data)); + } + + result *= 83; + result += digit; + } + + return result; + } +} \ No newline at end of file diff --git a/BlurHash.Core/BlurHash.Core.csproj b/BlurHash.Core/BlurHash.Core.csproj new file mode 100644 index 0000000..d748b97 --- /dev/null +++ b/BlurHash.Core/BlurHash.Core.csproj @@ -0,0 +1,24 @@ + + + enable + 10.0 + enable + library + win-x64 + net7.0 + + + BlurHash.Core + 2.0.0 + Markus Palcer + BlurHash is a compact representation of a placeholder for an image. + false + Copyright 2019 Markus Palcer + blurhash image preview compression encoding + https://github.com/MarkusPalcer/blurhash.net + MIT + + + + + \ No newline at end of file diff --git a/BlurHash.Core/Core.cs b/BlurHash.Core/Core.cs new file mode 100644 index 0000000..24dceae --- /dev/null +++ b/BlurHash.Core/Core.cs @@ -0,0 +1,267 @@ +using System.Numerics; + +namespace BlurHash; + +public static class Core +{ + /// + /// Encodes a 2-dimensional array of pixels into a BlurHash string + /// + /// The 2-dimensional array of pixels to encode + /// The number of components used on the X-Axis for the DCT + /// The number of components used on the Y-Axis for the DCT + /// An optional progress handler to receive progress updates + /// The resulting BlurHash string + public static string Encode(Pixel[,] pixels, int componentsX, int componentsY, IProgress? progressCallback = null) + { + if (componentsX < 1) + throw new ArgumentException("componentsX needs to be at least 1"); + if (componentsX > 9) + throw new ArgumentException("componentsX needs to be at most 9"); + if (componentsY < 1) + throw new ArgumentException("componentsY needs to be at least 1"); + if (componentsY > 9) + throw new ArgumentException("componentsY needs to be at most 9"); + + Span factors = stackalloc Pixel[componentsX * componentsY]; + Span resultBuffer = stackalloc char[4 + 2 * componentsX * componentsY]; + + int factorCount = componentsX * componentsY; + int processedFactors = 0; + + int width = pixels.GetLength(0); + int height = pixels.GetLength(1); + + double[] xCosines = new double[width]; + double[] yCosines = new double[height]; + + for (int yComponent = 0; yComponent < componentsY; yComponent++) + for (int xComponent = 0; xComponent < componentsX; xComponent++) + { + double r = 0, g = 0, b = 0; + double normalization = (xComponent == 0 && yComponent == 0) ? 1 : 2; + + for (int xPixel = 0; xPixel < width; xPixel++) + { + xCosines[xPixel] = Math.Cos(Math.PI * xComponent * xPixel / width); + } + + for (int yPixel = 0; yPixel < height; yPixel++) + { + yCosines[yPixel] = Math.Cos(Math.PI * yComponent * yPixel / height); + } + + for (int xPixel = 0; xPixel < width; xPixel++) + for (int yPixel = 0; yPixel < height; yPixel++) + { + double basis = xCosines[xPixel] * yCosines[yPixel]; + Pixel pixel = pixels[xPixel, yPixel]; + r += basis * pixel._Red; + g += basis * pixel._Green; + b += basis * pixel._Blue; + } + + double scale = normalization / (width * height); + factors[componentsX * yComponent + xComponent]._Red = r * scale; + factors[componentsX * yComponent + xComponent]._Green = g * scale; + factors[componentsX * yComponent + xComponent]._Blue = b * scale; + + progressCallback?.Report(processedFactors * 100 / factorCount); + processedFactors++; + } + + Pixel dc = factors[0]; + int acCount = componentsX * componentsY - 1; + + int sizeFlag = componentsX - 1 + (componentsY - 1) * 9; + sizeFlag.EncodeBase83(resultBuffer[..1]); + + float maximumValue; + if (acCount > 0) + { + // Get maximum absolute value of all AC components + double actualMaximumValue = 0.0; + for (int yComponent = 0; yComponent < componentsY; yComponent++) + for (int xComponent = 0; xComponent < componentsX; xComponent++) + { + // Ignore DC component + if (xComponent == 0 && yComponent == 0) + continue; + + int factorIndex = componentsX * yComponent + xComponent; + + actualMaximumValue = Math.Max(Math.Abs(factors[factorIndex]._Red), actualMaximumValue); + actualMaximumValue = Math.Max(Math.Abs(factors[factorIndex]._Green), actualMaximumValue); + actualMaximumValue = Math.Max(Math.Abs(factors[factorIndex]._Blue), actualMaximumValue); + } + + int quantizedMaximumValue = (int)Math.Max(0.0, Math.Min(82.0, Math.Floor(actualMaximumValue * 166 - 0.5))); + maximumValue = ((float)quantizedMaximumValue + 1) / 166; + quantizedMaximumValue.EncodeBase83(resultBuffer.Slice(1, 1)); + } + else + { + maximumValue = 1; + resultBuffer[1] = '0'; + } + + EncodeDc(dc._Red, dc._Green, dc._Blue).EncodeBase83(resultBuffer.Slice(2, 4)); + + for (int yComponent = 0; yComponent < componentsY; yComponent++) + for (int xComponent = 0; xComponent < componentsX; xComponent++) + { + // Ignore DC component + if (xComponent == 0 && yComponent == 0) + continue; + + int factorIndex = componentsX * yComponent + xComponent; + + EncodeAc(factors[factorIndex]._Red, factors[factorIndex]._Green, factors[factorIndex]._Blue, maximumValue).EncodeBase83(resultBuffer.Slice(6 + (factorIndex - 1) * 2, 2)); + } + + return resultBuffer.ToString(); + } + + /// + /// Decodes a BlurHash string into a 2-dimensional array of pixels + /// + /// The blurhash string to decode + /// + /// A two-dimensional array that will be filled with the pixel data.
+ /// First dimension is the width, second dimension is the height + /// + /// A value that affects the contrast of the decoded image. 1 means normal, smaller values will make the effect more subtle, and larger values will make it stronger. + /// An optional progress handler to receive progress updates + /// A 2-dimensional array of s + public static void Decode(string blurhash, Pixel[,] pixels, double punch = 1.0, IProgress? progressCallback = null) + { + if (blurhash.Length < 6) + { + throw new ArgumentException("BlurHash value needs to be at least 6 characters", nameof(blurhash)); + } + + ReadOnlySpan blurhashSpan = blurhash.AsSpan(); + + int outputWidth = pixels.GetLength(0); + int outputHeight = pixels.GetLength(1); + + int sizeFlag = blurhashSpan[..1].DecodeBase83(); + + int componentsY = sizeFlag / 9 + 1; + int componentsX = sizeFlag % 9 + 1; + int componentCount = componentsX * componentsY; + + if (blurhash.Length != 4 + 2 * componentsX * componentsY) + { + throw new ArgumentException("BlurHash value is missing data", nameof(blurhash)); + } + + double quantizedMaximumValue = blurhashSpan.Slice(1, 1).DecodeBase83(); + double maximumValue = (quantizedMaximumValue + 1.0) / 166.0; + + Pixel[,] coefficients = new Pixel[componentsX, componentsY]; + + int componentIndex = 0; + for (int yComponent = 0; yComponent < componentsY; yComponent++) + for (int xComponent = 0; xComponent < componentsX; xComponent++) + { + if (xComponent == 0 && yComponent == 0) + { + int value = blurhashSpan.Slice(2, 4).DecodeBase83(); + coefficients[xComponent, yComponent] = DecodeDc(value); + } + else + { + int value = blurhashSpan.Slice(4 + componentIndex * 2, 2).DecodeBase83(); + coefficients[xComponent, yComponent] = DecodeAc(value, maximumValue * punch); + } + + componentIndex++; + } + + for (int xPixel = 0; xPixel < outputWidth; xPixel++) + for (int yPixel = 0; yPixel < outputHeight; yPixel++) + { + ref Pixel result = ref pixels[xPixel, yPixel]; + + result._Red = 0.0; + result._Green = 0.0; + result._Blue = 0.0; + } + + double[] xCosines = new double[outputWidth]; + double[] yCosines = new double[outputHeight]; + + componentIndex = 1; + for (int componentX = 0; componentX < componentsX; componentX++) + for (int componentY = 0; componentY < componentsY; componentY++) + { + for (int xPixel = 0; xPixel < outputWidth; xPixel++) + { + xCosines[xPixel] = Math.Cos(Math.PI * xPixel * componentX / outputWidth); + } + + for (int yPixel = 0; yPixel < outputHeight; yPixel++) + { + yCosines[yPixel] = Math.Cos(Math.PI * yPixel * componentY / outputHeight); + } + + Pixel coefficient = coefficients[componentX, componentY]; + + for (int xPixel = 0; xPixel < outputWidth; xPixel++) + for (int yPixel = 0; yPixel < outputHeight; yPixel++) + { + ref Pixel result = ref pixels[xPixel, yPixel]; + + double basis = xCosines[xPixel] * yCosines[yPixel]; + + result._Red += coefficient._Red * basis; + result._Green += coefficient._Green * basis; + result._Blue += coefficient._Blue * basis; + } + + progressCallback?.Report(componentIndex * 100 / componentCount); + componentIndex++; + } + } + + private static int EncodeAc(double r, double g, double b, double maximumValue) + { + int quantizedR = (int)Math.Max(0, Math.Min(18, Math.Floor(MathUtils.SignPow(r / maximumValue, 0.5) * 9 + 9.5))); + int quantizedG = (int)Math.Max(0, Math.Min(18, Math.Floor(MathUtils.SignPow(g / maximumValue, 0.5) * 9 + 9.5))); + int quantizedB = (int)Math.Max(0, Math.Min(18, Math.Floor(MathUtils.SignPow(b / maximumValue, 0.5) * 9 + 9.5))); + + return quantizedR * 19 * 19 + quantizedG * 19 + quantizedB; + } + + private static int EncodeDc(double r, double g, double b) + { + int roundedR = MathUtils.LinearTosRgb(r); + int roundedG = MathUtils.LinearTosRgb(g); + int roundedB = MathUtils.LinearTosRgb(b); + return (roundedR << 16) + (roundedG << 8) + roundedB; + } + + private static Pixel DecodeDc(BigInteger value) + { + int intR = (int)value >> 16; + int intG = (int)(value >> 8) & 255; + int intB = (int)value & 255; + return new Pixel(MathUtils.SRgbToLinear(intR), MathUtils.SRgbToLinear(intG), MathUtils.SRgbToLinear(intB)); + } + + private static Pixel DecodeAc(BigInteger value, double maximumValue) + { + double quantizedR = (double)(value / (19 * 19)); + double quantizedG = (double)(value / 19 % 19); + double quantizedB = (double)(value % 19); + + Pixel result = new( + MathUtils.SignPow((quantizedR - 9.0) / 9.0, 2.0) * maximumValue, + MathUtils.SignPow((quantizedG - 9.0) / 9.0, 2.0) * maximumValue, + MathUtils.SignPow((quantizedB - 9.0) / 9.0, 2.0) * maximumValue + ); + + return result; + } +} \ No newline at end of file diff --git a/BlurHash.Core/MathUtils.cs b/BlurHash.Core/MathUtils.cs new file mode 100644 index 0000000..1b68f89 --- /dev/null +++ b/BlurHash.Core/MathUtils.cs @@ -0,0 +1,38 @@ +namespace BlurHash; + +/// +/// Utility methods for mathematical calculations +/// +public static class MathUtils +{ + /// + /// Calculates Math.Pow(base, exponent) but retains the sign of base in the result. + /// + /// The base of the power. The sign of this value will be the sign of the result + /// The exponent of the power + public static double SignPow(double @base, double exponent) => Math.Sign(@base) * Math.Pow(Math.Abs(@base), exponent); + + /// + /// Converts an sRGB input value (0 to 255) into a linear double value + /// + public static double SRgbToLinear(int value) + { + double v = value / 255.0; + if (v <= 0.04045) + return v / 12.92; + else + return Math.Pow((v + 0.055) / 1.055, 2.4); + } + + /// + /// Converts a linear double value into an sRGB input value (0 to 255) + /// + public static int LinearTosRgb(double value) + { + double v = Math.Max(0.0, Math.Min(1.0, value)); + if (v <= 0.0031308) + return (int)(v * 12.92 * 255 + 0.5); + else + return (int)((1.055 * Math.Pow(v, 1 / 2.4) - 0.055) * 255 + 0.5); + } +} \ No newline at end of file diff --git a/BlurHash.Core/Pixel.cs b/BlurHash.Core/Pixel.cs new file mode 100644 index 0000000..ce49ad5 --- /dev/null +++ b/BlurHash.Core/Pixel.cs @@ -0,0 +1,18 @@ +namespace BlurHash; + +/// +/// Represents a pixel within the BlurHash algorithm +/// +public struct Pixel +{ + public double _Red; + public double _Green; + public double _Blue; + + public Pixel(double red, double green, double blue) + { + _Red = red; + _Green = green; + _Blue = blue; + } +} \ No newline at end of file diff --git a/BlurHash.System.Drawing.Common/.vscode/format-report.json b/BlurHash.System.Drawing.Common/.vscode/format-report.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/BlurHash.System.Drawing.Common/.vscode/format-report.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/BlurHash.System.Drawing.Common/AssemblyInfo.cs b/BlurHash.System.Drawing.Common/AssemblyInfo.cs new file mode 100644 index 0000000..9732c11 --- /dev/null +++ b/BlurHash.System.Drawing.Common/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("BlurHash.System.Drawing.Common.Test")] +[assembly: InternalsVisibleTo("Benchmarks")] \ No newline at end of file diff --git a/BlurHash.System.Drawing.Common/BlurHash.System.Drawing.Common.csproj b/BlurHash.System.Drawing.Common/BlurHash.System.Drawing.Common.csproj new file mode 100644 index 0000000..5faa244 --- /dev/null +++ b/BlurHash.System.Drawing.Common/BlurHash.System.Drawing.Common.csproj @@ -0,0 +1,30 @@ + + + enable + 10.0 + enable + library + win-x64 + net7.0 + + + true + + + BlurHash.System.Drawing.Common + 3.0.0 + Markus Palcer + BlurHash is a compact representation of a placeholder for an image. + false + Copyright 2019 Markus Palcer + blurhash image preview compression encoding + https://github.com/MarkusPalcer/blurhash.net + MIT + + + + + + + + \ No newline at end of file diff --git a/BlurHash.System.Drawing.Common/BlurHasher.cs b/BlurHash.System.Drawing.Common/BlurHasher.cs new file mode 100644 index 0000000..6f1efb4 --- /dev/null +++ b/BlurHash.System.Drawing.Common/BlurHasher.cs @@ -0,0 +1,110 @@ +using BlurHash; +using System.Drawing.Imaging; + +namespace System.Drawing.BlurHash; + +public static class BlurHasher +{ + +#pragma warning disable CA1416 + + /// + /// Encodes a picture into a BlurHash string + /// + /// The picture to encode + /// The number of components used on the X-Axis for the DCT + /// The number of components used on the Y-Axis for the DCT + /// The resulting BlurHash string + public static string Encode(Image image, int componentsX, int componentsY) => Core.Encode(ConvertBitmap(image as Bitmap ?? new Bitmap(image)), componentsX, componentsY); + + /// + /// Decodes a BlurHash string into a System.Drawing.Image + /// + /// The blurhash string to decode + /// The desired width of the output in pixels + /// The desired height of the output in pixels + /// A value that affects the contrast of the decoded image. 1 means normal, smaller values will make the effect more subtle, and larger values will make it stronger. + /// The decoded preview + public static Image Decode(string blurhash, int outputWidth, int outputHeight, double punch = 1.0) + { + Pixel[,] pixelData = new Pixel[outputWidth, outputHeight]; + Core.Decode(blurhash, pixelData, punch); + return ConvertToBitmap(pixelData); + } + + /// + /// Converts the given bitmap to the library-independent representation used within the BlurHash-core + /// + /// The bitmap to encode + public static unsafe Pixel[,] ConvertBitmap(Image sourceBitmap) + { + int width = sourceBitmap.Width; + int height = sourceBitmap.Height; + + using Bitmap temporaryBitmap = new(width, height, PixelFormat.Format24bppRgb); + using (Graphics graphics = Graphics.FromImage(temporaryBitmap)) + { + graphics.DrawImageUnscaled(sourceBitmap, 0, 0); + } + + // Lock the bitmap's bits. + BitmapData bmpData = temporaryBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, temporaryBitmap.PixelFormat); + + // Get the address of the first line. + nint ptr = bmpData.Scan0; + + Pixel[,] result = new Pixel[width, height]; + + byte* rgb = (byte*)ptr.ToPointer(); + for (int y = 0; y < height; y++) + { + int index = bmpData.Stride * y; + + for (int x = 0; x < width; x++) + { + ref Pixel res = ref result[x, y]; + res._Blue = MathUtils.SRgbToLinear(rgb[index++]); + res._Green = MathUtils.SRgbToLinear(rgb[index++]); + res._Red = MathUtils.SRgbToLinear(rgb[index++]); + } + } + + temporaryBitmap.UnlockBits(bmpData); + + return result; + } + + /// + /// Converts the library-independent representation of pixels into a bitmap + /// + /// The library-independent representation of the image + /// A System.Drawing.Bitmap in 32bpp-RGB representation + public static unsafe Bitmap ConvertToBitmap(Pixel[,] pixelData) + { + int width = pixelData.GetLength(0); + int height = pixelData.GetLength(1); + + byte[] data = new byte[width * height * 4]; + + int index = 0; + for (int yPixel = 0; yPixel < height; yPixel++) + for (int xPixel = 0; xPixel < width; xPixel++) + { + Pixel pixel = pixelData[xPixel, yPixel]; + + data[index++] = (byte)MathUtils.LinearTosRgb(pixel._Blue); + data[index++] = (byte)MathUtils.LinearTosRgb(pixel._Green); + data[index++] = (byte)MathUtils.LinearTosRgb(pixel._Red); + data[index++] = 0; + } + + Bitmap bmp; + + fixed (byte* ptr = data) + { + bmp = new Bitmap(width, height, width * 4, PixelFormat.Format32bppRgb, new IntPtr(ptr)); + } + + return bmp; + } +} \ No newline at end of file diff --git a/BlurHash/BlurHash.csproj b/BlurHash/BlurHash.csproj new file mode 100644 index 0000000..8540e2e --- /dev/null +++ b/BlurHash/BlurHash.csproj @@ -0,0 +1,45 @@ + + + enable + 10.0 + enable + library + win-x64 + net7.0 + + + Phares.View.by.Distance.BlurHash + false + 7.0.101.1 + Mike Phares + Phares + true + snupkg + + + true + true + true + + + Windows + + + OSX + + + Linux + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BlurHash/Models/BlurHasher.cs b/BlurHash/Models/BlurHasher.cs new file mode 100644 index 0000000..41b9441 --- /dev/null +++ b/BlurHash/Models/BlurHasher.cs @@ -0,0 +1,52 @@ +using System.Drawing; +using System.Text; +using View_by_Distance.Shared.Models.Methods; + +namespace View_by_Distance.BlurHash.Models; + +public class BlurHasher : IBlurHasher +{ + + string IBlurHasher.Encode(Image image) => +#pragma warning disable CA1416 + image.Width < image.Height ? Encode(image, 4, 3) : Encode(image, 3, 4); +#pragma warning restore CA1416 + + string IBlurHasher.EncodeAndSave(Image image, string directory) => +#pragma warning disable CA1416 + image.Width < image.Height ? EncodeAndSave(image, 4, 3, 300, 190, directory) : EncodeAndSave(image, 3, 4, 300, 190, directory); +#pragma warning restore CA1416 + + string IBlurHasher.Encode(Image image, int x, int y) => + Encode(image, x, y); + + string IBlurHasher.EncodeAndSave(Image image, int x, int y, string directory) => + EncodeAndSave(image, x, y, 300, 190, directory); + + internal static string Encode(Image image, int x, int y) => + System.Drawing.BlurHash.BlurHasher.Encode(image, x, y); + + public string EncodeAndSave(Image image, int x, int y, int width, int height, string directory) + { + string result; + int actualByte; + result = System.Drawing.BlurHash.BlurHasher.Encode(image, x, y); + byte[] blurHashBytes = Encoding.UTF8.GetBytes(result); + using Bitmap actualImage = (Bitmap)System.Drawing.BlurHash.BlurHasher.Decode(result, width, height); + string joined = string.Join(string.Empty, blurHashBytes.Select(l => l.ToString("000"))); + string fileName = Path.Combine(directory, $"{x}x{y}-{width}x{height}-{joined}.png"); + if (!File.Exists(fileName)) + { + using FileStream fileStream = new(fileName, FileMode.CreateNew); +#pragma warning disable CA1416 + actualImage.Save(fileStream, System.Drawing.Imaging.ImageFormat.Png); +#pragma warning restore CA1416 + _ = fileStream.Seek(0, SeekOrigin.Begin); + actualByte = fileStream.ReadByte(); + while (actualByte > -1) + actualByte = fileStream.ReadByte(); + } + return result; + } + +} \ No newline at end of file diff --git a/Compare/Compare.csproj b/Compare/Compare.csproj index c69adda..42443d5 100644 --- a/Compare/Compare.csproj +++ b/Compare/Compare.csproj @@ -38,7 +38,7 @@ - + diff --git a/Compare/Program.cs b/Compare/Program.cs index b7e031d..5456c4a 100644 --- a/Compare/Program.cs +++ b/Compare/Program.cs @@ -19,7 +19,8 @@ public class Program IsEnvironment isEnvironment = new(processesCount: null, nullASPNetCoreEnvironmentIsDevelopment: debuggerWasAttachedAtLineZero, nullASPNetCoreEnvironmentIsProduction: !debuggerWasAttachedAtLineZero); IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/Date-Group/.vscode/UserSecrets/secrets.json b/Date-Group/.vscode/UserSecrets/secrets.json new file mode 100644 index 0000000..9e01026 --- /dev/null +++ b/Date-Group/.vscode/UserSecrets/secrets.json @@ -0,0 +1,16 @@ +{ + "_Application": "Date-Group", + "Windows": { + "Configuration": { + "xByHash": false, + "ByHash": true, + "xPopulatePropertyId": false, + "PopulatePropertyId": true, + "RootDirectory": "D:/Tmp/phares/Pictures", + "xxRootDirectory": "C:/Tmp/Phares/Compare/Images-1e85c0ba", + "xxxRootDirectory": "F:/Tmp/Phares/Compare/Not-Copy-Copy-1e85c0ba", + "xxxxRootDirectory": "C:/Tmp/Phares/Compare/Not-Copy-Copy-1e85c0ba", + "xxxxxRootDirectory": "F:/Tmp/Phares/2022-11-03-DCIM/DCIM/100D3400 2022" + } + } +} \ No newline at end of file diff --git a/Date-Group/.vscode/tasks.json b/Date-Group/.vscode/tasks.json index 77966c4..f440d49 100644 --- a/Date-Group/.vscode/tasks.json +++ b/Date-Group/.vscode/tasks.json @@ -1,38 +1,72 @@ { "version": "2.0.0", + "options": { + "env": { + "serverUserSecretsId": "8004d966-1a9e-4545-a220-83f32b6a13e9" + } + }, "tasks": [ + { + "label": "userSecretsInit", + "command": "dotnet", + "type": "process", + "args": [ + "user-secrets", + "-p", + "${workspaceFolder}/Date-Group.csproj", + "init" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "userSecretsSet", + "command": "dotnet", + "type": "process", + "args": [ + "user-secrets", + "-p", + "${workspaceFolder}/Date-Group.csproj", + "set", + "SaveDirectory", + "D:/1) Images A/Images-1e85c0ba-Results/A2) People/1e85c0ba/([])" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "userSecretsMkLink", + "command": "cmd", + "type": "shell", + "args": [ + "/c", + "mklink", + "/J", + ".vscode\\UserSecrets", + "${userHome}\\AppData\\Roaming\\Microsoft\\UserSecrets\\$env:serverUserSecretsId" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "format", + "command": "dotnet", + "type": "process", + "args": [ + "format", + "--report", + ".vscode", + "--verbosity", + "detailed", + "--severity", + "warn" + ], + "problemMatcher": "$msCompile" + }, { "label": "build", "command": "dotnet", "type": "process", "args": [ "build", - "${workspaceFolder}/Date-Group.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "publish", - "command": "dotnet", - "type": "process", - "args": [ - "publish", - "${workspaceFolder}/Date-Group.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "watch", - "command": "dotnet", - "type": "process", - "args": [ - "watch", - "run", - "${workspaceFolder}/Date-Group.csproj", + "${workspaceFolder}/View-by-Distance-MKLink-Console.sln", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], diff --git a/Date-Group/Date-Group.csproj b/Date-Group/Date-Group.csproj index d9be6fd..7a8ca48 100644 --- a/Date-Group/Date-Group.csproj +++ b/Date-Group/Date-Group.csproj @@ -6,7 +6,8 @@ Exe win-x64 net7.0 - + 8004d966-1a9e-4545-a220-83f32b6a13e9 + Phares.View.by.Distance.Date.Group false @@ -38,8 +39,8 @@ - - + + @@ -49,6 +50,7 @@ + diff --git a/Date-Group/DateGroup.cs b/Date-Group/DateGroup.cs index e1c09d5..53b6d48 100644 --- a/Date-Group/DateGroup.cs +++ b/Date-Group/DateGroup.cs @@ -65,7 +65,8 @@ public class DateGroup throw new Exception(); if (propertyConfiguration.PopulatePropertyId && (configuration.ByCreateDateShortcut || configuration.ByHash) && Shared.Models.Stateless.Methods.IProperty.Any(containers)) { - propertyLogic.SavePropertyParallelWork(ticks, t, containers); + IBlurHasher? blurHasher = new BlurHash.Models.BlurHasher(); + propertyLogic.SavePropertyParallelWork(ticks, t, containers, blurHasher); if (appSettings.MaxDegreeOfParallelism < 2) ticks = LogDelta(ticks, nameof(A_Property.SavePropertyParallelWork)); if (propertyLogic.ExceptionsDirectories.Any()) diff --git a/Date-Group/Program.cs b/Date-Group/Program.cs index 0778a31..a776c82 100644 --- a/Date-Group/Program.cs +++ b/Date-Group/Program.cs @@ -20,7 +20,8 @@ public class Program IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/Date-Group/appsettings.Development.json b/Date-Group/appsettings.Development.json index 8017ab2..ad9619a 100644 --- a/Date-Group/appsettings.Development.json +++ b/Date-Group/appsettings.Development.json @@ -4,22 +4,7 @@ "Log4netProvider": "Debug" } }, - "MaxDegreeOfParallelism": 6, "Serilog": { "MinimumLevel": "Debug" - }, - "Windows": { - "Configuration": { - "xByHash": false, - "ByHash": true, - "xPopulatePropertyId": false, - "PopulatePropertyId": true, - "xRootDirectory": "C:/Tmp/phares/Pictures", - "xxRootDirectory": "C:/Tmp/Phares/Compare/Images-1e85c0ba", - "xxxRootDirectory": "F:/Tmp/Phares/Compare/Not-Copy-Copy-1e85c0ba", - "xxxxRootDirectory": "C:/Tmp/Phares/Compare/Not-Copy-Copy-1e85c0ba", - "xxxxxRootDirectory": "F:/Tmp/Phares/2022-11-03-DCIM/DCIM/100D3400 2022", - "RootDirectory": "D:/1) Images A/Images-1e85c0ba/_" - } } } \ No newline at end of file diff --git a/Delete-By-Distinct/Delete-By-Distinct.csproj b/Delete-By-Distinct/Delete-By-Distinct.csproj index 0d7ddc0..2a4f794 100644 --- a/Delete-By-Distinct/Delete-By-Distinct.csproj +++ b/Delete-By-Distinct/Delete-By-Distinct.csproj @@ -38,7 +38,7 @@ - + diff --git a/Delete-By-Distinct/Program.cs b/Delete-By-Distinct/Program.cs index 61be4b9..03a299a 100644 --- a/Delete-By-Distinct/Program.cs +++ b/Delete-By-Distinct/Program.cs @@ -20,7 +20,8 @@ public class Program IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/Delete-By-Relative/Delete-By-Relative.csproj b/Delete-By-Relative/Delete-By-Relative.csproj index ab2d677..e6a2aa7 100644 --- a/Delete-By-Relative/Delete-By-Relative.csproj +++ b/Delete-By-Relative/Delete-By-Relative.csproj @@ -38,7 +38,7 @@ - + diff --git a/Delete-By-Relative/Program.cs b/Delete-By-Relative/Program.cs index 2a8ae46..72e172e 100644 --- a/Delete-By-Relative/Program.cs +++ b/Delete-By-Relative/Program.cs @@ -20,7 +20,8 @@ public class Program IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/Distance/Distance.csproj b/Distance/Distance.csproj index d44f098..295db12 100644 --- a/Distance/Distance.csproj +++ b/Distance/Distance.csproj @@ -35,7 +35,7 @@ - + diff --git a/Drag-Drop-Explorer/Drag-Drop-Explorer.csproj b/Drag-Drop-Explorer/Drag-Drop-Explorer.csproj index 2de1341..8247eab 100644 --- a/Drag-Drop-Explorer/Drag-Drop-Explorer.csproj +++ b/Drag-Drop-Explorer/Drag-Drop-Explorer.csproj @@ -30,7 +30,7 @@ - + diff --git a/Drag-Drop-Explorer/DragDropExplorer.cs b/Drag-Drop-Explorer/DragDropExplorer.cs index 6c66069..d083457 100644 --- a/Drag-Drop-Explorer/DragDropExplorer.cs +++ b/Drag-Drop-Explorer/DragDropExplorer.cs @@ -40,7 +40,8 @@ public partial class DragDropExplorer : Form IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); configurationRoot = configurationBuilder.Build(); appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (string.IsNullOrEmpty(appSettings.WorkingDirectoryName)) diff --git a/Drag-Drop-Explorer/Program.cs b/Drag-Drop-Explorer/Program.cs index a11572d..e4112ad 100644 --- a/Drag-Drop-Explorer/Program.cs +++ b/Drag-Drop-Explorer/Program.cs @@ -1,6 +1,6 @@ namespace View_by_Distance.Drag_Drop_Explorer; -static class Program +public class Program { /// /// The main entry point for the application. diff --git a/Drag-Drop-Move/Drag-Drop-Move.csproj b/Drag-Drop-Move/Drag-Drop-Move.csproj index 244aabf..590c4cc 100644 --- a/Drag-Drop-Move/Drag-Drop-Move.csproj +++ b/Drag-Drop-Move/Drag-Drop-Move.csproj @@ -30,7 +30,7 @@ - + diff --git a/Drag-Drop-Move/DragDropMove.cs b/Drag-Drop-Move/DragDropMove.cs index ffffd91..748ac36 100644 --- a/Drag-Drop-Move/DragDropMove.cs +++ b/Drag-Drop-Move/DragDropMove.cs @@ -39,7 +39,8 @@ public partial class DragDropMove : Form IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); configurationRoot = configurationBuilder.Build(); appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (string.IsNullOrEmpty(appSettings.WorkingDirectoryName)) @@ -201,10 +202,11 @@ public partial class DragDropMove : Form List<(string, int, DateTime)> results = new(); DateTime dateTime; Shared.Models.Property property; + Shared.Models.Methods.IBlurHasher? blurHasher = null; string[] files = Directory.GetFiles(checkDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string file in files) { - property = Property.Models.A_Property.GetImageProperty(file); + property = Property.Models.A_Property.GetImageProperty(blurHasher, file); if (property.Id is null || property.DateTimeOriginal is null) continue; dateTime = property.DateTimeOriginal.Value.AddTicks(ticks); @@ -238,6 +240,7 @@ public partial class DragDropMove : Form ticks++; } + Shared.Models.Methods.IBlurHasher? blurHasher = null; List<(string, int, DateTime)> collection = GetCollection(checkDirectory, minimumDateTime, maximumDateTime, ticks); ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, Array.Empty(), null) ?? throw new Exception(); foreach ((string file, int id, DateTime dateTime) in collection) @@ -252,7 +255,7 @@ public partial class DragDropMove : Form bitmap.SetPropertyItem(propertyItem); bitmap.Save(checkFile); bitmap.Dispose(); - property = Property.Models.A_Property.GetImageProperty(checkFile); + property = Property.Models.A_Property.GetImageProperty(blurHasher, checkFile); if (property.Id is null || property.Id.Value != id) throw new Exception(); } @@ -287,10 +290,11 @@ public partial class DragDropMove : Form _Logger.Error("bad file(s) or target file(s) or maximum directory doesn't equal 1!"); else { + Shared.Models.Methods.IBlurHasher? blurHasher = null; DateTime minimumDateTime = DateTime.ParseExact(Path.GetFileName(minimumDirectory.First()), format, null, System.Globalization.DateTimeStyles.None); DateTime maximumDateTime = DateTime.ParseExact(Path.GetFileName(maximumDirectory.First()), format, null, System.Globalization.DateTimeStyles.None).AddHours(23); - Shared.Models.Property badProperty = Property.Models.A_Property.GetImageProperty(badFiles.First()); - Shared.Models.Property targetProperty = Property.Models.A_Property.GetImageProperty(targetFiles.First()); + Shared.Models.Property badProperty = Property.Models.A_Property.GetImageProperty(blurHasher, badFiles.First()); + Shared.Models.Property targetProperty = Property.Models.A_Property.GetImageProperty(blurHasher, targetFiles.First()); if (badProperty.DateTimeOriginal is null || targetProperty.DateTimeOriginal is null) _Logger.Error("Date is null!"); else diff --git a/Drag-Drop-Move/Program.cs b/Drag-Drop-Move/Program.cs index a50a30d..057901c 100644 --- a/Drag-Drop-Move/Program.cs +++ b/Drag-Drop-Move/Program.cs @@ -1,6 +1,6 @@ namespace View_by_Distance.Drag_Drop_Explorer; -static class Program +public class Program { /// /// The main entry point for the application. diff --git a/Drag-Drop-Search/Drag-Drop-Search.csproj b/Drag-Drop-Search/Drag-Drop-Search.csproj index 5ccab13..b7c7324 100644 --- a/Drag-Drop-Search/Drag-Drop-Search.csproj +++ b/Drag-Drop-Search/Drag-Drop-Search.csproj @@ -30,7 +30,7 @@ - + diff --git a/Drag-Drop-Search/DragDropSearch.cs b/Drag-Drop-Search/DragDropSearch.cs index d83a031..9f0d7b1 100644 --- a/Drag-Drop-Search/DragDropSearch.cs +++ b/Drag-Drop-Search/DragDropSearch.cs @@ -44,7 +44,8 @@ public partial class DragDropSearch : Form IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); configurationRoot = configurationBuilder.Build(); appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (string.IsNullOrEmpty(appSettings.WorkingDirectoryName)) diff --git a/Drag-Drop-Search/Program.cs b/Drag-Drop-Search/Program.cs index fd9ef43..06f1161 100644 --- a/Drag-Drop-Search/Program.cs +++ b/Drag-Drop-Search/Program.cs @@ -2,7 +2,7 @@ using View_by_Distance.Shared.Models; namespace View_by_Distance.Drag_Drop; -static class Program +public class Program { /// /// The main entry point for the application. diff --git a/Duplicate-Search/Duplicate-Search.csproj b/Duplicate-Search/Duplicate-Search.csproj index 304773b..fa06b3c 100644 --- a/Duplicate-Search/Duplicate-Search.csproj +++ b/Duplicate-Search/Duplicate-Search.csproj @@ -38,7 +38,7 @@ - + diff --git a/Duplicate-Search/Program.cs b/Duplicate-Search/Program.cs index 170dbbb..02e681f 100644 --- a/Duplicate-Search/Program.cs +++ b/Duplicate-Search/Program.cs @@ -20,7 +20,8 @@ public class Program IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/Instance/DlibDotNet.cs b/Instance/DlibDotNet.cs index 6b6e90e..c1fe1b9 100644 --- a/Instance/DlibDotNet.cs +++ b/Instance/DlibDotNet.cs @@ -2,7 +2,9 @@ using Phares.Shared; using ShellProgressBar; using System.Collections.ObjectModel; +using System.Drawing; using System.Drawing.Imaging; +using System.Text.Json; using System.Text.RegularExpressions; using View_by_Distance.Distance.Models; using View_by_Distance.Face.Models; @@ -30,6 +32,7 @@ public partial class DlibDotNet private readonly Serilog.ILogger? _Log; private readonly D2_FaceParts _FaceParts; private readonly AppSettings _AppSettings; + private readonly IBlurHasher _IBlurHasher; private readonly List _Exceptions; private readonly IsEnvironment _IsEnvironment; private readonly bool _PropertyRootExistedBefore; @@ -56,6 +59,7 @@ public partial class DlibDotNet long ticks = DateTime.Now.Ticks; _Exceptions = new List(); _Log = Serilog.Log.ForContext(); + _IBlurHasher = new BlurHash.Models.BlurHasher(); Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); Models.Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); _Log.Information(propertyConfiguration.RootDirectory); @@ -367,12 +371,37 @@ public partial class DlibDotNet if (_Log is null) throw new NullReferenceException(nameof(_Log)); List faces; - Shared.Models.Property property; long ticks = DateTime.Now.Ticks; DateTime dateTime = DateTime.Now; + Shared.Models.Property? property; List parseExceptions = new(); List> subFileTuples = new(); List> metadataCollection; + if (item.Property is not null && item.Property.Id is not null && !item.Any() && item.Property.BlurHash is null) + { + (string aResultsFullGroupDirectory, _) = GetResultsFullGroupDirectories(); + string aPropertySingletonDirectory = Path.Combine(aResultsFullGroupDirectory, "{}"); + string[] files = Directory.GetFiles(aPropertySingletonDirectory, $"{item.Property.Id.Value}*", SearchOption.AllDirectories); + if (files.Length == 1) + { + string matchFile = files.First(); + string json = File.ReadAllText(matchFile); + string find = "\"CreationTime\":"; + if (!json.Contains(find)) + throw new NotImplementedException(); +#pragma warning disable CA1416 + using Image image = Image.FromFile(item.ImageFileHolder.FullName); +#pragma warning restore CA1416 + string blurHash = _IBlurHasher.Encode(image); + json = json.Replace(find, $"\"{nameof(item.Property.BlurHash)}\": \"{blurHash}\", {find}"); + property = JsonSerializer.Deserialize(json); + if (property is null || property.BlurHash is null) + throw new NullReferenceException(nameof(property)); + json = JsonSerializer.Serialize(property, new JsonSerializerOptions { WriteIndented = true }); + File.WriteAllText(matchFile, json); + File.SetLastWriteTime(matchFile, item.Property.LastWriteTime); + } + } if (item.Property is not null && item.Property.Id is not null && !item.Any()) { property = item.Property; @@ -398,7 +427,7 @@ public partial class DlibDotNet _Log.Information(string.Concat("LastWriteTimeChanged <", item.ImageFileHolder.FullName, '>')); else if (item.Moved.HasValue && item.Moved.Value) _Log.Information(string.Concat("Moved <", item.ImageFileHolder.FullName, '>')); - property = propertyLogic.GetProperty(item, subFileTuples, parseExceptions); + property = propertyLogic.GetProperty(_IBlurHasher, item, subFileTuples, parseExceptions); item.Update(property); if (propertyHashCode is null) { @@ -1134,7 +1163,7 @@ public partial class DlibDotNet List distinctFilteredFaces = Map.Models.Stateless.Methods.IMapLogic.GetFaces(distinctFilteredItems); Mapping[] distinctFilteredMappingCollection = GetMappings(_Configuration.PropertyConfiguration, containers, mapLogic, distinctItems: true); int totalNotMapped = mapLogic.UpdateMappingFromPerson(distinctFilteredMappingCollection); - string json = System.Text.Json.JsonSerializer.Serialize(distinctFilteredMappingCollection); + string json = JsonSerializer.Serialize(distinctFilteredMappingCollection); File.WriteAllText(Path.Combine(eDistanceContentDirectory, $"{ticks}.json"), json); for (int i = 1; i < 5; i++) _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(eDistanceContentDirectory); diff --git a/Instance/Instance.csproj b/Instance/Instance.csproj index 9f834fb..efbd6d1 100644 --- a/Instance/Instance.csproj +++ b/Instance/Instance.csproj @@ -40,8 +40,8 @@ - - + + @@ -49,6 +49,7 @@ + diff --git a/Instance/Program.cs b/Instance/Program.cs index 285e643..52aa054 100644 --- a/Instance/Program.cs +++ b/Instance/Program.cs @@ -19,7 +19,8 @@ public class Program IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/Map/Map.csproj b/Map/Map.csproj index c228ce2..c2497ff 100644 --- a/Map/Map.csproj +++ b/Map/Map.csproj @@ -35,7 +35,7 @@ - + diff --git a/Metadata/Metadata.csproj b/Metadata/Metadata.csproj index 6f1d7d8..f44689e 100644 --- a/Metadata/Metadata.csproj +++ b/Metadata/Metadata.csproj @@ -34,7 +34,7 @@ - + diff --git a/Move-By-Id/Move-By-Id.csproj b/Move-By-Id/Move-By-Id.csproj index b34ba0d..a7da0e4 100644 --- a/Move-By-Id/Move-By-Id.csproj +++ b/Move-By-Id/Move-By-Id.csproj @@ -38,7 +38,7 @@ - + diff --git a/Move-By-Id/Program.cs b/Move-By-Id/Program.cs index c917231..e4d3994 100644 --- a/Move-By-Id/Program.cs +++ b/Move-By-Id/Program.cs @@ -20,7 +20,8 @@ public class Program IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/Person/Person.csproj b/Person/Person.csproj index c686fbc..d403867 100644 --- a/Person/Person.csproj +++ b/Person/Person.csproj @@ -1,4 +1,4 @@ - + enable 10.0 @@ -39,7 +39,7 @@ - + diff --git a/Person/Program.cs b/Person/Program.cs index 1f01815..a70d154 100644 --- a/Person/Program.cs +++ b/Person/Program.cs @@ -20,7 +20,8 @@ public class Program IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/PhotoPrism/PhotoPrism.csproj b/PhotoPrism/PhotoPrism.csproj index 28a62a8..c0b73ca 100644 --- a/PhotoPrism/PhotoPrism.csproj +++ b/PhotoPrism/PhotoPrism.csproj @@ -35,7 +35,7 @@ - + diff --git a/PrepareForOld/PrepareForOld.csproj b/PrepareForOld/PrepareForOld.csproj index 0dbe449..442b510 100644 --- a/PrepareForOld/PrepareForOld.csproj +++ b/PrepareForOld/PrepareForOld.csproj @@ -37,12 +37,12 @@ - + - + diff --git a/PrepareForOld/Program.cs b/PrepareForOld/Program.cs index 8f2631a..3f045f5 100644 --- a/PrepareForOld/Program.cs +++ b/PrepareForOld/Program.cs @@ -20,7 +20,8 @@ public class Program IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/Property/Models/A_Property.cs b/Property/Models/A_Property.cs index 908aeb5..5e1aa3b 100644 --- a/Property/Models/A_Property.cs +++ b/Property/Models/A_Property.cs @@ -102,7 +102,7 @@ public class A_Property return results; } - private static Shared.Models.Property GetImageProperty(FileHolder fileHolder, Shared.Models.Property? property, bool populateId, bool isIgnoreExtension, bool isValidImageFormatExtension, bool isValidMetadataExtensions, int? id, ASCIIEncoding asciiEncoding, bool writeBitmapDataBytes, string? angleBracket) + private static Shared.Models.Property GetImageProperty(Shared.Models.Methods.IBlurHasher? blurHasher, FileHolder fileHolder, Shared.Models.Property? property, bool populateId, bool isIgnoreExtension, bool isValidImageFormatExtension, bool isValidMetadataExtensions, int? id, ASCIIEncoding asciiEncoding, bool writeBitmapDataBytes, string? angleBracket) { Shared.Models.Property result; byte[] bytes; @@ -114,6 +114,7 @@ public class A_Property string? model = null; string dateTimeFormat; DateTime checkDateTime; + string? blurHash = null; DateTime? dateTime = null; PropertyItem? propertyItem; string? orientation = null; @@ -150,6 +151,16 @@ public class A_Property File.WriteAllBytes(Path.ChangeExtension(contentFileInfo.FullName, string.Empty), bytes); } } + if (blurHasher is not null && property?.BlurHash is null) + { + if (angleBracket is null) + blurHash = blurHasher.Encode(image); + else + { + string blurHashDirectory = angleBracket.Replace("<>", "()"); + blurHash = blurHasher.EncodeAndSave(image, blurHashDirectory); + } + } width = image.Width; height = image.Height; dateTimeFormat = Shared.Models.Stateless.Methods.IProperty.DateTimeFormat(); @@ -242,15 +253,15 @@ public class A_Property if (fileHolder.LastWriteTime is null && property?.LastWriteTime is null) throw new NullReferenceException(nameof(fileHolder.LastWriteTime)); if (fileHolder.CreationTime is not null && fileHolder.LastWriteTime is not null) - result = new(fileHolder.CreationTime.Value, dateTime, dateTimeDigitized, dateTimeFromName, dateTimeOriginal, fileLength, gpsDateStamp, height, id, fileHolder.LastWriteTime.Value, make, model, orientation, width); + result = new(blurHash, fileHolder.CreationTime.Value, dateTime, dateTimeDigitized, dateTimeFromName, dateTimeOriginal, fileLength, gpsDateStamp, height, id, fileHolder.LastWriteTime.Value, make, model, orientation, width); else if (property is not null) - result = new(property.CreationTime, dateTime, dateTimeDigitized, dateTimeFromName, dateTimeOriginal, fileLength, gpsDateStamp, height, id, property.LastWriteTime, make, model, orientation, width); + result = new(blurHash, property.CreationTime, dateTime, dateTimeDigitized, dateTimeFromName, dateTimeOriginal, fileLength, gpsDateStamp, height, id, property.LastWriteTime, make, model, orientation, width); else throw new NullReferenceException(nameof(property)); return result; } - public static Shared.Models.Property GetImageProperty(string fileName) + public static Shared.Models.Property GetImageProperty(Shared.Models.Methods.IBlurHasher? blurHasher, string fileName) { int? id = null; bool populateId = true; @@ -262,18 +273,18 @@ public class A_Property FileHolder fileHolder = new(fileName); bool isValidImageFormatExtension = true; Shared.Models.Property? property = null; - Shared.Models.Property result = GetImageProperty(fileHolder, property, populateId, isIgnoreExtension, isValidImageFormatExtension, isValidMetadataExtensions, id, asciiEncoding, writeBitmapDataBytes, angleBracket); + Shared.Models.Property result = GetImageProperty(blurHasher, fileHolder, property, populateId, isIgnoreExtension, isValidImageFormatExtension, isValidMetadataExtensions, id, asciiEncoding, writeBitmapDataBytes, angleBracket); return result; } #pragma warning restore CA1416 - private Shared.Models.Property GetPropertyOfPrivate(Item item, List> sourceDirectoryFileTuples, List parseExceptions, bool isIgnoreExtension, bool isValidMetadataExtensions) + private Shared.Models.Property GetPropertyOfPrivate(Shared.Models.Methods.IBlurHasher? blurHasher, Item item, List> sourceDirectoryFileTuples, List parseExceptions, bool isIgnoreExtension, bool isValidMetadataExtensions) { Shared.Models.Property? result; - string json; int? id = null; FileInfo fileInfo; + string? json = null; bool hasWrongYearProperty = false; string[] changesFrom = Array.Empty(); string angleBracket = _AngleBracketCollection[0]; @@ -356,10 +367,28 @@ public class A_Property parseExceptions.Add(nameof(A_Property)); } } + if (!string.IsNullOrEmpty(json) && result is not null && blurHasher is not null && result.BlurHash is null) + { + string find = "\"CreationTime\":"; + if (!json.Contains(find)) + throw new NotImplementedException(); + string blurHashDirectory = angleBracket.Replace("<>", "()"); +#pragma warning disable CA1416 + using Image image = Image.FromFile(item.ImageFileHolder.FullName); +#pragma warning restore CA1416 + string blurHash = blurHasher.EncodeAndSave(image, blurHashDirectory); + json = json.Replace(find, $"\"{nameof(result.BlurHash)}\": \"{blurHash}\", {find}"); + result = JsonSerializer.Deserialize(json); + if (result is null || result.BlurHash is null) + throw new NullReferenceException(nameof(result)); + json = JsonSerializer.Serialize(result, _WriteIndentedJsonSerializerOptions); + File.WriteAllText(fileInfo.FullName, json); + File.SetLastWriteTime(fileInfo.FullName, fileInfo.LastWriteTime); + } if (result is null) { id ??= item.ImageFileHolder.Id; - result = GetImageProperty(item.ImageFileHolder, result, populateId, isIgnoreExtension, item.IsValidImageFormatExtension, isValidMetadataExtensions, id, _ASCIIEncoding, _Configuration.WriteBitmapDataBytes, angleBracket); + result = GetImageProperty(blurHasher, item.ImageFileHolder, result, populateId, isIgnoreExtension, item.IsValidImageFormatExtension, isValidMetadataExtensions, id, _ASCIIEncoding, _Configuration.WriteBitmapDataBytes, angleBracket); json = JsonSerializer.Serialize(result, _WriteIndentedJsonSerializerOptions); if (populateId && Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: true, compareBeforeWrite: true)) { @@ -388,7 +417,7 @@ public class A_Property return result; } - private void SavePropertyParallelForWork(string sourceDirectory, List> sourceDirectoryFileTuples, List> sourceDirectoryChanges, Item item) + private void SavePropertyParallelForWork(Shared.Models.Methods.IBlurHasher? blurHasher, string sourceDirectory, List> sourceDirectoryFileTuples, List> sourceDirectoryChanges, Item item) { Shared.Models.Property property; List parseExceptions = new(); @@ -399,7 +428,7 @@ public class A_Property File.Move(item.ImageFileHolder.FullName, filteredSourceDirectoryFileExtensionLowered); if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.Property is null) { - property = GetPropertyOfPrivate(item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension, isValidMetadataExtensions); + property = GetPropertyOfPrivate(blurHasher, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension, isValidMetadataExtensions); lock (sourceDirectoryChanges) sourceDirectoryChanges.Add(new Tuple(nameof(A_Property), DateTime.Now)); lock (item) @@ -407,10 +436,10 @@ public class A_Property } } - private void SavePropertyParallelWork(List exceptions, List> sourceDirectoryChanges, Container container, List items, string message) + private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IBlurHasher? blurHasher, List exceptions, List> sourceDirectoryChanges, Container container, List items, string message) { List> sourceDirectoryFileTuples = new(); - ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = _MaxDegreeOfParallelism }; + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(items.Count, message, options); _ = Parallel.For(0, items.Count, parallelOptions, (i, state) => @@ -420,7 +449,7 @@ public class A_Property long ticks = DateTime.Now.Ticks; DateTime dateTime = DateTime.Now; List> collection; - SavePropertyParallelForWork(container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]); + SavePropertyParallelForWork(blurHasher, container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]); if (i == 0 || sourceDirectoryChanges.Any()) progressBar.Tick(); lock (sourceDirectoryFileTuples) @@ -449,6 +478,9 @@ public class A_Property singletonDescription: "Properties for each image", collectionDescription: string.Empty, converted: false)); + string directory = _AngleBracketCollection[0].Replace("<>", "()"); + if (!Directory.Exists(directory)) + _ = Directory.CreateDirectory(directory); } private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName) @@ -463,7 +495,7 @@ public class A_Property SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); } - public void SavePropertyParallelWork(long ticks, int t, Container[] containers) + public void SavePropertyParallelWork(long ticks, int t, Container[] containers, Shared.Models.Methods.IBlurHasher? blurHasher) { if (_Log is null) throw new NullReferenceException(nameof(_Log)); @@ -489,7 +521,7 @@ public class A_Property SetAngleBracketCollection(container.SourceDirectory, anyNullOrNoIsUniqueFileName); totalSeconds = (int)Math.Truncate(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); message = $"{i + 1:000} [{container.Items.Count:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}"; - SavePropertyParallelWork(exceptions, sourceDirectoryChanges, container, container.Items, message); + SavePropertyParallelWork(_MaxDegreeOfParallelism, blurHasher, exceptions, sourceDirectoryChanges, container, container.Items, message); foreach (Exception exception in exceptions) _Log.Error(string.Concat(container.SourceDirectory, Environment.NewLine, exception.Message, Environment.NewLine, exception.StackTrace), exception); if (exceptions.Count == container.Items.Count) @@ -510,7 +542,7 @@ public class A_Property } } - public Shared.Models.Property GetProperty(Item item, List> sourceDirectoryFileTuples, List parseExceptions) + public Shared.Models.Property GetProperty(Shared.Models.Methods.IBlurHasher? blurHasher, Item item, List> sourceDirectoryFileTuples, List parseExceptions) { Shared.Models.Property result; bool angleBracketCollectionAny = _AngleBracketCollection.Any(); @@ -522,7 +554,7 @@ public class A_Property } bool isValidMetadataExtensions = _Configuration.ValidMetadataExtensions.Contains(item.ImageFileHolder.ExtensionLowered); bool isIgnoreExtension = item.IsValidImageFormatExtension && _Configuration.IgnoreExtensions.Contains(item.ImageFileHolder.ExtensionLowered); - result = GetPropertyOfPrivate(item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension, isValidMetadataExtensions); + result = GetPropertyOfPrivate(blurHasher, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension, isValidMetadataExtensions); if (!angleBracketCollectionAny) _AngleBracketCollection.Clear(); return result; diff --git a/Property/Property.csproj b/Property/Property.csproj index 2083bfd..3b5eefe 100644 --- a/Property/Property.csproj +++ b/Property/Property.csproj @@ -40,10 +40,10 @@ - + - + diff --git a/Rename/.vscode/UserSecrets/secrets.json b/Rename/.vscode/UserSecrets/secrets.json new file mode 100644 index 0000000..bccf74a --- /dev/null +++ b/Rename/.vscode/UserSecrets/secrets.json @@ -0,0 +1,9 @@ +{ + "_Application": "Rename", + "ComparePathsFile": "C:/Users/mikep/AppData/Local/PharesApps/Drag-Drop-Explorer/2023_21/638202586000194405.json", + "Windows": { + "Configuration": { + "RootDirectory": "D:/1) Images A/Images-1e85c0ba" + } + } +} \ No newline at end of file diff --git a/Rename/.vscode/tasks.json b/Rename/.vscode/tasks.json index c51ceb7..be99a3b 100644 --- a/Rename/.vscode/tasks.json +++ b/Rename/.vscode/tasks.json @@ -1,41 +1,76 @@ { "version": "2.0.0", + "options": { + "env": { + "serverUserSecretsId": "6e026d2f-9edf-4c6c-a042-162758114e9a" + } + }, "tasks": [ + { + "label": "userSecretsInit", + "command": "dotnet", + "type": "process", + "args": [ + "user-secrets", + "-p", + "${workspaceFolder}/Rename.csproj", + "init" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "userSecretsSet", + "command": "dotnet", + "type": "process", + "args": [ + "user-secrets", + "-p", + "${workspaceFolder}/Rename.csproj", + "set", + "SaveDirectory", + "D:/1) Images A/Images-1e85c0ba-Results/A2) People/1e85c0ba/([])" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "userSecretsMkLink", + "command": "cmd", + "type": "shell", + "args": [ + "/c", + "mklink", + "/J", + ".vscode\\UserSecrets", + "${userHome}\\AppData\\Roaming\\Microsoft\\UserSecrets\\$env:serverUserSecretsId" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "format", + "command": "dotnet", + "type": "process", + "args": [ + "format", + "--report", + ".vscode", + "--verbosity", + "detailed", + "--severity", + "warn" + ], + "problemMatcher": "$msCompile" + }, { "label": "build", "command": "dotnet", "type": "process", "args": [ "build", - "${workspaceFolder}/Rename.csproj", + "${workspaceFolder}/View-by-Distance-MKLink-Console.sln", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary" ], "problemMatcher": "$msCompile" - }, - { - "label": "publish", - "command": "dotnet", - "type": "process", - "args": [ - "publish", - "${workspaceFolder}/Rename.csproj", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "watch", - "command": "dotnet", - "type": "process", - "args": [ - "watch", - "run", - "--project", - "${workspaceFolder}/Rename.csproj" - ], - "problemMatcher": "$msCompile" } ] } \ No newline at end of file diff --git a/Rename/Program.cs b/Rename/Program.cs index 70c7b6f..f6996c1 100644 --- a/Rename/Program.cs +++ b/Rename/Program.cs @@ -20,7 +20,8 @@ public class Program IConfigurationBuilder configurationBuilder = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true); + .AddJsonFile(isEnvironment.AppSettingsFileName, optional: false, reloadOnChange: true) + .AddUserSecrets(); IConfigurationRoot configurationRoot = configurationBuilder.Build(); AppSettings appSettings = Models.Binder.AppSettings.Get(configurationRoot); if (appSettings.MaxDegreeOfParallelism > Environment.ProcessorCount) diff --git a/Rename/Rename.cs b/Rename/Rename.cs index 5d334c6..64ef3db 100644 --- a/Rename/Rename.cs +++ b/Rename/Rename.cs @@ -3,9 +3,11 @@ using Phares.Shared; using Serilog; using ShellProgressBar; using System.Text.Json; +using View_by_Distance.Property.Models; using View_by_Distance.Rename.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; +using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Rename; @@ -15,7 +17,7 @@ public class Rename private readonly AppSettings _AppSettings; private readonly string _WorkingDirectory; private readonly IsEnvironment _IsEnvironment; - private readonly Configuration _Configuration; + private readonly Models.Configuration _Configuration; private readonly IConfigurationRoot _ConfigurationRoot; private readonly Property.Models.Configuration _PropertyConfiguration; @@ -34,7 +36,7 @@ public class Rename ILogger? log = Log.ForContext(); Dictionary>> fileSizeToCollection = new(); Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); - Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); + Models.Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); _PropertyConfiguration = propertyConfiguration; _Configuration = configuration; propertyConfiguration.Update(); @@ -63,6 +65,8 @@ public class Rename MoveMatches(matchNginxCollection[0]); else if (matchNginxCollection.All(l => l.Name.StartsWith("#")) || matchNginxCollection.All(l => l.Name.StartsWith(" #")) || matchNginxCollection.All(l => l.Name.StartsWith("=20")) || matchNginxCollection.All(l => l.Name.StartsWith("#20"))) Rename2000(log, matchNginxCollection); + else if (matchNginxCollection.All(l => l.Name.Length > 4) && matchNginxCollection.All(l => l.Name[0..3] is "198" or "199" or "200" or "201")) + RenameByDateTaken(log, matchNginxCollection); else if (matchNginxCollection.Any()) { List lines = RenameFilesInDirectories(log, matchNginxCollection); @@ -323,6 +327,8 @@ public class Rename continue; if (File.Exists(matchNginx.ConvertedPath)) continue; + if (!Directory.Exists(matchNginx.ConvertedPath)) + continue; files = Directory.GetFiles(matchNginx.ConvertedPath, "*", SearchOption.AllDirectories); if (files.All(l => l.EndsWith(".id"))) { @@ -425,8 +431,8 @@ public class Rename List allFiles; ProgressBar progressBar; List<(FileHolder, string)> toDoCollection; - List<(FileHolder, string)> verifiedToDoCollection; allFiles = GetAllFiles(matchNginxCollection); + List<(FileHolder, string)> verifiedToDoCollection; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; for (int i = 1; i < 3; i++) { @@ -464,4 +470,88 @@ public class Rename return results; } -} \ No newline at end of file + private void RenameByDateTakenB(MatchNginx[] matchNginxCollection, string aPropertySingletonDirectory, string[] jsonFiles) + { + string json; + char directory; + string[] files; + string fileName; + string extension; + string[] matches; + string checkFile; + DateTime dateTime; + string[] segments; + string? checkFileName; + string checkDirectory; + string? subdirectory; + Shared.Models.Property? property; + foreach (MatchNginx matchNginx in matchNginxCollection) + { + if (File.Exists(matchNginx.ConvertedPath)) + continue; + if (!Directory.Exists(matchNginx.ConvertedPath)) + continue; + subdirectory = Path.GetDirectoryName(matchNginx.ConvertedPath); + if (string.IsNullOrEmpty(subdirectory)) + continue; + files = Directory.GetFiles(matchNginx.ConvertedPath, "*", SearchOption.AllDirectories); + for (int i = 65; i < 91; i++) + { + checkDirectory = Path.Combine(subdirectory, nameof(Rename), ((char)i).ToString()); + if (!Directory.Exists(checkDirectory)) + _ = Directory.CreateDirectory(checkDirectory); + } + foreach (string file in files) + { + fileName = Path.GetFileName(file); + segments = fileName.Split('.'); + extension = Path.GetExtension(file); + directory = IDirectory.GetDirectory(fileName); + checkFileName = $"{segments.First()}{Path.GetExtension(Path.GetFileNameWithoutExtension(file))}.json"; + checkDirectory = Path.Combine(aPropertySingletonDirectory, _PropertyConfiguration.ResultAllInOne, directory.ToString()); + checkFile = Path.Combine(checkDirectory, checkFileName); + matches = jsonFiles.Where(l => l == checkFile).ToArray(); + if (!matches.Any()) + { + matches = jsonFiles.Where(l => l.EndsWith(checkFileName)).ToArray(); + if (!matches.Any()) + continue; + } + json = File.ReadAllText(matches.First()); + property = JsonSerializer.Deserialize(json); + if (property is null) + continue; + checkFileName = null; + dateTime = property.DateTimeOriginal is not null ? property.DateTimeOriginal.Value : Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(property); + for (int i = 65; i < 91; i++) + { + if (checkFileName is not null && !File.Exists(checkFileName)) + break; + checkFileName = Path.Combine(subdirectory, nameof(Rename), ((char)i).ToString(), $"{dateTime.Ticks}{extension}"); + } + if (checkFileName is null || File.Exists(checkFileName)) + continue; + File.Move(file, checkFileName); + } + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(subdirectory); + } + } + + private void RenameByDateTaken(ILogger log, MatchNginx[] matchNginxCollection) + { + string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( + _PropertyConfiguration, + nameof(A_Property), + string.Empty, + includeResizeGroup: false, + includeModel: false, + includePredictorModel: false); + string aPropertySingletonDirectory = Path.GetFullPath(Path.Combine(aResultsFullGroupDirectory, "{}")); + string[] jsonFiles = !Directory.Exists(aPropertySingletonDirectory) ? Array.Empty() : Directory.GetFiles(aPropertySingletonDirectory, "*.json", SearchOption.AllDirectories); + if (!jsonFiles.Any()) + log.Information($"No json file(s) found! Check directory <{aPropertySingletonDirectory}>"); + else + RenameByDateTakenB(matchNginxCollection, aPropertySingletonDirectory, jsonFiles); + } + +} diff --git a/Rename/Rename.csproj b/Rename/Rename.csproj index 775ee97..149874d 100644 --- a/Rename/Rename.csproj +++ b/Rename/Rename.csproj @@ -6,6 +6,7 @@ Exe win-x64 net7.0 + 6e026d2f-9edf-4c6c-a042-162758114e9a Phares.View.by.Distance.Rename @@ -38,7 +39,7 @@ - + diff --git a/Rename/appsettings.Development.json b/Rename/appsettings.Development.json index 91a8073..03bc01d 100644 --- a/Rename/appsettings.Development.json +++ b/Rename/appsettings.Development.json @@ -1,14 +1,9 @@ { - "ComparePathsFile": "C:/Users/mikep/AppData/Local/PharesApps/Drag-Drop-Explorer/2023_13/638158781544395303.json", - "CopyTo": "", "Logging": { "LogLevel": { "Log4netProvider": "Debug" } }, - "MaxDegreeOfParallelism": 6, - "MaxMinutesDelta": 2, - "RenameUndo": false, "Serilog": { "MinimumLevel": "Debug" }, diff --git a/Rename/appsettings.json b/Rename/appsettings.json index e65eaa4..ecdf9c1 100644 --- a/Rename/appsettings.json +++ b/Rename/appsettings.json @@ -68,7 +68,7 @@ "ResultCollection": "[]", "ResultContent": "()", "ResultSingleton": "{}", - "RootDirectory": "C:/Tmp/Phares/Compare/Images-1e85c0ba", + "RootDirectory": "D:/Images", "WriteBitmapDataBytes": false, "IgnoreExtensions": [ ".gif", diff --git a/Resize/Models/_C_Resize.cs b/Resize/Models/_C_Resize.cs index 562799a..3713b13 100644 --- a/Resize/Models/_C_Resize.cs +++ b/Resize/Models/_C_Resize.cs @@ -56,9 +56,7 @@ public class C_Resize _PropertiesChangedForResize = propertiesChangedForResize; _ForceResizeLastWriteTimeToCreationTime = forceResizeLastWriteTimeToCreationTime; _WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; - ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, Array.Empty(), null); - if (constructorInfo is null) - throw new Exception(); + ConstructorInfo? constructorInfo = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, Array.Empty(), null) ?? throw new Exception(); _ConstructorInfo = constructorInfo; } diff --git a/Resize/Resize.csproj b/Resize/Resize.csproj index 2e3a5a5..944a413 100644 --- a/Resize/Resize.csproj +++ b/Resize/Resize.csproj @@ -35,7 +35,7 @@ - + @@ -43,5 +43,6 @@ + \ No newline at end of file diff --git a/Shared/Models/Methods/IBlurHasher.cs b/Shared/Models/Methods/IBlurHasher.cs new file mode 100644 index 0000000..138fa9b --- /dev/null +++ b/Shared/Models/Methods/IBlurHasher.cs @@ -0,0 +1,14 @@ +using System.Drawing; + +namespace View_by_Distance.Shared.Models.Methods; + +public interface IBlurHasher +{ + + string Encode(Image image); + string Encode(Image image, int x, int y); + string EncodeAndSave(Image image, string directory); + string EncodeAndSave(Image image, int x, int y, string directory); + string EncodeAndSave(Image image, int x, int y, int width, int height, string directory); + +} \ No newline at end of file diff --git a/Shared/Models/Properties/IProperty.cs b/Shared/Models/Properties/IProperty.cs index f1454da..079d832 100644 --- a/Shared/Models/Properties/IProperty.cs +++ b/Shared/Models/Properties/IProperty.cs @@ -3,6 +3,7 @@ namespace View_by_Distance.Shared.Models.Properties; public interface IProperty { + public string? BlurHash { init; get; } public DateTime CreationTime { init; get; } public DateTime? DateTime { init; get; } public DateTime? DateTimeDigitized { init; get; } diff --git a/Shared/Models/Property.cs b/Shared/Models/Property.cs index c8bec8a..db94eb3 100644 --- a/Shared/Models/Property.cs +++ b/Shared/Models/Property.cs @@ -6,6 +6,7 @@ namespace View_by_Distance.Shared.Models; public class Property : Properties.IProperty { + public string? BlurHash { init; get; } public DateTime CreationTime { init; get; } public DateTime? DateTime { init; get; } public DateTime? DateTimeDigitized { init; get; } @@ -22,8 +23,9 @@ public class Property : Properties.IProperty public int? Width { init; get; } [JsonConstructor] - public Property(DateTime creationTime, DateTime? dateTime, DateTime? dateTimeDigitized, DateTime? dateTimeFromName, DateTime? dateTimeOriginal, long fileSize, DateTime? gpsDateStamp, int? height, int? id, DateTime lastWriteTime, string? make, string? model, string? orientation, int? width) + public Property(string? blurHash, DateTime creationTime, DateTime? dateTime, DateTime? dateTimeDigitized, DateTime? dateTimeFromName, DateTime? dateTimeOriginal, long fileSize, DateTime? gpsDateStamp, int? height, int? id, DateTime lastWriteTime, string? make, string? model, string? orientation, int? width) { + BlurHash = blurHash; DateTimeFromName = dateTimeFromName; CreationTime = creationTime; DateTime = dateTime; diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 73334a3..ebfb3df 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -27,25 +27,25 @@ Linux - + - + - + + - diff --git a/Tests/UnitTestIsEnvironment.cs b/Tests/UnitTestIsEnvironment.cs index 060c367..3cfdae3 100644 --- a/Tests/UnitTestIsEnvironment.cs +++ b/Tests/UnitTestIsEnvironment.cs @@ -46,6 +46,13 @@ public class UnitTestIsEnvironment _ConfigurationRoot = configurationRoot; } + private static void NonThrowTryCatch() + { + try + { throw new Exception(); } + catch (Exception) { } + } + [TestMethod] public void TestMethodNull() { diff --git a/Tests/UnitTestResize.cs b/Tests/UnitTestResize.cs index d0f168c..0ef38dd 100644 --- a/Tests/UnitTestResize.cs +++ b/Tests/UnitTestResize.cs @@ -64,6 +64,13 @@ public class UnitTestResize _PropertyConfiguration = propertyConfiguration; } + private static void NonThrowTryCatch() + { + try + { throw new Exception(); } + catch (Exception) { } + } + [TestMethod] public void TestMethodNull() { @@ -74,6 +81,7 @@ public class UnitTestResize Assert.IsFalse(_WorkingDirectory is null); Assert.IsFalse(_ConfigurationRoot is null); Assert.IsFalse(_PropertyConfiguration is null); + NonThrowTryCatch(); } private A_Property GetPropertyLogic(bool reverse, string aResultsFullGroupDirectory) @@ -121,17 +129,8 @@ public class UnitTestResize [TestMethod] public void TestMethodResize() { - // string sourceFileName = "IMG_0067.jpg"; - // string sourceDirectoryName = "Mackenzie Prom 2017"; - // string sourceFileName = "Fall 2005 (113).jpg"; - // string sourceDirectoryName = "=2005.3 Fall"; - // string sourceFileName = "DSCN0534.jpg"; - // string sourceDirectoryName = "Logan Swimming Lessons 2013"; - // string sourceFileName = "DSC_4913.jpg"; - // string sourceDirectoryName = "Disneyland 2014"; - // string sourceFileName = "Logan Michael Sept 08 (193).jpg"; - // string sourceDirectoryName = "=2008.2 Summer Logan Michael"; - string sourceFileName = "Halloween 2006 (112).jpg"; + string sourceFileName = "640794601.jpg"; + // string sourceFileName = "input.jpg"; string sourceDirectoryName = "Halloween 2006"; Item item; bool reverse = false; @@ -145,7 +144,6 @@ public class UnitTestResize int length = _PropertyConfiguration.RootDirectory.Length; string outputResolution = _Configuration.OutputResolutions[0]; (string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution); - string sourceDirectory = Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName); (string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories(); _Logger.Information(_Configuration.ModelDirectory); A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory); @@ -160,16 +158,18 @@ public class UnitTestResize bool isUniqueFileName = false; bool? isNotUniqueAndNeedsReview = null; FileHolder sourceDirectoryFileHolder = new(".json"); + string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName)); FileHolder fileHolder = new(Path.Combine(sourceDirectory, sourceFileName)); string relativePath = IPath.GetRelativePath(fileHolder.FullName, length); - sourceDirectory = Path.Combine(aPropertySingletonDirectory, sourceDirectoryName); - propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory); + string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName)); + propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory); resize.SetAngleBracketCollection(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, sourceDirectory); item = new(sourceDirectoryFileHolder, relativePath, fileHolder, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, property, false, false, false); Assert.IsNotNull(item.ImageFileHolder); if (item.Property is null) { - property = propertyLogic.GetProperty(item, subFileTuples, parseExceptions); + Shared.Models.Methods.IBlurHasher? blurHasher = new BlurHash.Models.BlurHasher(); + property = propertyLogic.GetProperty(blurHasher, item, subFileTuples, parseExceptions); item.Update(property); } if (property is null || item.Property is null) @@ -181,6 +181,7 @@ public class UnitTestResize outputResolutionToResize = resize.GetResizeKeyValuePairs(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, subFileTuples, parseExceptions, metadataCollection, item.Property, mappingFromItem); Assert.IsNotNull(mappingFromItem.ResizedFileHolder); resize.SaveResizedSubfile(_Configuration.PropertyConfiguration, outputResolution, cResultsFullGroupDirectory, subFileTuples, item, item.Property, mappingFromItem, outputResolutionToResize); + NonThrowTryCatch(); } } \ No newline at end of file diff --git a/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj b/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj index d717f1c..2617c20 100644 --- a/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj +++ b/TestsWithFaceRecognitionDotNet/TestsWithFaceRecognitionDotNet.csproj @@ -27,19 +27,20 @@ Linux - + - + - + + diff --git a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs index 294dfb3..dbbae36 100644 --- a/TestsWithFaceRecognitionDotNet/UnitTestFace.cs +++ b/TestsWithFaceRecognitionDotNet/UnitTestFace.cs @@ -66,6 +66,13 @@ public class UnitTestFace _PropertyConfiguration = propertyConfiguration; } + private static void NonThrowTryCatch() + { + try + { throw new Exception(); } + catch (Exception) { } + } + [TestMethod] public void TestConfiguration() { @@ -73,6 +80,7 @@ public class UnitTestFace throw new Exception("Configuration has to match interface!"); if (_Configuration.LocationFactor != Shared.Models.Stateless.ILocation.Factor) throw new Exception("Configuration has to match interface!"); + NonThrowTryCatch(); } [TestMethod] @@ -85,6 +93,7 @@ public class UnitTestFace Assert.IsFalse(_WorkingDirectory is null); Assert.IsFalse(_ConfigurationRoot is null); Assert.IsFalse(_PropertyConfiguration is null); + NonThrowTryCatch(); } private A_Property GetPropertyLogic(bool reverse, string aResultsFullGroupDirectory) @@ -156,6 +165,7 @@ public class UnitTestFace Assert.IsTrue(checkB > checkA); int checkC = (int)(_Configuration.RangeDistanceTolerance[1] * DistanceFactor); Assert.IsTrue(checkC == ToleranceAfterFactor); + NonThrowTryCatch(); } private (string, string) GetResultsFullGroupDirectories() @@ -194,20 +204,8 @@ public class UnitTestFace [TestMethod] public void TestMethodFace() { - // string sourceFileName = "IMG_0067.jpg"; - // string sourceDirectoryName = "Mackenzie Prom 2017"; - // string sourceFileName = "Fall 2005 (113).jpg"; - // string sourceDirectoryName = "=2005.3 Fall"; - // string sourceFileName = "DSCN0534.jpg"; - // string sourceDirectoryName = "Logan Swimming Lessons 2013"; - // string sourceFileName = "DSC_4913.jpg"; - // string sourceDirectoryName = "Disneyland 2014"; - // string sourceFileName = "Logan Michael Sept 08 (193).jpg"; - // string sourceDirectoryName = "=2008.2 Summer Logan Michael"; - // string sourceFileName = "Halloween 2006 (112).jpg"; - // string sourceDirectoryName = "Halloween 2006"; - string sourceFileName = "1384160978.jpg"; - string sourceDirectoryName = "zzz Portrait Innovations Files 2007"; + string sourceFileName = "640794601.jpg"; + string sourceDirectoryName = "Halloween 2006"; Item item; bool reverse = false; FileHolder resizedFileHolder; @@ -220,7 +218,6 @@ public class UnitTestFace int length = _PropertyConfiguration.RootDirectory.Length; string outputResolution = _Configuration.OutputResolutions[0]; (string cResultsFullGroupDirectory, _, _) = GetResultsFullGroupDirectories(outputResolution); - string sourceDirectory = Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName); (string aResultsFullGroupDirectory, string bResultsFullGroupDirectory) = GetResultsFullGroupDirectories(); _Logger.Information(_Configuration.ModelDirectory); A_Property propertyLogic = GetPropertyLogic(reverse, aResultsFullGroupDirectory); @@ -234,18 +231,19 @@ public class UnitTestFace _ = resize.ToString(); bool isUniqueFileName = false; bool? isNotUniqueAndNeedsReview = null; - bool anyNullOrNoIsUniqueFileName = true; FileHolder sourceDirectoryFileHolder = new(".json"); + string sourceDirectory = Path.GetFullPath(Path.Combine(_PropertyConfiguration.RootDirectory, sourceDirectoryName)); FileHolder fileHolder = new(Path.Combine(sourceDirectory, sourceFileName)); string relativePath = IPath.GetRelativePath(fileHolder.FullName, length); - sourceDirectory = Path.Combine(aPropertySingletonDirectory, sourceDirectoryName); - propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); + string propertyLogicSourceDirectory = Path.GetFullPath(Path.Combine(aPropertySingletonDirectory, sourceDirectoryName)); + propertyLogic.SetAngleBracketCollection(aResultsFullGroupDirectory, propertyLogicSourceDirectory); resize.SetAngleBracketCollection(_Configuration.PropertyConfiguration, cResultsFullGroupDirectory, sourceDirectory); item = new(sourceDirectoryFileHolder, relativePath, fileHolder, isNotUniqueAndNeedsReview, isUniqueFileName, isValidImageFormatExtension, property, false, false, false); Assert.IsNotNull(item.ImageFileHolder); if (item.Property is null) { - property = propertyLogic.GetProperty(item, subFileTuples, parseExceptions); + Shared.Models.Methods.IBlurHasher? blurHasher = new BlurHash.Models.BlurHasher(); + property = propertyLogic.GetProperty(blurHasher, item, subFileTuples, parseExceptions); item.Update(property); } if (property is null || item.Property is null) @@ -268,6 +266,7 @@ public class UnitTestFace List faceDistanceLengths = FaceRecognition.FaceDistances(faceDistanceEncodings, faceDistanceEncodings[0]); Assert.IsTrue(faceDistanceLengths.Count == 2); Assert.IsNotNull(sourceFileName); + NonThrowTryCatch(); } } \ No newline at end of file diff --git a/View-by-Distance-MKLink-Console.sln b/View-by-Distance-MKLink-Console.sln index e14d3af..77953b1 100644 --- a/View-by-Distance-MKLink-Console.sln +++ b/View-by-Distance-MKLink-Console.sln @@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlurHash", "BlurHash\BlurHash.csproj", "{9689371E-F67C-4392-A636-C398D28C089B}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Date-Group", "Date-Group\Date-Group.csproj", "{DFEDB5F9-AFFC-40A2-9FEC-9B84C83B63D9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Delete-By-Distinct", "Delete-By-Distinct\Delete-By-Distinct.csproj", "{3F00BDD5-75F8-470C-ACED-1A26FDC8D7B3}" @@ -13,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Distance", "Distance\Distan EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Explorer", "Drag-Drop-Explorer\Drag-Drop-Explorer.csproj", "{986B009B-2937-4624-AC9C-13806868DB8C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Move", "Drag-Drop-Move\Drag-Drop-Move.csproj", "{CF05EFAC-C212-4EE0-A644-3F728991AA54}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Search", "Drag-Drop-Search\Drag-Drop-Search.csproj", "{87EB76BC-32A9-4FD0-922A-BD7E9B6E7D8B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicate-Search", "Duplicate-Search\Duplicate-Search.csproj", "{48E87D9B-B802-467A-BDC7-E86F7FD01D5C}" @@ -29,6 +33,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Map", "Map\Map.csproj", "{9 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Metadata", "Metadata\Metadata.csproj", "{961D11A0-44C8-48CD-BEEE-A6E6903AE58F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Move-By-Id", "Move-By-Id\Move-By-Id.csproj", "{0FDFBC71-3801-483F-A4AC-CC8CF857D54F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Person", "Person\Person.csproj", "{C5003A39-334B-444B-9873-39B26E58D667}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhotoPrism", "PhotoPrism\PhotoPrism.csproj", "{DF4B0776-E0E5-4220-8721-8D1E491FF263}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrepareForOld", "PrepareForOld\PrepareForOld.csproj", "{F73F9468-0139-4B05-99CE-C6C0403D03E5}" @@ -47,12 +55,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestsWithFaceRecognitionDot EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "View-by-Distance.Shared", "shared\View-by-Distance.Shared.csproj", "{1D231660-33B4-4763-9C9F-C6ACC8BA600D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Move-By-Id", "Move-By-Id\Move-By-Id.csproj", "{0FDFBC71-3801-483F-A4AC-CC8CF857D54F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Person", "Person\Person.csproj", "{C5003A39-334B-444B-9873-39B26E58D667}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Drag-Drop-Move", "Drag-Drop-Move\Drag-Drop-Move.csproj", "{CF05EFAC-C212-4EE0-A644-3F728991AA54}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -166,5 +168,9 @@ Global {CF05EFAC-C212-4EE0-A644-3F728991AA54}.Debug|Any CPU.Build.0 = Debug|Any CPU {CF05EFAC-C212-4EE0-A644-3F728991AA54}.Release|Any CPU.ActiveCfg = Release|Any CPU {CF05EFAC-C212-4EE0-A644-3F728991AA54}.Release|Any CPU.Build.0 = Release|Any CPU + {9689371E-F67C-4392-A636-C398D28C089B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9689371E-F67C-4392-A636-C398D28C089B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9689371E-F67C-4392-A636-C398D28C089B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9689371E-F67C-4392-A636-C398D28C089B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal