Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebP support #1813

Merged
merged 6 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions history.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ 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)

## version 0.6.2 - 2024-10-11 {#v0.6.2}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace starsky.foundation.platform.Helpers;
public static partial class ExtensionRolesHelper
{
/// <summary>
/// ImageFormat based on first bytes
/// ImageFormat based on first bytes, so read first bytes
/// </summary>
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum ImageFormat
Expand All @@ -25,6 +25,7 @@ public enum ImageFormat
bmp = 13,
gif = 14,
png = 15,
webp = 16,

// Sidecar files
xmp = 30,
Expand Down Expand Up @@ -109,6 +110,11 @@ private static readonly List<string>
/// </summary>
private static readonly List<string> ExtensionMp4 = new() { "mp4", "mov" };

/// <summary>
/// WebP imageFormat
/// </summary>
private static readonly List<string> ExtensionWebp = new() { "webp" };

private static readonly Dictionary<ImageFormat, List<string>>
MapFileTypesToExtensionDictionary =
new()
Expand All @@ -120,7 +126,8 @@ private static readonly Dictionary<ImageFormat, List<string>>
{ ImageFormat.png, ExtensionPng },
{ ImageFormat.gpx, ExtensionGpx },
{ ImageFormat.mp4, ExtensionMp4 },
{ ImageFormat.xmp, ExtensionXmp }
{ ImageFormat.xmp, ExtensionXmp },
{ ImageFormat.webp, ExtensionWebp }
};

/// <summary>
Expand All @@ -141,6 +148,7 @@ public static List<string> ExtensionSyncSupportedList
extensionList.AddRange(ExtensionMp4);
extensionList.AddRange(ExtensionXmp);
extensionList.AddRange(ExtensionJsonSidecar);
extensionList.AddRange(ExtensionWebp);
return extensionList;
}
}
Expand All @@ -159,6 +167,7 @@ private static List<string> ExtensionExifToolSupportedList
extensionList.AddRange(ExtensionGif);
extensionList.AddRange(ExtensionPng);
extensionList.AddRange(ExtensionMp4);
extensionList.AddRange(ExtensionWebp);
return extensionList;
}
}
Expand All @@ -180,6 +189,7 @@ public static List<string> ExtensionThumbSupportedList
extensionList.AddRange(ExtensionBmp);
extensionList.AddRange(ExtensionGif);
extensionList.AddRange(ExtensionPng);
extensionList.AddRange(ExtensionWebp);
return extensionList;
}
}
Expand Down Expand Up @@ -441,7 +451,7 @@ private static byte[] ReadBuffer(Stream stream, int size)

/// <summary>
/// Get the format of the image by looking the first bytes
/// Stream is Flushed / Disposed afterwards
/// Stream is Flushed / Disposed afterward
/// </summary>
/// <param name="stream">stream</param>
/// <returns>ImageFormat enum</returns>
Expand Down Expand Up @@ -525,9 +535,30 @@ public static ImageFormat GetImageFormat(byte[] bytes)
return ImageFormat.meta_json;
}

if ( GetImageFormatMetaWebp(bytes) != null )
{
return ImageFormat.webp;
}

return ImageFormat.unknown;
}

private static ImageFormat? GetImageFormatMetaWebp(byte[] bytes)
{
var webpFirstPart = new byte[] { 82, 73, 70, 70 };
var webpSecondPart = new byte[] { 87, 69, 66, 80 };

var isFirstPart = webpFirstPart.SequenceEqual(bytes.Take(webpFirstPart.Length));
var isSecondPart = webpSecondPart.SequenceEqual(bytes.Skip(8).Take(webpSecondPart.Length));

if ( isFirstPart && isSecondPart )
{
return ImageFormat.webp;
}

return null;
}

private static ImageFormat? GetImageFormatMetaJson(byte[] bytes)
{
var metaJsonUnix = new byte[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@ private static ExtensionRolesHelper.ImageFormat GetFileSpecificTags(
return ExtensionRolesHelper.ImageFormat.gif;
}

if ( allExifItems.Exists(p => p.Name == "WebP") )
{
return ExtensionRolesHelper.ImageFormat.webp;
}

return ExtensionRolesHelper.ImageFormat.unknown;
}

Expand Down Expand Up @@ -565,13 +570,13 @@ private static string GetXmpData(Directory? exifItem, string propertyPath)
if ( !string.IsNullOrEmpty(xmpTitle) )
{
return xmpTitle;
}
}

var iptcDirectory = allExifItems.OfType<IptcDirectory>().FirstOrDefault();
var iptcObjectName = iptcDirectory?.Tags.FirstOrDefault(
p => p.Name == "Object Name")?.Description;
iptcObjectName ??= string.Empty;

return iptcObjectName;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const ListImage: React.FunctionComponent<IListImageProps> = memo((props) => {
props.imageFormat !== ImageFormat.bmp &&
props.imageFormat !== ImageFormat.gif &&
props.imageFormat !== ImageFormat.jpg &&
props.imageFormat !== ImageFormat.webp &&
props.imageFormat !== ImageFormat.png
) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,10 @@ describe("PreferencesAppSettingsDesktop", () => {

await waitFor(() => {
const query =
"DefaultDesktopEditor%5B0%5D.ImageFormats%5B0%5D=jpg&DefaultDesktopEditor%5B0%5D.ImageFormats%" +
"5B1%5D=png&DefaultDesktopEditor%5B0%5D.ImageFormats%5B2%5D=bmp&DefaultDesktopEditor%5B0%5D.ImageFormats%5B3%5D=tiff&" +
"DefaultDesktopEditor%5B0%5D.ApplicationPath=test";
"DefaultDesktopEditor%5B0%5D.ImageFormats%5B0%5D=jpg&DefaultDesktopEditor%5B0%5D." +
"ImageFormats%5B1%5D=png&DefaultDesktopEditor%5B0%5D.ImageFormats%5B2%5D=bmp&" +
"DefaultDesktopEditor%5B0%5D.ImageFormats%5B3%5D=tiff&DefaultDesktopEditor%5B0%5D." +
"ImageFormats%5B4%5D=webp&DefaultDesktopEditor%5B0%5D.ApplicationPath=test";

expect(spyFetchPost).toHaveBeenCalledTimes(1);
expect(spyFetchPost).toHaveBeenCalledWith(new UrlQuery().UrlApiAppSettings(), query);
Expand Down Expand Up @@ -354,13 +355,15 @@ describe("updateDefaultEditorPhotos", () => {

expect(spyFetchPost).toHaveBeenCalled();
const query =
"DefaultDesktopEditor%5B0%5D.ImageFormats%5B0%5D=jpg&DefaultDesktopEditor%5B0%5D.ImageFormats%" +
"5B1%5D=png&DefaultDesktopEditor%5B0%5D.ImageFormats%5B2%5D=bmp&DefaultDesktopEditor%5B0%5D.ImageFormats%5B3%5D=tiff&" +
"DefaultDesktopEditor%5B0%5D.ApplicationPath=test";
"DefaultDesktopEditor%5B0%5D.ImageFormats%5B0%5D=jpg&DefaultDesktopEditor%5B0%5D.ImageFormats" +
"%5B1%5D=png&DefaultDesktopEditor%5B0%5D.ImageFormats%5B2%5D=bmp&DefaultDesktopEditor%5B0%5D." +
"ImageFormats%5B3%5D=tiff&DefaultDesktopEditor%5B0%5D.ImageFormats%5B4%5D=webp&DefaultDesktopEditor%5B0%5D." +
"ApplicationPath=test";

expect(spyFetchPost).toHaveBeenCalledWith(new UrlQuery().UrlApiAppSettings(), query);
});

it("Create new item in Array if emthy array", async () => {
it("should update existing editor and add new editor if array is not empty", async () => {
const value = {
target: { innerText: "test" }
} as unknown as ChangeEvent<HTMLDivElement>;
Expand Down Expand Up @@ -388,10 +391,14 @@ describe("updateDefaultEditorPhotos", () => {
expect(spyFetchPost).toHaveBeenCalled();

const query2 =
"DefaultDesktopEditor%5B0%5D.ImageFormats%5B0%5D=jpg&DefaultDesktopEditor%5B0%5D.ImageFormats%5B1%5D=png&DefaultDesktopEditor" +
"%5B0%5D.ImageFormats%5B2%5D=bmp&DefaultDesktopEditor%5B0%5D.ImageFormats%5B3%5D=tiff&DefaultDesktopEditor%5B0%5D.ApplicationPath=%2Fexist_app&" +
"DefaultDesktopEditor%5B1%5D.ImageFormats%5B0%5D=jpg&DefaultDesktopEditor%5B1%5D.ImageFormats%5B1%5D=png&DefaultDesktopEditor%5B1%5D.ImageFormats%5B2%5D=bmp&" +
"DefaultDesktopEditor%5B1%5D.ImageFormats%5B3%5D=tiff&DefaultDesktopEditor%5B1%5D.ApplicationPath=test";
"DefaultDesktopEditor%5B0%5D.ImageFormats%5B0%5D=jpg&DefaultDesktopEditor%5B0%5D." +
"ImageFormats%5B1%5D=png&DefaultDesktopEditor%5B0%5D.ImageFormats%5B2%5D=bmp&" +
"DefaultDesktopEditor%5B0%5D.ImageFormats%5B3%5D=tiff&" +
"DefaultDesktopEditor%5B0%5D.ImageFormats%5B4%5D=webp&DefaultDesktopEditor" +
"%5B0%5D.ApplicationPath=%2Fexist_app&DefaultDesktopEditor%5B1%5D.ImageFormats%5B0%5D=jpg" +
"&DefaultDesktopEditor%5B1%5D.ImageFormats%5B1%5D=png&DefaultDesktopEditor%5B1%5D.ImageFormats%5B2%5D=bmp" +
"&DefaultDesktopEditor%5B1%5D.ImageFormats%5B3%5D=tiff&DefaultDesktopEditor%5B1%5D.ImageFormats%5B4%5D=webp" +
"&DefaultDesktopEditor%5B1%5D.ApplicationPath=test";

expect(spyFetchPost).toHaveBeenCalledWith(new UrlQuery().UrlApiAppSettings(), query2);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@ import FormControl from "../../atoms/form-control/form-control";
import SwitchButton from "../../atoms/switch-button/switch-button";

const defaultEditorApplication = {
imageFormats: [ImageFormat.jpg, ImageFormat.png, ImageFormat.bmp, ImageFormat.tiff]
imageFormats: [
ImageFormat.jpg,
ImageFormat.png,
ImageFormat.bmp,
ImageFormat.tiff,
ImageFormat.webp
]
} as IAppSettingsDefaultEditorApplication;

export async function UpdateDefaultEditorPhotos(
Expand Down
1 change: 1 addition & 0 deletions starsky/starsky/clientapp/src/interfaces/IFileIndexItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export enum ImageFormat {
bmp = "bmp",
gif = "gif",
png = "png",
webp = "webp",
xmp = "xmp",
meta_json = "meta_json",
gpx = "gpx",
Expand Down
Loading