mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-28 20:40:39 +00:00
Index the number of transparent pixels
This commit is contained in:
parent
284ba82541
commit
1c49e29030
5 changed files with 130 additions and 13 deletions
|
|
@ -19,7 +19,7 @@ import fetch from "node-fetch";
|
|||
import JSZip from "jszip";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import regionParser from "../regionParser";
|
||||
import { getTransparentAreaSize } from "../transparency";
|
||||
|
||||
export const IS_README = /(file_id\.diz)|(\.txt)$/i;
|
||||
// Skinning Updates.txt ?
|
||||
|
|
@ -340,18 +340,17 @@ export default class SkinModel {
|
|||
|
||||
// Has transparency
|
||||
async hasTransparency(): Promise<boolean> {
|
||||
const size = await this.transparentAreaSize();
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
async transparentPixels(): Promise<number> {
|
||||
const region = await this._getFile("region", "txt");
|
||||
if (region == null) {
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
const text = await region.getTextContent();
|
||||
if (text == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const regions = regionParser(text);
|
||||
|
||||
return Object.values(regions).some((region) => region.length > 0);
|
||||
return getTransparentAreaSize(text);
|
||||
}
|
||||
|
||||
async hasAni(): Promise<boolean> {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import * as CloudFlare from "../CloudFlare";
|
|||
import SkinModel from "./SkinModel";
|
||||
import UserContext from "./UserContext";
|
||||
import TweetModel from "./TweetModel";
|
||||
import { TweetStatus } from "../types";
|
||||
|
||||
export const SKIN_TYPE = {
|
||||
CLASSIC: 1,
|
||||
|
|
@ -158,17 +159,20 @@ export async function getUploadStatuses(
|
|||
type SearchIndex = {
|
||||
objectID: string;
|
||||
md5: string;
|
||||
// TODO: Can be deprecated in favor of tweetStatus
|
||||
nsfw: boolean;
|
||||
tweetStatus: TweetStatus;
|
||||
fileName: string;
|
||||
twitterLikes: number;
|
||||
equalizer: boolean;
|
||||
playlist: boolean;
|
||||
browser: boolean;
|
||||
media: boolean;
|
||||
general: boolean;
|
||||
video: boolean;
|
||||
cur: boolean;
|
||||
ani: boolean;
|
||||
transparency: boolean;
|
||||
transparentPixels: number;
|
||||
mikro: boolean;
|
||||
vidamp: boolean;
|
||||
avs: boolean;
|
||||
|
|
@ -186,6 +190,9 @@ async function getSearchIndexes(
|
|||
|
||||
return Promise.all(
|
||||
skins.map(async (skin) => {
|
||||
if (skin.getSkinType() !== "CLASSIC") {
|
||||
throw new Error("Only classic skins are supported");
|
||||
}
|
||||
const readmeText = await skin.getReadme();
|
||||
const tweets = await skin.getTweets();
|
||||
const likes = tweets.reduce((acc: number, tweet: TweetModel) => {
|
||||
|
|
@ -195,17 +202,19 @@ async function getSearchIndexes(
|
|||
objectID: skin.getMd5(),
|
||||
md5: skin.getMd5(),
|
||||
nsfw: await skin.getIsNsfw(),
|
||||
tweetStatus: await skin.getTweetStatus(),
|
||||
readmeText: readmeText ? truncate(readmeText, 4800) : null,
|
||||
fileName: await skin.getFileName(),
|
||||
twitterLikes: likes,
|
||||
equalizer: await skin.hasEqualizer(),
|
||||
media: await skin.hasMediaLibrary(),
|
||||
playlist: await skin.hasPlaylist(),
|
||||
browser: await skin.hasBrowser(),
|
||||
general: await skin.hasGeneral(),
|
||||
video: await skin.hasVideo(),
|
||||
cur: await skin.hasCur(),
|
||||
ani: await skin.hasAni(),
|
||||
transparency: await skin.hasTransparency(),
|
||||
transparentPixels: await skin.transparentPixels(),
|
||||
mikro: await skin.hasMikro(),
|
||||
vidamp: await skin.hasVidamp(),
|
||||
avs: await skin.hasAVS(),
|
||||
|
|
@ -219,8 +228,6 @@ export async function updateSearchIndexs(
|
|||
md5s: string[]
|
||||
): Promise<any> {
|
||||
const skinIndexes = await getSearchIndexes(ctx, md5s);
|
||||
console.log(skinIndexes);
|
||||
return;
|
||||
|
||||
const results = await searchIndex.partialUpdateObjects(skinIndexes, {
|
||||
createIfNotExists: true,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
"lru-cache": "^6.0.0",
|
||||
"md5": "^2.2.1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"polygon-clipping": "^0.15.3",
|
||||
"puppeteer": "^13.3.2",
|
||||
"sharp": "^0.28.3",
|
||||
"sqlite3": "^5.0.2",
|
||||
|
|
|
|||
98
packages/skin-database/transparency.ts
Normal file
98
packages/skin-database/transparency.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import polygonClipping, {
|
||||
Polygon,
|
||||
MultiPolygon,
|
||||
Pair,
|
||||
Ring,
|
||||
} from "polygon-clipping";
|
||||
import regionParser from "./regionParser";
|
||||
|
||||
const HEIGHT = 116;
|
||||
const SHADE_HEIGHT = 14;
|
||||
const WIDTH = 275;
|
||||
const largeWindowBounds = [[makeBox(WIDTH, HEIGHT)]] as MultiPolygon;
|
||||
const shadeWindowBounds = [[makeBox(WIDTH, SHADE_HEIGHT)]] as MultiPolygon;
|
||||
|
||||
const maxBounds: { [name: string]: MultiPolygon } = {
|
||||
normal: largeWindowBounds,
|
||||
equalizer: largeWindowBounds,
|
||||
windowshade: shadeWindowBounds,
|
||||
equalizerws: shadeWindowBounds,
|
||||
};
|
||||
|
||||
export function getTransparentAreaSize(regionTxt: string): number {
|
||||
const regions = regionParser(regionTxt);
|
||||
|
||||
let totalTransparentArea = 0;
|
||||
for (const _name of Object.keys(regions)) {
|
||||
const name = _name.toLocaleLowerCase();
|
||||
const region = regions[name];
|
||||
if (region == null || region.length === 0) {
|
||||
continue;
|
||||
}
|
||||
const max = maxBounds[name];
|
||||
if (max == null) {
|
||||
// This is an invalid region.
|
||||
continue;
|
||||
}
|
||||
|
||||
const opaque = regionStringsToPolygons(region);
|
||||
const transparent = polygonClipping.difference(max, opaque);
|
||||
const transparentArea = getMultipolygonArea(transparent);
|
||||
totalTransparentArea += transparentArea;
|
||||
}
|
||||
return totalTransparentArea;
|
||||
}
|
||||
|
||||
// Compute the area of a multi-polygon.
|
||||
// Note: If there are overlaps they might be computed multiple times.
|
||||
function getMultipolygonArea(multipolygon: MultiPolygon) {
|
||||
let area = 0;
|
||||
for (const polygon of multipolygon) {
|
||||
for (const ring of polygon) {
|
||||
area += getArea(ring);
|
||||
}
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
// Make a box polygon with the given width and height.
|
||||
function makeBox(width: number, height: number): Ring {
|
||||
return [
|
||||
[0, 0],
|
||||
[width, 0],
|
||||
[width, height],
|
||||
[0, height],
|
||||
[0, 0],
|
||||
];
|
||||
}
|
||||
|
||||
// Given a parsed list of region.txt polygon strings, return a list of polygons
|
||||
// which are closed (end where they begin).
|
||||
function regionStringsToPolygons(region: string[]): Polygon[] {
|
||||
return region.map((p) => {
|
||||
const points: Ring = p.split(" ").map((p) => {
|
||||
return p.split(",").map((n) => parseInt(n, 10)) as Pair;
|
||||
});
|
||||
// Ensure these close properly.
|
||||
points.push(points[0]);
|
||||
return [points];
|
||||
});
|
||||
}
|
||||
|
||||
// Get the area of a single Ring (simple polygon).
|
||||
// Assumes the polygon is closed (ends where it begins).
|
||||
function getArea(points: Ring) {
|
||||
let det = 0;
|
||||
|
||||
const l = points.length;
|
||||
if (l < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (let i = 1; i < l; i++) {
|
||||
const p1 = points[i - 1];
|
||||
const p2 = points[i];
|
||||
det += p1[0] * p2[1] - p1[1] * p2[0];
|
||||
}
|
||||
return Math.abs(det) / 2;
|
||||
}
|
||||
12
yarn.lock
12
yarn.lock
|
|
@ -20023,6 +20023,13 @@ pngparse@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/pngparse/-/pngparse-2.0.1.tgz#86852de4de349f4efb1e852e7525655e5ac5dfb8"
|
||||
integrity sha512-RyB1P0BBwt3CNIZ5wT53lR1dT3CUtopnMOuP8xZdHjPhI/uXNNRnkx1yQb/3MMMyyMeo6p19fiIRHcLopWIkxA==
|
||||
|
||||
polygon-clipping@^0.15.3:
|
||||
version "0.15.3"
|
||||
resolved "https://registry.yarnpkg.com/polygon-clipping/-/polygon-clipping-0.15.3.tgz#0215840438470ba2e9e6593625e4ea5c1087b4b7"
|
||||
integrity sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==
|
||||
dependencies:
|
||||
splaytree "^3.1.0"
|
||||
|
||||
portfinder@^1.0.13, portfinder@^1.0.26:
|
||||
version "1.0.26"
|
||||
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.26.tgz#475658d56ca30bed72ac7f1378ed350bd1b64e70"
|
||||
|
|
@ -23631,6 +23638,11 @@ spdy@^4.0.2:
|
|||
select-hose "^2.0.0"
|
||||
spdy-transport "^3.0.0"
|
||||
|
||||
splaytree@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/splaytree/-/splaytree-3.1.1.tgz#e1bc8e68e64ef5a9d5f09d36e6d9f3621795a438"
|
||||
integrity sha512-9FaQ18FF0+sZc/ieEeXHt+Jw2eSpUgUtTLDYB/HXKWvhYVyOc7h1hzkn5MMO3GPib9MmXG1go8+OsBBzs/NMww==
|
||||
|
||||
split-string@^3.0.1, split-string@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue