feat: s3si upload
parent
f754303a05
commit
b73769618d
|
|
@ -2,3 +2,4 @@
|
|||
.vscode/
|
||||
export/
|
||||
cache/
|
||||
.DS_Store
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { BattleExporter, VsHistoryDetail } from "../types.ts";
|
||||
import { datetime, path } from "../deps.ts";
|
||||
import { NSOAPP_VERSION, S3SI_VERSION } from "../constant.ts";
|
||||
|
||||
const FILENAME_FORMAT = "yyyyMMddHHmmss";
|
||||
|
||||
type FileExporterType = {
|
||||
|
|
@ -40,6 +39,8 @@ export class FileExporter implements BattleExporter<VsHistoryDetail> {
|
|||
await Deno.writeTextFile(filepath, JSON.stringify(body));
|
||||
}
|
||||
async getLatestBattleTime() {
|
||||
await Deno.mkdir(this.exportPath, { recursive: true });
|
||||
|
||||
const dirs: Deno.DirEntry[] = [];
|
||||
for await (const i of Deno.readDir(this.exportPath)) dirs.push(i);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
// deno-lint-ignore-file no-unused-vars require-await
|
||||
import { USERAGENT } from "../constant.ts";
|
||||
import { BattleExporter, VsHistoryDetail } from "../types.ts";
|
||||
|
||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* Exporter to stat.ink.
|
||||
*
|
||||
|
|
@ -12,10 +16,20 @@ export class StatInkExporter implements BattleExporter<VsHistoryDetail> {
|
|||
throw new Error("Invalid stat.ink API key");
|
||||
}
|
||||
}
|
||||
async exportBattle(detail: VsHistoryDetail) {
|
||||
throw new Error("Function not implemented.");
|
||||
requestHeaders() {
|
||||
return {
|
||||
"User-Agent": USERAGENT,
|
||||
"Authorization": `Bearer ${this.statInkApiKey}`,
|
||||
};
|
||||
}
|
||||
async getLatestBattleTime() {
|
||||
return new Date();
|
||||
async exportBattle(detail: VsHistoryDetail) {
|
||||
await sleep(1000);
|
||||
}
|
||||
async getLatestBattleTime(): Promise<Date> {
|
||||
const uuids = await (await fetch("https://stat.ink/api/v3/s3s/uuid-list", {
|
||||
headers: this.requestHeaders(),
|
||||
})).json();
|
||||
console.log("\n\n uuid:", uuids);
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
113
s3si.ts
113
s3si.ts
|
|
@ -144,6 +144,7 @@ Options:
|
|||
const bar = !this.opts.noProgress
|
||||
? new MultiProgressBar({
|
||||
title: "Export battles",
|
||||
display: "[:bar] :text :percent :time eta: :eta :completed/:total",
|
||||
})
|
||||
: undefined;
|
||||
const exporters = await this.getExporters();
|
||||
|
|
@ -197,12 +198,14 @@ Options:
|
|||
console.log("Fetching battle list...");
|
||||
const battleList = await getBattleList(this.state);
|
||||
|
||||
const allProgress: Record<string, Progress> = Object.fromEntries(
|
||||
exporters.map((i) => [i.name, {
|
||||
current: 0,
|
||||
total: 1,
|
||||
}]),
|
||||
);
|
||||
await this.prepareBattles({
|
||||
bar,
|
||||
battleList,
|
||||
fetcher,
|
||||
exporters,
|
||||
});
|
||||
|
||||
const allProgress: Record<string, Progress> = {};
|
||||
const redraw = (name: string, progress: Progress) => {
|
||||
allProgress[name] = progress;
|
||||
bar?.render(
|
||||
|
|
@ -213,6 +216,10 @@ Options:
|
|||
})),
|
||||
);
|
||||
};
|
||||
const stats: Record<string, number> = Object.fromEntries(
|
||||
exporters.map((e) => [e.name, 0]),
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
exporters.map((e) =>
|
||||
this.exportBattleList({
|
||||
|
|
@ -221,8 +228,16 @@ Options:
|
|||
battleList,
|
||||
onStep: (progress) => redraw(e.name, progress),
|
||||
})
|
||||
.then((count) => {
|
||||
stats[e.name] = count;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`\nFailed to export ${e.name}:`, err);
|
||||
})
|
||||
),
|
||||
);
|
||||
|
||||
console.log("\nDone.", stats);
|
||||
} catch (e) {
|
||||
if (e instanceof APIError) {
|
||||
console.error(`APIError: ${e.message}`, e.response, e.json);
|
||||
|
|
@ -231,6 +246,49 @@ Options:
|
|||
}
|
||||
}
|
||||
}
|
||||
async prepareBattles({
|
||||
bar,
|
||||
exporters,
|
||||
battleList,
|
||||
fetcher,
|
||||
}: {
|
||||
bar?: MultiProgressBar;
|
||||
exporters: BattleExporter<VsHistoryDetail>[];
|
||||
battleList: string[];
|
||||
fetcher: BattleFetcher;
|
||||
}) {
|
||||
let prepared = 0;
|
||||
bar?.render([{
|
||||
text: "preparing",
|
||||
completed: prepared,
|
||||
total: battleList.length,
|
||||
}]);
|
||||
|
||||
const latestBattleTimes = await Promise.all(
|
||||
exporters.map((e) => e.getLatestBattleTime()),
|
||||
);
|
||||
const latestBattleTime = latestBattleTimes.reduce(
|
||||
(a, b) => a > b ? b : a,
|
||||
new Date(0),
|
||||
);
|
||||
|
||||
for (const battleId of battleList) {
|
||||
const battle = await fetcher.fetchBattle(battleId);
|
||||
const playedTime = new Date(battle.playedTime);
|
||||
|
||||
prepared += 1;
|
||||
bar?.render([{
|
||||
text: "preparing",
|
||||
completed: prepared,
|
||||
total: battleList.length,
|
||||
}]);
|
||||
|
||||
// if battle is older than latest battle, break
|
||||
if (playedTime <= latestBattleTime) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Export battle list.
|
||||
*
|
||||
|
|
@ -244,34 +302,55 @@ Options:
|
|||
fetcher,
|
||||
exporter,
|
||||
battleList,
|
||||
onStep
|
||||
onStep,
|
||||
}: {
|
||||
fetcher: BattleFetcher,
|
||||
exporter: BattleExporter<VsHistoryDetail>,
|
||||
battleList: string[],
|
||||
onStep?: (progress: Progress) => void,
|
||||
fetcher: BattleFetcher;
|
||||
exporter: BattleExporter<VsHistoryDetail>;
|
||||
battleList: string[];
|
||||
onStep?: (progress: Progress) => void;
|
||||
},
|
||||
): Promise<number> {
|
||||
const latestBattleTime = await exporter.getLatestBattleTime();
|
||||
let toUpload = 0;
|
||||
let exported = 0;
|
||||
|
||||
for (const battleId of battleList) {
|
||||
const battle = await fetcher.fetchBattle(battleId);
|
||||
const playedTime = new Date(battle.playedTime);
|
||||
|
||||
// if battle is older than latest battle, break
|
||||
if (playedTime <= latestBattleTime) {
|
||||
break;
|
||||
}
|
||||
|
||||
toUpload += 1;
|
||||
}
|
||||
|
||||
const workQueue = battleList.slice(0, toUpload).reverse();
|
||||
|
||||
if (workQueue.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
) {
|
||||
const workQueue = battleList;
|
||||
let done = 0;
|
||||
|
||||
const step = async (battle: string) => {
|
||||
const detail = await fetcher.fetchBattle(battle);
|
||||
await exporter.exportBattle(detail);
|
||||
done += 1;
|
||||
exported += 1;
|
||||
onStep?.({
|
||||
current: done,
|
||||
current: exported,
|
||||
total: workQueue.length,
|
||||
});
|
||||
};
|
||||
|
||||
onStep?.({
|
||||
current: done,
|
||||
current: exported,
|
||||
total: workQueue.length,
|
||||
});
|
||||
for (const battle of workQueue) {
|
||||
await step(battle);
|
||||
}
|
||||
|
||||
return exported;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue