more seeds
This commit is contained in:
@@ -21,6 +21,76 @@ type RequestAvatarGenerationInput = RequestAvatarGenerationInputDTO;
|
||||
type UploadMediaInput = UploadMediaInputDTO;
|
||||
type UpdateAvatarInput = UpdateAvatarInputDTO;
|
||||
|
||||
function hashToHue(input: string): number {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < input.length; i += 1) {
|
||||
hash = (hash * 31 + input.charCodeAt(i)) | 0;
|
||||
}
|
||||
return Math.abs(hash) % 360;
|
||||
}
|
||||
|
||||
function escapeXml(input: string): string {
|
||||
return input
|
||||
.replaceAll('&', '\u0026amp;')
|
||||
.replaceAll('<', '\u0026lt;')
|
||||
.replaceAll('>', '\u0026gt;')
|
||||
.replaceAll('"', '\u0026quot;')
|
||||
.replaceAll("'", '\u0026apos;');
|
||||
}
|
||||
|
||||
function deriveLeagueLabel(leagueId: string): string {
|
||||
const digits = leagueId.match(/\d+/)?.[0];
|
||||
if (digits) return digits.slice(-2);
|
||||
return leagueId.replaceAll(/[^a-zA-Z]/g, '').slice(0, 2).toUpperCase() || 'GP';
|
||||
}
|
||||
|
||||
function buildLeagueLogoSvg(leagueId: string): string {
|
||||
const hue = hashToHue(leagueId);
|
||||
const label = escapeXml(deriveLeagueLabel(leagueId));
|
||||
const bg = `hsl(${hue} 70% 38%)`;
|
||||
const border = `hsl(${hue} 70% 28%)`;
|
||||
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96" role="img" aria-label="League logo">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="${bg}"/>
|
||||
<stop offset="100%" stop-color="hsl(${hue} 80% 46%)"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect x="2" y="2" width="92" height="92" rx="18" fill="url(#g)" stroke="${border}" stroke-width="4"/>
|
||||
<text x="48" y="56" font-family="ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial" font-size="34" font-weight="800" text-anchor="middle" fill="white">${label}</text>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
function buildLeagueCoverSvg(leagueId: string): string {
|
||||
const hue = hashToHue(leagueId);
|
||||
const title = escapeXml(leagueId);
|
||||
const bg1 = `hsl(${hue} 70% 28%)`;
|
||||
const bg2 = `hsl(${(hue + 35) % 360} 85% 35%)`;
|
||||
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="400" viewBox="0 0 1200 400" role="img" aria-label="League cover">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="${bg1}"/>
|
||||
<stop offset="100%" stop-color="${bg2}"/>
|
||||
</linearGradient>
|
||||
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.07)" stroke-width="2"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<rect width="1200" height="400" fill="url(#bg)"/>
|
||||
<rect width="1200" height="400" fill="url(#grid)"/>
|
||||
<circle cx="1020" cy="120" r="180" fill="rgba(255,255,255,0.06)"/>
|
||||
<circle cx="1080" cy="170" r="120" fill="rgba(255,255,255,0.05)"/>
|
||||
|
||||
<text x="64" y="110" font-family="ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial" font-size="40" font-weight="800" fill="rgba(255,255,255,0.92)">GridPilot League</text>
|
||||
<text x="64" y="165" font-family="ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial" font-size="22" font-weight="600" fill="rgba(255,255,255,0.75)">${title}</text>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
@ApiTags('media')
|
||||
@Controller('media')
|
||||
export class MediaController {
|
||||
@@ -61,6 +131,34 @@ export class MediaController {
|
||||
}
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get('leagues/:leagueId/logo')
|
||||
@ApiOperation({ summary: 'Get league logo (placeholder)' })
|
||||
@ApiParam({ name: 'leagueId', description: 'League ID' })
|
||||
async getLeagueLogo(
|
||||
@Param('leagueId') leagueId: string,
|
||||
@Res() res: Response,
|
||||
): Promise<void> {
|
||||
const svg = buildLeagueLogoSvg(leagueId);
|
||||
res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
|
||||
res.setHeader('Cache-Control', 'public, max-age=86400');
|
||||
res.status(HttpStatus.OK).send(svg);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get('leagues/:leagueId/cover')
|
||||
@ApiOperation({ summary: 'Get league cover (placeholder)' })
|
||||
@ApiParam({ name: 'leagueId', description: 'League ID' })
|
||||
async getLeagueCover(
|
||||
@Param('leagueId') leagueId: string,
|
||||
@Res() res: Response,
|
||||
): Promise<void> {
|
||||
const svg = buildLeagueCoverSvg(leagueId);
|
||||
res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
|
||||
res.setHeader('Cache-Control', 'public, max-age=86400');
|
||||
res.status(HttpStatus.OK).send(svg);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Get(':mediaId')
|
||||
@ApiOperation({ summary: 'Get media by ID' })
|
||||
|
||||
Reference in New Issue
Block a user