mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-17 13:53:52 +02:00
Save instance emojis
This commit is contained in:
@@ -257,5 +257,26 @@ namespace VRCX
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public async Task<string> SaveEmojiToFile(string url, string ugcFolderPath, string monthFolder, string fileName)
|
||||
{
|
||||
var folder = Path.Join(GetUGCPhotoLocation(ugcFolderPath), "Emoji", MakeValidFileName(monthFolder));
|
||||
Directory.CreateDirectory(folder);
|
||||
var filePath = Path.Join(folder, MakeValidFileName(fileName));
|
||||
if (File.Exists(filePath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
await ImageCache.SaveImageToFile(url, filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, "Failed to save print to file");
|
||||
return null;
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
src/app.js
84
src/app.js
@@ -32,6 +32,7 @@ import {
|
||||
groupRequest,
|
||||
imageRequest,
|
||||
instanceRequest,
|
||||
inventoryRequest,
|
||||
inviteMessagesRequest,
|
||||
miscRequest,
|
||||
notificationRequest,
|
||||
@@ -119,6 +120,7 @@ import { userDialogGroupSortingOptions } from './composables/user/constants/user
|
||||
import {
|
||||
getPrintFileName,
|
||||
getPrintLocalDate,
|
||||
getEmojiFileName,
|
||||
languageClass
|
||||
} from './composables/user/utils';
|
||||
import InteropApi from './ipc-electron/interopApi.js';
|
||||
@@ -6768,6 +6770,9 @@ console.log(`isLinux: ${LINUX}`);
|
||||
case 'VRCX_saveInstanceStickers':
|
||||
this.saveInstanceStickers = !this.saveInstanceStickers;
|
||||
break;
|
||||
case 'VRCX_saveInstanceEmoji':
|
||||
this.saveInstanceEmoji = !this.saveInstanceEmoji;
|
||||
break;
|
||||
case 'VRCX_StartAsMinimizedState':
|
||||
this.isStartAsMinimizedState = !this.isStartAsMinimizedState;
|
||||
break;
|
||||
@@ -6808,6 +6813,11 @@ console.log(`isLinux: ${LINUX}`);
|
||||
this.saveInstanceStickers
|
||||
);
|
||||
|
||||
await configRepository.setBool(
|
||||
'VRCX_saveInstanceEmoji',
|
||||
this.saveInstanceEmoji
|
||||
);
|
||||
|
||||
VRCXStorage.Set(
|
||||
'VRCX_StartAsMinimizedState',
|
||||
this.isStartAsMinimizedState.toString()
|
||||
@@ -10954,6 +10964,75 @@ console.log(`isLinux: ${LINUX}`);
|
||||
}
|
||||
};
|
||||
|
||||
// #endregion
|
||||
// #region | Emoji
|
||||
|
||||
$app.data.instanceInventoryCache = [];
|
||||
$app.data.instanceInventoryQueue = [];
|
||||
$app.data.instanceInventoryQueueWorker = null;
|
||||
|
||||
$app.methods.queueCheckInstanceInventory = function (inventoryId) {
|
||||
if (this.instanceInventoryCache.includes(inventoryId)) {
|
||||
return;
|
||||
}
|
||||
this.instanceInventoryCache.push(inventoryId);
|
||||
if (this.instanceInventoryCache.length > 100) {
|
||||
this.instanceInventoryCache.shift();
|
||||
}
|
||||
|
||||
this.instanceInventoryQueue.push(inventoryId);
|
||||
|
||||
if (!this.instanceInventoryQueueWorker) {
|
||||
this.instanceInventoryQueueWorker = workerTimers.setInterval(() => {
|
||||
let inventoryId = this.instanceInventoryQueue.shift();
|
||||
if (inventoryId) {
|
||||
this.trySaveEmojiToFile(inventoryId);
|
||||
}
|
||||
}, 2_500);
|
||||
}
|
||||
};
|
||||
|
||||
$app.methods.trySaveEmojiToFile = async function (inventoryId) {
|
||||
const args = await inventoryRequest.getInventoryItem({
|
||||
inventoryId
|
||||
});
|
||||
|
||||
if (args.json.itemType !== 'emoji') {
|
||||
// Not an emoji, skip
|
||||
return;
|
||||
}
|
||||
|
||||
const userArgs = await userRequest.getCachedUser({
|
||||
userId: args.json.holderId
|
||||
});
|
||||
const displayName = userArgs.json?.displayName ?? '';
|
||||
|
||||
let emoji = args.json.metadata;
|
||||
emoji.name = `${displayName}_${inventoryId}`;
|
||||
|
||||
const emojiFileName = getEmojiFileName(emoji);
|
||||
const imageUrl = args.json.metadata?.imageUrl ?? args.json.imageUrl;
|
||||
const createdAt = args.json.created_at;
|
||||
const monthFolder = createdAt.slice(0, 7);
|
||||
|
||||
const filePath = await AppApi.SaveEmojiToFile(
|
||||
imageUrl,
|
||||
this.ugcFolderPath,
|
||||
monthFolder,
|
||||
emojiFileName
|
||||
);
|
||||
if (filePath) {
|
||||
console.log(
|
||||
`Emoji saved to file: ${monthFolder}\\${emojiFileName}`
|
||||
);
|
||||
}
|
||||
|
||||
if (this.instanceInventoryQueue.length === 0) {
|
||||
workerTimers.clearInterval(this.instanceInventoryQueueWorker);
|
||||
this.instanceInventoryQueueWorker = null;
|
||||
}
|
||||
};
|
||||
|
||||
// #endregion
|
||||
// #region | Prints
|
||||
$app.methods.cropPrintsChanged = function () {
|
||||
@@ -11038,6 +11117,11 @@ console.log(`isLinux: ${LINUX}`);
|
||||
false
|
||||
);
|
||||
|
||||
$app.data.saveInstanceEmoji = await configRepository.getBool(
|
||||
'VRCX_saveInstanceEmoji',
|
||||
false
|
||||
);
|
||||
|
||||
$app.data.printCache = [];
|
||||
$app.data.printQueue = [];
|
||||
$app.data.printQueueWorker = null;
|
||||
|
||||
@@ -260,6 +260,10 @@ export default class extends baseClass {
|
||||
this.processScreenshot(gameLog.screenshotPath);
|
||||
break;
|
||||
case 'api-request':
|
||||
if ($app.debugWebRequests) {
|
||||
console.log('API Request:', gameLog.url);
|
||||
}
|
||||
|
||||
// var userId = '';
|
||||
// try {
|
||||
// var url = new URL(gameLog.url);
|
||||
@@ -277,23 +281,43 @@ export default class extends baseClass {
|
||||
// break;
|
||||
// }
|
||||
|
||||
if (!$app.saveInstancePrints) {
|
||||
break;
|
||||
if ($app.saveInstanceEmoji) {
|
||||
try {
|
||||
// https://api.vrchat.cloud/api/1/inventory/spawn?id=inv_75781d65-92fe-4a80-a1ff-27ee6e843b08
|
||||
const url = new URL(gameLog.url);
|
||||
if (
|
||||
url.pathname.substring(0, 22) ===
|
||||
'/api/1/inventory/spawn'
|
||||
) {
|
||||
const inventoryId = url.searchParams.get('id');
|
||||
if (inventoryId && inventoryId.length === 40) {
|
||||
$app.queueCheckInstanceInventory(
|
||||
inventoryId
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
try {
|
||||
var printId = '';
|
||||
var url = new URL(gameLog.url);
|
||||
if (
|
||||
url.pathname.substring(0, 14) === '/api/1/prints/'
|
||||
) {
|
||||
var pathArray = url.pathname.split('/');
|
||||
printId = pathArray[4];
|
||||
|
||||
if ($app.saveInstancePrints) {
|
||||
try {
|
||||
let printId = '';
|
||||
const url1 = new URL(gameLog.url);
|
||||
if (
|
||||
url1.pathname.substring(0, 14) ===
|
||||
'/api/1/prints/'
|
||||
) {
|
||||
const pathArray = url1.pathname.split('/');
|
||||
printId = pathArray[4];
|
||||
}
|
||||
if (printId && printId.length === 41) {
|
||||
$app.queueSavePrintToFile(printId);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
if (printId && printId.length === 41) {
|
||||
$app.queueSavePrintToFile(printId);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
break;
|
||||
case 'avatar-change':
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
<span>{{ t('dialog.gallery_icons.recommended_image_size') }}: 1024x1024px (1:1)</span>
|
||||
<br />
|
||||
<br />
|
||||
<div style="display: flex; align-items: center">
|
||||
<div>
|
||||
<el-button-group style="margin-right: 10px">
|
||||
<el-button type="default" size="small" @click="refreshEmojiTable" icon="el-icon-refresh">
|
||||
{{ t('dialog.gallery_icons.refresh') }}
|
||||
@@ -237,7 +237,7 @@
|
||||
@click="
|
||||
showFullscreenImageDialog(
|
||||
image.versions[image.versions.length - 1].file.url,
|
||||
getEmojiFileName(image)
|
||||
getEmojiName(image)
|
||||
)
|
||||
">
|
||||
<template v-if="image.frames">
|
||||
@@ -271,7 +271,7 @@
|
||||
@click="
|
||||
showFullscreenImageDialog(
|
||||
image.versions[image.versions.length - 1].file.url,
|
||||
getEmojiFileName(image)
|
||||
getEmojiName(image)
|
||||
)
|
||||
"
|
||||
size="mini"
|
||||
@@ -489,6 +489,7 @@
|
||||
@click="consumeInventoryBundle(item.id)"
|
||||
size="mini"
|
||||
icon="el-icon-plus"
|
||||
style="float: right"
|
||||
circle>
|
||||
{{ t('dialog.gallery_icons.consume_bundle') }}
|
||||
</el-button>
|
||||
@@ -504,7 +505,7 @@
|
||||
import { inventoryRequest, miscRequest, userRequest, vrcPlusIconRequest, vrcPlusImageRequest } from '../../api';
|
||||
import { extractFileId } from '../../composables/shared/utils';
|
||||
import { emojiAnimationStyleList, emojiAnimationStyleUrl } from '../../composables/user/constants/emoji';
|
||||
import { getPrintFileName } from '../../composables/user/utils';
|
||||
import { getPrintFileName, getEmojiFileName } from '../../composables/user/utils';
|
||||
import Location from '../Location.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -822,7 +823,7 @@
|
||||
emojiAnimFps.value = parseInt(value.replace('fps', ''));
|
||||
}
|
||||
if (value.endsWith('loopStyle')) {
|
||||
emojiAnimLoopPingPong.value = value === 'pingpong';
|
||||
emojiAnimLoopPingPong.value = value.replace('loopStyle', '').toLowerCase() === 'pingpong';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -891,13 +892,8 @@
|
||||
document.getElementById('EmojiUploadButton').click();
|
||||
}
|
||||
|
||||
function getEmojiFileName(emoji) {
|
||||
if (emoji.frames) {
|
||||
const loopStyle = emoji.loopStyle || 'linear';
|
||||
return `${emoji.name}_${emoji.animationStyle}animationStyle_${emoji.frames}frames_${emoji.framesOverTime}fps_${loopStyle}loopStyle.png`;
|
||||
} else {
|
||||
return `${emoji.name}_${emoji.animationStyle}animationStyle.png`;
|
||||
}
|
||||
function getEmojiName(emoji) {
|
||||
getEmojiFileName(emoji);
|
||||
}
|
||||
|
||||
function generateEmojiStyle(url, fps, frameCount, loopStyle) {
|
||||
|
||||
@@ -35,6 +35,15 @@ function getPrintFileName(print) {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
function getEmojiFileName(emoji) {
|
||||
if (emoji.frames) {
|
||||
const loopStyle = emoji.loopStyle || 'linear';
|
||||
return `${emoji.name}_${emoji.animationStyle}animationStyle_${emoji.frames}frames_${emoji.framesOverTime}fps_${loopStyle}loopStyle.png`;
|
||||
} else {
|
||||
return `${emoji.name}_${emoji.animationStyle}animationStyle.png`;
|
||||
}
|
||||
}
|
||||
|
||||
function getPrintLocalDate(print) {
|
||||
if (print.createdAt) {
|
||||
const createdAt = new Date(print.createdAt);
|
||||
@@ -75,5 +84,6 @@ export {
|
||||
languageClass,
|
||||
getPrintFileName,
|
||||
getPrintLocalDate,
|
||||
getEmojiFileName,
|
||||
isFriendOnline
|
||||
};
|
||||
|
||||
@@ -568,6 +568,10 @@
|
||||
"header": "Save Instance Stickers To File",
|
||||
"description": "Save placed stickers to your VRChat Pictures folder"
|
||||
},
|
||||
"save_instance_emoji_to_file": {
|
||||
"header": "Save Instance Emoji To File",
|
||||
"description": "Save spawned emoji to your VRChat Pictures folder"
|
||||
},
|
||||
"remote_database": {
|
||||
"header": "Remote Avatar Database",
|
||||
"enable": "Enable",
|
||||
|
||||
@@ -871,6 +871,18 @@ mixin settingsTab
|
||||
:value='saveInstanceStickers'
|
||||
@change='saveVRCXWindowOption("VRCX_saveInstanceStickers")'
|
||||
:long-label='true')
|
||||
br
|
||||
span.sub-header {{ $t('view.settings.advanced.advanced.save_instance_emoji_to_file.header') }}
|
||||
el-tooltip(
|
||||
placement='top'
|
||||
style='margin-left: 5px'
|
||||
:content='$t("view.settings.advanced.advanced.save_instance_prints_to_file.header_tooltip")')
|
||||
i.el-icon-info
|
||||
simple-switch(
|
||||
:label='$t("view.settings.advanced.advanced.save_instance_emoji_to_file.description")'
|
||||
:value='saveInstanceEmoji'
|
||||
@change='saveVRCXWindowOption("VRCX_saveInstanceEmoji")'
|
||||
:long-label='true')
|
||||
|
||||
//- "Advanced" Tab
|
||||
el-tab-pane(lazy :label='$t("view.settings.category.advanced")')
|
||||
|
||||
Reference in New Issue
Block a user