diff --git a/DBMerger/DBMerger.csproj b/DBMerger/DBMerger.csproj index 99e91aef..9f7e0f56 100644 --- a/DBMerger/DBMerger.csproj +++ b/DBMerger/DBMerger.csproj @@ -43,7 +43,7 @@ - + diff --git a/Dotnet/ScreenshotMetadata/ScreenshotHelper.cs b/Dotnet/ScreenshotMetadata/ScreenshotHelper.cs index 670744be..f3490376 100644 --- a/Dotnet/ScreenshotMetadata/ScreenshotHelper.cs +++ b/Dotnet/ScreenshotMetadata/ScreenshotHelper.cs @@ -170,15 +170,15 @@ namespace VRCX { var xmlString = metadataString.Substring(xmlIndex); // everything after index - var result = ParseVRCPrint(xmlString.Substring(xmlIndex - 7)); + var result = ParseVRCImage(xmlString); result.SourceFile = path; return result; } catch (Exception ex) { - Logger.Error(ex, "Failed to parse VRCPrint XML metadata for file '{0}'", path); - return ScreenshotMetadata.JustError(path, "Failed to parse VRCPrint metadata."); + Logger.Error(ex, "Failed to parse VRC image XML metadata for file '{0}'", path); + return ScreenshotMetadata.JustError(path, "Failed to parse VRC image metadata."); } } @@ -204,7 +204,7 @@ namespace VRCX } } - public static ScreenshotMetadata ParseVRCPrint(string xmlString) + public static ScreenshotMetadata ParseVRCImage(string xmlString) { var doc = new XmlDocument(); doc.LoadXml(xmlString); @@ -217,10 +217,22 @@ namespace VRCX nsManager.AddNamespace("dc", "http://purl.org/dc/elements/1.1/"); nsManager.AddNamespace("vrc", "http://ns.vrchat.com/vrc/1.0/"); var creatorTool = root.SelectSingleNode("//xmp:CreatorTool", nsManager)?.InnerText; - var authorId = root.SelectSingleNode("//xmp:Author", nsManager)?.InnerText; + var authorName = root.SelectSingleNode("//xmp:Author", nsManager)?.InnerText; // legacy, it was authorId var dateTime = root.SelectSingleNode("//tiff:DateTime", nsManager)?.InnerText; var note = root.SelectSingleNode("//dc:title/rdf:Alt/rdf:li", nsManager)?.InnerText; - var worldId = root.SelectSingleNode("//vrc:World", nsManager)?.InnerText; + var worldId = root.SelectSingleNode("//vrc:WorldID", nsManager)?.InnerText; + var worldDisplayName = root.SelectSingleNode("//vrc:WorldDisplayName", nsManager)?.InnerText; // new, 01.08.2025 + var authorId = root.SelectSingleNode("//vrc:AuthorID", nsManager)?.InnerText; // new, 01.08.2025 + + if (string.IsNullOrEmpty(worldId)) + worldId = root.SelectSingleNode("//vrc:World", nsManager)?.InnerText; // legacy, it's gone now + + if (string.IsNullOrEmpty(authorId)) + { + // If authorId is not set, we assume legacy metadata format where authorName is used as authorId. + authorId = authorName; + authorName = null; + } return new ScreenshotMetadata { @@ -229,13 +241,13 @@ namespace VRCX Author = new ScreenshotMetadata.AuthorDetail { Id = authorId, - DisplayName = null + DisplayName = authorName }, World = new ScreenshotMetadata.WorldDetail { Id = worldId, InstanceId = worldId, - Name = null + Name = worldDisplayName }, Timestamp = DateTime.TryParse(dateTime, out var dt) ? dt : null, Note = note @@ -254,16 +266,21 @@ namespace VRCX /// public static bool WritePNGDescription(string path, string text) { - if (!File.Exists(path) || !IsPNGFile(path)) return false; + if (!File.Exists(path) || !IsPNGFile(path)) + return false; var png = File.ReadAllBytes(path); - var newChunkIndex = FindEndOfChunk(png, "IHDR"); - if (newChunkIndex == -1) return false; + if (newChunkIndex == -1) + return false; // If this file already has a text chunk, chances are it got logged twice for some reason. Stop. - // var existingiTXt = FindChunkIndex(png, "iTXt"); - // if (existingiTXt != -1) return false; + var screenShotMetadata = GetScreenshotMetadata(path); + if (screenShotMetadata != null && screenShotMetadata.Application == "VRCX") + { + Logger.Error("Screenshot file '{0}' already has VRCX metadata", path); + return false; + } var newChunk = new PNGChunk("iTXt"); newChunk.InitializeTextChunk("Description", text); diff --git a/Dotnet/VRCX-Cef.csproj b/Dotnet/VRCX-Cef.csproj index de5a0996..0c9b8dd8 100644 --- a/Dotnet/VRCX-Cef.csproj +++ b/Dotnet/VRCX-Cef.csproj @@ -96,10 +96,10 @@ - + - + diff --git a/Dotnet/VRCX-Electron.csproj b/Dotnet/VRCX-Electron.csproj index 5801fe05..55129d11 100644 --- a/Dotnet/VRCX-Electron.csproj +++ b/Dotnet/VRCX-Electron.csproj @@ -1,4 +1,4 @@ - + obj1\ ..\build\Electron\ @@ -98,10 +98,10 @@ - + - + diff --git a/src/views/Settings/dialogs/ScreenshotMetadataDialog.vue b/src/views/Settings/dialogs/ScreenshotMetadataDialog.vue index 08d629a0..b7debe57 100644 --- a/src/views/Settings/dialogs/ScreenshotMetadataDialog.vue +++ b/src/views/Settings/dialogs/ScreenshotMetadataDialog.vue @@ -436,8 +436,9 @@ D.searchIndex = searchIndex; } - function getAndDisplayScreenshot(path, needsCarouselFiles = true) { - AppApi.GetScreenshotMetadata(path).then((metadata) => displayScreenshotMetadata(metadata, needsCarouselFiles)); + async function getAndDisplayScreenshot(path, needsCarouselFiles = true) { + const metadata = await AppApi.GetScreenshotMetadata(path); + displayScreenshotMetadata(metadata, needsCarouselFiles); } /** @@ -446,13 +447,19 @@ * Example: {"error":"Invalid file selected. Please select a valid VRChat screenshot."} * See docs/screenshotMetadata.json for schema * @param {string} metadata - JSON string grabbed from PNG file - * @param {string} needsCarouselFiles - Whether or not to get the last/next files for the carousel - * @returns {void} + * @param {boolean} needsCarouselFiles - Whether or not to get the last/next files for the carousel + * @returns {Promise} */ async function displayScreenshotMetadata(json, needsCarouselFiles = true) { let time; let date; const D = props.screenshotMetadataDialog; + D.metadata.author = {}; + D.metadata.world = {}; + D.metadata.players = []; + D.metadata.creationDate = ''; + D.metadata.application = ''; + const metadata = JSON.parse(json); if (!metadata?.sourceFile) { D.metadata = {};