mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-21 09:09:01 +00:00
Get skins by tweet id
This commit is contained in:
parent
986aecc708
commit
afe22daee5
7 changed files with 119 additions and 16 deletions
|
|
@ -82,7 +82,7 @@ describe("/skins/", () => {
|
|||
const { body } = await request(app).get("/skins/");
|
||||
expect(body).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"skinCount": 5,
|
||||
"skinCount": 6,
|
||||
"skins": Array [
|
||||
Object {
|
||||
"fileName": "Zelda_Amp_3.wsz",
|
||||
|
|
@ -94,6 +94,11 @@ describe("/skins/", () => {
|
|||
"md5": "a_fake_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "tweeted.wsz",
|
||||
"md5": "a_tweeted_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "approved.wsz",
|
||||
"md5": "an_approved_md5",
|
||||
|
|
@ -119,7 +124,7 @@ describe("/skins/", () => {
|
|||
.query({ first: 2, offset: 1 });
|
||||
expect(body).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"skinCount": 5,
|
||||
"skinCount": 6,
|
||||
"skins": Array [
|
||||
Object {
|
||||
"fileName": "path.wsz",
|
||||
|
|
@ -127,8 +132,8 @@ describe("/skins/", () => {
|
|||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "approved.wsz",
|
||||
"md5": "an_approved_md5",
|
||||
"fileName": "tweeted.wsz",
|
||||
"md5": "a_tweeted_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
],
|
||||
|
|
@ -212,7 +217,7 @@ test("/skins/a_md5_that_does_not_exist/reject (404)", async () => {
|
|||
expect(handler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test.only("/skins/a_fake_md5/nsfw", async () => {
|
||||
test("/skins/a_fake_md5/nsfw", async () => {
|
||||
const ctx = new UserContext();
|
||||
const { body } = await request(app)
|
||||
.post("/skins/a_fake_md5/nsfw")
|
||||
|
|
@ -338,6 +343,10 @@ test("/stylegan.json", async () => {
|
|||
"fileName": "rejected.wsz",
|
||||
"url": "https://cdn.webampskins.org/screenshots/a_rejected_md5.png",
|
||||
},
|
||||
Object {
|
||||
"fileName": "tweeted.wsz",
|
||||
"url": "https://cdn.webampskins.org/screenshots/a_tweeted_md5.png",
|
||||
},
|
||||
Object {
|
||||
"fileName": "approved.wsz",
|
||||
"url": "https://cdn.webampskins.org/screenshots/an_approved_md5.png",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import UserContext, { ctxWeakMapMemoize } from "./UserContext";
|
|||
import TweetModel, { TweetDebugData } from "./TweetModel";
|
||||
import IaItemModel from "./IaItemModel";
|
||||
import FileModel, { FileDebugData } from "./FileModel";
|
||||
import { MD5_REGEX } from "../utils";
|
||||
import { MD5_REGEX, TWEET_ID_REGEX } from "../utils";
|
||||
import DataLoader from "dataloader";
|
||||
import { knex } from "../db";
|
||||
|
||||
|
|
@ -32,7 +32,14 @@ export default class SkinModel {
|
|||
}
|
||||
}
|
||||
const iaItem = await IaItemModel.fromAnything(ctx, anything);
|
||||
return iaItem?.getSkin() ?? null;
|
||||
if (iaItem != null) {
|
||||
return iaItem.getSkin();
|
||||
}
|
||||
const tweet = await TweetModel.fromAnything(ctx, anything);
|
||||
if (tweet != null) {
|
||||
return tweet.getSkin();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static async exists(ctx: UserContext, md5: string): Promise<boolean> {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import UserContext, { ctxWeakMapMemoize } from "./UserContext";
|
|||
import { TweetRow } from "../types";
|
||||
import DataLoader from "dataloader";
|
||||
import { knex } from "../db";
|
||||
import { TWEET_SNOWFLAKE_REGEX } from "../utils";
|
||||
import SkinModel from "./SkinModel";
|
||||
|
||||
export type TweetDebugData = {
|
||||
row: TweetRow;
|
||||
|
|
@ -15,6 +17,37 @@ export default class TweetModel {
|
|||
return rows.map((row) => new TweetModel(ctx, row));
|
||||
}
|
||||
|
||||
static async fromUrl(
|
||||
ctx: UserContext,
|
||||
url: string
|
||||
): Promise<TweetModel | null> {
|
||||
const row = await getTweetByTweetUrlLoader(ctx).load(url);
|
||||
if (row == null) {
|
||||
return null;
|
||||
}
|
||||
return new TweetModel(ctx, row);
|
||||
}
|
||||
|
||||
static async fromAnything(
|
||||
ctx: UserContext,
|
||||
anything: string
|
||||
): Promise<TweetModel | null> {
|
||||
const snowflakeMatch = anything.match(TWEET_SNOWFLAKE_REGEX);
|
||||
if (snowflakeMatch == null) {
|
||||
return null;
|
||||
}
|
||||
const snowflake = snowflakeMatch[1];
|
||||
const found = await TweetModel.fromUrl(
|
||||
ctx,
|
||||
`https://twitter.com/winampskins/status/${snowflake}`
|
||||
);
|
||||
return found || null;
|
||||
}
|
||||
|
||||
getMd5(): string {
|
||||
return this.row.skin_md5;
|
||||
}
|
||||
|
||||
getUrl(): string {
|
||||
return this.row.url;
|
||||
}
|
||||
|
|
@ -25,6 +58,14 @@ export default class TweetModel {
|
|||
return this.row.retweets;
|
||||
}
|
||||
|
||||
async getSkin(): Promise<SkinModel> {
|
||||
const skin = await SkinModel.fromMd5(this.ctx, this.getMd5());
|
||||
if (skin == null) {
|
||||
throw new Error(`Could not find skin for md5 "${this.getMd5()}"`);
|
||||
}
|
||||
return skin;
|
||||
}
|
||||
|
||||
async debug(): Promise<TweetDebugData> {
|
||||
return {
|
||||
row: this.row,
|
||||
|
|
@ -39,3 +80,13 @@ const getTweetsLoader = ctxWeakMapMemoize<DataLoader<string, TweetRow[]>>(
|
|||
return md5s.map((md5) => rows.filter((x) => x.skin_md5 === md5));
|
||||
})
|
||||
);
|
||||
|
||||
const getTweetByTweetUrlLoader = ctxWeakMapMemoize<
|
||||
DataLoader<string, TweetRow>
|
||||
>(
|
||||
() =>
|
||||
new DataLoader(async (urls) => {
|
||||
const rows = await knex("tweets").whereIn("url", urls).select();
|
||||
return urls.map((url) => rows.find((x) => x.url === url));
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -51,4 +51,12 @@ describe("fromAnything", () => {
|
|||
);
|
||||
expect(skin?.getMd5()).toBe("a_fake_md5");
|
||||
});
|
||||
test("Tweet URL", async () => {
|
||||
const ctx = new UserContext();
|
||||
const skin = await SkinModel.fromAnything(
|
||||
ctx,
|
||||
"https://twitter.com/winampskins/status/1333893671326871552"
|
||||
);
|
||||
expect(skin?.getMd5()).toBe("a_tweeted_md5");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -52,6 +52,10 @@ describe("seeded", () => {
|
|||
"fileName": "rejected.wsz",
|
||||
"md5": "a_rejected_md5",
|
||||
},
|
||||
Object {
|
||||
"fileName": "tweeted.wsz",
|
||||
"md5": "a_tweeted_md5",
|
||||
},
|
||||
Object {
|
||||
"fileName": "approved.wsz",
|
||||
"md5": "an_approved_md5",
|
||||
|
|
@ -60,7 +64,7 @@ describe("seeded", () => {
|
|||
`);
|
||||
});
|
||||
test("getClassicSkinCount", async () => {
|
||||
expect(await Skins.getClassicSkinCount()).toBe(5);
|
||||
expect(await Skins.getClassicSkinCount()).toBe(6);
|
||||
});
|
||||
test("getTweetableSkinCount", async () => {
|
||||
expect(await Skins.getTweetableSkinCount()).toBe(1);
|
||||
|
|
@ -84,6 +88,10 @@ describe("seeded", () => {
|
|||
"fileName": "rejected.wsz",
|
||||
"url": "https://cdn.webampskins.org/screenshots/a_rejected_md5.png",
|
||||
},
|
||||
Object {
|
||||
"fileName": "tweeted.wsz",
|
||||
"url": "https://cdn.webampskins.org/screenshots/a_tweeted_md5.png",
|
||||
},
|
||||
Object {
|
||||
"fileName": "approved.wsz",
|
||||
"url": "https://cdn.webampskins.org/screenshots/an_approved_md5.png",
|
||||
|
|
@ -105,6 +113,11 @@ describe("seeded", () => {
|
|||
"md5": "a_fake_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "tweeted.wsz",
|
||||
"md5": "a_tweeted_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "approved.wsz",
|
||||
"md5": "an_approved_md5",
|
||||
|
|
@ -124,14 +137,16 @@ describe("seeded", () => {
|
|||
`);
|
||||
});
|
||||
test("getStats", async () => {
|
||||
expect(await Skins.getStats()).toEqual({
|
||||
approved: 1,
|
||||
rejected: 1,
|
||||
nsfw: 1,
|
||||
tweeted: 0,
|
||||
tweetable: 1,
|
||||
webUploads: 0,
|
||||
});
|
||||
expect(await Skins.getStats()).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"approved": 2,
|
||||
"nsfw": 1,
|
||||
"rejected": 1,
|
||||
"tweetable": 1,
|
||||
"tweeted": 1,
|
||||
"webUploads": 0,
|
||||
}
|
||||
`);
|
||||
});
|
||||
test("getSkinToTweet", async () => {
|
||||
expect(await Skins.getSkinToTweet()).toEqual({
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export async function seed(knex: Knex): Promise<any> {
|
|||
await knex("files").del();
|
||||
await knex("skin_reviews").del();
|
||||
await knex("ia_items").del();
|
||||
await knex("tweets").del();
|
||||
// Inserts seed entries
|
||||
await knex("skins").insert([
|
||||
{ md5: "a_fake_md5", skin_type: 1, emails: "" },
|
||||
|
|
@ -13,6 +14,7 @@ export async function seed(knex: Knex): Promise<any> {
|
|||
{ md5: "an_approved_md5", skin_type: 1, emails: "" },
|
||||
{ md5: "a_rejected_md5", skin_type: 1, emails: "" },
|
||||
{ md5: "a_nsfw_md5", skin_type: 1, emails: "" },
|
||||
{ md5: "a_tweeted_md5", skin_type: 1, emails: "" },
|
||||
{ md5: "48bbdbbeb03d347e59b1eebda4d352d0", skin_type: 1, emails: "" },
|
||||
]);
|
||||
await knex("files").insert([
|
||||
|
|
@ -21,6 +23,7 @@ export async function seed(knex: Knex): Promise<any> {
|
|||
{ skin_md5: "an_approved_md5", file_path: "/a/fake/approved.wsz" },
|
||||
{ skin_md5: "a_rejected_md5", file_path: "/a/fake/rejected.wsz" },
|
||||
{ skin_md5: "a_nsfw_md5", file_path: "/a/fake/nsfw.wsz" },
|
||||
{ skin_md5: "a_tweeted_md5", file_path: "/a/fake/tweeted.wsz" },
|
||||
{
|
||||
skin_md5: "48bbdbbeb03d347e59b1eebda4d352d0",
|
||||
file_path: "/a/fake/Zelda_Amp_3.wsz",
|
||||
|
|
@ -29,10 +32,19 @@ export async function seed(knex: Knex): Promise<any> {
|
|||
await knex("skin_reviews").insert([
|
||||
{ skin_md5: "an_approved_md5", review: "APPROVED" },
|
||||
{ skin_md5: "a_rejected_md5", review: "REJECTED" },
|
||||
{ skin_md5: "a_tweeted_md5", review: "APPROVED" },
|
||||
{ skin_md5: "a_nsfw_md5", review: "NSFW" },
|
||||
]);
|
||||
|
||||
await knex("ia_items").insert([
|
||||
{ skin_md5: "a_fake_md5", identifier: "a_fake_ia_identifier" },
|
||||
]);
|
||||
|
||||
await knex("tweets").insert([
|
||||
{
|
||||
skin_md5: "a_tweeted_md5",
|
||||
url: "https://twitter.com/winampskins/status/1333893671326871552",
|
||||
tweet_id: "1333893671326871552",
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,3 +12,4 @@ export function truncate(str: string, len: number): string {
|
|||
}
|
||||
|
||||
export const MD5_REGEX = /([a-fA-F0-9]{32})/;
|
||||
export const TWEET_SNOWFLAKE_REGEX = /([0-9]{19})/;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue