diff --git a/config.yaml b/config.yaml
index 0538059..a37f7fb 100644
--- a/config.yaml
+++ b/config.yaml
@@ -2,9 +2,21 @@ folder:
operator: ./operator/
release: ./release/
background: background
- voice: voice
directory: _assets
share: _share
+ voice:
+ main: voice
+ sub:
+ - name: jp
+ lang: JP
+ - name: cn
+ lang: CN_MANDARIN
+ - name: en
+ lang: EN
+ - name: kr
+ lang: KR
+ - name: custom
+ lang: CUSTOM
operators:
chen: !include config/chen.yaml
dusk: !include config/dusk.yaml
diff --git a/libs/env_generator.js b/libs/env_generator.js
index d6ee719..a0487f1 100644
--- a/libs/env_generator.js
+++ b/libs/env_generator.js
@@ -23,6 +23,7 @@ export default class EnvGenerator {
`VITE_IMAGE_HEIGHT=2048`,
`VITE_BACKGROUND_FILES=${JSON.stringify(this.#assets.backgrounds)}`,
`VITE_BACKGROUND_FOLDER=${__config.folder.background}`,
+ `VITE_VOICE_FOLDERS=${JSON.stringify(__config.folder.voice)}`,
].join('\n')
}
diff --git a/libs/file.js b/libs/file.js
index 6335258..58c7ee8 100644
--- a/libs/file.js
+++ b/libs/file.js
@@ -53,6 +53,15 @@ export async function copy(sourcePath, targetPath) {
return await fsP.copyFile(sourcePath, targetPath)
}
+export async function copyDir(sourcePath, targetPath) {
+ if (!exists(sourcePath)) {
+ console.warn(`Source file ${sourcePath} does not exist.`)
+ return
+ }
+ mkdir(targetPath)
+ return await fsP.cp(sourcePath, targetPath, { recursive: true })
+}
+
export function appendSync(content, filePath) {
return fs.appendFileSync(filePath, content, 'utf8');
}
diff --git a/libs/initializer.js b/libs/initializer.js
index 0d7597b..5725165 100644
--- a/libs/initializer.js
+++ b/libs/initializer.js
@@ -5,7 +5,9 @@ import { mkdir, writeSync } from './file.js'
import { appendMainConfig } from './append.js'
export default function init(operatorName, extractedDir) {
- mkdir(extractedDir)
+ extractedDir.forEach((dir) => {
+ mkdir(dir)
+ })
const date = new Date()
const template = readYAML(path.join(__dirname, 'config', '_template.yaml'))
template.link = operatorName
diff --git a/runner.js b/runner.js
index e302c76..b74fd93 100644
--- a/runner.js
+++ b/runner.js
@@ -5,7 +5,7 @@ import { fork } from 'child_process';
import getConfig from './libs/config.js'
import ProjectJson from './libs/project_json.js'
import EnvGenerator from './libs/env_generator.js'
-import { write, rmdir, copy, writeSync } from './libs/file.js'
+import { write, rmdir, copy, writeSync, copyDir } from './libs/file.js'
import AssetsProcessor from './libs/assets_processor.js'
import init from './libs/initializer.js'
import directory from './libs/directory.js'
@@ -54,6 +54,7 @@ async function main() {
const OPERATOR_RELEASE_FOLDER = path.join(__dirname, __config.folder.release, OPERATOR_NAME)
const SHOWCASE_PUBLIC_ASSSETS_FOLDER = path.join(OPERATOR_RELEASE_FOLDER, "assets")
const EXTRACTED_FOLDER = path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, 'extracted')
+ const VOICE_FOLDERS = __config.folder.voice.sub.map((sub) => path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, __config.folder.voice.main, sub.name))
const OPERATOR_SHARE_FOLDER = path.join(OPERATOR_SOURCE_FOLDER, __config.folder.share)
/**
@@ -63,7 +64,7 @@ async function main() {
*/
switch (op) {
case 'init':
- init(OPERATOR_NAME, EXTRACTED_FOLDER)
+ init(OPERATOR_NAME, [EXTRACTED_FOLDER, ...VOICE_FOLDERS])
process.exit(0)
case 'readme':
appendReadme(OPERATOR_NAME)
@@ -115,6 +116,16 @@ async function main() {
copy(path.join(file.source, file.filename), path.join(file.target, file.filename))
})
+ const foldersToCopy = [
+ {
+ source: path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, __config.folder.voice.main),
+ target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER, __config.folder.voice.main)
+ }
+ ]
+ foldersToCopy.forEach((folder) => {
+ copyDir(folder.source, folder.target)
+ })
+
const envPath = path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, '.env')
writeSync((new EnvGenerator(OPERATOR_NAME, {
backgrounds
diff --git a/src/components/player.js b/src/components/player.js
index b0a757d..6dba470 100644
--- a/src/components/player.js
+++ b/src/components/player.js
@@ -55,6 +55,7 @@ export default function spinePlayer(el) {
entry.mixDuration = 0.3;
widget.animationState.addAnimation(0, "Idle", true, 0);
}
+ window.voice.success()
window.settings.success()
},
})
diff --git a/src/components/settings.js b/src/components/settings.js
index 796aa68..a9503fa 100644
--- a/src/components/settings.js
+++ b/src/components/settings.js
@@ -38,6 +38,8 @@ export default class Settings {
#logoX = this.#defaultLogoX
#logoY = this.#defaultLogoY
#isInsightsInited = false
+ #doNotTrack = false
+ #lastFunctionInsights = null
constructor(el, logoEl) {
this.#el = el
@@ -65,22 +67,31 @@ export default class Settings {
success() {
this.loadViewport()
- this.insights(false)
+ this.insights(false, false)
}
- insights(flag) {
- if (this.#isInsightsInited) return
- window.umami.trackView(`/${import.meta.env.VITE_LINK}${flag ? "?steam" : ""}`);
+ insights(isWallpaperEngine, doNotTrack) {
this.#isInsightsInited = true
+ if (this.#isInsightsInited || doNotTrack || import.meta.env.MODE === 'development') return
+ this.#doNotTrack = doNotTrack
+ window.umami?.trackView(`/${import.meta.env.VITE_LINK}${isWallpaperEngine ? "?steam" : ""}`);
+ }
+
+ functionInsights(functionName) {
+ if (!this.#isInsightsInited || this.#doNotTrack || import.meta.env.MODE === 'development' || functionName === this.#lastFunctionInsights) return
+ this.#lastFunctionInsights = functionName
+ window.umami?.trackEvent(`${functionName}`);
}
setFPS(value) {
this.#fps = value
this.spinePlayer.setFps(value)
+ this.functionInsights("setFPS")
}
setLogoDisplay(flag) {
this.#logoEl.hidden = flag;
+ this.functionInsights("setLogoDisplay")
}
#resize(_this, value) {
@@ -101,6 +112,7 @@ export default class Settings {
this.#logoEl.src = src
this.#resize()
this.#setLogoInvertFilter(invert_filter)
+ this.functionInsights("setLogo")
}
#readFile(e, onload, callback) {
@@ -131,23 +143,27 @@ export default class Settings {
setLogoRatio(value) {
this.#ratio = value
this.#resize(this, value)
+ this.functionInsights("setLogoRatio")
}
setLogoOpacity(value) {
this.#logoEl.style.opacity = value / 100
this.#opacity = value
+ this.functionInsights("setLogoOpacity")
}
- setBackgoundImage(v) {
+ setBackgoundImage(v, skipInsights = false) {
document.body.style.backgroundImage = v
+ if (!skipInsights) this.functionInsights("setBackgoundImage");
}
setDefaultBackground(e) {
const backgroundURL = `url("${import.meta.env.BASE_URL}assets/${import.meta.env.VITE_BACKGROUND_FOLDER}/${e}")`
if (document.getElementById("custom_background_clear").disabled && !document.body.style.backgroundImage.startsWith("url(\"file:")) {
- this.setBackgoundImage(backgroundURL)
+ this.setBackgoundImage(backgroundURL, true)
}
this.#defaultBackgroundImage = backgroundURL
+ this.functionInsights("setDefaultBackground")
}
setBackground(e) {
@@ -198,6 +214,7 @@ export default class Settings {
break;
}
this.loadViewport()
+ this.functionInsights("positionPadding")
}
positionReset() {
@@ -242,6 +259,7 @@ export default class Settings {
break;
}
this.elementPosition(this.#logoEl, this.#logoX, this.#logoY)
+ this.functionInsights("logoPadding")
}
logoReset() {
diff --git a/src/components/voice.js b/src/components/voice.js
index 1a0a9a1..6bd8861 100644
--- a/src/components/voice.js
+++ b/src/components/voice.js
@@ -3,11 +3,15 @@ import '@/components/voice.css'
export default class Voice {
#el
+ #widgetEl
+ #audioEl = new Audio()
+ #audioElId = 'voice-audio'
#defaultVoiceLang = "JP"
#defaultRegion = charword_table.config.default_region
#defaultIdleDuration = 10 * 60 * 1000
#defaultNextDuration = 3 * 60 * 1000
#voiceLang = this.#defaultVoiceLang
+ #voiceLanguages = Object.keys(this.#getCVInfo(this.#defaultRegion))
#subtitleLang = this.#defaultRegion
#useSubtitle = false
#useVoice = false
@@ -19,19 +23,22 @@ export default class Voice {
#nextListener = -1
#nextDuration = this.#defaultNextDuration
#lastClickToNext = false
+ #voiceFolderObject = this.#getVoiceFolderObject()
- constructor(el) {
+ constructor(el, widgetEl) {
this.#el = el
+ this.#widgetEl = widgetEl
}
init() {
- this.#playTitleVoice()
+ this.#insertHTML()
+ this.#audioEl = document.getElementById(this.#audioElId)
}
success() {
this.#playEntryVoice()
this.#initNextVoiceTimer()
- document.addEventListener('click', e => {
+ this.#widgetEl.addEventListener('click', e => {
this.#lastClickToNext = true
this.#nextVoice()
})
@@ -86,7 +93,7 @@ export default class Voice {
* @param {string} lang
*/
set language(lang) {
- if (this.#getVoiceLanguages().includes(lang)) {
+ if (this.#voiceLanguages.includes(lang)) {
this.#voiceLang = lang
} else {
this.#voiceLang = this.#defaultVoiceLang
@@ -102,7 +109,7 @@ export default class Voice {
}
get languages() {
- return this.#getVoiceLanguages()
+ return this.#voiceLanguages
}
/**
@@ -135,10 +142,6 @@ export default class Voice {
return this.#nextDuration / 60 / 1000
}
- #playTitleVoice() {
- this.#playSpecialVoice("标题")
- }
-
#initIdleVoiceTimer() {
this.#idleListener = setInterval(() => {
this.#playSpecialVoice("闲置")
@@ -169,16 +172,26 @@ export default class Voice {
#playVoice(id) {
this.#currentVoiceId = id
- const audio = new Audio()
- // audio.src = `https://cdn.jsdelivr.net/gh/Arondight/Adachi-BOT@master/src/assets/voice/${id}.mp3`
- audio.play()
- audio.addEventListener('ended', () => {
- this.#lastVoiceId = this.#currentVoiceId
- this.#currentVoiceId = null
- this.#setCurrentSubtitle(null)
- this.#lastClickToNext = false
- })
- this.#setCurrentSubtitle(id)
+ this.#audioEl.src = `./assets/${this.#getVoiceFoler()
+}/${id}.wav`
+ let startPlayPromise = this.#audioEl.play()
+ if (startPlayPromise !== undefined) {
+ startPlayPromise
+ .then(() => {
+ const audioEndedFunc = () => {
+ this.#lastVoiceId = this.#currentVoiceId
+ this.#currentVoiceId = null
+ this.#setCurrentSubtitle(null)
+ this.#lastClickToNext = false
+ this.#audioEl.removeEventListener('ended', audioEndedFunc)
+ }
+ this.#audioEl.addEventListener('ended', audioEndedFunc)
+ this.#setCurrentSubtitle(id)
+ })
+ .catch(() => {
+ return
+ })
+ }
}
#playSpecialVoice(matcher) {
@@ -186,6 +199,11 @@ export default class Voice {
this.#playVoice(voiceId)
}
+ #getVoiceFoler() {
+ const folderObject = this.#voiceFolderObject
+ return `${folderObject.main}/${folderObject.sub.find(e => e.lang === this.#voiceLang).name}`
+ }
+
#getSpecialVoiceId(matcher) {
const voices = this.#getVoices()
const voiceId = Object.keys(voices).find(e => voices[e].title === matcher)
@@ -200,8 +218,17 @@ export default class Voice {
return charword_table.operator.voice[this.#subtitleLang][this.#getWordKeyByVoiceLang()[this.#voiceLang]][id]
}
- #getVoiceLanguages() {
- return Object.keys(this.#getCVInfo(this.#defaultRegion))
+ #getVoiceFolderObject() {
+ const folderObject = JSON.parse(import.meta.env.VITE_VOICE_FOLDERS)
+ const languagesCopy = this.#voiceLanguages.slice()
+ const customVoiceName = languagesCopy.filter(i => !folderObject.sub.map(e => e.lang).includes(i))[0]
+ folderObject.sub = folderObject.sub.map(e => {
+ return {
+ name: e.name,
+ lang: e.lang === "CUSTOM" ? customVoiceName : e.lang
+ }
+ })
+ return folderObject
}
/**
@@ -251,4 +278,12 @@ export default class Voice {
return Object.keys(this.#getCVInfoByVoiceLang()[this.#voiceLang])
}
+ #insertHTML() {
+ this.#el.innerHTML = `
+
+ `
+ }
}
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 00a3e4f..e15e92b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -7,16 +7,16 @@ import Voice from '@/components/voice'
document.querySelector('#app').innerHTML = `