Commit 8f62bc29 authored by nanahira's avatar nanahira

support direct load from ygocdb data

parent 4c8cf63d
Pipeline #41215 passed with stages
in 4 minutes and 2 seconds
...@@ -36,6 +36,7 @@ lerna-debug.log* ...@@ -36,6 +36,7 @@ lerna-debug.log*
/data /data
/output /output
/config.yaml /config.yaml
/ygocdb-data
.git* .git*
Dockerfile Dockerfile
......
...@@ -35,4 +35,5 @@ lerna-debug.log* ...@@ -35,4 +35,5 @@ lerna-debug.log*
/data /data
/output /output
/config.yaml /config.yaml
\ No newline at end of file /ygocdb-data
\ No newline at end of file
...@@ -15,12 +15,22 @@ import { ...@@ -15,12 +15,22 @@ import {
import { FileInterceptor } from '@nestjs/platform-express'; import { FileInterceptor } from '@nestjs/platform-express';
import { FileUploadDto } from './dto/file-upload.dto'; import { FileUploadDto } from './dto/file-upload.dto';
import YGOProDeck from 'ygopro-deck-encode'; import YGOProDeck from 'ygopro-deck-encode';
import { ApiError, BlankReturnMessageDto, DataQuery } from 'nesties'; import {
ApiBlankResponse,
ApiError,
BlankReturnMessageDto,
DataQuery,
RequireToken,
} from 'nesties';
import { FillOptionsDto } from './dto/fill-options.dto'; import { FillOptionsDto } from './dto/fill-options.dto';
import { CardDataService } from './card-data/card-data.service';
@Controller() @Controller()
export class AppController { export class AppController {
constructor(private readonly appService: AppService) {} constructor(
private readonly appService: AppService,
private readonly cardDataService: CardDataService,
) {}
@Post('fill') @Post('fill')
@ApiOperation({ @ApiOperation({
...@@ -53,4 +63,15 @@ export class AppController { ...@@ -53,4 +63,15 @@ export class AppController {
} }
return this.appService.fillDeckForm(ydk, dto); return this.appService.fillDeckForm(ydk, dto);
} }
@Post('reload-ygocdb')
@ApiOperation({
summary: 'Reload YGOCDB Data',
})
@ApiBlankResponse()
@RequireToken()
async reloadYGOCDBData() {
await this.cardDataService.loadYGOCDBData();
return new BlankReturnMessageDto(200, 'success');
}
} }
...@@ -29,6 +29,7 @@ export class AppService extends ConsoleLogger { ...@@ -29,6 +29,7 @@ export class AppService extends ConsoleLogger {
const doc = await PDFDocument.load( const doc = await PDFDocument.load(
await fs.promises.readFile('./resources/deck_cn.pdf'), await fs.promises.readFile('./resources/deck_cn.pdf'),
); );
this.log(`Loaded PDF template for deck export.`);
const directDrawFields = ['name', 'event', 'lastInitial'] as const; const directDrawFields = ['name', 'event', 'lastInitial'] as const;
for (const field of directDrawFields) { for (const field of directDrawFields) {
if (!dto[field]) continue; if (!dto[field]) continue;
...@@ -38,6 +39,7 @@ export class AppService extends ConsoleLogger { ...@@ -38,6 +39,7 @@ export class AppService extends ConsoleLogger {
Coordinates[field], Coordinates[field],
); );
} }
this.log(`Filled direct draw fields.`);
if (dto.date) { if (dto.date) {
const date = moment(dto.date); const date = moment(dto.date);
// we need a dd-mm-yy format // we need a dd-mm-yy format
...@@ -61,6 +63,7 @@ export class AppService extends ConsoleLogger { ...@@ -61,6 +63,7 @@ export class AppService extends ConsoleLogger {
moveRight(Coordinates.dateFirst, i), moveRight(Coordinates.dateFirst, i),
); );
} }
this.log(`Filled date field.`);
} }
const totalCounts = [ydk.main.length, ydk.extra.length, ydk.side.length]; const totalCounts = [ydk.main.length, ydk.extra.length, ydk.side.length];
...@@ -73,6 +76,8 @@ export class AppService extends ConsoleLogger { ...@@ -73,6 +76,8 @@ export class AppService extends ConsoleLogger {
); );
} }
this.log(`Filled total counts.`);
const copyFirstPageToBottom = async () => { const copyFirstPageToBottom = async () => {
const [newPage] = await doc.copyPages(doc, [0]); const [newPage] = await doc.copyPages(doc, [0]);
doc.addPage(newPage); doc.addPage(newPage);
...@@ -87,6 +92,7 @@ export class AppService extends ConsoleLogger { ...@@ -87,6 +92,7 @@ export class AppService extends ConsoleLogger {
const addCount = pc - pageCount; const addCount = pc - pageCount;
for (let i = 0; i < addCount; ++i) { for (let i = 0; i < addCount; ++i) {
await copyFirstPageToBottom(); await copyFirstPageToBottom();
this.log(`Added page ${pageCount + i + 1} to PDF for deck export.`);
} }
pageCount = pc; pageCount = pc;
}; };
...@@ -138,14 +144,22 @@ export class AppService extends ConsoleLogger { ...@@ -138,14 +144,22 @@ export class AppService extends ConsoleLogger {
const filterType = (type: number) => const filterType = (type: number) =>
ydk.main.filter((id) => cardDataMap[id].type === type); ydk.main.filter((id) => cardDataMap[id].type === type);
await drawCards(filterType(1), Coordinates.main.monsters); await drawCards(filterType(1), Coordinates.main.monsters);
this.log(`Filled monster cards.`);
await drawCards(filterType(2), Coordinates.main.spells); await drawCards(filterType(2), Coordinates.main.spells);
this.log(`Filled spell cards.`);
await drawCards(filterType(4), Coordinates.main.traps); await drawCards(filterType(4), Coordinates.main.traps);
this.log(`Filled trap cards.`);
await drawCards(ydk.extra, Coordinates.extra, 15); await drawCards(ydk.extra, Coordinates.extra, 15);
this.log(`Filled extra deck cards.`);
await drawCards(ydk.side, Coordinates.side, 15); await drawCards(ydk.side, Coordinates.side, 15);
this.log(`Filled side deck cards.`);
doc.removePage(0); doc.removePage(0);
return new StreamableFile(Buffer.from(await doc.save()), { const resBuf = Buffer.from(await doc.save());
this.log(`Generated PDF buffer for deck export.`);
return new StreamableFile(resBuf, {
type: 'application/pdf', type: 'application/pdf',
disposition: `attachment; filename="deck_${Date.now()}.pdf"`, disposition: `attachment; filename="deck_${Date.now()}.pdf"`,
}); });
......
...@@ -6,6 +6,7 @@ import { CacheKey, CacheTTL } from 'aragami'; ...@@ -6,6 +6,7 @@ import { CacheKey, CacheTTL } from 'aragami';
import { lastValueFrom } from 'rxjs'; import { lastValueFrom } from 'rxjs';
import BetterLock from 'better-lock'; import BetterLock from 'better-lock';
import { GenericReturnMessageDto } from 'nesties'; import { GenericReturnMessageDto } from 'nesties';
import * as fs from 'node:fs';
@CacheTTL(86400 * 30) @CacheTTL(86400 * 30)
export class CardData { export class CardData {
...@@ -19,6 +20,13 @@ export class CardData { ...@@ -19,6 +20,13 @@ export class CardData {
type: number; // 0x1 for monster, 0x2 for spell, 0x4 for trap type: number; // 0x1 for monster, 0x2 for spell, 0x4 for trap
} }
type YGOCDBData = {
id: number;
sc_name: string;
cn_name: string;
data: { type: number };
};
@Injectable() @Injectable()
export class CardDataService { export class CardDataService {
constructor( constructor(
...@@ -28,15 +36,20 @@ export class CardDataService { ...@@ -28,15 +36,20 @@ export class CardDataService {
private logger = new ConsoleLogger('CardDataService'); private logger = new ConsoleLogger('CardDataService');
private async fetchCardData(id: number): Promise<CardData> { private parseYgoCardType(card: YGOCDBData) {
// console.log(card);
const data = new CardData(); const data = new CardData();
data.id = id; if (!card.id || !card.data) return;
data.id = card.id;
data.name = card.sc_name || card.cn_name || '未知卡片';
data.type = card.data.type & 0x7;
return data;
}
private async fetchCardData(id: number): Promise<CardData> {
this.logger.log(`Fetching card data for ID ${id}`); this.logger.log(`Fetching card data for ID ${id}`);
const { data: resp, status } = await lastValueFrom( const { data: resp, status } = await lastValueFrom(
this.http.get<{ this.http.get<YGOCDBData>('https://ygocdb.com/api/v0/card/' + id, {
sc_name: string;
data: { type: number };
}>('https://ygocdb.com/api/v0/card/' + id, {
timeout: 30000, timeout: 30000,
params: { params: {
show: 'all', show: 'all',
...@@ -51,8 +64,7 @@ export class CardDataService { ...@@ -51,8 +64,7 @@ export class CardDataService {
`Card ID ${id} not found`, `Card ID ${id} not found`,
).toException(); ).toException();
} }
data.name = resp.sc_name; const data = this.parseYgoCardType(resp);
data.type = resp.data.type & 0x7;
this.logger.log( this.logger.log(
`Fetched card data for ID ${id}: ${data.type} ${data.name}`, `Fetched card data for ID ${id}: ${data.type} ${data.name}`,
); );
...@@ -61,7 +73,36 @@ export class CardDataService { ...@@ -61,7 +73,36 @@ export class CardDataService {
private cardIdLock = new BetterLock(); private cardIdLock = new BetterLock();
private ygocdbExisting = new Map<number, CardData>();
async loadYGOCDBData() {
const content = await fs.promises.readFile('./ygocdb-data/cards.json');
const cards: YGOCDBData[] = Object.values(
JSON.parse(content.toString('utf-8')),
);
this.logger.log(`Loading ${cards.length} cards from YGOCDB data`);
this.ygocdbExisting.clear();
for (const card of cards) {
const data = this.parseYgoCardType(card);
this.ygocdbExisting.set(card.id, data);
}
}
async onApplicationBootstrap() {
try {
await this.loadYGOCDBData();
this.logger.log(
`Loaded ${this.ygocdbExisting.size} cards from YGOCDB data`,
);
} catch (e) {
this.logger.warn(`Failed to load YGOCDB data: ${e.message}`);
}
}
async getCardData(id: number): Promise<CardData> { async getCardData(id: number): Promise<CardData> {
if (this.ygocdbExisting.has(id)) {
return this.ygocdbExisting.get(id);
}
return this.cardIdLock.acquire(id.toString(), () => return this.cardIdLock.acquire(id.toString(), () =>
this.aragami.cache(CardData, id.toString(), () => this.fetchCardData(id)), this.aragami.cache(CardData, id.toString(), () => this.fetchCardData(id)),
); );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment