Add support for SDVX Akanames
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
Trenton Zimmer
2025-03-05 10:59:40 -05:00
parent 21921af1ea
commit 0ff0099754
5 changed files with 5343 additions and 1 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,136 @@
<script setup>
import axios from "axios";
import { watch, ref } from "vue";
import { useRouter } from "vue-router";
import { mdiLoading } from "@mdi/js";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import CardBox from "@/components/CardBox.vue";
import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
import PillTag from "@/components/PillTag.vue";
import { GameConstants } from "@/constants";
import { APIUpdateProfile } from "@/stores/api/profile";
const props = defineProps({
profile: {
type: Object,
default: null,
},
version: {
type: Number,
default: 6,
},
});
const router = useRouter();
const userProfile = ref(JSON.parse(JSON.stringify(props.profile)));
const version = ref(props.version);
const AkanameKey = ref(0);
const newAkaname = ref(userProfile.value?.akaname ?? 0);
const AkanameSettings = ref([]);
const isModified = ref(false);
const loading = ref(false);
watch(
() => props.version,
() => {
userProfile.value = JSON.parse(JSON.stringify(props.profile));
loadAkanameSettings();
newAkaname.value = userProfile.value?.akaname;
isModified.value = false;
}
);
watch(
() => props.version,
() => {
version.value = props.version;
}
);
watch(
newAkaname,
() => {
AkanameKey.value++;
isModified.value = !akanameEquals(newAkaname.value, props.profile.akaname);
},
{ deep: true }
);
loadAkanameSettings();
function loadAkanameSettings() {
axios
.get(`/data-sources/sdvx/akaname/${version.value}.json`)
.then((r) => {
if (r.data) {
AkanameSettings.value = r.data;
}
})
.catch((error) => {
console.log(error.message);
});
}
function akanameEquals(akaname1, akaname2) {
return JSON.stringify({ akaname1 }) === JSON.stringify({ akaname2 });
}
async function updateProfile() {
loading.value = true;
var newProfile = JSON.parse(JSON.stringify(props.profile));
newProfile.akaname = newAkaname.value;
const profileStatus = await APIUpdateProfile(
GameConstants.SDVX,
props.version,
{ akaname: newAkaname.value }
);
if (profileStatus.status != "error") {
router.go();
}
}
function revert() {
newAkaname.value = userProfile.value?.akaname ?? 0;
isModified.value = false;
}
</script>
<template>
<CardBox class="mt-6">
<PillTag color="info" label="Akaname" class="mb-2" />
<div class="grid md:grid-cols-2 space-y-6 align-center">
<form>
<FormField label="Akaname" help="Sets your profile's subtitle">
<FormControl v-model="newAkaname" :options="AkanameSettings" />
</FormField>
</form>
</div>
<template #footer>
<div class="space-x-2 pb-4">
<BaseButton
v-if="isModified"
color="success"
label="Save"
@click="updateProfile()"
/>
<BaseButton
v-if="isModified"
color="danger"
label="Revert"
@click="revert()"
/>
<BaseIcon
v-if="loading"
:path="mdiLoading"
color="text-yellow-500"
class="animate animate-spin"
/>
</div>
</template>
</CardBox>
</template>

View File

@@ -1,12 +1,14 @@
<script setup>
import axios from "axios";
import {
mdiCog,
mdiAccountDetails,
mdiPlaylistMusicOutline,
mdiFormatListText,
} from "@mdi/js";
import { ref, onMounted } from "vue";
import { dashCode } from "@/constants/userData";
import { getGameInfo } from "@/constants";
import { GameConstants, getGameInfo } from "@/constants";
import BaseButton from "@/components/BaseButton.vue";
import UserEmblem from "@/components/UserEmblem.vue";
import UserQpro from "@/components/UserQpro.vue";
@@ -40,6 +42,32 @@ function colorText() {
}
const thisGame = getGameInfo(props.game);
const version = ref(props.version);
const AkanameSettings = ref([]);
const fullyLoaded = ref(false);
async function loadAkanameSettings() {
try {
const response = await axios.get(
`/data-sources/sdvx/akaname/${version.value}.json`
);
if (response.data) {
AkanameSettings.value = response.data;
}
} catch (error) {
console.error("Error loading Akaname settings:", error.message);
} finally {
fullyLoaded.value = true;
}
}
onMounted(async () => {
if (thisGame.id === GameConstants.SDVX) {
await loadAkanameSettings();
} else {
fullyLoaded.value = true;
}
});
</script>
<template>
@@ -70,6 +98,16 @@ const thisGame = getGameInfo(props.game);
<h1 :class="colorText()" class="text-4xl md:text-5xl font-bold">
{{ profile.username }}
</h1>
<template v-if="fullyLoaded">
<p
v-if="profile.akaname"
class="text-2xl tracking-widest font-light my-1"
>
{{
AkanameSettings.find((item) => item.id === profile.akaname)?.label
}}
</p>
</template>
<p class="text-xl font-mono">{{ dashCode(profile.extid) }}</p>
</div>
</div>

View File

@@ -13,6 +13,7 @@ import FormCheckRadio from "@/components/FormCheckRadio.vue";
import FormControl from "@/components/FormControl.vue";
import EmblemCardBox from "@/components/Cards/EmblemCardBox.vue";
import QproCardBox from "@/components/Cards/QproCardBox.vue";
import AkanameCardBox from "@/components/Cards/AkanameCardBox.vue";
import PillTag from "@/components/PillTag.vue";
import { APIGetProfile, APIUpdateProfile } from "@/stores/api/profile";
@@ -275,6 +276,11 @@ async function updateProfile() {
:profile="myProfile"
:version="versionForm.currentVersion"
/>
<AkanameCardBox
v-if="gameID == 'sdvx' && versionForm.currentVersion >= 5"
:profile="myProfile"
:version="versionForm.currentVersion"
/>
</div>
</SectionMain>
</LayoutAuthenticated>

52
tools/akaname_parser.py Normal file
View File

@@ -0,0 +1,52 @@
import sys
import os
import re
import json
import xml.etree.ElementTree as ET
args = sys.argv
if len(args) != 2:
print('<akaname_parser> <akaname_parts.xml path>\nWrites a resulting JSON file to the same path.')
sys.exit()
inFilePath = args[1]
if not os.path.exists(inFilePath):
print(f'The path {inFilePath} does not exist!')
sys.exit()
if not inFilePath.endswith('.xml'):
print('You must supply an XML file.')
sys.exit()
def parseXML(xmlFile: str) -> list[dict]:
root = ET.fromstring(xmlFile)
parts = []
for part in root.findall('part'):
partId = part.attrib.get('id', None)
if not partId:
continue
wordFormat = part.find('word')
if wordFormat.text == None:
continue
parsedData = {}
if wordFormat.text.startswith('['):
pattern = re.findall(r"\[([a-z]+):([^\]]+)\]", wordFormat.text)
parsedData = {key: value.split(',') if ',' in value else value for key, value in pattern}
contentMatch = re.search(r"\[c:[^\]]+\](.*?)\[/c\]", wordFormat.text)
parsedData['label'] = contentMatch.group(1) if contentMatch else ""
else:
parsedData['label'] = wordFormat.text
parsedData['id'] = int(partId)
parts.append(parsedData)
return parts
akanameData = []
with open(inFilePath, 'r', encoding='shift-jis') as inFile:
akanameData = parseXML(inFile.read())
with open(inFilePath.replace('.xml', '.json'), 'w', encoding='utf-8') as outFile:
outFile.write(json.dumps(akanameData, indent=4))
print(f'Converted {len(akanameData)} akaname parts\nyippie!')