feat: implement auto list-method
parent
a5f35c78c9
commit
63ea9347da
3
s3si.ts
3
s3si.ts
|
|
@ -32,7 +32,8 @@ Options:
|
||||||
(e.g. "stat.ink,file")
|
(e.g. "stat.ink,file")
|
||||||
--list-method When set to "latest", the latest 50 matches will be obtained.
|
--list-method When set to "latest", the latest 50 matches will be obtained.
|
||||||
When set to "all", matches of all modes will be obtained with a maximum of 250 matches (5 modes x 50 matches).
|
When set to "all", matches of all modes will be obtained with a maximum of 250 matches (5 modes x 50 matches).
|
||||||
"latest" is the default setting.
|
When set to "auto", the latest 50 matches will be obtained. If 50 matches have not been uploaded yet, matches will be obtained from the list of all modes.
|
||||||
|
"auto" is the default setting.
|
||||||
--no-progress, -n Disable progress bar
|
--no-progress, -n Disable progress bar
|
||||||
--monitor, -m Monitor mode
|
--monitor, -m Monitor mode
|
||||||
--skip-mode <mode>, -s Skip mode (default: null)
|
--skip-mode <mode>, -s Skip mode (default: null)
|
||||||
|
|
|
||||||
143
src/app.ts
143
src/app.ts
|
|
@ -1,8 +1,8 @@
|
||||||
import { loginManually } from "./iksm.ts";
|
import { loginManually } from "./iksm.ts";
|
||||||
import { MultiProgressBar } from "../deps.ts";
|
import { MultiProgressBar, Mutex } from "../deps.ts";
|
||||||
import { FileStateBackend, Profile, StateBackend } from "./state.ts";
|
import { FileStateBackend, Profile, StateBackend } from "./state.ts";
|
||||||
import { Splatnet3 } from "./splatnet3.ts";
|
import { Splatnet3 } from "./splatnet3.ts";
|
||||||
import { BattleListType, Game, GameExporter } from "./types.ts";
|
import { BattleListType, Game, GameExporter, ListMethod } from "./types.ts";
|
||||||
import { Cache, FileCache } from "./cache.ts";
|
import { Cache, FileCache } from "./cache.ts";
|
||||||
import { StatInkExporter } from "./exporters/stat.ink.ts";
|
import { StatInkExporter } from "./exporters/stat.ink.ts";
|
||||||
import { FileExporter } from "./exporters/file.ts";
|
import { FileExporter } from "./exporters/file.ts";
|
||||||
|
|
@ -17,7 +17,7 @@ export type Opts = {
|
||||||
monitor: boolean;
|
monitor: boolean;
|
||||||
withSummary: boolean;
|
withSummary: boolean;
|
||||||
skipMode?: string;
|
skipMode?: string;
|
||||||
listMethod: string;
|
listMethod?: string;
|
||||||
cache?: Cache;
|
cache?: Cache;
|
||||||
stateBackend?: StateBackend;
|
stateBackend?: StateBackend;
|
||||||
env: Env;
|
env: Env;
|
||||||
|
|
@ -54,6 +54,103 @@ class StepProgress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GameListFetcher {
|
||||||
|
/**
|
||||||
|
* Return not exported game list.
|
||||||
|
* [0] is the latest game.
|
||||||
|
* @param exporter GameExporter
|
||||||
|
*/
|
||||||
|
fetch(exporter: GameExporter): Promise<string[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BattleListFetcher implements GameListFetcher {
|
||||||
|
protected listMethod: ListMethod;
|
||||||
|
protected allBattleList?: string[];
|
||||||
|
protected latestBattleList?: string[];
|
||||||
|
protected allLock = new Mutex();
|
||||||
|
protected latestLock = new Mutex();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
listMethod: string,
|
||||||
|
protected splatnet: Splatnet3,
|
||||||
|
) {
|
||||||
|
if (listMethod === "all") {
|
||||||
|
this.listMethod = "all";
|
||||||
|
} else if (listMethod === "latest") {
|
||||||
|
this.listMethod = "latest";
|
||||||
|
} else {
|
||||||
|
this.listMethod = "auto";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getAllBattleList() {
|
||||||
|
return this.allLock.use(async () => {
|
||||||
|
if (!this.allBattleList) {
|
||||||
|
this.allBattleList = await this.splatnet.getAllBattleList();
|
||||||
|
}
|
||||||
|
return this.allBattleList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getLatestBattleList() {
|
||||||
|
return this.latestLock.use(async () => {
|
||||||
|
if (!this.latestBattleList) {
|
||||||
|
this.latestBattleList = await this.splatnet.getBattleList();
|
||||||
|
}
|
||||||
|
return this.latestBattleList;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async innerFetch(exporter: GameExporter) {
|
||||||
|
if (this.listMethod === "latest") {
|
||||||
|
return await exporter.notExported({
|
||||||
|
type: "VsInfo",
|
||||||
|
list: await this.getLatestBattleList(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.listMethod === "all") {
|
||||||
|
return await exporter.notExported({
|
||||||
|
type: "VsInfo",
|
||||||
|
list: await this.getAllBattleList(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.listMethod === "auto") {
|
||||||
|
const latestList = await exporter.notExported({
|
||||||
|
type: "VsInfo",
|
||||||
|
list: await this.getLatestBattleList(),
|
||||||
|
});
|
||||||
|
if (latestList.length === 50) {
|
||||||
|
return await exporter.notExported({
|
||||||
|
type: "VsInfo",
|
||||||
|
list: await this.getAllBattleList(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return latestList;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TypeError(`Unknown listMethod: ${this.listMethod}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetch(exporter: GameExporter) {
|
||||||
|
return [...await this.innerFetch(exporter)].reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CoopListFetcher implements GameListFetcher {
|
||||||
|
constructor(
|
||||||
|
protected splatnet: Splatnet3,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async fetch(exporter: GameExporter) {
|
||||||
|
return [
|
||||||
|
...await exporter.notExported({
|
||||||
|
type: "CoopInfo",
|
||||||
|
list: await this.splatnet.getBattleList(BattleListType.Coop),
|
||||||
|
}),
|
||||||
|
].reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function progress({ total, currentUrl, done }: StepProgress): Progress {
|
function progress({ total, currentUrl, done }: StepProgress): Progress {
|
||||||
return {
|
return {
|
||||||
total,
|
total,
|
||||||
|
|
@ -74,6 +171,12 @@ export class App {
|
||||||
env: opts.env,
|
env: opts.env,
|
||||||
});
|
});
|
||||||
this.env = opts.env;
|
this.env = opts.env;
|
||||||
|
|
||||||
|
if (
|
||||||
|
opts.listMethod && !["all", "auto", "latest"].includes(opts.listMethod)
|
||||||
|
) {
|
||||||
|
throw new TypeError(`Unknown listMethod: ${opts.listMethod}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSkipMode(): ("vs" | "coop")[] {
|
getSkipMode(): ("vs" | "coop")[] {
|
||||||
|
|
@ -164,13 +267,10 @@ export class App {
|
||||||
if (skipMode.includes("vs") || exporters.length === 0) {
|
if (skipMode.includes("vs") || exporters.length === 0) {
|
||||||
this.env.logger.log("Skip exporting VS games.");
|
this.env.logger.log("Skip exporting VS games.");
|
||||||
} else {
|
} else {
|
||||||
this.env.logger.log("Fetching battle list...");
|
const gameListFetcher = new BattleListFetcher(
|
||||||
let gameList: string[];
|
this.opts.listMethod ?? "auto",
|
||||||
if (this.opts.listMethod === "all") {
|
splatnet,
|
||||||
gameList = await splatnet.getAllBattleList();
|
);
|
||||||
} else {
|
|
||||||
gameList = await splatnet.getBattleList();
|
|
||||||
}
|
|
||||||
|
|
||||||
const { redraw, endBar } = this.exporterProgress("Export vs games");
|
const { redraw, endBar } = this.exporterProgress("Export vs games");
|
||||||
const fetcher = new GameFetcher({
|
const fetcher = new GameFetcher({
|
||||||
|
|
@ -189,7 +289,7 @@ export class App {
|
||||||
type: "VsInfo",
|
type: "VsInfo",
|
||||||
fetcher,
|
fetcher,
|
||||||
exporter: e,
|
exporter: e,
|
||||||
gameList,
|
gameListFetcher,
|
||||||
stepProgress: stats[e.name],
|
stepProgress: stats[e.name],
|
||||||
onStep: () => {
|
onStep: () => {
|
||||||
redraw(e.name, progress(stats[e.name]));
|
redraw(e.name, progress(stats[e.name]));
|
||||||
|
|
@ -223,10 +323,7 @@ export class App {
|
||||||
if (skipMode.includes("coop") || exporters.length === 0) {
|
if (skipMode.includes("coop") || exporters.length === 0) {
|
||||||
this.env.logger.log("Skip exporting coop games.");
|
this.env.logger.log("Skip exporting coop games.");
|
||||||
} else {
|
} else {
|
||||||
this.env.logger.log("Fetching coop battle list...");
|
const gameListFetcher = new CoopListFetcher(splatnet);
|
||||||
const coopBattleList = await splatnet.getBattleList(
|
|
||||||
BattleListType.Coop,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { redraw, endBar } = this.exporterProgress("Export coop games");
|
const { redraw, endBar } = this.exporterProgress("Export coop games");
|
||||||
const fetcher = new GameFetcher({
|
const fetcher = new GameFetcher({
|
||||||
|
|
@ -243,7 +340,7 @@ export class App {
|
||||||
type: "CoopInfo",
|
type: "CoopInfo",
|
||||||
fetcher,
|
fetcher,
|
||||||
exporter: e,
|
exporter: e,
|
||||||
gameList: coopBattleList,
|
gameListFetcher,
|
||||||
stepProgress: stats[e.name],
|
stepProgress: stats[e.name],
|
||||||
onStep: () => {
|
onStep: () => {
|
||||||
redraw(e.name, progress(stats[e.name]));
|
redraw(e.name, progress(stats[e.name]));
|
||||||
|
|
@ -349,30 +446,24 @@ export class App {
|
||||||
* @param gameList ID list of games, sorted by date, newest first
|
* @param gameList ID list of games, sorted by date, newest first
|
||||||
* @param onStep Callback function called when a game is exported
|
* @param onStep Callback function called when a game is exported
|
||||||
*/
|
*/
|
||||||
async exportGameList({
|
private async exportGameList({
|
||||||
type,
|
type,
|
||||||
fetcher,
|
fetcher,
|
||||||
exporter,
|
exporter,
|
||||||
gameList,
|
gameListFetcher,
|
||||||
stepProgress,
|
stepProgress,
|
||||||
onStep,
|
onStep,
|
||||||
}: {
|
}: {
|
||||||
type: Game["type"];
|
type: Game["type"];
|
||||||
exporter: GameExporter;
|
exporter: GameExporter;
|
||||||
fetcher: GameFetcher;
|
fetcher: GameFetcher;
|
||||||
gameList: string[];
|
gameListFetcher: GameListFetcher;
|
||||||
stepProgress: StepProgress;
|
stepProgress: StepProgress;
|
||||||
onStep: () => void;
|
onStep: () => void;
|
||||||
}): Promise<StepProgress> {
|
}): Promise<StepProgress> {
|
||||||
onStep?.();
|
onStep?.();
|
||||||
|
|
||||||
const workQueue = [
|
const workQueue = await gameListFetcher.fetch(exporter);
|
||||||
...await exporter.notExported({
|
|
||||||
type,
|
|
||||||
list: gameList,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
.reverse();
|
|
||||||
|
|
||||||
const step = async (id: string) => {
|
const step = async (id: string) => {
|
||||||
const detail = await fetcher.fetch(type, id);
|
const detail = await fetcher.fetch(type, id);
|
||||||
|
|
|
||||||
|
|
@ -613,7 +613,7 @@ export enum BattleListType {
|
||||||
Coop,
|
Coop,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ListMethod = "latest" | "all";
|
export type ListMethod = "latest" | "all" | "auto";
|
||||||
|
|
||||||
export type StatInkUuidList = {
|
export type StatInkUuidList = {
|
||||||
status: number;
|
status: number;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue