Skip to content

Commit

Permalink
feat: automation for models downloading
Browse files Browse the repository at this point in the history
  • Loading branch information
nekomeowww committed Dec 2, 2024
1 parent 0bbdb0c commit 43623a6
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 31 deletions.
2 changes: 1 addition & 1 deletion components/MainStage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ onUnmounted(() => {
</div>
<div flex="~ row 1" w-full items-end space-x-2>
<div w-full min-h="100 sm:100">
<Live2DViewer ref="live2DViewerRef" :mouth-open-size="mouthOpenSize" model="assets/live2d/models/hiyori_pro_zh/hiyori_pro_t11.model3.json" />
<Live2DViewer ref="live2DViewerRef" :mouth-open-size="mouthOpenSize" model="assets/live2d/models/hiyori_pro_zh/runtime/hiyori_pro_t11.model3.json" />
<!-- <div>
<input v-model.number="mouthOpenSize" type="range" max="1" min="0" step="0.01">
<span>{{ mouthOpenSize }}</span>
Expand Down
1 change: 1 addition & 0 deletions cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ words:
- rehype
- unocss
- vueuse
- live2dcubismcore
ignoreWords: []
import: []
72 changes: 62 additions & 10 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default defineNuxtConfig({
prerender: {
crawlLinks: false,
routes: ['/'],
ignore: ['/hi'],
ignore: ['/assets/js/CubismSdkForWeb-5-r.1/Core/live2dcubismcore.min.js'],
},
experimental: {
websocket: true,
Expand Down Expand Up @@ -83,20 +83,72 @@ export default defineNuxtConfig({
vite: {
plugins: [
{
name: 'live2d-cubism',
name: 'live2d-cubism-sdk',
async configResolved(config) {
if (await exists(join(config.root, 'public/assets/js/CubismSdkForWeb-5-r.1'))) {
return
try {
if (await exists(join(config.root, 'public/assets/js/CubismSdkForWeb-5-r.1'))) {
return
}

console.log('Downloading Cubism SDK...')
const stream = await ofetch('https://dist.ayaka.moe/npm/live2d-cubism/CubismSdkForWeb-5-r.1.zip', { responseType: 'arrayBuffer' })

console.log('Unzipping Cubism SDK...')
await mkdir(join(config.root, 'public/assets/js'), { recursive: true })
await unzip(Buffer.from(stream), join(config.root, 'public/assets/js'))

console.log('Cubism SDK downloaded and unzipped.')
}
catch (err) {
console.error(err)
throw err
}
},
},
{
name: 'live2d-models-hiyori-free',
async configResolved(config) {
try {
if (await exists(join(config.root, 'public/assets/live2d/models/hiyori_free_zh'))) {
return
}

console.log('Downloading Cubism SDK...')
const stream = await ofetch('https://dist.ayaka.moe/npm/live2d-cubism/CubismSdkForWeb-5-r.1.zip', { responseType: 'arrayBuffer' })
console.log('Downloading Demo Live2D Model - Hiyori Free...')
const stream = await ofetch('https://dist.ayaka.moe/live2d-models/hiyori_free_zh.zip', { responseType: 'arrayBuffer' })

console.log('Unzipping Cubism SDK...')
await mkdir(join(config.root, 'public/assets/js'), { recursive: true })
await unzip(Buffer.from(stream), join(config.root, 'public/assets/js'))
console.log('Unzipping Demo Live2D Model - Hiyori Free...')
await mkdir(join(config.root, 'public/assets/live2d/models'), { recursive: true })
await unzip(Buffer.from(stream), join(config.root, 'public/assets/live2d/models'))

console.log('Cubism SDK downloaded and unzipped.')
console.log('Demo Live2D Model - Hiyori Free downloaded and unzipped.')
}
catch (err) {
console.error(err)
throw err
}
},
},
{
name: 'live2d-models-hiyori-pro',
async configResolved(config) {
try {
if (await exists(join(config.root, 'public/assets/live2d/models/hiyori_pro_zh'))) {
return
}

console.log('Downloading Demo Live2D Model - Hiyori Pro...')
const stream = await ofetch('https://dist.ayaka.moe/live2d-models/hiyori_pro_zh.zip', { responseType: 'arrayBuffer' })

console.log('Unzipping Demo Live2D Model - Hiyori Pro...')
await mkdir(join(config.root, 'public/assets/live2d/models'), { recursive: true })
await unzip(Buffer.from(stream), join(config.root, 'public/assets/live2d/models'))

console.log('Demo Live2D Model - Hiyori Pro downloaded and unzipped.')
}
catch (err) {
console.error(err)
throw err
}
},
},
],
Expand Down
11 changes: 5 additions & 6 deletions scripts/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@ export function rejectIfError<E = unknown>(error: E | undefined, reject: (error?
}
}

export function onError<E = unknown>(reject: (error?: E) => void, handler?: (error?: E) => void) {
return (error?: E) => rejectIfError(error, reject, handler)
}

export function resolveWhenNoError<R = void, E = unknown>(reject: (error?: E) => void, resolve: (result?: R) => void) {
return (err: E) => {
return (err?: E) => {
if (err) {
reject(err)
}
Expand All @@ -20,6 +16,10 @@ export function resolveWhenNoError<R = void, E = unknown>(reject: (error?: E) =>
}
}

export function onError<E = unknown>(reject: (error?: E) => void, handler?: (error?: E) => void) {
return (error?: E) => rejectIfError(error, reject, handler)
}

export function noError<
T,
U extends unknown[],
Expand All @@ -33,7 +33,6 @@ export function noError<
rejectIfError(err, reject)
return
}

return fn(...args)
}
}
21 changes: 14 additions & 7 deletions scripts/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@ export async function exists(path: string) {
return true
}
catch (error) {
if (!(error instanceof Error))
throw error
if (!('code' in error))
throw error
if (error.code !== 'ENOENT')
throw error
if (isENOENTError(error))
return false

return false
throw error
}
}

export function isENOENTError(error: unknown): boolean {
if (!(error instanceof Error))
return false
if (!('code' in error))
return false
if (error.code !== 'ENOENT')
return false

return true
}
35 changes: 28 additions & 7 deletions scripts/unzip.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Buffer } from 'node:buffer'
import { createWriteStream, mkdirSync } from 'node:fs'
import { join } from 'node:path'
import { createWriteStream, existsSync, mkdirSync } from 'node:fs'
import { dirname, join } from 'node:path'
import { fromBuffer } from 'yauzl'
import { noError, onError, resolveWhenNoError } from './errors'

Expand All @@ -21,6 +21,8 @@ import { noError, onError, resolveWhenNoError } from './errors'
*/
export async function unzip(buffer: Buffer, target: string) {
return new Promise<void>((resolve, reject) => {
let pendingWrites = 0

fromBuffer(buffer, { lazyEntries: true }, noError(reject, (zipFile) => {
// This is the key. We start by reading the first entry.
zipFile.readEntry()
Expand All @@ -29,26 +31,45 @@ export async function unzip(buffer: Buffer, target: string) {
// to disk. Then call zipFile.readEntry() again to
// trigger the next cycle.
zipFile.on('entry', (entry) => {
// Directories
// Directories
if (/\/$/.test(entry.fileName)) {
// Create the directory then read the next entry.
mkdirSync(join(target, entry.fileName))
// Create the directory then read the next entry.
mkdirSync(join(target, entry.fileName), { recursive: true })
zipFile.readEntry()

return
}

// Files
const dir = dirname(join(target, entry.fileName))
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true })
}

// Write the file to disk.
pendingWrites++
zipFile.openReadStream(entry, noError(reject, (readStream) => {
const file = createWriteStream(join(target, entry.fileName))
readStream.pipe(file)

// Handle errors
file.on('error', onError(reject, zipFile.close))
file.on('error', (err) => {
pendingWrites--
zipFile.close()
reject(err)
})

// Wait until the file is finished writing, then read the next entry.
file.on('finish', () => file.close(() => { zipFile.readEntry() }))
file.on('finish', () => {
file.close(() => {
pendingWrites--
if (pendingWrites === 0) {
resolve()
}

zipFile.readEntry()
})
})
}))
})

Expand Down

0 comments on commit 43623a6

Please sign in to comment.