import UserContext from "../data/UserContext"; import SkinModel from "../data/SkinModel"; import { knex } from "../db"; import { setHashesForSkin } from "../skinHash"; import Shooter from "../shooter"; import { screenshot } from "./screenshotSkin"; import * as Skins from "../data/skins"; import { SkinType } from "../types"; import { getSkinType } from "../addSkin"; // TODO Move this into the function so that we clean up on each run? async function getExtractionError(skin: SkinModel): Promise { try { await skin.getZip(); } catch (e) { return e.message; } return null; } // A task to download a skin and refresh all the data we scrape from it export async function getSkinsToRefresh( ctx: UserContext, n: number, neverDone = false ): Promise { let query = knex("skins") .leftJoin("refreshes", "skins.md5", "refreshes.skin_md5") .where("skin_type", 1) .orderBy("refreshes.timestamp", "asc") .limit(n); if (neverDone) { query = query.where("refreshes.skin_md5", null); } const skins = await query.select(); return skins.map((row) => new SkinModel(ctx, row)); } export async function refreshSkins( skins: SkinModel[], options: { noScreenshot: boolean } ): Promise { if (options.noScreenshot) { while (skins.length > 0) { const skin = skins.pop(); // @ts-ignore await refresh(skin, null); } return; } const shooterLogger = () => { // Don't log }; await Shooter.withShooter(async (shooter: Shooter) => { for (const [i, skin] of skins.entries()) { await refresh(skin, shooter); // We end up caching a lot of stuff (the whole skin/zip) on the model, so we can't just leave these around for the whole process. delete skins[i]; } }, shooterLogger); } export async function _refresh( skin: SkinModel, shooter: Shooter | null ): Promise { const extractionError = await getExtractionError(skin); if (extractionError != null) { throw new Error(extractionError); } await setHashesForSkin(skin); await Skins.setContentHash(skin.getMd5()); let skinType: SkinType; try { skinType = await getSkinType(await skin.getZip()); } catch (e) { throw new Error("Not a skin (no main.bmp/skin.xml)"); } if (skinType !== "CLASSIC") { // TODO: // Reviews are invalid // Delete from algolia // Delete screenshot // Tweets are invalid throw new Error("Skin type mismatch"); } // Retake screenshot if (shooter != null) { await screenshot(skin, shooter); } await knex("refreshes").insert({ skin_md5: skin.getMd5(), }); } export async function refresh( skin: SkinModel, shooter: Shooter | null ): Promise { if (skin.getSkinType() !== "CLASSIC") { throw new Error("Can't refresh non-classic skins"); } try { await _refresh(skin, shooter); } catch (e) { console.error(e); await knex("refreshes").insert({ skin_md5: skin.getMd5(), error: e.message, }); return; } }