diff --git a/Dotnet/AppApi/AppApi.cs b/Dotnet/AppApi/AppApi.cs index b0d603dd..e960e356 100644 --- a/Dotnet/AppApi/AppApi.cs +++ b/Dotnet/AppApi/AppApi.cs @@ -575,14 +575,15 @@ namespace VRCX return null; } - public async Task SavePrintToFile(string url, string fileName) + public async Task SavePrintToFile(string url, string path, string fileName) { - var path = Path.Combine(GetVRChatPhotosLocation(), fileName); - Directory.CreateDirectory(Path.GetDirectoryName(path)); - if (File.Exists(path)) + var folder = Path.Combine(GetVRChatPhotosLocation(), "Prints", MakeValidFileName(path)); + Directory.CreateDirectory(folder); + var filePath = Path.Combine(folder, MakeValidFileName(fileName)); + if (File.Exists(filePath)) return false; - return await ImageCache.SaveImageToFile(url, path); + return await ImageCache.SaveImageToFile(url, filePath); } } } \ No newline at end of file diff --git a/Dotnet/AppApi/Folders.cs b/Dotnet/AppApi/Folders.cs index 29b93050..6e5f9a13 100644 --- a/Dotnet/AppApi/Folders.cs +++ b/Dotnet/AppApi/Folders.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Microsoft.Win32; @@ -246,5 +247,21 @@ namespace VRCX Process.Start("explorer.exe", $"/select,\"{path}\""); } } + + private static readonly Regex _folderRegex = new Regex(string.Format(@"([{0}]*\.+$)|([{0}]+)", + Regex.Escape(new string(Path.GetInvalidPathChars())))); + + private static readonly Regex _fileRegex = new Regex(string.Format(@"([{0}]*\.+$)|([{0}]+)", + Regex.Escape(new string(Path.GetInvalidFileNameChars())))); + + public static string MakeValidFileName(string name) + { + name = name.Replace("/", ""); + name = name.Replace("\\", ""); + name = _folderRegex.Replace(name, ""); + name = _fileRegex.Replace(name, ""); + + return name; + } } } \ No newline at end of file diff --git a/Dotnet/LogWatcher.cs b/Dotnet/LogWatcher.cs index b0b4213c..c791deaf 100644 --- a/Dotnet/LogWatcher.cs +++ b/Dotnet/LogWatcher.cs @@ -278,8 +278,9 @@ namespace VRCX } } } - catch + catch (Exception ex) { + logger.Warn("Failed to parse log file: {0} {1}", fileInfo.FullName, ex.Message); } } diff --git a/html/src/app.js b/html/src/app.js index 0909d75a..ea69b634 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -4581,6 +4581,10 @@ speechSynthesis.getVoices(); ); } } + if (!this.friends.has(id)) { + console.log('Friend not found', id); + return; + } var isVIP = this.localFavoriteFriends.has(id); var ref = ctx.ref; if (ctx.state !== newState && typeof ctx.ref !== 'undefined') { @@ -17615,7 +17619,12 @@ speechSynthesis.getVoices(); console.error('Print image URL is missing', print); return; } - var createdAt = new Date(print.json.createdAt); + var createdAt = new Date(); + if (print.json.timestamp) { + createdAt = new Date(print.json.timestamp); + } else if (print.json.createdAt) { + createdAt = new Date(print.json.createdAt); + } var authorName = print.json.authorName; // fileDate format: 2024-11-03_16-14-25.757 var fileNameDate = createdAt @@ -17623,10 +17632,11 @@ speechSynthesis.getVoices(); .replace(/:/g, '-') .replace(/T/g, '_') .replace(/Z/g, ''); - var fileName = `Prints\\${createdAt.toISOString().slice(0, 7)}\\${authorName}_${fileNameDate}_${printId}.png`; - var status = await AppApi.SavePrintToFile(imageUrl, fileName); + var path = `${createdAt.toISOString().slice(0, 7)}`; + var fileName = `${authorName}_${fileNameDate}_${printId}.png`; + var status = await AppApi.SavePrintToFile(imageUrl, path, fileName); if (status) { - console.log(`Print saved to file: ${fileName}`); + console.log(`Print saved to file: ${path}\\${fileName}`); } }; diff --git a/html/src/mixins/dialogs/currentUser.pug b/html/src/mixins/dialogs/currentUser.pug index e800ddb5..c1a74293 100644 --- a/html/src/mixins/dialogs/currentUser.pug +++ b/html/src/mixins/dialogs/currentUser.pug @@ -161,12 +161,13 @@ mixin currentUser() .x-friend-item(v-for="image in printTable" :key="image.id" style="display:inline-block;margin-top:10px;width:unset;cursor:default") .vrcplus-icon(style="overflow:hidden" @click="showFullscreenImageDialog(image.files.image)") img.avatar(v-lazy="image.files.image") - div(style="margin-top:5px") - span(v-text="image.note") - br - location(v-if="image.worldId" :location="image.worldId" :hint="image.worldName") - br - display-name(v-if="image.authorId" :userid="image.authorId" :hint="image.authorName" style="color:#909399;font-family:monospace") - div(style="float:right;margin-top:5px") + div(style="margin-top:5px;width:208px") + span.x-ellipsis(v-if="image.note" v-text="image.note" style="display:block") + span(v-else style="display:block")   + location.x-ellipsis(v-if="image.worldId" :location="image.worldId" :hint="image.worldName" style="display:block") + span(v-else style="display:block")   + display-name.x-ellipsis(v-if="image.authorId" :userid="image.authorId" :hint="image.authorName" style="color:#909399;font-family:monospace;display:block") + span(v-else style="font-family:monospace;display:block")   + div(style="float:right") el-button(type="default" @click="showFullscreenImageDialog(image.files.image)" size="mini" icon="el-icon-picture-outline" circle) el-button(type="default" @click="deletePrint(image.id)" size="mini" icon="el-icon-delete" circle style="margin-left:5px")