From c4a19966a48cf08d99fc0efc51ce258258e1af29 Mon Sep 17 00:00:00 2001 From: Nekromateion <43814053+Nekromateion@users.noreply.github.com> Date: Tue, 26 Nov 2024 05:36:02 +0100 Subject: [PATCH] Implement automatic sticker saving (#985) * Implement automatic sticker saving * Prevent multiple requests for same sticker or print --- Dotnet/AppApi/AppApi.cs | 11 +++++++++ Dotnet/LogWatcher.cs | 29 ++++++++++++++++++++++- html/src/app.js | 39 +++++++++++++++++++++++++++++++ html/src/classes/gameLog.js | 7 ++++++ html/src/localization/en/en.json | 4 ++++ html/src/mixins/tabs/settings.pug | 4 ++++ html/src/service/gamelog.js | 6 +++++ 7 files changed, 99 insertions(+), 1 deletion(-) diff --git a/Dotnet/AppApi/AppApi.cs b/Dotnet/AppApi/AppApi.cs index 1d4ae12b..d736e258 100644 --- a/Dotnet/AppApi/AppApi.cs +++ b/Dotnet/AppApi/AppApi.cs @@ -592,5 +592,16 @@ namespace VRCX return await ImageCache.SaveImageToFile(url, filePath); } + + public async Task SaveStickerToFile(string url, string path, string fileName) + { + var folder = Path.Combine(GetVRChatPhotosLocation(), "Stickers", MakeValidFileName(path)); + Directory.CreateDirectory(folder); + var filePath = Path.Combine(folder, MakeValidFileName(fileName)); + if (File.Exists(filePath)) + return false; + + return await ImageCache.SaveImageToFile(url, filePath); + } } } \ No newline at end of file diff --git a/Dotnet/LogWatcher.cs b/Dotnet/LogWatcher.cs index 7de45174..8cd55c9a 100644 --- a/Dotnet/LogWatcher.cs +++ b/Dotnet/LogWatcher.cs @@ -259,7 +259,8 @@ namespace VRCX ParseFailedToJoin(fileInfo, logContext, line, offset) || ParseInstanceResetWarning(fileInfo, logContext, line, offset) || ParseVoteKickInitiation(fileInfo, logContext, line, offset) || - ParseVoteKickSuccess(fileInfo, logContext, line, offset)) + ParseVoteKickSuccess(fileInfo, logContext, line, offset) || + ParseStickerSpawn(fileInfo, logContext, line, offset)) { } } @@ -1270,6 +1271,32 @@ namespace VRCX return true; } + private bool ParseStickerSpawn(FileInfo fileInfo, LogContext logContext, string line, int offset) + { + var index = line.IndexOf("[StickersManager] User "); + if (index == -1 || !line.Contains("file_") || !line.Contains("spawned sticker")) + return false; + + string info = line.Substring(index + 23); + + var (userId, displayName) = ParseUserInfo(info); + + var fileIdIndex = info.IndexOf("file_"); + string fileId = info.Substring(fileIdIndex); + + AppendLog(new[] + { + fileInfo.Name, + ConvertLogTimeToISO8601(line), + "sticker-spawn", + userId, + displayName, + fileId, + }); + + return true; + } + public string[][] Get() { Update(); diff --git a/html/src/app.js b/html/src/app.js index c395ff78..aa770cc6 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -8207,6 +8207,10 @@ speechSynthesis.getVoices(); 'VRCX_saveInstancePrints', this.saveInstancePrints ); + await configRepository.setBool( + 'VRCX_saveInstanceStickers', + this.saveInstanceStickers + ); VRCXStorage.Set( 'VRCX_StartAsMinimizedState', this.isStartAsMinimizedState.toString() @@ -17480,6 +17484,29 @@ speechSynthesis.getVoices(); } }); + $app.data.stickersCache = []; + + $app.methods.trySaveStickerToFile = async function (displayName, fileId) { + if ($app.stickersCache.includes(fileId)) return; + $app.stickersCache.push(fileId); + if ($app.stickersCache.size > 100) { + $app.stickersCache.shift(); + } + var args = await API.call(`file/${fileId}`); + var imageUrl = args.versions[1].file.url; + var createdAt = args.versions[0].created_at; + var path = `${createdAt.slice(0, 7)}`; + var fileNameDate = createdAt + .replace(/:/g, '-') + .replace(/T/g, '_') + .replace(/Z/g, ''); + var fileName = `${displayName}_${fileNameDate}_${fileId}.png`; + var status = await AppApi.SaveStickerToFile(imageUrl, path, fileName); + if (status) { + console.log(`Sticker saved to file: ${path}\\${fileName}`); + } + }; + // #endregion // #region | Prints API.$on('LOGIN', function () { @@ -17651,6 +17678,11 @@ speechSynthesis.getVoices(); false ); + $app.data.saveInstanceStickers = await configRepository.getBool( + 'VRCX_saveInstanceStickers', + false + ); + $app.methods.getPrintDate = function (print) { var createdAt = new Date(); if (print.createdAt) { @@ -17674,7 +17706,14 @@ speechSynthesis.getVoices(); return fileName; }; + $app.data.printCache = []; + $app.methods.trySavePrintToFile = async function (printId) { + if ($app.printCache.includes(printId)) return; + $app.printCache.push(printId); + if ($app.printCache.length > 100) { + $app.printCache.shift(); + } var args = await API.getPrint({ printId }); var imageUrl = args.json?.files?.image; if (!imageUrl) { diff --git a/html/src/classes/gameLog.js b/html/src/classes/gameLog.js index 8a6bc564..3a1665c8 100644 --- a/html/src/classes/gameLog.js +++ b/html/src/classes/gameLog.js @@ -405,6 +405,13 @@ export default class extends baseClass { // }; // database.addGamelogEventToDatabase(entry); break; + case 'sticker-spawn': + if (!$app.saveInstanceStickers) { + break; + } + + $app.trySaveStickerToFile(gameLog.displayName, gameLog.fileId); + break; } if (entry) { // add tag colour diff --git a/html/src/localization/en/en.json b/html/src/localization/en/en.json index aa41c856..eee2fc90 100644 --- a/html/src/localization/en/en.json +++ b/html/src/localization/en/en.json @@ -441,6 +441,10 @@ "header_tooltip": "Requires \"--enable-sdk-log-levels\" VRC launch option", "description": "Save spawned prints to your VRChat Pictures folder" }, + "save_instance_stickers_to_file": { + "header": "Save Instance Stickers To File", + "description": "Save dropped Stickers to your VRChat Pictures folder" + }, "remote_database": { "header": "Remote Avatar Database", "enable": "Enable", diff --git a/html/src/mixins/tabs/settings.pug b/html/src/mixins/tabs/settings.pug index 1055eaa9..2de04b79 100644 --- a/html/src/mixins/tabs/settings.pug +++ b/html/src/mixins/tabs/settings.pug @@ -489,6 +489,10 @@ mixin settingsTab() div.options-container-item span.name(style="min-width:300px") {{ $t('view.settings.advanced.advanced.save_instance_prints_to_file.description') }} el-switch(v-model="saveInstancePrints" @change="saveVRCXWindowOption") + span.sub-header {{ $t('view.settings.advanced.advanced.save_instance_stickers_to_file.header') }} + div.options-container-item + span.name(style="min-width:300px") {{ $t('view.settings.advanced.advanced.save_instance_stickers_to_file.description') }} + el-switch(v-model="saveInstanceStickers" @change="saveVRCXWindowOption") //- Advanced | Remote Avatar Database div.options-container span.header {{ $t('view.settings.advanced.advanced.remote_database.header') }} diff --git a/html/src/service/gamelog.js b/html/src/service/gamelog.js index 66f15753..02248f5b 100644 --- a/html/src/service/gamelog.js +++ b/html/src/service/gamelog.js @@ -87,6 +87,12 @@ class GameLogService { gameLog.data = args[0]; break; + case 'sticker-spawn': + gameLog.userId = args[0]; + gameLog.displayName = args[1]; + gameLog.fileId = args[2]; + break; + default: break; }