feat(aklive2d): use cf pages to store assets (upload) [1/2]

This commit is contained in:
Haoyu Xu
2024-08-14 17:29:42 +08:00
parent 4e4f6ada50
commit 0a7ef8531a
26 changed files with 1010 additions and 107 deletions

View File

@@ -1,4 +0,0 @@
**/*
!.git
!CNAME
!.nojekyll

View File

@@ -16,9 +16,9 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: pnpm i run: pnpm i
- name: Build all - name: Build all
run: pnpm run build-all run: pnpm run operator:build-all
- name: Build directory - name: Build directory
run: pnpm run vite:directory:build run: pnpm run directory:build
- name: Publish to Cloudflare Pages - name: Publish to Cloudflare Pages
uses: cloudflare/pages-action@v1 uses: cloudflare/pages-action@v1
with: with:

View File

@@ -22,8 +22,6 @@ jobs:
run: pnpm i run: pnpm i
- name: Update charwords - name: Update charwords
run: pnpm run charwords:update run: pnpm run charwords:update
- name: Build operator charwords
run: pnpm run charwords:build
- name: Commit changes if any - name: Commit changes if any
uses: stefanzweifel/git-auto-commit-action@v4 uses: stefanzweifel/git-auto-commit-action@v4
with: with:

2
.gitignore vendored
View File

@@ -138,3 +138,5 @@ _*.json
*_v2/* *_v2/*
assets/* assets/*
temp/* temp/*
operator/*
data/operator/*

9
.vscode/launch.json vendored
View File

@@ -25,7 +25,7 @@
"type": "node-terminal", "type": "node-terminal",
"name": "Run Script: build-all", "name": "Run Script: build-all",
"request": "launch", "request": "launch",
"command": "pnpm run build-all", "command": "pnpm run operator:build-all",
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
}, },
{ {
@@ -76,6 +76,13 @@
"request": "launch", "request": "launch",
"command": "pnpm run offical_update", "command": "pnpm run offical_update",
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
},
{
"type": "node-terminal",
"name": "Run Script: cf:upload",
"request": "launch",
"command": "pnpm run cf:upload",
"cwd": "${workspaceFolder}"
} }
] ]
} }

View File

@@ -7,7 +7,7 @@ import { fork } from 'child_process';
import getConfig from './libs/config.js' import getConfig from './libs/config.js'
import ProjectJson from './libs/project_json.js' import ProjectJson from './libs/project_json.js'
import EnvGenerator from './libs/env_generator.js' import EnvGenerator from './libs/env_generator.js'
import { write, rmdir, copy, writeSync, copyDir, readdirSync, exists } from './libs/file.js' import { write, rmdir, copy, writeSync, copyDir, readdirSync, exists, mkdir } from './libs/file.js'
import AssetsProcessor from './libs/assets_processor.js' import AssetsProcessor from './libs/assets_processor.js'
import init from './libs/initializer.js' import init from './libs/initializer.js'
import directory from './libs/directory.js' import directory from './libs/directory.js'
@@ -15,14 +15,18 @@ import Background from './libs/background.js'
import CharwordTable from './libs/charword_table.js'; import CharwordTable from './libs/charword_table.js';
import Music from './libs/music.js'; import Music from './libs/music.js';
import OfficalInfo from './libs/offical_info.js'; import OfficalInfo from './libs/offical_info.js';
import CFPages from './libs/cf_pages.js';
async function main() { async function main() {
global.__projectRoot = path.dirname(fileURLToPath(import.meta.url)) global.__projectRoot = path.dirname(fileURLToPath(import.meta.url))
const officalInfo = new OfficalInfo() const officalInfo = new OfficalInfo()
global.__config = getConfig(officalInfo) global.__config = getConfig(officalInfo)
global.__error = [] global.__error = []
const OPERATOR_SOURCE_FOLDER = path.join(__projectRoot, __config.folder.operator)
const OPERATOR_SOURCE_DATA_FOLDER = path.join(__projectRoot, __config.folder.operator_data)
const OPERATOR_SHARE_FOLDER = path.join(OPERATOR_SOURCE_DATA_FOLDER, __config.folder.share)
const op = process.argv[2] const op = process.argv[2]
let OPERATOR_NAMES = process.argv.slice(3); let OPERATOR_NAMES = process.argv.slice(3);
@@ -35,17 +39,12 @@ async function main() {
* directory: build directory * directory: build directory
*/ */
switch (op) { switch (op) {
case 'directory': case 'operator:build-all':
assert(OPERATOR_NAMES.length !== 0, 'Please set a mode for Directory.')
fork(path.join(__projectRoot, 'vite.config.js'), [op, OPERATOR_NAMES])
return
case 'build-all':
case 'charwords:build':
for (const [key,] of Object.entries(__config.operators)) { for (const [key,] of Object.entries(__config.operators)) {
OPERATOR_NAMES.push(key) OPERATOR_NAMES.push(key)
} }
break break
case 'preview': case 'operator:preview':
assert(OPERATOR_NAMES.length !== 0, 'Please set the operator name.') assert(OPERATOR_NAMES.length !== 0, 'Please set the operator name.')
fork(path.join(__projectRoot, 'vite.config.js'), [op, OPERATOR_NAMES]) fork(path.join(__projectRoot, 'vite.config.js'), [op, OPERATOR_NAMES])
return return
@@ -58,16 +57,22 @@ async function main() {
case 'offical_update': case 'offical_update':
await officalInfo.update() await officalInfo.update()
process.exit(0) process.exit(0)
case 'cf:upload':
(new CFPages()).upload()
process.exit(0)
case 'cf:download':
(new CFPages()).download()
process.exit(0)
default: default:
break break
} }
assert(OPERATOR_NAMES.length !== 0, 'Please set the operator name.') assert(OPERATOR_NAMES.length !== 0, 'Please set the operator name.')
const background = new Background() const background = new Background(OPERATOR_SHARE_FOLDER, OPERATOR_SOURCE_FOLDER)
await background.process() await background.process()
const backgrounds = ['operator_bg.png', ...background.files] const backgrounds = ['operator_bg.png', ...background.files]
const { musicToCopy, musicMapping } = musicTable.copy() const { musicToCopy, musicMapping } = musicTable.copy(OPERATOR_SHARE_FOLDER)
for (const e of musicToCopy) { for (const e of musicToCopy) {
const musicPath = path.join(e.source, e.filename) const musicPath = path.join(e.source, e.filename)
@@ -83,12 +88,10 @@ async function main() {
} }
for (const OPERATOR_NAME of OPERATOR_NAMES) { for (const OPERATOR_NAME of OPERATOR_NAMES) {
const OPERATOR_SOURCE_FOLDER = path.join(__projectRoot, __config.folder.operator)
const OPERATOR_RELEASE_FOLDER = path.join(__projectRoot, __config.folder.release, OPERATOR_NAME) const OPERATOR_RELEASE_FOLDER = path.join(__projectRoot, __config.folder.release, OPERATOR_NAME)
const SHOWCASE_PUBLIC_ASSSETS_FOLDER = path.join(OPERATOR_RELEASE_FOLDER, "assets") const SHOWCASE_PUBLIC_ASSSETS_FOLDER = path.join(OPERATOR_RELEASE_FOLDER, "assets")
const EXTRACTED_FOLDER = path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, 'extracted') const EXTRACTED_FOLDER = path.join(OPERATOR_SOURCE_DATA_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 VOICE_FOLDERS = __config.folder.voice.sub.map((sub) => path.join(OPERATOR_SOURCE_DATA_FOLDER, OPERATOR_NAME, __config.folder.voice.main, sub.name))
const OPERATOR_SHARE_FOLDER = path.join(OPERATOR_SOURCE_FOLDER, __config.folder.share)
/** /**
* Skip assets generation part * Skip assets generation part
@@ -104,6 +107,7 @@ async function main() {
} }
rmdir(OPERATOR_RELEASE_FOLDER) rmdir(OPERATOR_RELEASE_FOLDER)
mkdir(path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME))
const charwordTableLookup = charwordTable.lookup(OPERATOR_NAME) const charwordTableLookup = charwordTable.lookup(OPERATOR_NAME)
const voiceJson = {} const voiceJson = {}
@@ -143,13 +147,6 @@ async function main() {
console.log(`charword_table is not available`) console.log(`charword_table is not available`)
} }
switch (op) {
case 'charwords:build':
continue
default:
break
}
// check whether voice files has been added // check whether voice files has been added
const customVoiceName = voiceLangs.filter(i => !__config.folder.voice.sub.map(e => e.lang).includes(i))[0] const customVoiceName = voiceLangs.filter(i => !__config.folder.voice.sub.map(e => e.lang).includes(i))[0]
const voiceLangMapping = __config.folder.voice.sub.filter(e => { const voiceLangMapping = __config.folder.voice.sub.filter(e => {
@@ -161,7 +158,7 @@ async function main() {
} }
}) })
for (const voiceSubFolderMapping of voiceLangMapping) { for (const voiceSubFolderMapping of voiceLangMapping) {
const voiceSubFolder = path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, __config.folder.voice.main, voiceSubFolderMapping.name) const voiceSubFolder = path.join(OPERATOR_SOURCE_DATA_FOLDER, OPERATOR_NAME, __config.folder.voice.main, voiceSubFolderMapping.name)
if (readdirSync(voiceSubFolder).length === 0) { if (readdirSync(voiceSubFolder).length === 0) {
__error.push(`Voice folder ${voiceSubFolderMapping.name} for ${OPERATOR_NAME} is empty.`) __error.push(`Voice folder ${voiceSubFolderMapping.name} for ${OPERATOR_NAME} is empty.`)
} }
@@ -237,10 +234,10 @@ async function main() {
}) })
const assetsProcessor = new AssetsProcessor(OPERATOR_NAME, OPERATOR_SHARE_FOLDER) const assetsProcessor = new AssetsProcessor(OPERATOR_NAME, OPERATOR_SHARE_FOLDER)
assetsProcessor.process(EXTRACTED_FOLDER).then((content) => { const assetContent = await assetsProcessor.process(EXTRACTED_FOLDER)
write(JSON.stringify(content.assetsJson, null), path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, `assets.json`)) write(JSON.stringify(assetContent.assetsJson, null), path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, `assets.json`))
})
// copy remaining files
const filesToCopy = [ const filesToCopy = [
...background.getFilesToCopy(SHOWCASE_PUBLIC_ASSSETS_FOLDER), ...background.getFilesToCopy(SHOWCASE_PUBLIC_ASSSETS_FOLDER),
...musicToCopy.map(entry => { ...musicToCopy.map(entry => {
@@ -251,7 +248,7 @@ async function main() {
}), }),
{ {
filename: 'preview.jpg', filename: 'preview.jpg',
source: path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME), source: path.join(OPERATOR_SOURCE_DATA_FOLDER, OPERATOR_NAME),
target: path.join(OPERATOR_RELEASE_FOLDER) target: path.join(OPERATOR_RELEASE_FOLDER)
}, },
{ {
@@ -281,7 +278,7 @@ async function main() {
const foldersToCopy = [ const foldersToCopy = [
{ {
source: path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, __config.folder.voice.main), source: path.join(OPERATOR_SOURCE_DATA_FOLDER, OPERATOR_NAME, __config.folder.voice.main),
target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER, __config.folder.voice.main) target: path.join(SHOWCASE_PUBLIC_ASSSETS_FOLDER, __config.folder.voice.main)
} }
] ]
@@ -289,20 +286,18 @@ async function main() {
copyDir(folder.source, folder.target) copyDir(folder.source, folder.target)
}) })
} }
switch (op) {
directory({ backgrounds, musicMapping }) case op.startsWith('directory'):
directory(OPERATOR_SOURCE_DATA_FOLDER, { backgrounds, musicMapping })
default:
break
}
if (__error.length > 0) { if (__error.length > 0) {
const str = `${__error.length} error${__error.length > 1 ? 's were' : ' was'} found:\n${__error.join('\n')}` const str = `${__error.length} error${__error.length > 1 ? 's were' : ' was'} found:\n${__error.join('\n')}`
throw new Error(str) throw new Error(str)
} else { } else {
switch (op) { for (const OPERATOR_NAME of OPERATOR_NAMES) {
case 'charwords:build': fork(path.join(__projectRoot, 'vite.config.js'), [op, OPERATOR_NAME])
return
default:
for (const OPERATOR_NAME of OPERATOR_NAMES) {
fork(path.join(__projectRoot, 'vite.config.js'), [op, OPERATOR_NAME])
}
return
} }
} }
} }

View File

@@ -1,4 +1,6 @@
folder: folder:
auto_update_data: ./data/auto_update
operator_data: ./data/operator/
directory_src: directory directory_src: directory
showcase_src: showcase showcase_src: showcase
operator: ./operator/ operator: ./operator/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
[{"id":"bg_rhodes_day","intro":"Audio/Sound_Beta_2/Music/beta1_180603/m_sys_void_intro","loop":"Audio/Sound_Beta_2/Music/beta1_180603/m_sys_void_loop"},{"id":"bg_rhodes_night","intro":"Audio/Sound_Beta_2/Music/beta2_180603/m_sys_tech_intro","loop":"Audio/Sound_Beta_2/Music/beta2_180603/m_sys_tech_loop"},{"id":"bg_main_victoria_1","intro":"Audio/Sound_Beta_2/Music/AVG/m_avg_ghosthunter_intro","loop":"Audio/Sound_Beta_2/Music/AVG/m_avg_ghosthunter_loop"},{"id":"bg_siesta_1","intro":"Audio/Sound_Beta_2/Music/obt/m_sys_ddd_intro","loop":"Audio/Sound_Beta_2/Music/obt/m_sys_ddd_loop"},{"id":"bg_kazimierz_1","intro":"Audio/Sound_Beta_2/Music/beta3_181101/m_dia_street_intro","loop":"Audio/Sound_Beta_2/Music/beta3_181101/m_dia_street_loop"},{"id":"bg_ursus_1","intro":"Audio/Sound_Beta_2/Music/static/m_avg/m_avg_loneliness_intro","loop":"Audio/Sound_Beta_2/Music/static/m_avg/m_avg_loneliness_loop"},{"id":"bg_yan_1","intro":null,"loop":"Audio/Sound_Beta_2/Music/act15side/m_sys_bitw_loop"},{"id":"bg_iberia_1","intro":"Audio/Sound_Beta_2/Music/act18d3d0/m_sys_act18d3d0_intro","loop":"Audio/Sound_Beta_2/Music/act18d3d0/m_sys_act18d3d0_loop"},{"id":"bg_anniversary_1","intro":"Audio/Sound_Beta_2/Music/beta2_180603/m_dia_nightoflongmen_intro","loop":"Audio/Sound_Beta_2/Music/beta2_180603/m_dia_nightoflongmen_loop"},{"id":"bg_rogue_1","intro":null,"loop":"Audio/Sound_Beta_2/Music/rogue_1/m_avg_rglk1secretevent_loop"},{"id":"bg_rogue_2","intro":null,"loop":"Audio/Sound_Beta_2/Music/rogue_2/m_sys_rglk2DLC_loop"},{"id":"bg_laterano_1","intro":"Audio/Sound_Beta_2/Music/act16side/m_sys_act16side_intro","loop":"Audio/Sound_Beta_2/Music/act16side/m_sys_act16side_loop"},{"id":"bg_rhine_1","intro":"Audio/Sound_Beta_2/Music/act19side/m_sys_act19side_intro","loop":"Audio/Sound_Beta_2/Music/act19side/m_sys_act19side_loop"},{"id":"bg_kalts_1","intro":"Audio/Sound_Beta_2/Music/act18d0d0/m_sys_act18d0d0_intro","loop":"Audio/Sound_Beta_2/Music/act18d0d0/m_sys_act18d0d0_loop"},{"id":"bg_rogue_3","intro":"Audio/Sound_Beta_2/Music/rogue_3/m_bat_rglk3DLC_intro","loop":"Audio/Sound_Beta_2/Music/rogue_3/m_bat_rglk3DLC_loop"},{"id":"bg_rainbowsix_1","intro":null,"loop":"Audio/Sound_Beta_2/Music/act32side/m_act32side_sys_loop"},{"id":"bg_rhodes_flower_1","intro":"Audio/Sound_Beta_2/Music/act16mini/m_sys_act16mini_intro","loop":"Audio/Sound_Beta_2/Music/act16mini/m_sys_act16mini_loop"},{"id":"bg_sanrio_1","intro":null,"loop":"Audio/Sound_Beta_2/Music/act27side/m_avg_SiestaCity"},{"id":"operator_bg","intro":"m_sys_void_intro","loop":"m_sys_void_loop"}]

View File

@@ -3,7 +3,7 @@ VITE_APP_VOICE_URL=jp/CN_037.ogg
VITE_VOICE_FOLDERS={"main":"voice","sub":[{"name":"jp","lang":"JP"},{"name":"cn","lang":"CN_MANDARIN"},{"name":"en","lang":"EN"},{"name":"kr","lang":"KR"},{"name":"custom","lang":"CUSTOM"}]} VITE_VOICE_FOLDERS={"main":"voice","sub":[{"name":"jp","lang":"JP"},{"name":"cn","lang":"CN_MANDARIN"},{"name":"en","lang":"EN"},{"name":"kr","lang":"KR"},{"name":"custom","lang":"CUSTOM"}]}
VITE_DIRECTORY_FOLDER="_assets" VITE_DIRECTORY_FOLDER="_assets"
VITE_BACKGROUND_FOLDER="background" VITE_BACKGROUND_FOLDER="background"
VITE_AVAILABLE_OPERATORS=["chen","dusk","dusk_everything_is_a_miracle","ling","nearl","nian","nian_unfettered_freedom","phatom_focus","rosmontis","skadi","skadi_sublimation","w","w_wonder","specter","gavial","surtr_colorful_wonderland","lee_trust_your_eyes","texas_the_omertosa","nearl_relight","rosmontis_become_anew","passager_dream_in_a_moment","mizuki_summer_feast","chongyue","ling_it_does_wash_the_strings","pozemka_snowy_plains_in_words","chen_ten_thousand_mountains","specter_born_as_one","muelsyse","kaltsit_remnant","eyjafjalla_the_hvit_aska","goldenglow_summer_flowers_fa394","gavial_the_invincible_holiday_hd26","ling_towering_is_cliff_of_nostalgia","virtuosa","texas_the_omertosa_wingbreaker","mwynar_w_dali","reed_the_frame_shadow_curator","shu","lin_heavenly_mirage","chongyue_alighting","wisadel","muelsyse_young_branch","skadi_the_corrupting_heart_red_countess","ines_under_the_flaming_dome","silverash_never_melting_ice","reed_the_frame_shadow_summer_flower"] VITE_AVAILABLE_OPERATORS=["chen","dusk","dusk_everything_is_a_miracle","ling","nearl","nian","nian_unfettered_freedom","phatom_focus","rosmontis","skadi","skadi_sublimation","w","w_wonder","specter","gavial","surtr_colorful_wonderland","lee_trust_your_eyes","texas_the_omertosa","nearl_relight","rosmontis_become_anew","passager_dream_in_a_moment","mizuki_summer_feast","chongyue","ling_it_does_wash_the_strings","pozemka_snowy_plains_in_words","chen_ten_thousand_mountains","specter_born_as_one","muelsyse","kaltsit_remnant","eyjafjalla_the_hvit_aska","goldenglow_summer_flowers_fa394","gavial_the_invincible_holiday_hd26","ling_towering_is_cliff_of_nostalgia","virtuosa","texas_the_omertosa_wingbreaker","mwynar_w_dali","reed_the_frame_shadow_curator","shu","lin_heavenly_mirage","chongyue_alighting","wisadel","muelsyse_young_branch","skadi_the_corrupting_heart_red_countess","ines_under_the_flaming_dome","silverash_never_melting_ice","reed_the_frame_shadow_summer_flower","eyjafjalla_the_hvit_aska_a_picnic_before_a_long_trip","pepe","nightingale_iakhu_of_flows"]
VITE_ERROR_FILES={"files":[{"key":"build_char_128_plosis_epoque%233","paddings":{"left":-120,"right":150,"top":10,"bottom":0}},{"key":"build_char_128_plosis","paddings":{"left":-90,"right":100,"top":10,"bottom":0}}],"voice":"CN_034.ogg"} VITE_ERROR_FILES={"files":[{"key":"build_char_128_plosis_epoque%233","paddings":{"left":-120,"right":150,"top":10,"bottom":0}},{"key":"build_char_128_plosis","paddings":{"left":-90,"right":100,"top":10,"bottom":0}}],"voice":"CN_034.ogg"}
VITE_MUSIC_FOLDER=music VITE_MUSIC_FOLDER=music
VITE_MUSIC_MAPPING={"bg_rhodes_day.png":{"intro":"m_sys_void_intro.ogg","loop":"m_sys_void_loop.ogg"},"bg_rhodes_night.png":{"intro":"m_sys_tech_intro.ogg","loop":"m_sys_tech_loop.ogg"},"bg_main_victoria_1.png":{"intro":"m_avg_ghosthunter_intro.ogg","loop":"m_avg_ghosthunter_loop.ogg"},"bg_siesta_1.png":{"intro":"m_sys_ddd_intro.ogg","loop":"m_sys_ddd_loop.ogg"},"bg_kazimierz_1.png":{"intro":"m_dia_street_intro.ogg","loop":"m_dia_street_loop.ogg"},"bg_ursus_1.png":{"intro":"m_avg_loneliness_intro.ogg","loop":"m_avg_loneliness_loop.ogg"},"bg_yan_1.png":{"intro":null,"loop":"m_sys_bitw_loop.ogg"},"bg_iberia_1.png":{"intro":"m_sys_act18d3d0_intro.ogg","loop":"m_sys_act18d3d0_loop.ogg"},"bg_anniversary_1.png":{"intro":"m_dia_nightoflongmen_intro.ogg","loop":"m_dia_nightoflongmen_loop.ogg"},"bg_rogue_1.png":{"intro":null,"loop":"m_avg_rglk1secretevent_loop.ogg"},"bg_rogue_2.png":{"intro":null,"loop":"m_sys_rglk2DLC_loop.ogg"},"bg_laterano_1.png":{"intro":"m_sys_act16side_intro.ogg","loop":"m_sys_act16side_loop.ogg"},"bg_rhine_1.png":{"intro":"m_sys_act19side_intro.ogg","loop":"m_sys_act19side_loop.ogg"},"bg_kalts_1.png":{"intro":"m_sys_act18d0d0_intro.ogg","loop":"m_sys_act18d0d0_loop.ogg"},"bg_rogue_3.png":{"intro":"m_bat_rglk3DLC_intro.ogg","loop":"m_bat_rglk3DLC_loop.ogg"},"bg_rainbowsix_1.png":{"intro":null,"loop":"m_act32side_sys_loop.ogg"},"bg_rhodes_flower_1.png":{"intro":"m_sys_act16mini_intro.ogg","loop":"m_sys_act16mini_loop.ogg"},"bg_sanrio_1.png":{"intro":null,"loop":"m_avg_SiestaCity.ogg"},"operator_bg.png":{"intro":"m_sys_void_intro.ogg","loop":"m_sys_void_loop.ogg"}} VITE_MUSIC_MAPPING={"bg_rhodes_day.png":{"intro":"m_sys_void_intro.ogg","loop":"m_sys_void_loop.ogg"},"bg_rhodes_night.png":{"intro":"m_sys_tech_intro.ogg","loop":"m_sys_tech_loop.ogg"},"bg_main_victoria_1.png":{"intro":"m_avg_ghosthunter_intro.ogg","loop":"m_avg_ghosthunter_loop.ogg"},"bg_siesta_1.png":{"intro":"m_sys_ddd_intro.ogg","loop":"m_sys_ddd_loop.ogg"},"bg_kazimierz_1.png":{"intro":"m_dia_street_intro.ogg","loop":"m_dia_street_loop.ogg"},"bg_ursus_1.png":{"intro":"m_avg_loneliness_intro.ogg","loop":"m_avg_loneliness_loop.ogg"},"bg_yan_1.png":{"intro":null,"loop":"m_sys_bitw_loop.ogg"},"bg_iberia_1.png":{"intro":"m_sys_act18d3d0_intro.ogg","loop":"m_sys_act18d3d0_loop.ogg"},"bg_anniversary_1.png":{"intro":"m_dia_nightoflongmen_intro.ogg","loop":"m_dia_nightoflongmen_loop.ogg"},"bg_rogue_1.png":{"intro":null,"loop":"m_avg_rglk1secretevent_loop.ogg"},"bg_rogue_2.png":{"intro":null,"loop":"m_sys_rglk2DLC_loop.ogg"},"bg_laterano_1.png":{"intro":"m_sys_act16side_intro.ogg","loop":"m_sys_act16side_loop.ogg"},"bg_rhine_1.png":{"intro":"m_sys_act19side_intro.ogg","loop":"m_sys_act19side_loop.ogg"},"bg_kalts_1.png":{"intro":"m_sys_act18d0d0_intro.ogg","loop":"m_sys_act18d0d0_loop.ogg"},"bg_rogue_3.png":{"intro":"m_bat_rglk3DLC_intro.ogg","loop":"m_bat_rglk3DLC_loop.ogg"},"bg_rainbowsix_1.png":{"intro":null,"loop":"m_act32side_sys_loop.ogg"},"bg_rhodes_flower_1.png":{"intro":"m_sys_act16mini_intro.ogg","loop":"m_sys_act16mini_loop.ogg"},"bg_sanrio_1.png":{"intro":null,"loop":"m_avg_SiestaCity.ogg"},"operator_bg.png":{"intro":"m_sys_void_intro.ogg","loop":"m_sys_void_loop.ogg"}}

View File

@@ -0,0 +1,142 @@
@use "@/scss/_page_base.scss";
.main {
padding: 3rem 0 2rem 0;
display: flex;
justify-content: space-between;
align-items: flex-start;
.container {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
width: 100%;
position: relative;
margin-bottom: 2rem;
user-select: none;
&:before {
content: "";
display: block;
padding-top: 100%;
}
.wrapper {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
@media (max-width: 1280px) {
width: 100%;
position: relative;
margin-bottom: 2rem;
&:before {
content: "";
display: block;
padding-top: 100%;
}
}
}
.settings {
margin-right: 1.5rem;
user-select: none;
margin-left: 1rem;
.title {
font-size: 1.25rem;
border-left: 3px solid currentColor;
padding-left: 0.75rem;
margin-bottom: 0.8rem;
display: flex;
justify-content: space-between;
cursor: pointer;
.switch {
bottom: -10px;
position: relative;
}
}
}
.text {
color: var(--text-color);
line-height: 1.2;
}
.logo {
position: absolute;
top: 0;
left: 0;
width: 30%;
height: auto;
opacity: 0.3;
}
.voice {
position: absolute;
left: 0;
bottom: 20%;
z-index: 1;
max-width: 480px;
width: 85%;
opacity: 0;
margin: 16px;
transition: all 0.5s cubic-bezier(0.65, 0.05, 0.36, 1);
visibility: hidden;
font-family: 'Noto Sans SC', 'Noto Sans JP', 'Noto Sans KR', 'Noto Sans', sans-serif;
.type {
background-color: #9e9e9e;
color: #000;
display: inline-block;
position: absolute;
top: -12px;
left: -8px;
padding: 2px 8px;
font-size: 14px;
max-width: 180px;
width: 65%;
box-shadow: 0 3px 6px #00000080;
z-index: 1;
}
.subtitle {
background-color: #000000a6;
color: #fff;
padding: 16px;
font-size: 18px;
box-shadow: 0 6px 12px #00000080;
position: relative;
word-break: break-word;
}
.triangle {
position: absolute;
bottom: 0px;
right: 8px;
width: 0;
height: 0;
border-style: solid;
border-width: 8px 8px 8px 8px;
border-color: white transparent transparent transparent;
}
&.active {
opacity: 1;
visibility: visible;
}
@media (max-width: 1280px) {
bottom: 0;
}
}
@media (max-width: 1280px) {
flex-direction: column-reverse;
align-items: stretch;
}
}

View File

@@ -2,29 +2,29 @@
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import sharp from "sharp"; import sharp from "sharp";
import { mkdir } from './file.js'
export default class Background { export default class Background {
#backgroundFolder #backgroundFolder
#backgroundOutputFolder
#extractFolder #extractFolder
#files #files
constructor() { constructor(shareDir, targetDir) {
this.#backgroundFolder = path.join(__projectRoot, __config.folder.operator, __config.folder.share, __config.folder.background); this.#backgroundFolder = path.join(shareDir, __config.folder.background);
this.#backgroundOutputFolder = path.join(targetDir, `_${__config.folder.background}`);
this.#extractFolder = path.join(this.#backgroundFolder, 'extracted'); this.#extractFolder = path.join(this.#backgroundFolder, 'extracted');
mkdir(this.#backgroundOutputFolder);
} }
async process() { async process() {
this.#files = fs.readdirSync(this.#extractFolder).filter((f) => { this.#files = fs.readdirSync(this.#extractFolder).filter((f) => {
return f.endsWith('.png') && f.includes('_left'); return f.endsWith('.png') && f.includes('_left');
}) })
if (this.#files.length !== fs.readdirSync(this.#backgroundFolder).length - 1) { await Promise.all(this.#files.map(async (f) => {
await Promise.all(this.#files.map(async (f) => { const filenamePrefix = path.parse(f).name.replace('_left', '');
const filenamePrefix = path.parse(f).name.replace('_left', ''); await this.#composite(filenamePrefix, '.png');
await this.#composite(filenamePrefix, '.png'); }))
}))
} else {
console.log('Background images already exist, skip generation.')
}
} }
async #composite(filenamePrefix, fileExt) { async #composite(filenamePrefix, fileExt) {
@@ -45,7 +45,7 @@ export default class Background {
left: metadata.width, left: metadata.width,
}, },
]) ])
.toFile(path.join(this.#backgroundFolder, `${filenamePrefix}${fileExt}`)); .toFile(path.join(this.#backgroundOutputFolder, `${filenamePrefix}${fileExt}`));
} }
get files() { get files() {
@@ -56,7 +56,7 @@ export default class Background {
return this.#files.map((f) => { return this.#files.map((f) => {
return { return {
filename: f.replace('_left', ''), filename: f.replace('_left', ''),
source: path.join(this.#backgroundFolder), source: path.join(this.#backgroundOutputFolder),
target: path.join(publicAssetsDir, __config.folder.background) target: path.join(publicAssetsDir, __config.folder.background)
}; };
}) })

66
libs/cf_pages.js Normal file
View File

@@ -0,0 +1,66 @@
/* eslint-disable no-undef */
import path from 'path';
import { spawnSync } from 'child_process';
import { readdirSync, fileTypeSync, writeSync } from './file.js';
export default class CFPages {
#uploadPath = path.join(__projectRoot, __config.folder.operator_data);
#gitignorePath = path.join(__projectRoot, '.gitignore');
constructor() {
}
upload() {
const tree = this.#generateDirTree(this.#uploadPath);
writeSync(JSON.stringify(tree, null), path.join(this.#uploadPath, 'index.json'));
const wrangler = spawnSync('pnpm', ['wrangler', 'pages', 'deploy', this.#uploadPath]);
console.log('error', wrangler.error);
console.log('stdout ', wrangler.stdout.toString());
console.log('stderr ', wrangler.stderr.toString());
}
download() {
}
#generateDirTree(dir) {
const files = readdirSync(dir);
let tree = {
name: path.basename(dir),
type: 'dir',
children: []
};
for (const file of files) {
if (this.#isToIgnore(file)) {
continue;
}
const filePath = path.join(dir, file);
const dirType = fileTypeSync(filePath);
if (dirType === 'dir') {
tree.children.push(this.#generateDirTree(filePath))
} else {
tree.children.push({
name: file,
type: 'file'
});
}
}
if (tree.children.length === 0) {
return null;
}
return tree
}
// TODO
#isToIgnore(file) {
switch (file) {
case '.DS_Store':
case 'index.json':
return true;
default:
return false;
}
}
}

View File

@@ -30,7 +30,7 @@ export function getOperatorId(operatorConfig) {
export default class CharwordTable { export default class CharwordTable {
#operatorIDs = Object.values(__config.operators).map(operator => { return getOperatorId(operator) }) #operatorIDs = Object.values(__config.operators).map(operator => { return getOperatorId(operator) })
#charwordTablePath = path.join(__projectRoot, __config.folder.operator, __config.folder.share) #charwordTablePath = path.join(__projectRoot, __config.folder.auto_update_data)
#charwordTableFile = path.join(this.#charwordTablePath, 'charword_table.json') #charwordTableFile = path.join(this.#charwordTablePath, 'charword_table.json')
#charwordTable = JSON.parse(readSync(this.#charwordTableFile)) || { #charwordTable = JSON.parse(readSync(this.#charwordTableFile)) || {
config: { config: {

View File

@@ -1,12 +1,11 @@
/* eslint-disable no-undef */ /* eslint-disable no-undef */
import path from 'path' import path from 'path'
import { writeSync, copy, readSync as readFile } from './file.js' import { writeSync, copy, readSync as readFile } from './file.js'
import { read } from './yaml.js';
import AssetsProcessor from './assets_processor.js' import AssetsProcessor from './assets_processor.js'
import EnvGenerator from './env_generator.js' import EnvGenerator from './env_generator.js'
export default function ({ backgrounds, musicMapping }) { export default function (dataDir, { backgrounds, musicMapping }) {
const extractedFolder = path.join(__projectRoot, __config.folder.operator, '_directory') const extractedFolder = path.join(dataDir, '_directory')
const targetFolder = path.join(__projectRoot, __config.folder.release, __config.folder.directory); const targetFolder = path.join(__projectRoot, __config.folder.release, __config.folder.directory);
const directoryAssetFolder = path.join(__projectRoot, 'directory', 'src'); const directoryAssetFolder = path.join(__projectRoot, 'directory', 'src');
const sourceFolder = path.join(__projectRoot, __config.folder.operator); const sourceFolder = path.join(__projectRoot, __config.folder.operator);
@@ -24,7 +23,7 @@ export default function ({ backgrounds, musicMapping }) {
cur.workshopId = null cur.workshopId = null
try { try {
cur.workshopId = JSON.parse(readFile(path.join(__projectRoot, __config.folder.operator, cur.link, 'project.json'))).workshopid cur.workshopId = JSON.parse(readFile(path.join(dataDir, cur.link, 'project.json'))).workshopid
} catch (e) { } catch (e) {
console.log(`No workshop id for ${cur.link}!`) console.log(`No workshop id for ${cur.link}!`)
} }

View File

@@ -73,3 +73,11 @@ export function readdirSync(dir) {
} }
return fs.readdirSync(dir) return fs.readdirSync(dir)
} }
export function fileTypeSync(dir) {
if (!exists(dir)) {
console.warn(`Source ${dir} does not exist.`)
return null
}
return fs.statSync(dir).isDirectory() ? 'dir' : 'file'
}

View File

@@ -5,7 +5,7 @@ import Downloader from "./downloader.js"
export default class Music { export default class Music {
#downloader = new Downloader() #downloader = new Downloader()
#sharedPath = path.join(__projectRoot, __config.folder.operator, __config.folder.share) #sharedPath = path.join(__projectRoot, __config.folder.auto_update_data)
async process() { async process() {
const { metaTable, audioDataTable } = await this.#download() const { metaTable, audioDataTable } = await this.#download()
@@ -49,8 +49,8 @@ export default class Music {
} }
} }
copy() { copy(shareDir) {
const musicFolder = path.join(__projectRoot, __config.folder.operator, __config.folder.share, __config.folder.music); const musicFolder = path.join(shareDir, __config.folder.music);
const musicTable = JSON.parse(readSync(path.join(this.#sharedPath, `music_table.json`))) const musicTable = JSON.parse(readSync(path.join(this.#sharedPath, `music_table.json`)))
const musicMapping = {} const musicMapping = {}
const musicToCopy = [] const musicToCopy = []

View File

@@ -4,23 +4,18 @@
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "node aklive2d dev", "operator:dev": "node aklive2d operator:dev",
"build": "node aklive2d build", "operator:build": "node aklive2d operator:build",
"build-all": "node aklive2d build-all", "operator:build-all": "node aklive2d operator:build-all",
"preview": "node aklive2d preview",
"init": "node aklive2d init", "init": "node aklive2d init",
"directory": "node aklive2d directory",
"charwords:update": "node aklive2d charwords:update", "charwords:update": "node aklive2d charwords:update",
"charwords:build": "node aklive2d charwords:build",
"logo": "node aklive2d logo",
"music": "node aklive2d music", "music": "node aklive2d music",
"vite:dev": "vite", "directory:dev": "vite dev",
"vite:build": "vite build", "directory:build": "vite build",
"vite:preview": "vite preview", "directory:preview": "vite preview",
"vite:directory:dev": "vite dev", "offical_update": "node aklive2d offical_update",
"vite:directory:build": "vite build", "cf:upload": "node aklive2d cf:upload",
"vite:directory:preview": "vite preview", "cf:download": "node aklive2d cf:download"
"offical_update": "node aklive2d offical_update"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
@@ -37,7 +32,8 @@
"stylelint": "^16.8.1", "stylelint": "^16.8.1",
"stylelint-config-standard": "^36.0.1", "stylelint-config-standard": "^36.0.1",
"stylelint-config-standard-scss": "^13.1.0", "stylelint-config-standard-scss": "^13.1.0",
"vite": "^5.3.5" "vite": "^5.3.5",
"wrangler": "^3.71.0"
}, },
"dependencies": { "dependencies": {
"@parcellab/react-use-umami": "^2.0.1", "@parcellab/react-use-umami": "^2.0.1",

697
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/* eslint-disable no-undef */ /* eslint-disable no-undef */
import path from 'path' import path from 'path'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
import { defineConfig, splitVendorChunkPlugin } from 'vite' import { defineConfig } from 'vite'
import assert from 'assert' import assert from 'assert'
import react from '@vitejs/plugin-react-swc' import react from '@vitejs/plugin-react-swc'
import getConfig from './libs/config.js' import getConfig from './libs/config.js'
@@ -18,7 +18,6 @@ class ViteRunner {
#globalConfig = getConfig(this.#officalInfo) #globalConfig = getConfig(this.#officalInfo)
#mode #mode
#baseViteConfig = { #baseViteConfig = {
plugins: [splitVendorChunkPlugin()],
configFile: false, configFile: false,
base: "", base: "",
server: { server: {
@@ -32,22 +31,19 @@ class ViteRunner {
get config() { get config() {
let result; let result;
const temp = process.env.npm_lifecycle_event.split(':') const temp = process.env.npm_lifecycle_event.split(':')
this.#mode = temp[0] === "vite" ? temp[1] : process.argv[2] const runType = temp[0]
switch (this.#mode) { this.#mode = temp[1]
switch (runType) {
case 'directory': case 'directory':
result = { result = {
data: this.#directoryConfig, data: this.#directoryConfig,
} }
const op = temp[2] || process.argv[3] if (this.#mode !== 'preview') {
if (op !== 'preview') {
rmdir(path.resolve(__projectRoot, this.#globalConfig.folder.release, "_directory")) rmdir(path.resolve(__projectRoot, this.#globalConfig.folder.release, "_directory"))
rmdir(path.resolve(__projectRoot, this.#globalConfig.folder.release, "index.html")) rmdir(path.resolve(__projectRoot, this.#globalConfig.folder.release, "index.html"))
} }
break break
case 'dev': case 'operator':
case 'build':
case 'build-all':
case 'preview':
result = { result = {
data: this.#operatorConfig, data: this.#operatorConfig,
} }
@@ -143,12 +139,10 @@ class ViteRunner {
} }
get #directoryConfig() { get #directoryConfig() {
if (process.env.npm_lifecycle_event === 'vite:directory:build') { if (process.env.npm_lifecycle_event === 'directory:build') {
global.__config = this.#globalConfig global.__config = this.#globalConfig
} }
const directoryDir = path.resolve(__projectRoot, this.#globalConfig.folder.directory_src) const directoryDir = path.resolve(__projectRoot, this.#globalConfig.folder.directory_src)
this.#mode = process.argv[3]
// const publicDir = path.resolve(__projectRoot, this.#globalConfig.folder.release)
const assetsDir = '_directory' const assetsDir = '_directory'
return { return {
...this.#baseViteConfig, ...this.#baseViteConfig,
@@ -191,15 +185,18 @@ class ViteRunner {
} }
async function main() { async function main() {
if (process.env.npm_lifecycle_event.includes('vite')) { if (process.env.npm_lifecycle_event.includes('directory')) {
const officalInfo = new OfficalInfo() const officalInfo = new OfficalInfo()
global.__config = getConfig(officalInfo) global.__config = getConfig(officalInfo)
const background = new Background() const OPERATOR_SOURCE_FOLDER = path.join(__projectRoot, __config.folder.operator)
const OPERATOR_SOURCE_DATA_FOLDER = path.join(__projectRoot, __config.folder.operator_data)
const OPERATOR_SHARE_FOLDER = path.join(OPERATOR_SOURCE_DATA_FOLDER, __config.folder.share)
const background = new Background(OPERATOR_SHARE_FOLDER, OPERATOR_SOURCE_FOLDER)
await background.process() await background.process()
const backgrounds = ['operator_bg.png', ...background.files] const backgrounds = ['operator_bg.png', ...background.files]
const { musicMapping } = (new Music()).copy() const { musicMapping } = (new Music()).copy(OPERATOR_SHARE_FOLDER)
directory({ backgrounds, musicMapping }) directory(OPERATOR_SOURCE_DATA_FOLDER, { backgrounds, musicMapping })
return return
} }
const runner = new ViteRunner() const runner = new ViteRunner()