@xrengine/server-core
Version:
Shared components for XREngine server
147 lines (127 loc) • 5.05 kB
text/typescript
import fs from 'fs'
import path from 'path'
import { CommonKnownContentTypes } from '@xrengine/common/src/utils/CommonKnownContentTypes'
import { Application } from '../../../declarations'
import { addGenericAssetToS3AndStaticResources } from '../../media/upload-asset/upload-asset.service'
import { getProjectPackageJson } from '../../projects/project/project-helper'
import logger from '../../ServerLogger'
import { UserParams } from '../user/user.class'
export type AvatarCreateArguments = {
modelResourceId?: string
thumbnailResourceId?: string
identifierName?: string
name: string
isPublic?: boolean
project?: string
}
export type AvatarPatchArguments = {
modelResourceId?: string
thumbnailResourceId?: string
identifierName?: string
name?: string
}
export type AvatarUploadArguments = {
avatar: Buffer
thumbnail: Buffer
avatarName: string
isPublic: boolean
avatarFileType?: string
avatarId?: string
project?: string
}
// todo: move this somewhere else
const supportedAvatars = ['glb', 'gltf', 'vrm', 'fbx']
const PROJECT_NAME_REGEX = /projects\/([a-zA-Z0-9-_.]+)\/public\/avatars$/
export const installAvatarsFromProject = async (app: Application, avatarsFolder: string) => {
const avatarsToInstall = fs
.readdirSync(avatarsFolder, { withFileTypes: true })
.filter((dirent) => supportedAvatars.includes(dirent.name.split('.').pop()!))
.map((dirent) => {
const avatarName = dirent.name.substring(0, dirent.name.lastIndexOf('.')) // remove extension
const avatarFileType = dirent.name.substring(dirent.name.lastIndexOf('.') + 1, dirent.name.length) // just extension
const pngPath = path.join(avatarsFolder, avatarName + '.png')
const thumbnail = fs.existsSync(pngPath) ? fs.readFileSync(pngPath) : Buffer.from([])
return {
avatar: fs.readFileSync(path.join(avatarsFolder, dirent.name)),
thumbnail,
avatarName,
avatarFileType,
isPublic: true
} as AvatarUploadArguments
})
const promises: Promise<any>[] = []
const projectNameExec = PROJECT_NAME_REGEX.exec(avatarsFolder)
let projectJSON
if (projectNameExec) projectJSON = getProjectPackageJson(projectNameExec[1])
let projectName
if (projectJSON) projectName = projectJSON.name
for (const avatar of avatarsToInstall) {
promises.push(
new Promise(async (resolve, reject) => {
try {
const existingAvatar = await app.service('avatar').Model.findOne({
where: {
name: avatar.avatarName,
isPublic: avatar.isPublic,
project: projectName || null
}
})
let selectedAvatar
if (!existingAvatar) {
selectedAvatar = await app.service('avatar').create({
name: avatar.avatarName,
isPublic: avatar.isPublic,
project: projectName || null
})
} else selectedAvatar = existingAvatar
avatar.avatarName = selectedAvatar.identifierName
avatar.project = projectName || null
const [modelResource, thumbnailResource] = await uploadAvatarStaticResource(app, avatar)
await app.service('avatar').patch(selectedAvatar.id, {
modelResourceId: modelResource.id,
thumbnailResourceId: thumbnailResource.id
})
resolve(null)
} catch (err) {
logger.error(err)
reject(err)
}
})
)
}
await Promise.all(promises)
}
export const uploadAvatarStaticResource = async (
app: Application,
data: AvatarUploadArguments,
params?: UserParams
) => {
const name = data.avatarName ? data.avatarName : 'Avatar-' + Math.round(Math.random() * 100000)
const key = `avatars/${data.isPublic ? 'public' : params?.user!.id}/${name}`
// const thumbnail = await generateAvatarThumbnail(data.avatar as Buffer)
// if (!thumbnail) throw new Error('Thumbnail generation failed - check the model')
const modelPromise = addGenericAssetToS3AndStaticResources(app, data.avatar, CommonKnownContentTypes.glb, {
userId: params?.user!.id,
key: `${key}.${data.avatarFileType ?? 'glb'}`,
staticResourceType: 'avatar',
project: data.project
})
const thumbnailPromise = addGenericAssetToS3AndStaticResources(app, data.thumbnail, CommonKnownContentTypes.png, {
userId: params?.user!.id,
key: `${key}.${data.avatarFileType ?? 'glb'}.png`,
staticResourceType: 'user-thumbnail',
project: data.project
})
const [modelResource, thumbnailResource] = await Promise.all([modelPromise, thumbnailPromise])
logger.info('Successfully uploaded avatar %o %o', modelResource, thumbnailResource)
if (data.avatarId) {
try {
await app.service('avatar').patch(data.avatarId, {
modelResourceId: modelResource.id,
thumbnailResourceId: thumbnailResource.id
})
} catch (err) {}
}
return [modelResource, thumbnailResource]
}
export const removeAvatarFromDatabase = async (app: Application, name: string) => {}