forked from noxious/server
#16: Working PoC avatar image generator
This commit is contained in:
73
src/http/avatar.ts
Normal file
73
src/http/avatar.ts
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Avatar generator API routes
|
||||
*/
|
||||
import { Router, Request, Response } from 'express'
|
||||
import sharp from 'sharp'
|
||||
import fs from 'fs'
|
||||
import CharacterRepository from '../repositories/characterRepository'
|
||||
import CharacterHairRepository from '../repositories/characterHairRepository'
|
||||
import CharacterTypeRepository from '../repositories/characterTypeRepository'
|
||||
import { getPublicPath } from '../utilities/storage'
|
||||
|
||||
const router = Router()
|
||||
|
||||
interface AvatarOptions {
|
||||
characterTypeId: number
|
||||
characterHairId?: number
|
||||
}
|
||||
|
||||
async function generateAvatar(res: Response, options: AvatarOptions) {
|
||||
try {
|
||||
const characterType = await CharacterTypeRepository.getById(options.characterTypeId)
|
||||
if (!characterType?.spriteId) {
|
||||
return res.status(404).json({ message: 'Character type not found' })
|
||||
}
|
||||
|
||||
const bodySpritePath = getPublicPath('sprites', characterType.spriteId, 'idle_right_down.png')
|
||||
if (!fs.existsSync(bodySpritePath)) {
|
||||
console.error(`Body sprite file not found: ${bodySpritePath}`)
|
||||
return res.status(404).json({ message: 'Body sprite file not found' })
|
||||
}
|
||||
|
||||
let avatar = sharp(bodySpritePath)
|
||||
|
||||
if (options.characterHairId) {
|
||||
const characterHair = await CharacterHairRepository.getById(options.characterHairId)
|
||||
if (characterHair?.spriteId) {
|
||||
const hairSpritePath = getPublicPath('sprites', characterHair.spriteId, 'front.png')
|
||||
if (fs.existsSync(hairSpritePath)) {
|
||||
avatar = avatar.composite([{ input: hairSpritePath, gravity: 'north' }])
|
||||
} else {
|
||||
console.error(`Hair sprite file not found: ${hairSpritePath}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'image/png')
|
||||
return avatar.pipe(res)
|
||||
} catch (error) {
|
||||
console.error('Error generating avatar:', error)
|
||||
return res.status(500).json({ message: 'Error generating avatar' })
|
||||
}
|
||||
}
|
||||
|
||||
router.get('/avatar/:characterName', async (req: Request, res: Response) => {
|
||||
const character = await CharacterRepository.getByName(req.params.characterName)
|
||||
if (!character?.characterType) {
|
||||
return res.status(404).json({ message: 'Character or character type not found' })
|
||||
}
|
||||
|
||||
return generateAvatar(res, {
|
||||
characterTypeId: character.characterType.id,
|
||||
characterHairId: character.characterHair?.id
|
||||
})
|
||||
})
|
||||
|
||||
router.get('/avatar/s/:characterTypeId/:characterHairId?', async (req: Request, res: Response) => {
|
||||
return generateAvatar(res, {
|
||||
characterTypeId: parseInt(req.params.characterTypeId),
|
||||
characterHairId: req.params.characterHairId ? parseInt(req.params.characterHairId) : undefined
|
||||
})
|
||||
})
|
||||
|
||||
export default router
|
@ -12,6 +12,11 @@ class CharacterHairRepository {
|
||||
}
|
||||
})
|
||||
}
|
||||
async getById(id: number): Promise<CharacterHair | null> {
|
||||
return prisma.characterHair.findUnique({
|
||||
where: { id }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default new CharacterHairRepository()
|
||||
|
@ -3,7 +3,19 @@ import { CharacterType } from '@prisma/client'
|
||||
|
||||
class CharacterTypeRepository {
|
||||
async getAll(): Promise<CharacterType[]> {
|
||||
return prisma.characterType.findMany()
|
||||
return prisma.characterType.findMany({
|
||||
include: {
|
||||
sprite: true
|
||||
}
|
||||
})
|
||||
}
|
||||
async getById(id: number): Promise<CharacterType | null> {
|
||||
return prisma.characterType.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
sprite: true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user