From 6fd0f3f2f1d3646ea83781122111394e149e4ef6 Mon Sep 17 00:00:00 2001 From: Samuel Elliott Date: Sun, 7 Jan 2024 09:39:23 +0000 Subject: [PATCH] Fix Splatfest team image when converting presence embed to PNG/JPEG/WEBP --- package-lock.json | 14 ++++++++++++++ package.json | 2 ++ src/cli/presence-server.ts | 12 ++++++++---- src/cli/util/presence-embed-render.ts | 6 +++++- src/cli/util/presence-embed-server.ts | 8 ++++++-- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 184cb24..d1a94e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "@types/discord-rpc": "^4.0.8", "@types/eventsource": "^1.1.15", "@types/express": "^4.17.21", + "@types/mime-types": "^2.1.4", "@types/node": "^18.18.10", "@types/node-notifier": "^8.0.5", "@types/node-persist": "^3.1.6", @@ -51,6 +52,7 @@ "@types/yargs": "^17.0.31", "electron": "^26.6.0", "electron-builder": "^24.6.4", + "mime-types": "^2.1.35", "react": "^18.2.0", "react-dom": "^18.2.0", "react-native-web": "^0.19.9", @@ -4242,6 +4244,12 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, + "node_modules/@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "dev": true + }, "node_modules/@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", @@ -14725,6 +14733,12 @@ "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", "dev": true }, + "@types/mime-types": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", + "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", + "dev": true + }, "@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", diff --git a/package.json b/package.json index 666c351..6565513 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "@types/discord-rpc": "^4.0.8", "@types/eventsource": "^1.1.15", "@types/express": "^4.17.21", + "@types/mime-types": "^2.1.4", "@types/node": "^18.18.10", "@types/node-notifier": "^8.0.5", "@types/node-persist": "^3.1.6", @@ -75,6 +76,7 @@ "@types/yargs": "^17.0.31", "electron": "^26.6.0", "electron-builder": "^24.6.4", + "mime-types": "^2.1.35", "react": "^18.2.0", "react-dom": "^18.2.0", "react-native-web": "^0.19.9", diff --git a/src/cli/presence-server.ts b/src/cli/presence-server.ts index eb381ee..2069df2 100644 --- a/src/cli/presence-server.ts +++ b/src/cli/presence-server.ts @@ -6,6 +6,7 @@ import { createHash } from 'node:crypto'; import express, { Request, Response } from 'express'; import { fetch } from 'undici'; import * as persist from 'node-persist'; +import mimetypes from 'mime-types'; import { BankaraMatchSetting_schedule, CoopRule, CoopSetting_schedule, DetailFestRecordDetailResult, DetailVotingStatusResult, FestMatchSetting_schedule, FestRecordResult, FestState, FestTeam_schedule, FestTeam_votingStatus, FestVoteState, Fest_schedule, FriendListResult, FriendOnlineState, Friend_friendList, GraphQLSuccessResponse, KnownRequestId, LeagueMatchSetting_schedule, RegularMatchSetting_schedule, StageScheduleResult, XMatchSetting_schedule } from 'splatnet3-types/splatnet3'; import type { Arguments as ParentArguments } from '../cli.js'; import { git, product, version } from '../util/product.js'; @@ -1189,7 +1190,7 @@ class Server extends HttpServer { const result = await this.handlePresenceRequest(req, null, presence_user_nsaid); - const {theme, friend_code, transparent, width} = getUserEmbedOptionsFromRequest(req); + const {theme, friend_code, transparent, width, options} = getUserEmbedOptionsFromRequest(req); const etag = createHash('sha256').update(JSON.stringify({ result, @@ -1197,6 +1198,7 @@ class Server extends HttpServer { friend_code, transparent, width, + options, v: version + '-' + git?.revision, })).digest('base64url'); @@ -1208,7 +1210,7 @@ class Server extends HttpServer { const url_map = await this.getImages(result, this.getResourceBaseUrls(req)); - const svg = renderUserEmbedSvg(result, url_map, theme, friend_code, 1, transparent, width); + const svg = renderUserEmbedSvg(result, url_map, theme, friend_code, options, 1, transparent, width); const [image, type] = await renderUserEmbedImage(svg, format); res.setHeader('Content-Type', type); @@ -1361,11 +1363,13 @@ class Server extends HttpServer { .replace(/(\/|^)\.\.(\/|$)/g, '$1...$2') + (path.extname(pathname) ? '' : '.jpeg'); + const type = (mimetypes.lookup(path.extname(pathname) || '.jpeg') || 'image/jpeg').split(';')[0]; + const promise = this.promise_image.get(dir + '/' + name) ?? Promise.resolve().then(async () => { try { if (return_image_data) { const data = await fs.readFile(path.join(dir, name)); - return [name, data, 'image/jpeg'] as const; + return [name, data, type] as const; } await fs.stat(path.join(dir, name)); @@ -1384,7 +1388,7 @@ class Server extends HttpServer { debug('Downloaded image %s', name); if (return_image_data) { - return [name, data, 'image/jpeg'] as const; + return [name, data, type] as const; } return name; diff --git a/src/cli/util/presence-embed-render.ts b/src/cli/util/presence-embed-render.ts index d1fa84a..aa4a391 100644 --- a/src/cli/util/presence-embed-render.ts +++ b/src/cli/util/presence-embed-render.ts @@ -1,3 +1,4 @@ +import mimetypes from 'mime-types'; import { FestVoteState } from 'splatnet3-types/splatnet3'; import type { Arguments as ParentArguments } from '../util.js'; import createDebug from '../../util/debug.js'; @@ -80,7 +81,10 @@ export async function handler(argv: ArgumentsCamelCase) { const response = await fetch(url); const data = new Uint8Array(await response.arrayBuffer()); - url_map[url] = [url, data, 'image/jpeg']; + const type = (mimetypes.contentType(response.headers.get('Content-Type') ?? 'application/octet-stream') + || 'application/octet-stream').split(';')[0]; + + url_map[url] = [url, data, type]; })); const svg = renderUserEmbedSvg(result, url_map, theme, argv.friendCode, { diff --git a/src/cli/util/presence-embed-server.ts b/src/cli/util/presence-embed-server.ts index 93c59ce..a3476e7 100644 --- a/src/cli/util/presence-embed-server.ts +++ b/src/cli/util/presence-embed-server.ts @@ -2,6 +2,7 @@ import * as os from 'node:os'; import * as net from 'node:net'; import express, { Request, Response } from 'express'; import { createHash } from 'node:crypto'; +import mimetypes from 'mime-types'; import { FestVoteState } from 'splatnet3-types/splatnet3'; import type { Arguments as ParentArguments } from '../util.js'; import createDebug from '../../util/debug.js'; @@ -123,7 +124,7 @@ class Server extends HttpServer { const image_urls = [result.friend.imageUri]; if ('imageUri' in result.friend.presence.game) image_urls.push(result.friend.presence.game.imageUri); - if (options.show_splatoon3_fest_team && result.splatoon3_fest_team?.image.url) image_urls.push(result.splatoon3_fest_team.image.url); + if (options.show_splatoon3_fest_team && result.splatoon3_fest_team?.myVoteState === FestVoteState.VOTED) image_urls.push(result.splatoon3_fest_team.image.url); const url_map: Record = {}; @@ -132,7 +133,10 @@ class Server extends HttpServer { const response = await fetch(url); const data = new Uint8Array(await response.arrayBuffer()); - url_map[url] = [url, data, 'image/jpeg']; + const type = (mimetypes.contentType(response.headers.get('Content-Type') ?? 'application/octet-stream') + || 'application/octet-stream').split(';')[0]; + + url_map[url] = [url, data, type]; })); const svg = renderUserEmbedSvg(result, url_map, theme, friend_code, options, 1, transparent, width);