From 22fb3a2e30568a0bb4d6b6db0c536a63da33567e Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Fri, 13 Mar 2026 22:07:10 +0000 Subject: [PATCH] feat: abandon webp --- next.config.ts | 2 +- public/guest.png | Bin 0 -> 3428 bytes public/guest.webp | Bin 1554 -> 0 bytes src/app/api/auth/picture/route.ts | 8 ++++---- src/app/api/mii/[id]/edit/route.ts | 6 +++--- src/app/api/submit/route.ts | 18 +++++++++--------- src/app/mii/[id]/image/route.ts | 5 ++--- src/app/profile/[id]/page.tsx | 4 ++-- src/app/profile/[id]/picture/route.ts | 2 +- src/components/description.tsx | 2 +- src/components/dropzone.tsx | 2 +- src/components/profile-information.tsx | 2 +- src/components/profile-overview.tsx | 2 +- src/components/profile-picture.tsx | 2 +- .../profile-settings/profile-picture.tsx | 4 ++-- src/components/report/user-form.tsx | 2 +- src/components/submit-form/edit-form.tsx | 2 +- src/lib/images.tsx | 10 +++------- 18 files changed, 34 insertions(+), 39 deletions(-) create mode 100644 public/guest.png delete mode 100644 public/guest.webp diff --git a/next.config.ts b/next.config.ts index 121c3a3..8e76b72 100644 --- a/next.config.ts +++ b/next.config.ts @@ -15,7 +15,7 @@ const nextConfig: NextConfig = { pathname: "/tutorial/**", }, { - pathname: "/guest.webp", + pathname: "/guest.png", }, ], remotePatterns: [ diff --git a/public/guest.png b/public/guest.png new file mode 100644 index 0000000000000000000000000000000000000000..f6c82fe74f43b857435dd0526966576ea1c4755d GIT binary patch literal 3428 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 E00#^OF#rGn diff --git a/src/app/api/auth/picture/route.ts b/src/app/api/auth/picture/route.ts index 8688d2a..813f7f8 100644 --- a/src/app/api/auth/picture/route.ts +++ b/src/app/api/auth/picture/route.ts @@ -49,7 +49,7 @@ export async function PATCH(request: NextRequest) { if (!image) { await prisma.user.update({ where: { id: Number(session.user.id) }, - data: { image: `/guest.webp`, imageUpdatedAt: new Date() }, + data: { image: `/guest.png`, imageUpdatedAt: new Date() }, }); return rateLimit.sendResponse({ success: true }); @@ -64,10 +64,10 @@ export async function PATCH(request: NextRequest) { try { const buffer = Buffer.from(await image.arrayBuffer()); - const webpBuffer = await sharp(buffer, { animated: true }).resize({ width: 128, height: 128 }).webp({ quality: 85 }).toBuffer(); - const fileLocation = path.join(uploadsDirectory, `${session.user.id}.webp`); + const pngBuffer = await sharp(buffer, { animated: true }).resize({ width: 128, height: 128 }).png({ quality: 85 }).toBuffer(); + const fileLocation = path.join(uploadsDirectory, `${session.user.id}.png`); - await fs.writeFile(fileLocation, webpBuffer); + await fs.writeFile(fileLocation, pngBuffer); } catch (error) { console.error("Error uploading profile picture:", error); Sentry.captureException(error, { extra: { stage: "upload-profile-picture" } }); diff --git a/src/app/api/mii/[id]/edit/route.ts b/src/app/api/mii/[id]/edit/route.ts index 85b40ac..29a35f1 100644 --- a/src/app/api/mii/[id]/edit/route.ts +++ b/src/app/api/mii/[id]/edit/route.ts @@ -126,10 +126,10 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< await Promise.all( images.map(async (image, index) => { const buffer = Buffer.from(await image.arrayBuffer()); - const webpBuffer = await sharp(buffer).webp({ quality: 85 }).toBuffer(); - const fileLocation = path.join(miiUploadsDirectory, `image${index}.webp`); + const pngBuffer = await sharp(buffer).png({ quality: 85 }).toBuffer(); + const fileLocation = path.join(miiUploadsDirectory, `image${index}.png`); - await fs.writeFile(fileLocation, webpBuffer); + await fs.writeFile(fileLocation, pngBuffer); }), ); } catch (error) { diff --git a/src/app/api/submit/route.ts b/src/app/api/submit/route.ts index 824d51f..11cd0fa 100644 --- a/src/app/api/submit/route.ts +++ b/src/app/api/submit/route.ts @@ -148,10 +148,10 @@ export async function POST(request: NextRequest) { try { // Compress and store - const studioWebpBuffer = await sharp(studioBuffer).webp({ quality: 85 }).toBuffer(); - const studioFileLocation = path.join(miiUploadsDirectory, "mii.webp"); + const studioPngBuffer = await sharp(studioBuffer).png({ quality: 85 }).toBuffer(); + const studioFileLocation = path.join(miiUploadsDirectory, "mii.png"); - await fs.writeFile(studioFileLocation, studioWebpBuffer); + await fs.writeFile(studioFileLocation, studioPngBuffer); // Generate a new QR code for aesthetic reasons const byteString = String.fromCharCode(...qrBytes); @@ -165,10 +165,10 @@ export async function POST(request: NextRequest) { const codeBuffer = Buffer.from(codeBase64, "base64"); // Compress and store - const codeWebpBuffer = await sharp(codeBuffer).webp({ quality: 85 }).toBuffer(); - const codeFileLocation = path.join(miiUploadsDirectory, "qr-code.webp"); + const codePngBuffer = await sharp(codeBuffer).png({ quality: 85 }).toBuffer(); + const codeFileLocation = path.join(miiUploadsDirectory, "qr-code.png"); - await fs.writeFile(codeFileLocation, codeWebpBuffer); + await fs.writeFile(codeFileLocation, codePngBuffer); await generateMetadataImage(miiRecord, session.user.name!); } catch (error) { // Clean up if something went wrong @@ -184,10 +184,10 @@ export async function POST(request: NextRequest) { await Promise.all( images.map(async (image, index) => { const buffer = Buffer.from(await image.arrayBuffer()); - const webpBuffer = await sharp(buffer).webp({ quality: 85 }).toBuffer(); - const fileLocation = path.join(miiUploadsDirectory, `image${index}.webp`); + const pngBuffer = await sharp(buffer).png({ quality: 85 }).toBuffer(); + const fileLocation = path.join(miiUploadsDirectory, `image${index}.png`); - await fs.writeFile(fileLocation, webpBuffer); + await fs.writeFile(fileLocation, pngBuffer); }), ); diff --git a/src/app/mii/[id]/image/route.ts b/src/app/mii/[id]/image/route.ts index 50e23df..d6d553d 100644 --- a/src/app/mii/[id]/image/route.ts +++ b/src/app/mii/[id]/image/route.ts @@ -32,8 +32,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ if (!searchParamsParsed.success) return rateLimit.sendResponse({ error: searchParamsParsed.error.issues[0].message }, 400); const { type: imageType } = searchParamsParsed.data; - const fileExtension = imageType === "metadata" ? ".png" : ".webp"; - const filePath = path.join(process.cwd(), "uploads", "mii", miiId.toString(), `${imageType}${fileExtension}`); + const filePath = path.join(process.cwd(), "uploads", "mii", miiId.toString(), `${imageType}.png`); let buffer: Buffer | undefined; // Only find Mii if image type is 'metadata' @@ -109,7 +108,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ } return rateLimit.sendResponse(buffer, 200, { - "Content-Type": "image/webp", + "Content-Type": "image/png", "X-Robots-Tag": "noindex, noimageindex, nofollow", "Cache-Control": "no-store", }); diff --git a/src/app/profile/[id]/page.tsx b/src/app/profile/[id]/page.tsx index 422d218..4140c3a 100644 --- a/src/app/profile/[id]/page.tsx +++ b/src/app/profile/[id]/page.tsx @@ -47,7 +47,7 @@ export async function generateMetadata({ params }: Props): Promise { type: "profile", title: `${user.name} (@${user.username}) - TomodachiShare`, description: `View ${user.name}'s profile on TomodachiShare. Creator of ${user._count.miis} Miis. Member since ${joinDate}.`, - images: [user.image ?? "/guest.webp"], + images: [user.image ?? "/guest.png"], username: user.username, firstName: user.name, }, @@ -55,7 +55,7 @@ export async function generateMetadata({ params }: Props): Promise { card: "summary", title: `${user.name} (@${user.username}) - TomodachiShare`, description: `View ${user.name}'s profile on TomodachiShare. Creator of ${user._count.miis} Miis. Member since ${joinDate}.`, - images: [user.image ?? "/guest.webp"], + images: [user.image ?? "/guest.png"], creator: user.username!, }, alternates: { diff --git a/src/app/profile/[id]/picture/route.ts b/src/app/profile/[id]/picture/route.ts index a050da7..352f887 100644 --- a/src/app/profile/[id]/picture/route.ts +++ b/src/app/profile/[id]/picture/route.ts @@ -16,7 +16,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400); const userId = parsed.data; - const filePath = path.join(process.cwd(), "uploads", "user", `${userId}.webp`); + const filePath = path.join(process.cwd(), "uploads", "user", `${userId}.png`); try { const buffer = await fs.readFile(filePath); diff --git a/src/components/description.tsx b/src/components/description.tsx index e37e707..deacf3f 100644 --- a/src/components/description.tsx +++ b/src/components/description.tsx @@ -63,7 +63,7 @@ export default function Description({ text, className }: Props) { href={`/profile/${id}`} className="inline-flex items-center align-bottom gap-1.5 pr-2 bg-orange-100 border border-orange-400 rounded-lg mx-1 text-orange-800 text-xs" > - + {linkedProfile.name} ); diff --git a/src/components/dropzone.tsx b/src/components/dropzone.tsx index ff4611e..6f9188f 100644 --- a/src/components/dropzone.tsx +++ b/src/components/dropzone.tsx @@ -22,7 +22,7 @@ export default function Dropzone({ onDrop, options, children }: Props) { onDrop: handleDrop, maxFiles: 3, accept: { - "image/*": [".png", ".jpg", ".jpeg", ".bmp", ".webp", ".heic"], + "image/*": [".png", ".jpg", ".jpeg", ".bmp", ".png", ".heic"], }, ...options, }); diff --git a/src/components/profile-information.tsx b/src/components/profile-information.tsx index c11454f..0de8b8d 100644 --- a/src/components/profile-information.tsx +++ b/src/components/profile-information.tsx @@ -30,7 +30,7 @@ export default async function ProfileInformation({ userId, page }: Props) {
{/* Profile picture */} - + {/* User information */}
diff --git a/src/components/profile-overview.tsx b/src/components/profile-overview.tsx index 463e3b4..f536f98 100644 --- a/src/components/profile-overview.tsx +++ b/src/components/profile-overview.tsx @@ -9,7 +9,7 @@ export default async function ProfileOverview() {
  • profile picture) { const { src, ...rest } = props; const [imgSrc, setImgSrc] = useState(src); - return {"profile setImgSrc("/guest.webp")} />; + return {"profile setImgSrc("/guest.png")} />; } diff --git a/src/components/profile-settings/profile-picture.tsx b/src/components/profile-settings/profile-picture.tsx index 58aeb9c..0eb2745 100644 --- a/src/components/profile-settings/profile-picture.tsx +++ b/src/components/profile-settings/profile-picture.tsx @@ -59,7 +59,7 @@ export default function ProfilePictureSettings() {

    new profile picture

    New profile picture:

    new profile picture
    - +

    {user.name}

    @{user.username}

    diff --git a/src/components/submit-form/edit-form.tsx b/src/components/submit-form/edit-form.tsx index f27b3f4..c20122e 100644 --- a/src/components/submit-form/edit-form.tsx +++ b/src/components/submit-form/edit-form.tsx @@ -90,7 +90,7 @@ export default function EditForm({ mii, likes }: Props) { const response = await fetch(path); const blob = await response.blob(); - return Object.assign(new File([blob], `image${index}.webp`, { type: "image/webp" }), { path }); + return Object.assign(new File([blob], `image${index}.png`, { type: "image/png" }), { path }); }), ); diff --git a/src/lib/images.tsx b/src/lib/images.tsx index 8fff17d..f7cfeed 100644 --- a/src/lib/images.tsx +++ b/src/lib/images.tsx @@ -130,16 +130,14 @@ export async function generateMetadataImage(mii: Mii, author: string): Promise<{ // Load assets concurrently const [miiImage, qrCodeImage, fonts] = await Promise.all([ - // Read and convert the .webp images to .png (because satori doesn't support it) - fs.readFile(path.join(miiUploadsDirectory, "mii.webp")).then((buffer) => + // Read and convert the images to data URI + fs.readFile(path.join(miiUploadsDirectory, "mii.png")).then((buffer) => sharp(buffer) - .png() .toBuffer() .then((pngBuffer) => `data:image/png;base64,${pngBuffer.toString("base64")}`), ), - fs.readFile(path.join(miiUploadsDirectory, "qr-code.webp")).then((buffer) => + fs.readFile(path.join(miiUploadsDirectory, "qr-code.png")).then((buffer) => sharp(buffer) - .png() .toBuffer() .then((pngBuffer) => `data:image/png;base64,${pngBuffer.toString("base64")}`), ), @@ -209,8 +207,6 @@ export async function generateMetadataImage(mii: Mii, author: string): Promise<{ // Store the file try { - // I tried using .webp here but the quality looked awful - // but it actually might be well-liked due to the hatred of .webp const fileLocation = path.join(miiUploadsDirectory, "metadata.png"); await fs.writeFile(fileLocation, buffer); } catch (error) {