@@ -3,6 +3,19 @@ import GameServer from "@/app/services/gamebridge/GameServer.js";
33import nodeHtmlToImage from "node-html-to-image" ;
44import path from "path" ;
55import pug from "pug" ;
6+ import { access , mkdir , readFile , writeFile } from "fs/promises" ;
7+ import { constants as fs_constants } from "fs" ;
8+ import mime from "mime" ;
9+
10+ const cacheFolder = path . join ( process . cwd ( ) , "cache" , "map-thumbnails" ) ;
11+
12+ const imageToDataURL = async filePath => {
13+ const mimeType = mime . getType ( filePath ) ;
14+ if ( ! mimeType ) throw new Error ( "No MIME type from path?" ) ;
15+
16+ const base64 = await readFile ( filePath , { encoding : "base64" } ) ;
17+ return `data:${ mimeType } ;base64,${ base64 } ` ;
18+ } ;
619
720export default async ( webApp : WebApp ) : Promise < void > => {
821 webApp . app . get ( "/server-status/:id{/:bruh}" , async ( req , res ) => {
@@ -25,11 +38,56 @@ export default async (webApp: WebApp): Promise<void> => {
2538 ! req . headers . accept ||
2639 req . headers [ "user-agent" ] ?. includes ( "+https://discordapp.com" ) ;
2740
41+ // #region Map Thumbnail
42+ const mapThumbnail = server . status . mapThumbnail ;
43+ const workshopMap = server . workshopMap ;
44+ let thumbFilepath : string | undefined = undefined ;
45+ if ( mapThumbnail ?. match ( / ^ h t t p s ? : \/ \/ / ) && workshopMap ) {
46+ await access ( cacheFolder , fs_constants . F_OK ) . catch ( ( ) =>
47+ mkdir ( cacheFolder , { recursive : true } )
48+ ) ;
49+
50+ const _thumbFilepath = path . join ( cacheFolder , `${ workshopMap . id } ` ) ;
51+ let hasFile = true ;
52+ // have to assume these are the only extensions we expect...
53+ for ( const ext of [ ".webp" , ".jpg" , ".jpeg" , ".png" , ".gif" ] ) {
54+ await access ( _thumbFilepath + ext , fs_constants . F_OK ) . catch ( ( ) => {
55+ hasFile = false ;
56+ } ) ;
57+ if ( hasFile ) {
58+ thumbFilepath = _thumbFilepath + ext ;
59+ break ;
60+ }
61+ }
62+ if ( ! thumbFilepath ) {
63+ try {
64+ const response = await fetch ( mapThumbnail ) ;
65+ if ( response . ok ) {
66+ const contentType = response . headers . get ( "content-type" ) ;
67+ if ( ! contentType ) throw new Error ( "No content-type" ) ;
68+
69+ const ext = mime . getExtension ( contentType ) ;
70+ if ( ! ext ) throw new Error ( "No extension" ) ;
71+
72+ thumbFilepath = [ _thumbFilepath , ext . toLowerCase ( ) ] . join ( "." ) ;
73+
74+ const buffer = Buffer . from ( await response . arrayBuffer ( ) ) ;
75+ await writeFile ( thumbFilepath , buffer ) ;
76+ }
77+ } catch ( err ) { }
78+ }
79+ } else {
80+ thumbFilepath = mapThumbnail as string ;
81+ }
82+ const mapThumbnail64 = await imageToDataURL ( thumbFilepath ) ;
83+ // #endregion
84+
2885 const html = pug . renderFile (
2986 path . join ( process . cwd ( ) , "resources/game-server-status/view.pug" ) ,
3087 {
3188 server,
3289 image : ! ! discordBot ,
90+ mapThumbnail64,
3391 }
3492 ) ;
3593 if ( discordBot ) {
0 commit comments