refactor: add ExportResult (fix #12)
parent
8d376cfd38
commit
aabc9cf733
109
src/app.ts
109
src/app.ts
|
|
@ -35,6 +35,29 @@ type Progress = {
|
|||
total: number;
|
||||
};
|
||||
|
||||
class StepProgress {
|
||||
currentUrl?: string;
|
||||
total: number;
|
||||
exported: number;
|
||||
done: number;
|
||||
skipped: Record<string, number>;
|
||||
|
||||
constructor() {
|
||||
this.total = 1;
|
||||
this.exported = 0;
|
||||
this.done = 0;
|
||||
this.skipped = {};
|
||||
}
|
||||
}
|
||||
|
||||
function progress({ total, currentUrl, done }: StepProgress): Progress {
|
||||
return {
|
||||
total,
|
||||
currentUrl,
|
||||
current: done,
|
||||
};
|
||||
}
|
||||
|
||||
export class App {
|
||||
profile: Profile;
|
||||
env: Env;
|
||||
|
|
@ -128,7 +151,7 @@ export class App {
|
|||
const exporters = await this.getExporters();
|
||||
const initStats = () =>
|
||||
Object.fromEntries(
|
||||
exporters.map((e) => [e.name, 0]),
|
||||
exporters.map((e) => [e.name, new StepProgress()]),
|
||||
);
|
||||
let stats = initStats();
|
||||
const skipMode = this.getSkipMode();
|
||||
|
|
@ -158,13 +181,10 @@ export class App {
|
|||
fetcher,
|
||||
exporter: e,
|
||||
gameList,
|
||||
onStep: (progress) => {
|
||||
redraw(e.name, progress);
|
||||
stats[e.name] = progress.current;
|
||||
stepProgress: stats[e.name],
|
||||
onStep: () => {
|
||||
redraw(e.name, progress(stats[e.name]));
|
||||
},
|
||||
})
|
||||
.then((count) => {
|
||||
stats[e.name] = count;
|
||||
}),
|
||||
)
|
||||
.catch((err) => {
|
||||
|
|
@ -215,13 +235,10 @@ export class App {
|
|||
fetcher,
|
||||
exporter: e,
|
||||
gameList: coopBattleList,
|
||||
onStep: (progress) => {
|
||||
stats[e.name] = progress.current;
|
||||
redraw(e.name, progress);
|
||||
stepProgress: stats[e.name],
|
||||
onStep: () => {
|
||||
redraw(e.name, progress(stats[e.name]));
|
||||
},
|
||||
})
|
||||
.then((count) => {
|
||||
stats[e.name] = count;
|
||||
}),
|
||||
)
|
||||
.catch((err) => {
|
||||
|
|
@ -295,20 +312,17 @@ export class App {
|
|||
fetcher,
|
||||
exporter,
|
||||
gameList,
|
||||
stepProgress,
|
||||
onStep,
|
||||
}: {
|
||||
type: Game["type"];
|
||||
exporter: GameExporter;
|
||||
fetcher: GameFetcher;
|
||||
gameList: string[];
|
||||
onStep: (progress: Progress) => void;
|
||||
}) {
|
||||
let exported = 0;
|
||||
|
||||
onStep?.({
|
||||
current: 0,
|
||||
total: 1,
|
||||
});
|
||||
stepProgress: StepProgress;
|
||||
onStep: () => void;
|
||||
}): Promise<StepProgress> {
|
||||
onStep?.();
|
||||
|
||||
const workQueue = [
|
||||
...await exporter.notExported({
|
||||
|
|
@ -320,39 +334,56 @@ export class App {
|
|||
|
||||
const step = async (id: string) => {
|
||||
const detail = await fetcher.fetch(type, id);
|
||||
const { url } = await exporter.exportGame(detail);
|
||||
exported += 1;
|
||||
onStep?.({
|
||||
currentUrl: url,
|
||||
current: exported,
|
||||
total: workQueue.length,
|
||||
});
|
||||
const result = await exporter.exportGame(detail);
|
||||
|
||||
stepProgress.done += 1;
|
||||
stepProgress.currentUrl = undefined;
|
||||
|
||||
if (result.status === "success") {
|
||||
stepProgress.exported += 1;
|
||||
stepProgress.currentUrl = result.url;
|
||||
} else if (result.status === "skip") {
|
||||
const { skipped } = stepProgress;
|
||||
skipped[result.reason] = (skipped[result.reason] ?? 0) + 1;
|
||||
} else {
|
||||
const _never: never = result;
|
||||
}
|
||||
|
||||
onStep?.();
|
||||
};
|
||||
|
||||
if (workQueue.length > 0) {
|
||||
onStep?.({
|
||||
current: exported,
|
||||
total: workQueue.length,
|
||||
});
|
||||
stepProgress.total = workQueue.length;
|
||||
onStep?.();
|
||||
for (const battle of workQueue) {
|
||||
await step(battle);
|
||||
}
|
||||
} else {
|
||||
onStep?.({
|
||||
current: 1,
|
||||
total: 1,
|
||||
});
|
||||
stepProgress.done = 1;
|
||||
onStep?.();
|
||||
}
|
||||
|
||||
return exported;
|
||||
return stepProgress;
|
||||
}
|
||||
printStats(stats: Record<string, number>) {
|
||||
printStats(stats: Record<string, StepProgress>) {
|
||||
this.env.logger.log(
|
||||
`Exported ${
|
||||
Object.entries(stats)
|
||||
.map(([name, count]) => `${name}: ${count}`)
|
||||
.map(([name, { exported }]) => `${name}: ${exported}`)
|
||||
.join(", ")
|
||||
}`,
|
||||
);
|
||||
if (Object.values(stats).some((i) => Object.keys(i.skipped).length > 0)) {
|
||||
this.env.logger.log(
|
||||
`Skipped ${
|
||||
Object.entries(stats)
|
||||
.map(([name, { skipped }]) =>
|
||||
Object.entries(skipped).map(([reason, count]) =>
|
||||
`${name}: ${reason} (${count})`
|
||||
).join(", ")
|
||||
)
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
import { CoopInfo, Game, GameExporter, VsInfo } from "../types.ts";
|
||||
import {
|
||||
CoopInfo,
|
||||
ExportResult,
|
||||
Game,
|
||||
GameExporter,
|
||||
VsInfo,
|
||||
} from "../types.ts";
|
||||
import { path } from "../../deps.ts";
|
||||
import { NSOAPP_VERSION, S3SI_VERSION } from "../constant.ts";
|
||||
import { parseHistoryDetailId, urlSimplify } from "../utils.ts";
|
||||
|
|
@ -83,7 +89,7 @@ export class FileExporter implements GameExporter {
|
|||
},
|
||||
}));
|
||||
}
|
||||
async exportGame(info: Game) {
|
||||
async exportGame(info: Game): Promise<ExportResult> {
|
||||
await Deno.mkdir(this.exportPath, { recursive: true });
|
||||
|
||||
const filename = this.getFilenameById(info.detail.id);
|
||||
|
|
@ -103,6 +109,7 @@ export class FileExporter implements GameExporter {
|
|||
);
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
url: filepath,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
CoopHistoryDetail,
|
||||
CoopHistoryPlayerResult,
|
||||
CoopInfo,
|
||||
ExportResult,
|
||||
Game,
|
||||
GameExporter,
|
||||
Image,
|
||||
|
|
@ -231,10 +232,13 @@ export class StatInkExporter implements GameExporter {
|
|||
isTriColor({ vsMode }: VsHistoryDetail): boolean {
|
||||
return vsMode.mode === "FEST" && b64Number(vsMode.id) === 8;
|
||||
}
|
||||
async exportGame(game: VsInfo | CoopInfo) {
|
||||
async exportGame(game: Game): Promise<ExportResult> {
|
||||
if (game.type === "VsInfo" && this.isTriColor(game.detail)) {
|
||||
// TODO: support tri-color fest
|
||||
return {};
|
||||
return {
|
||||
status: "skip",
|
||||
reason: "Tri-color fest is not supported",
|
||||
};
|
||||
}
|
||||
|
||||
if (game.type === "VsInfo") {
|
||||
|
|
@ -242,6 +246,7 @@ export class StatInkExporter implements GameExporter {
|
|||
const { url } = await this.api.postBattle(body);
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
url,
|
||||
};
|
||||
} else {
|
||||
|
|
@ -249,6 +254,7 @@ export class StatInkExporter implements GameExporter {
|
|||
const { url } = await this.api.postCoop(body);
|
||||
|
||||
return {
|
||||
status: "success",
|
||||
url,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
23
src/types.ts
23
src/types.ts
|
|
@ -296,12 +296,27 @@ export type CoopHistoryDetail = {
|
|||
jobBonus: null | number;
|
||||
};
|
||||
|
||||
export type ExportResult = {
|
||||
status: "success";
|
||||
url?: string;
|
||||
} | {
|
||||
status: "skip";
|
||||
reason: string;
|
||||
};
|
||||
|
||||
export type SummaryFetcher = {
|
||||
fetchSummary<T extends (typeof Queries)[keyof typeof SummaryEnum]>(
|
||||
type: T,
|
||||
): Promise<RespMap[T]>;
|
||||
};
|
||||
|
||||
export type GameExporter = {
|
||||
name: string;
|
||||
notExported: (
|
||||
{ type, list }: { type: Game["type"]; list: string[] },
|
||||
) => Promise<string[]>;
|
||||
exportGame: (game: Game) => Promise<{ url?: string }>;
|
||||
exportGame: (game: Game) => Promise<ExportResult>;
|
||||
exportSummary?: (fetcher: SummaryFetcher) => Promise<ExportResult>;
|
||||
};
|
||||
|
||||
export type BankaraBattleHistories = {
|
||||
|
|
@ -737,7 +752,7 @@ export type RankParam = {
|
|||
};
|
||||
|
||||
export enum SummaryEnum {
|
||||
ConfigureAnalyticsQuery,
|
||||
HistoryRecordQuery,
|
||||
CoopHistoryQuery,
|
||||
ConfigureAnalyticsQuery = Queries.ConfigureAnalyticsQuery,
|
||||
HistoryRecordQuery = Queries.HistoryRecordQuery,
|
||||
CoopHistoryQuery = Queries.CoopHistoryQuery,
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue