New image format (#675)

* use new image loading strategy: infer from gui.tea.xyz/stage/pkgFullname/dimension.webp
This commit is contained in:
Neil 2023-06-22 14:08:45 +08:00 committed by GitHub
parent 0dc057af8d
commit 53224e21b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 88 additions and 106 deletions

View file

@ -3,10 +3,12 @@ import { mkdirp } from "mkdirp";
import fs from "fs";
import log from "./logger";
import { getTeaPath } from "./tea-dir";
import { Packages } from "../../src/libs/types";
import { GUIPackage, Packages } from "../../src/libs/types";
import { isDev } from "./auto-updater";
const pkgsFilePath = path.join(getTeaPath(), "tea.xyz/gui/pkgs.json");
const pkgsFolder = path.join(getTeaPath(), "tea.xyz/gui");
const dev = isDev();
export async function writePackageCache(pkgs: Packages) {
try {
@ -29,13 +31,19 @@ export async function loadPackageCache(): Promise<Packages> {
log.info(`loading package cache from ${pkgsFilePath}`);
const pkgData = fs.readFileSync(pkgsFilePath);
const pkgs = JSON.parse(pkgData.toString()) as Packages;
if (pkgs?.packages) {
// Remove any temporary properties that may have been added to the package (like installation progress)
for (const [key, value] of Object.entries(pkgs.packages)) {
const { install_progress_percentage, isUninstalling, synced, displayState, ...rest } =
value;
pkgs.packages[key] = rest;
pkgs.packages[key] = rest as GUIPackage;
// possible user deletes cache files
delete pkgs.packages[key].cached_image_url;
if (rest.image_added_at) {
const prefix = `https://gui.tea.xyz/${dev ? "dev" : "prod"}/${rest.full_name}`;
pkgs.packages[key].image_128_url = `${prefix}/128x128.webp`;
pkgs.packages[key].image_512_url = `${prefix}/512x512.webp`;
}
}
}
return pkgs;

View file

@ -163,18 +163,24 @@ async function downloadImage(url: string, imagePath: string): Promise<void> {
const response = await fetch(url);
await new Promise<void>((resolve, reject) => {
const fileStream = fs.createWriteStream(imagePath);
response.body.pipe(fileStream);
fileStream.on("finish", () => resolve());
fileStream.on("error", (error) => reject(error));
if (response.status == 200) {
response.body.pipe(fileStream);
fileStream.on("finish", () => resolve());
fileStream.on("error", (error) => reject(error));
} else {
reject(new Error(`Failed to download image: ${url}`));
}
});
}
export async function cacheImage(url: string): Promise<string> {
const imageFolder = path.join(getGuiPath(), "cached_images");
const imageName = path.basename(url);
const imagePath = path.join(imageFolder, imageName);
await mkdirp(imageFolder);
const pkgFilePath = url.split("gui.tea.xyz")[1];
const imagePath = path.join(imageFolder, pkgFilePath);
const fileName = path.basename(imagePath);
const folderPath = imagePath.replace(fileName, "");
log.info("creating folder:", folderPath);
await mkdirp(folderPath);
if (!fs.existsSync(imagePath)) {
try {

View file

@ -1,6 +1,6 @@
{
"name": "tea",
"version": "0.2.27",
"version": "0.2.28",
"private": true,
"description": "tea gui app",
"author": "tea.xyz",

View file

@ -1,33 +0,0 @@
<script lang="ts">
import "$appcss";
import { onMount } from "svelte";
import type { Package } from "@tea/ui/types";
import Gallery from "@tea/ui/gallery/gallery.svelte";
import {
featuredPackages as featuredPackagesStore,
initializeFeaturedPackages
} from "$libs/stores";
let featuredPackages: Package[] = [];
featuredPackagesStore.subscribe((v) => {
featuredPackages = v;
});
onMount(() => {
if (!featuredPackages.length) {
initializeFeaturedPackages();
}
});
</script>
<Gallery
title="FEATURED PACKAGES"
items={featuredPackages.map((pkg) => ({
title: pkg.full_name,
subTitle: pkg.maintainer || "",
imageUrl: pkg.thumb_image_url,
link: `/packages/${pkg.slug}`
}))}
/>

View file

@ -1,6 +1,5 @@
<script lang="ts">
import type { GUIPackage } from "$libs/types";
import { onMount } from "svelte";
import { packagesStore } from "$libs/stores";
let clazz = "";
@ -15,8 +14,8 @@
let loaded = false;
let lastProcessedPkg: GUIPackage | null = null;
const loadImage = async (url: string): Promise<string> => {
if (url.includes("cached_images")) {
const loadImage = async (url: string, force = false): Promise<string> => {
if (url.includes("cached_images") || force) {
loadedImg = url;
loaded = true;
return url;
@ -38,21 +37,12 @@
});
};
const recachePkg = async () => {
const url = await packagesStore.cachePkgImage(pkg);
loadImage(url);
};
const getCache = async () => {
if (pkg.cached_image_url) {
loadImage(pkg.cached_image_url).catch(() => {
if (pkg.thumb_image_url) {
loadImage(pkg.thumb_image_url);
recachePkg();
}
if (pkg.image_512_url) {
loadImage(pkg.image_512_url, true).finally(async () => {
const nextImage = await packagesStore.cachePkgImage(pkg);
loadImage(nextImage, true);
});
} else if (pkg.thumb_image_url) {
recachePkg();
}
};
@ -69,7 +59,7 @@
<section class="overflow-hidden bg-black {clazz} {layout}">
<i
class="logo icon-tea-logo-iconasset-1 text-3xl text-gray {layout}"
class:animate-pulse={!pkg.thumb_image_url}
class:animate-pulse={!pkg.image_added_at}
/>
<div
class="opacity-0 transition-all duration-500 {layout}"

View file

@ -24,9 +24,7 @@
<ImgLoader
on:click={() => gotoPackagePage()}
class="pkg-image h-16 w-16 object-cover"
src={!pkg.thumb_image_url.includes("https://tea.xyz")
? "/images/default-thumb.jpg"
: pkg.thumb_image_url}
src={pkg.image_128_url || "/images/default-thumb.jpg"}
alt={pkg.name}
/>
<header data-testid="card-result-{pkg.slug}" class="flex-grow" on:click={() => gotoPackagePage()}>

View file

@ -20,15 +20,14 @@ const packages: Package[] = [
last_modified: "2022-10-06T15:45:08.000Z",
full_name: "mesonbuild.com",
dl_count: 270745,
thumb_image_name: "mesonbuild_com_option 1.jpg ",
maintainer: "",
desc: "Fast and user friendly build system",
thumb_image_url: "https://tea.xyz/Images/packages/mesonbuild_com.jpg",
installs: 0,
categories: ["foundation_essentials"],
created: "2022-10-06T15:45:08.000Z",
manual_sorting: 0,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
},
{
slug: "pixman_org",
@ -39,14 +38,13 @@ const packages: Package[] = [
last_modified: "2022-09-26T19:37:47.000Z",
full_name: "pixman.org",
dl_count: 0,
thumb_image_name: "pixman_org_option 1.jpg ",
desc: "Pixman is a library that provides low-level pixel manipulation features such as image compositing and trapezoid rasterization.",
thumb_image_url: "https://tea.xyz/Images/packages/pixman_org.jpg",
installs: 0,
categories: ["foundation_essentials"],
created: "2022-09-26T19:37:47.000Z",
manual_sorting: 1,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
},
{
slug: "freedesktop_org_pkg_config",
@ -57,14 +55,13 @@ const packages: Package[] = [
last_modified: "2022-10-20T01:32:15.000Z",
full_name: "freedesktop.org/pkg-config",
dl_count: 2661501,
thumb_image_name: "freedecktop_org_pkg_config option 1.jpg ",
desc: "Manage compile and link flags for libraries",
thumb_image_url: "https://tea.xyz/Images/packages/freedesktop_org_pkg_config.jpg",
installs: 0,
categories: ["foundation_essentials"],
created: "2022-10-20T01:32:15.000Z",
manual_sorting: 2,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
},
{
slug: "gnu_org_gettext",
@ -75,14 +72,13 @@ const packages: Package[] = [
last_modified: "2022-10-20T01:23:46.000Z",
full_name: "gnu.org/gettext",
dl_count: 3715970,
thumb_image_name: "gnu_org_gettext_option 1.jpg ",
desc: "GNU internationalization (i18n) and localization (l10n) library",
thumb_image_url: "https://tea.xyz/Images/packages/gnu_org_gettext.jpg",
installs: 0,
categories: ["foundation_essentials"],
created: "2022-10-20T01:23:46.000Z",
manual_sorting: 3,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
},
{
slug: "ipfs_tech",
@ -92,15 +88,14 @@ const packages: Package[] = [
last_modified: "2022-10-19T21:36:52.000Z",
full_name: "ipfs.tech",
dl_count: 14457,
thumb_image_name: "ipfs_tech_option 2.jpg ",
maintainer: "",
desc: "Peer-to-peer hypermedia protocol",
thumb_image_url: "https://tea.xyz/Images/packages/ipfs_tech.jpg",
installs: 0,
categories: ["foundation_essentials"],
created: "2022-10-19T21:36:52.000Z",
manual_sorting: 4,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
},
{
slug: "nixos_org_patchelf",
@ -111,14 +106,13 @@ const packages: Package[] = [
last_modified: "2022-09-27T04:50:44.000Z",
full_name: "nixos.org/patchelf",
dl_count: 0,
thumb_image_name: "nixos_org_patchelf_option 1.jpg ",
desc: "PatchELF is a simple utility for modifying existing ELF executables and libraries.",
thumb_image_url: "https://tea.xyz/Images/packages/nixos_org_patchelf.jpg",
installs: 0,
categories: ["top_packages", "foundation_essentials"],
created: "2022-09-27T04:50:44.000Z",
manual_sorting: 5,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
},
{
slug: "tea_xyz",
@ -129,14 +123,13 @@ const packages: Package[] = [
last_modified: "2022-10-19T19:13:51.000Z",
full_name: "tea.xyz",
dl_count: 0,
thumb_image_name: "tea_xyz_option 2.jpg ",
desc: "Website of tea.xyz",
thumb_image_url: "https://tea.xyz/Images/packages/tea_xyz.jpg",
installs: 0,
categories: ["top_packages", "foundation_essentials"],
created: "2022-10-19T19:13:51.000Z",
manual_sorting: 6,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
},
{
slug: "charm_sh_gum",
@ -147,14 +140,13 @@ const packages: Package[] = [
last_modified: "2022-10-21T02:15:16.000Z",
full_name: "charm.sh/gum",
dl_count: 0,
thumb_image_name: "charm_sh_gum.jpg ",
desc: "",
thumb_image_url: "https://tea.xyz/Images/packages/charm_sh_gum.jpg",
installs: 0,
categories: ["top_packages", "foundation_essentials"],
created: "2022-10-21T02:15:16.000Z",
manual_sorting: 7,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
},
{
slug: "pyyaml_org",
@ -164,15 +156,14 @@ const packages: Package[] = [
last_modified: "2022-10-03T15:35:14.000Z",
full_name: "pyyaml.org",
dl_count: 107505,
thumb_image_name: "pyyaml_org_option 1.jpg ",
maintainer: "",
desc: "YAML framework for Python",
thumb_image_url: "https://tea.xyz/Images/packages/pyyaml_org.jpg",
installs: 0,
categories: ["top_packages", "foundation_essentials"],
created: "2022-10-03T15:35:14.000Z",
manual_sorting: 8,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
},
{
slug: "tea_xyz_gx_cc",
@ -183,14 +174,13 @@ const packages: Package[] = [
last_modified: "2022-10-19T16:47:44.000Z",
full_name: "tea.xyz/gx/cc",
dl_count: 0,
thumb_image_name: "tea_xyz_gx.jpg ",
desc: "",
thumb_image_url: "https://tea.xyz/Images/packages/tea_xyz_gx_cc.jpg",
installs: 0,
categories: ["top_packages", "foundation_essentials"],
created: "2022-10-19T16:47:44.000Z",
manual_sorting: 9,
card_layout: "bottom"
card_layout: "bottom",
image_added_at: new Date()
}
];

View file

@ -15,7 +15,8 @@ import {
listenToChannel,
getInstalledVersionsForPackage,
monitorTeaDir,
stopMonitoringTeaDir
stopMonitoringTeaDir,
isDev
} from "@native";
import { getReadme, getContributors, getRepoAsPackage } from "$libs/github";
@ -38,6 +39,8 @@ let initialized = false;
let isDestroyed = false;
let refreshTimeoutId: ReturnType<typeof setTimeout> | null = null;
const dev = isDev();
const packageMap = writable<Packages>({ version: "0", packages: {} });
const packageList = derived(packageMap, ($packages) =>
Object.values($packages.packages).sort((a, b) => {
@ -162,12 +165,22 @@ const refreshPackages = async () => {
if (isDestroyed) return;
log.info("packages store: refreshing...");
const isDev = await dev;
const pkgs = await getDistPackages();
const guiPkgs: GUIPackage[] = pkgs.map((p) => ({
...p,
state: PackageStates.AVAILABLE
}));
const guiPkgs: GUIPackage[] = pkgs.map((p) => {
const prefix = `https://gui.tea.xyz/${isDev ? "dev" : "prod"}/${p.full_name}`;
return {
...p,
state: PackageStates.AVAILABLE,
...(p.image_added_at
? {
image_512_url: `${prefix}/512x512.webp`,
image_128_url: `${prefix}/128x128.webp`
}
: {})
};
});
if (!initialized) {
// set packages data so that i can render something in the UI already
@ -291,8 +304,8 @@ packageMap.subscribe(async (pkgs) => {
const cachePkgImage = async (pkg: GUIPackage): Promise<string> => {
let cacheFileURL = "";
updatePackage(pkg.full_name, { cached_image_url: "" });
if (pkg.thumb_image_url && !pkg.thumb_image_url.includes("package-thumb-nolabel4.jpg")) {
const result = await cacheImageURL(pkg.thumb_image_url);
if (pkg.image_added_at && pkg.image_512_url) {
const result = await cacheImageURL(pkg.image_512_url);
if (result) {
cacheFileURL = result;
updatePackage(pkg.full_name, { cached_image_url: cacheFileURL });
@ -301,6 +314,15 @@ const cachePkgImage = async (pkg: GUIPackage): Promise<string> => {
return cacheFileURL;
};
export const getPackageImageURL = async (
pkg: GUIPackage,
size: 512 | 1024 | 128
): Promise<string> => {
if (!pkg.image_added_at) return "";
const isDev = await dev;
return `https://gui.tea.xyz/${isDev ? "dev" : "prod"}/${pkg.full_name}/${size}x${size}.webp`;
};
listenToChannel("install-progress", ({ full_name, progress }: any) => {
if (!full_name) {
return;
@ -387,5 +409,6 @@ export default {
destroy,
cachePkgImage,
resetPackageDisplayState,
resetAllPackagesUpdatedState
resetAllPackagesUpdatedState,
getPackageImageURL
};

View file

@ -36,7 +36,8 @@ export type GUIPackage = Package & {
install_progress_percentage?: number;
isUninstalling?: boolean;
cached_image_url?: string;
image_512_url?: string;
image_128_url?: string;
displayState?: PackageDisplayState | null;
};

View file

@ -15,8 +15,6 @@ export interface Package {
homepage: string;
last_modified: Date | string;
created: Date | string;
thumb_image_url: string;
thumb_image_name: string;
desc: string;
dl_count: number;
installs: number;
@ -37,6 +35,7 @@ export interface Package {
};
manual_sorting: number;
card_layout: "bottom" | "right" | "left";
image_added_at: Date | null;
}
export type AirtablePost = {