diff --git a/history.md b/history.md index cf28a91cb5..80bfcc85f4 100644 --- a/history.md +++ b/history.md @@ -41,7 +41,7 @@ Semantic Versioning 2.0.0 is from version 0.1.6+ ## List of versions -## version 0.6.3 - _(Unreleased)_ - 2024-08-?? {#v0.6.3} +## version 0.6.3 - _(Unreleased)_ - 2024-11-?? {#v0.6.3} - [x] (Fixed) _Front-end_ OkAndSame status in Upload Modal gives the wrong status (PR #1783) - [x] (Changed) _Back-end_ Behavior of generating thumbnails on the background (PR #1780) @@ -63,7 +63,9 @@ Semantic Versioning 2.0.0 is from version 0.1.6+ - [x] (Fixed) _Front-end_ increase description limit (Issue #1810) (PR #1814) - [x] (Fixed) _Back-end_ Change reading order to favor XMP for description/title field (PR #1814) - [x] (Fixed) _Back-end_ OrderBy ImageFormat and then alphabet (PR #1815) -- [x] (Added) _Back-end_ WebP support for sync, reading & writing (PR #1813) +- [x] (Added) _Back-end_ WebP support for sync, thumbnails, reading & writing (PR #1813) +- [x] (Added) _Back-end_ Psd support for sync, reading & writing (no thumbnail) (PR #1817) +- [x] (Added) _Front-end_ Cache display issue with fileName contains (PR #1817) ## version 0.6.2 - 2024-10-11 {#v0.6.2} diff --git a/starsky/starsky.foundation.platform/Helpers/ExtensionRolesHelper.cs b/starsky/starsky.foundation.platform/Helpers/ExtensionRolesHelper.cs index de3d2fa289..a918d33c96 100644 --- a/starsky/starsky.foundation.platform/Helpers/ExtensionRolesHelper.cs +++ b/starsky/starsky.foundation.platform/Helpers/ExtensionRolesHelper.cs @@ -26,6 +26,7 @@ public enum ImageFormat gif = 14, png = 15, webp = 16, + psd = 17, // Sidecar files xmp = 30, @@ -115,6 +116,11 @@ private static readonly List /// private static readonly List ExtensionWebp = new() { "webp" }; + /// + /// Psd imageFormat + /// + private static readonly List ExtensionPsd = new() { "psd" }; + private static readonly Dictionary> MapFileTypesToExtensionDictionary = new() @@ -127,7 +133,8 @@ private static readonly Dictionary> { ImageFormat.gpx, ExtensionGpx }, { ImageFormat.mp4, ExtensionMp4 }, { ImageFormat.xmp, ExtensionXmp }, - { ImageFormat.webp, ExtensionWebp } + { ImageFormat.webp, ExtensionWebp }, + { ImageFormat.psd, ExtensionPsd } }; /// @@ -149,6 +156,7 @@ public static List ExtensionSyncSupportedList extensionList.AddRange(ExtensionXmp); extensionList.AddRange(ExtensionJsonSidecar); extensionList.AddRange(ExtensionWebp); + extensionList.AddRange(ExtensionPsd); return extensionList; } } @@ -168,6 +176,7 @@ private static List ExtensionExifToolSupportedList extensionList.AddRange(ExtensionPng); extensionList.AddRange(ExtensionMp4); extensionList.AddRange(ExtensionWebp); + extensionList.AddRange(ExtensionPsd); return extensionList; } } @@ -540,9 +549,26 @@ public static ImageFormat GetImageFormat(byte[] bytes) return ImageFormat.webp; } + if ( GetImageFormatPsd(bytes) != null ) + { + return ImageFormat.psd; + } + return ImageFormat.unknown; } + private static ImageFormat? GetImageFormatPsd(byte[] bytes) + { + // HEX 38 42 50 53 + var psd = new byte[] { 56, 66, 80, 83 }; + if ( psd.SequenceEqual(bytes.Take(psd.Length)) ) + { + return ImageFormat.psd; + } + + return null; + } + private static ImageFormat? GetImageFormatMetaWebp(byte[] bytes) { var webpFirstPart = new byte[] { 82, 73, 70, 70 }; diff --git a/starsky/starsky/clientapp/src/hooks/use-filelist.ts b/starsky/starsky/clientapp/src/hooks/use-filelist.ts index 0d6816e30f..9789c68b57 100644 --- a/starsky/starsky/clientapp/src/hooks/use-filelist.ts +++ b/starsky/starsky/clientapp/src/hooks/use-filelist.ts @@ -74,7 +74,7 @@ const fetchUseFileListContentCache = async ( const content = new FileListCache().CacheGet(locationSearch); if (content) { console.log( - ` -- Fetch Content ${new Date(content.dateCache).toLocaleTimeString()} ${locationSearch} -- ` + ` -- Cache Content ${new Date(content.dateCache).toLocaleTimeString()} ${locationSearch} -- ` ); setPageTypeHelper(content); } else { diff --git a/starsky/starsky/clientapp/src/interfaces/IFileIndexItem.ts b/starsky/starsky/clientapp/src/interfaces/IFileIndexItem.ts index 91c0ef074f..25d0cc5661 100644 --- a/starsky/starsky/clientapp/src/interfaces/IFileIndexItem.ts +++ b/starsky/starsky/clientapp/src/interfaces/IFileIndexItem.ts @@ -45,6 +45,7 @@ export enum ImageFormat { gif = "gif", png = "png", webp = "webp", + psd = "psd", xmp = "xmp", meta_json = "meta_json", gpx = "gpx", diff --git a/starsky/starsky/clientapp/src/shared/filelist-cache.spec.ts b/starsky/starsky/clientapp/src/shared/filelist-cache.spec.ts index 573caa5716..933e8a0638 100644 --- a/starsky/starsky/clientapp/src/shared/filelist-cache.spec.ts +++ b/starsky/starsky/clientapp/src/shared/filelist-cache.spec.ts @@ -1,6 +1,8 @@ import { IArchive, newIArchive, SortType } from "../interfaces/IArchive"; -import { newDetailView, PageType } from "../interfaces/IDetailView"; -import { newIFileIndexItem } from "../interfaces/IFileIndexItem"; +import { IDetailView, newDetailView, PageType } from "../interfaces/IDetailView"; +import { IExifStatus } from "../interfaces/IExifStatus"; +import { IFileIndexItem, newIFileIndexItem } from "../interfaces/IFileIndexItem"; +import { IUrl } from "../interfaces/IUrl"; import { FileListCache } from "./filelist-cache"; describe("FileListCache", () => { @@ -200,6 +202,108 @@ describe("FileListCache", () => { expect(imageFormatStorageKey === fileNameStorageKey).toBeFalsy(); }); + it("should not confuse 20241106_171136_DSC00389_e2.jpg and 20241106_171136_DSC00389.psd", () => { + const urlObject: IUrl = { + f: "/__REPLACE__", + collections: false, + colorClass: [], + sort: undefined + }; + + const parentDirectory = "/test"; + + const fileIndexItems: IFileIndexItem[] = [ + { + filePath: "/test/20241106_171136_DSC00389_e2.jpg", + fileName: "20241106_171136_DSC00389_e2.jpg", + fileCollectionName: "20241106_171136_DSC00389_e2", + fileHash: "", + status: IExifStatus.Ok, + isDirectory: false, + parentDirectory + }, + { + filePath: "/test/20241106_171136_DSC00389.psd", + fileName: "20241106_171136_DSC00389.psd", + fileCollectionName: "20241106_171136_DSC00389", + fileHash: "", + status: IExifStatus.Ok, + isDirectory: false, + parentDirectory + } + ]; + + const parentItem: IArchive = { + subPath: "/test", + fileIndexItems, + pageType: PageType.Archive, + breadcrumb: [], + relativeObjects: { + nextFilePath: "", + prevFilePath: "", + nextHash: "", + prevHash: "", + args: [""] + }, + colorClassActiveList: [], + colorClassUsage: [], + collectionsCount: 0, + isReadOnly: false, + dateCache: Date.now() + }; + + const detailViewItem: IDetailView = { + fileIndexItem: { + filePath: "/test/20241106_171136_DSC00389_e2.jpg", + fileCollectionName: "20241106_171136_DSC00389_e2", + fileHash: "", + fileName: "20241106_171136_DSC00389_e2.jpg", + status: IExifStatus.Ok, + isDirectory: false, + parentDirectory + }, + subPath: "", + pageType: PageType.DetailView, + breadcrumb: [], + relativeObjects: { + nextFilePath: "", + prevFilePath: "", + nextHash: "", + prevHash: "", + args: [""] + }, + colorClassActiveList: [], + isReadOnly: false, + dateCache: Date.now() + }; + + // Set the parent item in the cache + fileListCache.CacheSetObject({ ...urlObject, f: "/test" }, parentItem); + + // Update the detail view item in the cache + fileListCache.CacheSetObject( + { ...urlObject, f: "/test/20241106_171136_DSC00389_e2.jpg" }, + detailViewItem + ); + + // Retrieve the updated parent item from the cache + const updatedParentItem = fileListCache.CacheGetObject({ + ...urlObject, + f: parentDirectory + }) as IArchive; + + // Ensure the correct item was updated + const updatedItem = updatedParentItem.fileIndexItems.find( + (item) => item.fileName === "20241106_171136_DSC00389_e2.jpg" + ); + const notUpdatedItem = updatedParentItem.fileIndexItems.find( + (item) => item.fileName === "20241106_171136_DSC00389.psd" + ); + + expect(updatedItem).toEqual(detailViewItem.fileIndexItem); + expect(notUpdatedItem).toEqual(fileIndexItems[1]); + }); + it("ignore when old", () => { sessionStorage.setItem( fileListCache.CacheKeyGenerator({ diff --git a/starsky/starsky/clientapp/src/shared/filelist-cache.ts b/starsky/starsky/clientapp/src/shared/filelist-cache.ts index d738e16171..b6673bf232 100644 --- a/starsky/starsky/clientapp/src/shared/filelist-cache.ts +++ b/starsky/starsky/clientapp/src/shared/filelist-cache.ts @@ -90,7 +90,7 @@ export class FileListCache { } } if (urlObject.collections) { - if (item?.fileName.startsWith(detailview.fileIndexItem.fileCollectionName)) { + if (item?.fileCollectionName === detailview.fileIndexItem.fileCollectionName) { parentItem.fileIndexItems[index] = detailview.fileIndexItem; } } diff --git a/starsky/starsky/clientapp/src/style/css/21-archive-folder.css b/starsky/starsky/clientapp/src/style/css/21-archive-folder.css index 0289dc7875..0a7f3ae909 100644 --- a/starsky/starsky/clientapp/src/style/css/21-archive-folder.css +++ b/starsky/starsky/clientapp/src/style/css/21-archive-folder.css @@ -306,10 +306,6 @@ between 1 and 2 items background-image: url("../images/baseline-file-jpg-24px.svg"); } -.list-image-box > .box-content.isDirectory-false > .img-box--webp { - background-image: url("../images/baseline-file-outline.svg"); -} - .list-image-box > .box-content.isDirectory-false > .img-box--xmp { background-image: url("../images/baseline-file-xmp-24px.svg"); } @@ -322,6 +318,11 @@ between 1 and 2 items background-image: url("../images/baseline-file-png-24px.svg"); } +.list-image-box > .box-content.isDirectory-false > .img-box--psd, +.list-image-box > .box-content.isDirectory-false > .img-box--webp { + background-image: url("../images/baseline-file-outline.svg"); +} + @media (prefers-color-scheme: dark) { .list-image-box > .box-content.isDirectory-false > .img-box--error, .list-image-box > .box-content.isDirectory-false > .img-box--gpx, @@ -330,7 +331,8 @@ between 1 and 2 items .list-image-box > .box-content.isDirectory-false > .img-box--jpg, .list-image-box > .box-content.isDirectory-false > .img-box--bmp, .list-image-box > .box-content.isDirectory-false > .img-box--png, - .list-image-box > .box-content.isDirectory-false > .img-box--webp { + .list-image-box > .box-content.isDirectory-false > .img-box--webp, + .list-image-box > .box-content.isDirectory-false > .img-box--psd { filter: invert(100%); } } diff --git a/starsky/starskytest/FakeCreateAn/CreateAnImagePsd/CreateAnImagePsd.cs b/starsky/starskytest/FakeCreateAn/CreateAnImagePsd/CreateAnImagePsd.cs new file mode 100644 index 0000000000..c830eeaa87 --- /dev/null +++ b/starsky/starskytest/FakeCreateAn/CreateAnImagePsd/CreateAnImagePsd.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Immutable; +using System.IO; +using System.Reflection; +using starsky.foundation.storage.Storage; +using starskytest.FakeMocks; + +namespace starskytest.FakeCreateAn.CreateAnImagePsd; + +public class CreateAnImagePsd +{ + public readonly ImmutableArray Bytes = [..Array.Empty()]; + + public CreateAnImagePsd() + { + var dirName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + if ( string.IsNullOrEmpty(dirName) ) + { + return; + } + + var path = Path.Combine(dirName, "FakeCreateAn", + "CreateAnImagePsd", "test.psd"); + + Bytes = [..StreamToBytes(path)]; + } + + private static byte[] StreamToBytes(string path) + { + var input = new StorageHostFullPathFilesystem(new FakeIWebLogger()).ReadStream(path); + using var ms = new MemoryStream(); + input.CopyTo(ms); + input.Dispose(); + return ms.ToArray(); + } +} diff --git a/starsky/starskytest/FakeCreateAn/CreateAnImagePsd/test.psd b/starsky/starskytest/FakeCreateAn/CreateAnImagePsd/test.psd new file mode 100644 index 0000000000..24b23543f8 Binary files /dev/null and b/starsky/starskytest/FakeCreateAn/CreateAnImagePsd/test.psd differ diff --git a/starsky/starskytest/starsky.foundation.platform/Helpers/ExtensionRolesHelperTest.cs b/starsky/starskytest/starsky.foundation.platform/Helpers/ExtensionRolesHelperTest.cs index 934688a845..1b3909e0c1 100644 --- a/starsky/starskytest/starsky.foundation.platform/Helpers/ExtensionRolesHelperTest.cs +++ b/starsky/starskytest/starsky.foundation.platform/Helpers/ExtensionRolesHelperTest.cs @@ -5,6 +5,7 @@ using starsky.foundation.platform.Helpers; using starskytest.FakeCreateAn; using starskytest.FakeCreateAn.CreateAnImageCorrupt; +using starskytest.FakeCreateAn.CreateAnImagePsd; using starskytest.FakeCreateAn.CreateAnImageWebP; namespace starskytest.starsky.foundation.platform.Helpers; @@ -511,7 +512,7 @@ public void Gpx_CreateAnGpx() } [TestMethod] - public void Gpx_CreateAnWebP() + public void WebP_CreateAnWebP() { var createAnImage = new CreateAnImageWebP().Bytes.ToArray(); var result = ExtensionRolesHelper.GetImageFormat(createAnImage); @@ -538,6 +539,23 @@ public void UnknownFileFormat_Hex(string hexValue) Assert.AreEqual(ExtensionRolesHelper.ImageFormat.unknown, fileType); } + [TestMethod] + public void Psd_CreateAnPsd() + { + var createAnImage = new CreateAnImagePsd().Bytes.ToArray(); + var result = ExtensionRolesHelper.GetImageFormat(createAnImage); + Assert.AreEqual(ExtensionRolesHelper.ImageFormat.psd, result); + } + + [TestMethod] + public void Files_GetImageFormat_PsdHex() + { + var fileType = ExtensionRolesHelper.GetImageFormat( + ExtensionRolesHelper.HexStringToByteArray( + "38 42 50 53".Replace(" ", ""))); + Assert.AreEqual(ExtensionRolesHelper.ImageFormat.psd, fileType); + } + [TestMethod] public void StringToByteArrayTest() { diff --git a/starsky/starskytest/starskytest.csproj b/starsky/starskytest/starskytest.csproj index 96eb29710a..96e304a09a 100644 --- a/starsky/starskytest/starskytest.csproj +++ b/starsky/starskytest/starskytest.csproj @@ -26,8 +26,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -143,9 +143,13 @@ PreserveNewest - + - PreserveNewest + PreserveNewest + + + + PreserveNewest