feat: add charx import

This commit is contained in:
kwaroran
2024-06-03 16:59:44 +09:00
parent 8dff1a6cde
commit ad7ce3f0d1
3 changed files with 118 additions and 7 deletions

View File

@@ -12,13 +12,13 @@ import { CCardLib, type CharacterCardV3, type LorebookEntry } from '@risuai/ccar
import { reencodeImage } from "./process/files/image"
import { PngChunk } from "./pngChunk"
import type { OnnxModelFiles } from "./process/transformers"
import { CharXWriter } from "./process/processzip"
import { CharXReader, CharXWriter } from "./process/processzip"
export const hubURL = "https://sv.risuai.xyz"
export async function importCharacter() {
try {
const files = await selectFileByDom(['png', 'json'], 'multiple')
const files = await selectFileByDom(["*"], 'multiple')
if(!files){
return
}
@@ -63,6 +63,36 @@ async function importCharacterProcess(f:{
return
}
}
if(f.name.endsWith('charx')){
console.log('reading charx')
const reader = new CharXReader()
alertStore.set({
type: 'wait',
msg: 'Loading... (Reading)'
})
await reader.read(f.data)
const cardData = reader.cardData
if(!cardData){
alertError(language.errors.noData)
return
}
const card = JSON.parse(cardData)
if(CCardLib.character.check(card) !== 'v3'){
alertError(language.errors.noData)
return
}
await importCharacterCardSpec(card, undefined, 'normal', reader.assets)
let db = get(DataBase)
return db.characters.length - 1
}
if(!f.name.endsWith('png')){
alertError(language.errors.noData)
return
}
alertStore.set({
type: 'wait',
msg: 'Loading... (Reading)'
@@ -486,6 +516,13 @@ async function importCharacterCardSpec(card:CharacterCardV2Risu|CharacterCardV3,
else if(data.assets[i].uri === 'ccdefault:'){
imgp = im
}
else if(data.assets[i].uri.startsWith('embeded://')){
const key = data.assets[i].uri.replace('embeded://', '')
imgp = assetDict[key]
if(!imgp){
throw new Error('Error while importing, asset ' + key + ' not found')
}
}
else{
continue
}

View File

@@ -1,5 +1,6 @@
import { AppendableBuffer, type LocalWriter, type VirtualWriter } from "../storage/globalApi";
import { AppendableBuffer, saveAsset, type LocalWriter, type VirtualWriter } from "../storage/globalApi";
import * as fflate from "fflate";
import { sleep } from "../util";
export async function processZip(dataArray: Uint8Array): Promise<string> {
const jszip = await import("jszip");
@@ -69,4 +70,74 @@ export class CharXWriter{
await this.writer.close()
}
}
}
export class CharXReader{
unzip:fflate.Unzip
assets:{[key:string]:string} = {}
assetBuffers:{[key:string]:AppendableBuffer} = {}
assetPromises:Promise<void>[] = []
excludedFiles:string[] = []
cardData:string|undefined
constructor(){
this.unzip = new fflate.Unzip()
this.unzip.register(fflate.UnzipInflate)
this.unzip.onfile = (file) => {
const assetIndex = file.name
this.assetBuffers[assetIndex] = new AppendableBuffer()
file.ondata = (err, dat, final) => {
this.assetBuffers[assetIndex].append(dat)
if(final){
const assetData = this.assetBuffers[assetIndex].buffer
if(assetData.byteLength > 50 * 1024 * 1024){
this.excludedFiles.push(assetIndex)
}
else if(file.name === 'card.json'){
this.cardData = new TextDecoder().decode(assetData)
}
else{
this.assetPromises.push((async () => {
const assetId = await saveAsset(assetData)
this.assets[assetIndex] = assetId
})())
}
}
}
if(file.originalSize ?? 0 < 50 * 1024 * 1024){
file.start()
}
}
}
async read(data:Uint8Array|File|ReadableStream<Uint8Array>){
if(data instanceof Uint8Array){
this.unzip.push(data, true)
}
if(data instanceof File){
const reader = data.stream().getReader()
while(true){
const {done, value} = await reader.read()
if(done){
break
}
this.unzip.push(value, false)
}
this.unzip.push(new Uint8Array(0), true)
}
if(data instanceof ReadableStream){
const reader = data.getReader()
while(true){
const {done, value} = await reader.read()
if(done){
break
}
this.unzip.push(value, false)
}
this.unzip.push(new Uint8Array(0), true)
}
await sleep(500)
await Promise.all(this.assetPromises)
}
}

View File

@@ -125,12 +125,15 @@ export function selectFileByDom(allowedExtensions:string[], multiple:'multiple'|
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.multiple = multiple === 'multiple';
if(!(get(DataBase).allowAllExtentionFiles || checkIsIos())){
const acceptAll = (get(DataBase).allowAllExtentionFiles || checkIsIos() || allowedExtensions[0] === '*')
if(!acceptAll){
if (allowedExtensions && allowedExtensions.length) {
fileInput.accept = allowedExtensions.map(ext => `.${ext}`).join(',');
}
}
else{
fileInput.accept = '*'
}
fileInput.addEventListener('change', (event) => {
@@ -139,10 +142,10 @@ export function selectFileByDom(allowedExtensions:string[], multiple:'multiple'|
return;
}
const files = Array.from(fileInput.files).filter(file => {
const files = acceptAll ? Array.from(fileInput.files) :(Array.from(fileInput.files).filter(file => {
const fileExtension = file.name.split('.').pop().toLowerCase();
return !allowedExtensions || allowedExtensions.includes(fileExtension);
});
}))
fileInput.remove()
resolve(files);