From f3aa867e00a67051684b74aba05f3977b277fe27 Mon Sep 17 00:00:00 2001 From: Haoyu Xu Date: Wed, 15 Mar 2023 21:27:34 -0400 Subject: [PATCH] feat(showcase): add music --- Version | 2 +- aklive2d.js | 146 ++++++++++++++++------------- config.yaml | 1 + config/_project_json.yaml | 48 +++++++++- directory/src/scss/_page_base.scss | 25 +++-- index.html | 1 + libs/append.js | 7 +- libs/assets_processor.js | 3 +- libs/background.js | 3 +- libs/charword_table.js | 14 +-- libs/config.js | 7 +- libs/content_processor.js | 1 + libs/directory.js | 15 +-- libs/env_generator.js | 1 - libs/initializer.js | 5 +- libs/music.js | 19 ++++ libs/project_json.js | 5 +- src/components/music.js | 120 ++++++++++++++++++++++++ src/components/player.js | 1 + src/components/settings.js | 134 ++++++++++++++++++-------- src/components/voice.js | 4 +- src/index.js | 5 +- src/libs/wallpaper_engine.js | 14 ++- vite.config.js | 34 +++---- 24 files changed, 449 insertions(+), 166 deletions(-) create mode 100644 libs/music.js create mode 100644 src/components/music.js diff --git a/Version b/Version index eb54abf..8cf6caf 100644 --- a/Version +++ b/Version @@ -1 +1 @@ -3.3.63 \ No newline at end of file +3.4.1 \ No newline at end of file diff --git a/aklive2d.js b/aklive2d.js index 38b75a9..5b5c423 100644 --- a/aklive2d.js +++ b/aklive2d.js @@ -15,9 +15,10 @@ import { appendReadme } from './libs/append.js' import { increase } from './libs/version.js'; import Background from './libs/background.js' import CharwordTable from './libs/charword_table.js'; +import Music from './libs/music.js'; async function main() { - global.__projetRoot = path.dirname(fileURLToPath(import.meta.url)) + global.__projectRoot = path.dirname(fileURLToPath(import.meta.url)) global.__config = getConfig() const op = process.argv[2] @@ -33,17 +34,17 @@ async function main() { switch (op) { case 'directory': assert(OPERATOR_NAMES.length !== 0, 'Please set a mode for Directory.') - fork(path.join(__projetRoot, 'vite.config.js'), [op, OPERATOR_NAMES]) + fork(path.join(__projectRoot, 'vite.config.js'), [op, OPERATOR_NAMES]) return case 'build-all': - for (const [key, _] of Object.entries(__config.operators)) { + for (const [key,] of Object.entries(__config.operators)) { OPERATOR_NAMES.push(key) } - __config.version.showcase = increase(__projetRoot) + __config.version.showcase = increase(__projectRoot) break case 'preview': assert(OPERATOR_NAMES.length !== 0, 'Please set the operator name.') - fork(path.join(__projetRoot, 'vite.config.js'), [op, OPERATOR_NAMES]) + fork(path.join(__projectRoot, 'vite.config.js'), [op, OPERATOR_NAMES]) return case 'charword': await charwordTable.process() @@ -57,10 +58,11 @@ async function main() { const background = new Background() await background.process() const backgrounds = ['operator_bg.png', ...background.files] + const { musicToCopy, musicMapping } = Music() for (const OPERATOR_NAME of OPERATOR_NAMES) { - const OPERATOR_SOURCE_FOLDER = path.join(__projetRoot, __config.folder.operator) - const OPERATOR_RELEASE_FOLDER = path.join(__projetRoot, __config.folder.release, OPERATOR_NAME) + const OPERATOR_SOURCE_FOLDER = path.join(__projectRoot, __config.folder.operator) + const OPERATOR_RELEASE_FOLDER = path.join(__projectRoot, __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)) @@ -116,62 +118,6 @@ async function main() { writeSync(JSON.stringify(voiceJson), path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, 'charword_table.json')) - const projectJson = new ProjectJson(OPERATOR_NAME, OPERATOR_SHARE_FOLDER, { - backgrounds, - voiceLangs, - subtitleLangs - }) - projectJson.load().then((content) => { - write(JSON.stringify(content, null, 2), path.join(OPERATOR_RELEASE_FOLDER, 'project.json')) - }) - - const assetsProcessor = new AssetsProcessor(OPERATOR_NAME, OPERATOR_SHARE_FOLDER) - assetsProcessor.process(EXTRACTED_FOLDER).then((content) => { - write(JSON.stringify(content.assetsJson, null), path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, `assets.json`)) - }) - - const filesToCopy = [ - ...background.getFilesToCopy(SHOWCASE_PUBLIC_ASSSETS_FOLDER), - { - filename: 'preview.jpg', - source: path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME), - target: path.join(OPERATOR_RELEASE_FOLDER) - }, - { - filename: 'operator_bg.png', - source: path.join(OPERATOR_SHARE_FOLDER, __config.folder.background), - target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER, __config.folder.background) - }, - { - filename: `${__config.operators[OPERATOR_NAME].logo}.png`, - source: path.join(OPERATOR_SHARE_FOLDER, 'logo'), - target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER) - }, - { - filename: `${__config.operators[OPERATOR_NAME].fallback_name}.png`, - source: path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME), - target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER) - }, - { - filename: `${__config.operators[OPERATOR_NAME].fallback_name}_portrait.png`, - source: path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME), - target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER) - } - ] - filesToCopy.forEach((file) => { - 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()).generate([ { @@ -219,12 +165,82 @@ async function main() { }, { key: "voice_folders", value: JSON.stringify(__config.folder.voice) + }, { + key: "music_folder", + value: __config.folder.music + }, { + key: "music_mapping", + value: JSON.stringify(musicMapping) } ]), envPath) - fork(path.join(__projetRoot, 'vite.config.js'), [op, OPERATOR_NAME]) + + const projectJson = new ProjectJson(OPERATOR_NAME, OPERATOR_SHARE_FOLDER, { + backgrounds, + voiceLangs, + subtitleLangs, + music: Object.keys(musicMapping) + }) + projectJson.load().then((content) => { + write(JSON.stringify(content, null, 2), path.join(OPERATOR_RELEASE_FOLDER, 'project.json')) + }) + + const assetsProcessor = new AssetsProcessor(OPERATOR_NAME, OPERATOR_SHARE_FOLDER) + assetsProcessor.process(EXTRACTED_FOLDER).then((content) => { + write(JSON.stringify(content.assetsJson, null), path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, `assets.json`)) + }) + + const filesToCopy = [ + ...background.getFilesToCopy(SHOWCASE_PUBLIC_ASSSETS_FOLDER), + ...musicToCopy.map(entry => { + return { + ...entry, + target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER, __config.folder.music) + } + }), + { + filename: 'preview.jpg', + source: path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME), + target: path.join(OPERATOR_RELEASE_FOLDER) + }, + { + filename: 'operator_bg.png', + source: path.join(OPERATOR_SHARE_FOLDER, __config.folder.background), + target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER, __config.folder.background) + }, + { + filename: `${__config.operators[OPERATOR_NAME].logo}.png`, + source: path.join(OPERATOR_SHARE_FOLDER, 'logo'), + target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER) + }, + { + filename: `${__config.operators[OPERATOR_NAME].fallback_name}.png`, + source: path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME), + target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER) + }, + { + filename: `${__config.operators[OPERATOR_NAME].fallback_name}_portrait.png`, + source: path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME), + target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER) + } + ] + filesToCopy.forEach((file) => { + 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) + }) + + fork(path.join(__projectRoot, 'vite.config.js'), [op, OPERATOR_NAME]) } - directory({ backgrounds, charwordTable }) + directory({ backgrounds, musicMapping }) } main(); \ No newline at end of file diff --git a/config.yaml b/config.yaml index 8193f91..a46155a 100644 --- a/config.yaml +++ b/config.yaml @@ -2,6 +2,7 @@ folder: operator: ./operator/ release: ./release/ background: background + music: music directory: _assets share: _share voice: diff --git a/config/_project_json.yaml b/config/_project_json.yaml index 3fcecfc..66aaf81 100644 --- a/config/_project_json.yaml +++ b/config/_project_json.yaml @@ -32,6 +32,11 @@ localization: ui_privacy_title:

📜 Privacy


ui_privacy_text: Check out our privacy policy at https://privacy.halyul.dev ui_privacy_do_not_track: Send usage data + ui_music_title:

📝 Music


+ ui_music_notice: Please adjust the 'Offset' value if you notice audio cutoff + ui_music_selection: Music + ui_music_volume: Volume + ui_music_offset: Offset zh-chs: ui_notice_title:

📝 通知


ui_notice_changelog: 现在支持干员语音! @@ -64,6 +69,11 @@ localization: ui_privacy_title:

📜 隐私


ui_privacy_text: 在 https://privacy.halyul.dev 查看我们的隐私政策 ui_privacy_do_not_track: 发送使用数据 + ui_music_title:

🎵 音乐


+ ui_music_notice: 如若发现音频截止,请调节 '弥补' 数值 + ui_music_selection: 音乐 + ui_music_volume: 音量 + ui_music_offset: 弥补 properties: - key: notice_title value: @@ -106,8 +116,8 @@ properties: fraction: true max: 100 min: 0 - precision: 2 step: 0.1 + precision: 1 - key: logoopacity value: text: ui_logo_opacity @@ -214,6 +224,42 @@ properties: condition: voicesubtitle.value == true type: bool value: false + - key: music_title + value: + text: ui_music_title + type: bool + value: false + - key: music_notice + value: + text: ui_music_notice + condition: music_title.value == true + - key: music_selection + value: + text: ui_music_selection + condition: music_title.value == true + type: combo + value: !match ~{var('assets', "music")[0]} + options: !match ~{var('assets', "musicOptions")} + - key: music_volume + value: + text: ui_music_volume + type: slider + value: 50 + condition: music_title.value == true + fraction: false + max: 100 + min: 0 + - key: music_offset + value: + text: ui_music_offset + type: slider + value: 0.3 + condition: music_title.value == true + fraction: true + precision: 2 + step: 0.01 + max: 1 + min: 0 - key: position value: text: ui_position_title diff --git a/directory/src/scss/_page_base.scss b/directory/src/scss/_page_base.scss index 8ac3703..9c7c871 100644 --- a/directory/src/scss/_page_base.scss +++ b/directory/src/scss/_page_base.scss @@ -1,14 +1,3 @@ -%outline-share { - content: ""; - display: block; - position: absolute; - left: -3px; - height: 3px; - width: 100%; - border-left: var(--text-color) solid 3px; - border-right: var(--text-color) solid 3px; -} - .group { padding: 1rem; display: flex; @@ -61,13 +50,23 @@ border: var(--home-item-outline-color) 1px dashed; padding: 6px; + &:before, + &:after { + content: ""; + display: block; + position: absolute; + left: -3px; + height: 3px; + width: 100%; + border-left: var(--text-color) solid 3px; + border-right: var(--text-color) solid 3px; + } + &:before { - @extend %outline-share; top: -3px; } &:after { - @extend %outline-share; bottom: -3px; } } diff --git a/index.html b/index.html index b05bfbb..8ba7a98 100644 --- a/index.html +++ b/index.html @@ -12,5 +12,6 @@
+ \ No newline at end of file diff --git a/libs/append.js b/libs/append.js index c7d23a3..1c982e5 100644 --- a/libs/append.js +++ b/libs/append.js @@ -1,18 +1,19 @@ +/* eslint-disable no-undef */ import path from 'path' import { appendSync, readSync } from './file.js' export function appendReadme(operatorName) { const operatorConfig = __config.operators[operatorName] - const projectJson = JSON.parse(readSync(path.join(__projetRoot, __config.folder.operator, operatorName, 'project.json'))) + const projectJson = JSON.parse(readSync(path.join(__projectRoot, __config.folder.operator, operatorName, 'project.json'))) appendSync( `\n| ${operatorConfig.codename["en-US"]} | [Link](https://arknights.halyul.dev/${operatorConfig.link}/?settings) | [Link](https://steamcommunity.com/sharedfiles/filedetails/?id=${projectJson.workshopid}) |`, - path.join(__projetRoot, 'README.md') + path.join(__projectRoot, 'README.md') ) } export function appendMainConfig(operatorName) { appendSync( `\n ${operatorName}: !include config/${operatorName}.yaml`, - path.join(__projetRoot, 'config.yaml') + path.join(__projectRoot, 'config.yaml') ) } \ No newline at end of file diff --git a/libs/assets_processor.js b/libs/assets_processor.js index 5a9af8e..f9152e8 100644 --- a/libs/assets_processor.js +++ b/libs/assets_processor.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ import path from 'path' import { read, write, readSync } from './file.js' import AlphaComposite from './alpha_composite.js' @@ -9,7 +10,7 @@ export default class AssetsProcessor { #shareFolder constructor(operatorName, shareFolder) { - this.#operatorSourceFolder = path.join(__projetRoot, __config.folder.operator) + this.#operatorSourceFolder = path.join(__projectRoot, __config.folder.operator) this.#alphaCompositer = new AlphaComposite() this.#operatorName = operatorName this.#shareFolder = shareFolder diff --git a/libs/background.js b/libs/background.js index 3b63e1f..47542a8 100644 --- a/libs/background.js +++ b/libs/background.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ import path from 'path'; import fs from 'fs'; import sharp from "sharp"; @@ -8,7 +9,7 @@ export default class Background { #files constructor() { - this.#backgroundFolder = path.join(__projetRoot, __config.folder.operator, __config.folder.share, __config.folder.background); + this.#backgroundFolder = path.join(__projectRoot, __config.folder.operator, __config.folder.share, __config.folder.background); this.#extractFolder = path.join(this.#backgroundFolder, 'extracted'); } diff --git a/libs/charword_table.js b/libs/charword_table.js index 9f6f0e3..fe53cbe 100644 --- a/libs/charword_table.js +++ b/libs/charword_table.js @@ -23,7 +23,7 @@ export function getOperatorId(operatorConfig) { export default class CharwordTable { #operatorIDs = Object.values(__config.operators).map(operator => { return getOperatorId(operator) }) - #charwordTablePath = path.join(__projetRoot, __config.folder.operator, __config.folder.share) + #charwordTablePath = path.join(__projectRoot, __config.folder.operator, __config.folder.share) #charwordTableFile = path.join(this.#charwordTablePath, 'charword_table.json') #charwordTable = JSON.parse(readSync(this.#charwordTableFile)) || { config: { @@ -135,10 +135,10 @@ export default class CharwordTable { console.log(`charword_table_${region}.json is updated.`) // remove old file - const files = readdirSync(path.join(__projetRoot, __config.folder.operator, __config.folder.share)) + const files = readdirSync(path.join(__projectRoot, __config.folder.operator, __config.folder.share)) for (const file of files) { if (file.startsWith(`charword_table_${region}`) && file !== path.basename(filepath)) { - rm(path.join(__projetRoot, __config.folder.operator, __config.folder.share, file)) + rm(path.join(__projectRoot, __config.folder.operator, __config.folder.share, file)) } } return data @@ -215,10 +215,10 @@ export default class CharwordTable { console.log(`charword_table_${region}.json is updated.`) // remove old file - const files = readdirSync(path.join(__projetRoot, __config.folder.operator, __config.folder.share)) + const files = readdirSync(path.join(__projectRoot, __config.folder.operator, __config.folder.share)) for (const file of files) { if (file.startsWith(`charword_table_${region}`) && file !== path.basename(filepath)) { - rm(path.join(__projetRoot, __config.folder.operator, __config.folder.share, file)) + rm(path.join(__projectRoot, __config.folder.operator, __config.folder.share, file)) } } output.data = data @@ -233,10 +233,10 @@ export default class CharwordTable { console.log(`handbook_info_table_${region}.json is updated.`) // remove old file - const files = readdirSync(path.join(__projetRoot, __config.folder.operator, __config.folder.share)) + const files = readdirSync(path.join(__projectRoot, __config.folder.operator, __config.folder.share)) for (const file of files) { if (file.startsWith(`handbook_info_table_${region}`) && file !== path.basename(handbookFilepath)) { - rm(path.join(__projetRoot, __config.folder.operator, __config.folder.share, file)) + rm(path.join(__projectRoot, __config.folder.operator, __config.folder.share, file)) } } output.handbook = data diff --git a/libs/config.js b/libs/config.js index 15980e0..e8ecbc0 100644 --- a/libs/config.js +++ b/libs/config.js @@ -1,10 +1,11 @@ +/* eslint-disable no-undef */ import path from 'path' import { read } from './yaml.js' import { read as readVersion } from './version.js' import { getOperatorId } from './charword_table.js' export default function () { - return process(read(path.join(__projetRoot, 'config.yaml'))) + return process(read(path.join(__projectRoot, 'config.yaml'))) } function process(config) { @@ -23,8 +24,8 @@ function process(config) { // version config.version = { - showcase: readVersion(path.join(__projetRoot)), - directory: readVersion(path.join(__projetRoot, 'directory')), + showcase: readVersion(path.join(__projectRoot)), + directory: readVersion(path.join(__projectRoot, 'directory')), } return config diff --git a/libs/content_processor.js b/libs/content_processor.js index 0af15e0..3c24ea8 100644 --- a/libs/content_processor.js +++ b/libs/content_processor.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ export default class Matcher { #start #end diff --git a/libs/directory.js b/libs/directory.js index e1f7396..b9e06fe 100644 --- a/libs/directory.js +++ b/libs/directory.js @@ -3,11 +3,12 @@ import path from 'path' import { writeSync, copy, readSync as readFile } from './file.js' import { read } from './yaml.js'; import AssetsProcessor from './assets_processor.js' +import EnvGenerator from './env_generator.js' -export default function ({ backgrounds, charwordTable }) { - const extractedFolder = path.join(__projetRoot, __config.folder.operator, '_directory') - const targetFolder = path.join(__projetRoot, __config.folder.release, __config.folder.directory); - const sourceFolder = path.join(__projetRoot, __config.folder.operator); +export default function ({ backgrounds }) { + const extractedFolder = path.join(__projectRoot, __config.folder.operator, '_directory') + const targetFolder = path.join(__projectRoot, __config.folder.release, __config.folder.directory); + const sourceFolder = path.join(__projectRoot, __config.folder.operator); const filesToCopy = Object.keys(__config.operators) const directoryJson = { operators: Object.values( @@ -22,7 +23,7 @@ export default function ({ backgrounds, charwordTable }) { cur.workshopId = null try { - cur.workshopId = JSON.parse(readFile(path.join(__projetRoot, __config.folder.operator, cur.link, 'project.json'))).workshopid + cur.workshopId = JSON.parse(readFile(path.join(__projectRoot, __config.folder.operator, cur.link, 'project.json'))).workshopid } catch (e) { console.log(`No workshop id for ${cur.link}!`, e) } @@ -33,7 +34,7 @@ export default function ({ backgrounds, charwordTable }) { } const versionJson = __config.version - const changelogs = read(path.join(__projetRoot, 'changelogs.yaml')) + const changelogs = read(path.join(__projectRoot, 'changelogs.yaml')) const changelogsArray = Object.keys(changelogs).reduce((acc, cur) => { const array = [] Object.keys(changelogs[cur]).map((item) => { @@ -78,7 +79,7 @@ export default function ({ backgrounds, charwordTable }) { key: "error_files", value: JSON.stringify(__config.directory.error).replace('#', '%23') } - ]), path.join(__projetRoot, 'directory', '.env')) + ]), path.join(__projectRoot, 'directory', '.env')) writeSync(JSON.stringify(directoryJson, null), path.join(targetFolder, "directory.json")) writeSync(JSON.stringify(versionJson, null), path.join(targetFolder, "version.json")) diff --git a/libs/env_generator.js b/libs/env_generator.js index 2d105a1..08cfd04 100644 --- a/libs/env_generator.js +++ b/libs/env_generator.js @@ -4,5 +4,4 @@ export default class EnvGenerator { return `VITE_${value.key.toUpperCase()}=${value.value}` }).join('\n') } - } \ No newline at end of file diff --git a/libs/initializer.js b/libs/initializer.js index 1406581..7eb3b10 100644 --- a/libs/initializer.js +++ b/libs/initializer.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ import path from 'path' import { stringify } from 'yaml' import { read as readYAML } from './yaml.js' @@ -9,9 +10,9 @@ export default function init(operatorName, extractedDir) { mkdir(dir) }) const date = new Date() - const template = readYAML(path.join(__projetRoot, 'config', '_template.yaml')) + const template = readYAML(path.join(__projectRoot, 'config', '_template.yaml')) template.link = operatorName template.date = `${date.getFullYear()}/${(date.getMonth() + 1).toString().padStart(2, '0') }` - writeSync(stringify(template), path.join(__projetRoot, 'config', `${operatorName}.yaml`)) + writeSync(stringify(template), path.join(__projectRoot, 'config', `${operatorName}.yaml`)) appendMainConfig(operatorName) } \ No newline at end of file diff --git a/libs/music.js b/libs/music.js new file mode 100644 index 0000000..857183a --- /dev/null +++ b/libs/music.js @@ -0,0 +1,19 @@ +/* eslint-disable no-undef */ +import path from 'path'; +import { read } from './yaml.js'; + +export default function () { + const musicFolder = path.join(__projectRoot, __config.folder.operator, __config.folder.share, __config.folder.music); + const musicMapping = read(path.join(musicFolder, 'mapping.yaml')); + const musicToCopy = Object.values(musicMapping).map(entry => Object.values(entry)).flat(1).filter(entry => entry !== null).map(entry => { + return { + filename: entry, + source: musicFolder, + } + }) + + return { + musicToCopy, + musicMapping, + } +} \ No newline at end of file diff --git a/libs/project_json.js b/libs/project_json.js index 1a40441..7420f1f 100644 --- a/libs/project_json.js +++ b/libs/project_json.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ import path from 'path' import Matcher from './content_processor.js' import { read as readFile, exists } from './file.js' @@ -13,7 +14,7 @@ export default class ProjectJson { constructor(operatorName, operatorShareFolder, assets) { this.#operatorName = operatorName - this.#operatorSourceFolder = path.join(__projetRoot, __config.folder.operator) + this.#operatorSourceFolder = path.join(__projectRoot, __config.folder.operator) this.#operatorShareFolder = operatorShareFolder this.#assets = assets } @@ -44,7 +45,7 @@ export default class ProjectJson { return matcher.result } } - this.#template = readYAML(path.join(__projetRoot, 'config', '_project_json.yaml'), [match]) + this.#template = readYAML(path.join(__projectRoot, 'config', '_project_json.yaml'), [match]) this.#process() return this.#json } diff --git a/src/components/music.js b/src/components/music.js new file mode 100644 index 0000000..5ab7809 --- /dev/null +++ b/src/components/music.js @@ -0,0 +1,120 @@ +export default class Music { + #el + #mapping = JSON.parse(import.meta.env.VITE_MUSIC_MAPPING) + #folder = import.meta.env.VITE_MUSIC_FOLDER + #currentMusic = null + #audioIntroEl + #audioLoopEl + #audioIntroElId = 'music-intro' + #audioLoopElId = 'music-loop' + #useMusic = false + #timeOffset = 0.3 + #volume = 0.5 + + constructor(el) { + this.#el = el + this.#insertHTML() + this.#audioIntroEl = document.getElementById(this.#audioIntroElId) + this.#audioLoopEl = document.getElementById(this.#audioLoopElId) + this.#audioIntroEl.volume = this.#volume + this.#audioLoopEl.volume = this.#volume + this.#audioIntroEl.ontimeupdate = () => { + if (this.#audioIntroEl.currentTime >= this.#audioIntroEl.duration - this.#timeOffset) { + this.#audioIntroEl.pause() + this.#audioLoopEl.play() + } + } + this.#audioLoopEl.ontimeupdate = () => { + if (this.#audioLoopEl.currentTime >= this.#audioLoopEl.duration - this.#timeOffset) { + this.#audioLoopEl.currentTime = 0 + this.#audioLoopEl.play() + } + } + } + + get timeOffset() { + return this.#timeOffset + } + + set timeOffset(value) { + this.#timeOffset = value + } + + get volume() { + return this.#volume * 100 + } + + set volume(value) { + value = value / 100 + this.#volume = value + this.#audioIntroEl.volume = value + this.#audioLoopEl.volume = value + } + + get music() { + return Object.keys(this.#mapping) + } + + get useMusic() { + return this.#useMusic + } + + get currentMusic() { + return this.#currentMusic + } + + /** + * @param {bool} value + */ + set useMusic(value) { + this.#useMusic = value + if (value) { + this.#playMusic() + } else { + this.#stopMusic() + } + } + + success() { + this.changeMusic(window.settings.currentBackground) + } + + changeMusic(name) { + if (name !== this.#currentMusic) { + this.#currentMusic = name + if (this.#useMusic) { + this.#audioLoopEl.pause() + this.#audioIntroEl.pause() + this.#playMusic() + } + } + } + + #playMusic() { + const introOgg = this.#mapping[this.#currentMusic].intro + const intro = `./assets/${this.#folder}/${introOgg}` + const loop = `./assets/${this.#folder}/${this.#mapping[this.#currentMusic].loop}` + this.#audioLoopEl.src = loop + if (introOgg) { + this.#audioIntroEl.src = intro || loop + } else { + this.#audioLoopEl.play() + } + } + + #stopMusic() { + this.#audioIntroEl.pause() + this.#audioLoopEl.pause() + } + + #insertHTML() { + this.#el.innerHTML = ` + + + ` + } +} \ No newline at end of file diff --git a/src/components/player.js b/src/components/player.js index 810cb6d..b50279c 100644 --- a/src/components/player.js +++ b/src/components/player.js @@ -57,6 +57,7 @@ export default function spinePlayer(el) { } window.voice.success() window.settings.success() + window.music.success() }, }) } \ No newline at end of file diff --git a/src/components/settings.js b/src/components/settings.js index 607d66a..91965c0 100644 --- a/src/components/settings.js +++ b/src/components/settings.js @@ -119,7 +119,7 @@ export default class Settings { this.functionInsights("setLogo", this.isWallpaperEngine) } - #readFile(e, onload, callback) { + #readFile(e, onload, callback = () => { }) { const file = e.target.files[0] if (!file) return const reader = new FileReader() @@ -161,12 +161,18 @@ export default class Settings { if (!skipInsights) this.functionInsights("setBackgoundImage", this.isWallpaperEngine); } - 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, true) + get currentBackground() { + if (!document.getElementById("custom_background_clear").disabled) { + return null + } + return this.#defaultBackgroundImage.replace(/^(url\(('|"))(.+)(\/)(.+.png)(('|")\))$/, '$5') + } + + setDefaultBackground(e) { + this.#defaultBackgroundImage = `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(this.#defaultBackgroundImage, true) } - this.#defaultBackgroundImage = backgroundURL this.functionInsights("setDefaultBackground", this.isWallpaperEngine) } @@ -182,9 +188,9 @@ export default class Settings { } resetBackground() { - this.setBackgoundImage(this.#defaultBackgroundImage) document.getElementById("custom_background").value = "" document.getElementById("custom_background_clear").disabled = true + this.setBackgoundImage(this.#defaultBackgroundImage) } loadViewport() { @@ -374,31 +380,53 @@ export default class Settings {
- - -
-
- - + + +
+
+ + +
+
+ + + +
+
+ + + +
+
- - - -
-
- - - + +
- - -
+ + +
@@ -407,8 +435,8 @@ export default class Settings {
- - -
+ + +
@@ -443,17 +471,19 @@ export default class Settings { } #sync(source, targetID) { + if (typeof source === "string") source = document.getElementById(source); document.getElementById(targetID).value = source.value; } - #showRelated(e, relatedSettingsID) { + #showRelated(e, relatedSettingsID, revert = false) { const eRelatedSettings = document.getElementById(relatedSettingsID) - if (e.checked) { + const checked = revert ? !e.checked : e.checked; + if (checked) { eRelatedSettings.hidden = false; } else { eRelatedSettings.hidden = true; } - }; + } #updateOptions(id, array) { const e = document.getElementById(id); @@ -496,7 +526,7 @@ export default class Settings { }, { id: "logo_image", event: "change", handler: e => _this.setLogoImage(e) }, { - id: "logo_image_clear", event: "click", handler: e => _this.resetLogoImage() + id: "logo_image_clear", event: "click", handler: () => _this.resetLogoImage() }, { id: "logo_ratio_slider", event: "input", handler: e => { _this.#sync(e.currentTarget, "logo_ratio_input"); @@ -542,7 +572,7 @@ export default class Settings { }, { id: "custom_background", event: "change", handler: e => _this.setBackground(e) }, { - id: "custom_background_clear", event: "click", handler: e => _this.resetBackground() + id: "custom_background_clear", event: "click", handler: () => _this.resetBackground() }, { id: "voice", event: "click", handler: e => { _this.#showRelated(e.currentTarget, "voice_realted"); @@ -593,6 +623,34 @@ export default class Settings { id: "voice_actor", event: "click", handler: e => { window.voice.useVoiceActor = e.currentTarget.checked; } + }, { + id: "music", event: "click", handler: e => { + _this.#showRelated(e.currentTarget, "music_realted"); + window.music.useMusic = e.currentTarget.checked; + window.music.changeMusic(this.currentBackground) + } + }, { + id: "music_select", event: "change", handler: e => window.music.changeMusic(e.currentTarget.value) + }, { + id: "music_volume_slider", event: "input", handler: e => { + _this.#sync(e.currentTarget, "music_volume_input"); + window.music.volume = parseInt(e.currentTarget.value) + } + }, { + id: "music_volume_input", event: "change", handler: e => { + _this.#sync(e.currentTarget, "music_volume_slider"); + window.music.volume = parseInt(e.currentTarget.value) + } + }, { + id: "music_switch_offset_slider", event: "input", handler: e => { + _this.#sync(e.currentTarget, "music_switch_offset_input"); + window.music.timeOffset = parseFloat(e.currentTarget.value) + } + }, { + id: "music_switch_offset_input", event: "change", handler: e => { + _this.#sync(e.currentTarget, "music_switch_offset_slider"); + window.music.timeOffset = parseFloat(e.currentTarget.value) + } }, { id: "position", event: "click", handler: e => { _this.#showRelated(e.currentTarget, "position_realted"); @@ -651,9 +709,9 @@ export default class Settings { document.getElementById("settings_play").disabled = false; } }, { - id: "settings_reset", event: "click", handler: e => _this.reset() + id: "settings_reset", event: "click", handler: () => _this.reset() }, { - id: "settings_close", event: "click", handler: e => _this.close() + id: "settings_close", event: "click", handler: () => _this.close() }, { id: "animation_selection", event: "change", handler: e => { this.spinePlayer.animationState.setAnimation(0, e.currentTarget.value, false, 0) diff --git a/src/components/voice.js b/src/components/voice.js index b0bd673..a2bb8b6 100644 --- a/src/components/voice.js +++ b/src/components/voice.js @@ -50,11 +50,11 @@ export default class Voice { this.#audioEl.addEventListener('ended', audioEndedFunc) this.#playEntryVoice() this.#initNextVoiceTimer() - this.#widgetEl.addEventListener('click', e => { + this.#widgetEl.addEventListener('click', () => { this.#lastClickToNext = true this.#nextVoice() }) - document.addEventListener('mousemove', e => { + document.addEventListener('mousemove', () => { if (this.#idleListener === -1) { this.#initIdleVoiceTimer() } diff --git a/src/index.js b/src/index.js index 1de4077..b032e48 100644 --- a/src/index.js +++ b/src/index.js @@ -3,11 +3,13 @@ import '@/libs/wallpaper_engine' import check_web_gl from '@/libs/check_web_gl' import Settings from '@/components/settings' import Voice from '@/components/voice' +import Music from '@/components/music' document.querySelector('#app').innerHTML = `
- + +