info overlay + script to update images in games and logs

This commit is contained in:
Zutatensuppe 2021-07-11 16:37:34 +02:00
parent 0cb1cec210
commit 518092d269
14 changed files with 148 additions and 93 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8">
<title>🧩 jigsaw.hyottoko.club</title>
<script type="module" crossorigin src="/assets/index.19dfb063.js"></script>
<script type="module" crossorigin src="/assets/index.93936dee.js"></script>
<link rel="modulepreload" href="/assets/vendor.684f7bc8.js">
<link rel="stylesheet" href="/assets/index.22dc307c.css">
</head>

View file

@ -626,10 +626,12 @@ function getPieceCount(gameId) {
return GAMES[gameId].puzzle.tiles.length;
}
function getImageUrl(gameId) {
return GAMES[gameId].puzzle.info.imageUrl;
}
function setImageUrl(gameId, imageUrl) {
GAMES[gameId].puzzle.info.imageUrl = imageUrl;
const imageUrl = GAMES[gameId].puzzle.info.image?.url
|| GAMES[gameId].puzzle.info.imageUrl;
if (!imageUrl) {
throw new Error('[2021-07-11] no image url set');
}
return imageUrl;
}
function getScoreMode(gameId) {
return GAMES[gameId].scoreMode;
@ -1243,7 +1245,6 @@ var GameCommon = {
getFinishedPiecesCount,
getPieceCount,
getImageUrl,
setImageUrl,
get: get$1,
getAllGames,
getPlayerBgColor,
@ -1358,6 +1359,8 @@ var GameLog = {
exists,
log: _log,
get,
filename,
idxname,
};
const log$4 = logger('Images.ts');
@ -1433,7 +1436,6 @@ const imageFromDb = (db, imageId) => {
return {
id: i.id,
filename: i.filename,
file: `${UPLOAD_DIR}/${i.filename}`,
url: `${UPLOAD_URL}/${encodeURIComponent(i.filename)}`,
title: i.title,
tags: getTags(db, i.id),
@ -1472,7 +1474,6 @@ inner join images i on i.id = ixc.image_id ${where.sql};
return images.map(i => ({
id: i.id,
filename: i.filename,
file: `${UPLOAD_DIR}/${i.filename}`,
url: `${UPLOAD_URL}/${encodeURIComponent(i.filename)}`,
title: i.title,
tags: getTags(db, i.id),
@ -1490,7 +1491,6 @@ const allImagesFromDisk = (tags, sort) => {
.map(f => ({
id: 0,
filename: f,
file: `${UPLOAD_DIR}/${f}`,
url: `${UPLOAD_URL}/${encodeURIComponent(f)}`,
title: f.replace(/\.[a-z]+$/, ''),
tags: [],
@ -1501,12 +1501,12 @@ const allImagesFromDisk = (tags, sort) => {
switch (sort) {
case 'alpha_asc':
images = images.sort((a, b) => {
return a.file > b.file ? 1 : -1;
return a.filename > b.filename ? 1 : -1;
});
break;
case 'alpha_desc':
images = images.sort((a, b) => {
return a.file < b.file ? 1 : -1;
return a.filename < b.filename ? 1 : -1;
});
break;
case 'date_asc':
@ -1552,7 +1552,7 @@ var Images = {
// final resized version of the puzzle image
const TILE_SIZE = 64;
async function createPuzzle(rng, targetTiles, image, ts, shapeMode) {
const imagePath = image.file;
const imagePath = `${UPLOAD_DIR}/${image.filename}`;
const imageUrl = image.url;
// determine puzzle information from the image dimensions
const dim = await Images.getDimensions(imagePath);
@ -1651,6 +1651,7 @@ async function createPuzzle(rng, targetTiles, image, ts, shapeMode) {
// information that was used to create the puzzle
targetTiles: targetTiles,
imageUrl,
image: image,
width: info.width,
height: info.height,
tileSize: info.tileSize,
@ -2114,7 +2115,8 @@ app.get('/api/replay-data', async (req, res) => {
let game = null;
if (offset === 0) {
// also need the game
game = await Game.createGameObject(gameId, log[0][2], log[0][3], log[0][4], log[0][5], log[0][6], log[0][7]);
game = await Game.createGameObject(gameId, log[0][2], log[0][3], // must be ImageInfo
log[0][4], log[0][5], log[0][6], log[0][7]);
}
res.send({ log, game: game ? Util.encodeGame(game) : null });
});

View file

@ -0,0 +1,90 @@
import GameCommon from '../src/common/GameCommon'
import GameLog from '../src/server/GameLog'
import { Game } from '../src/common/Types'
import { logger } from '../src/common/Util'
import { DB_FILE, DB_PATCHES_DIR, UPLOAD_DIR } from '../src/server/Dirs'
import Db from '../src/server/Db'
import GameStorage from '../src/server/GameStorage'
import fs from 'fs'
const log = logger('fix_games_image_info.ts')
import Images from '../src/server/Images'
console.log(DB_FILE)
const db = new Db(DB_FILE, DB_PATCHES_DIR)
db.patch(true)
// ;(async () => {
// let images = db.getMany('images')
// for (let image of images) {
// console.log(image.filename)
// let dim = await Images.getDimensions(`${UPLOAD_DIR}/${image.filename}`)
// console.log(await Images.getDimensions(`${UPLOAD_DIR}/${image.filename}`))
// image.width = dim.w
// image.height = dim.h
// db.upsert('images', image, { id: image.id })
// }
// })()
function fixOne(gameId: string) {
let g = GameCommon.get(gameId)
if (!g) {
return
}
if (!g.puzzle.info.image && g.puzzle.info.imageUrl) {
log.log('game id: ', gameId)
const parts = g.puzzle.info.imageUrl.split('/')
const fileName = parts[parts.length - 1]
const imageRow = db.get('images', {filename: fileName})
if (!imageRow) {
return
}
g.puzzle.info.image = Images.imageFromDb(db, imageRow.id)
log.log(g.puzzle.info.image.title, imageRow.id)
GameStorage.persistGame(gameId)
} else if (g.puzzle.info.image?.id) {
const imageId = g.puzzle.info.image.id
g.puzzle.info.image = Images.imageFromDb(db, imageId)
log.log(g.puzzle.info.image.title, imageId)
GameStorage.persistGame(gameId)
}
// fix log
const file = GameLog.filename(gameId, 0)
if (!fs.existsSync(file)) {
return
}
const lines = fs.readFileSync(file, 'utf-8').split("\n")
const l = lines.filter(line => !!line).map(line => {
return JSON.parse(`[${line}]`)
})
if (l && l[0] && !l[0][3].id) {
log.log(l[0][3])
l[0][3] = g.puzzle.info.image
const newlines = l.map(ll => {
return JSON.stringify(ll).slice(1, -1)
}).join("\n") + "\n"
console.log(g.puzzle.info.image)
// process.exit(0)
fs.writeFileSync(file, newlines)
}
}
function fix() {
GameStorage.loadGames()
GameCommon.getAllGames().forEach((game: Game) => {
fixOne(game.id)
})
}
fix()

View file

@ -1,23 +0,0 @@
import GameCommon from '../src/common/GameCommon'
import { logger } from '../src/common/Util'
import GameStorage from '../src/server/GameStorage'
const log = logger('fix_image.js')
function fix(gameId) {
GameStorage.loadGame(gameId)
let changed = false
let imgUrl = GameCommon.getImageUrl(gameId)
if (imgUrl.match(/^\/example-images\//)) {
log.log(`found bad imgUrl: ${imgUrl}`)
imgUrl = imgUrl.replace(/^\/example-images\//, '/uploads/')
GameCommon.setImageUrl(gameId, imgUrl)
changed = true
}
if (changed) {
GameStorage.persistGame(gameId)
}
}
fix(process.argv[2])

View file

@ -162,11 +162,12 @@ function getPieceCount(gameId: string): number {
}
function getImageUrl(gameId: string): string {
return GAMES[gameId].puzzle.info.imageUrl
}
function setImageUrl(gameId: string, imageUrl: string): void {
GAMES[gameId].puzzle.info.imageUrl = imageUrl
const imageUrl = GAMES[gameId].puzzle.info.image?.url
|| GAMES[gameId].puzzle.info.imageUrl
if (!imageUrl) {
throw new Error('[2021-07-11] no image url set')
}
return imageUrl
}
function getScoreMode(gameId: string): ScoreMode {
@ -895,7 +896,6 @@ export default {
getFinishedPiecesCount,
getPieceCount,
getImageUrl,
setImageUrl,
get,
getAllGames,
getPlayerBgColor,

View file

@ -93,7 +93,7 @@ export interface Image {
export interface GameSettings {
tiles: number
image: Image
image: ImageInfo
scoreMode: ScoreMode
shapeMode: ShapeMode
snapMode: SnapMode
@ -152,11 +152,23 @@ export interface PieceChange {
group?: number
}
export interface ImageInfo
{
id: number
filename: string
url: string
title: string
tags: Tag[]
created: Timestamp
width: number
height: number
}
export interface PuzzleInfo {
table: PuzzleTable
targetTiles: number
imageUrl: string
imageTitle: string
imageUrl?: string // deprecated, use image.url instead
image?: ImageInfo
width: number
height: number

View file

@ -6,19 +6,19 @@
</tr>
<tr>
<td>Image Title: </td>
<td>{{game.puzzle.info.imageTitle}}</td>
<td>{{game.puzzle.info.image.title}}</td>
</tr>
<tr>
<td>Snap Mode: </td>
<td>{{scoreMode[0]}}</td>
<td><span :title="snapMode[1]">{{scoreMode[0]}}</span></td>
</tr>
<tr>
<td>Shape Mode: </td>
<td>{{shapeMode[0]}}</td>
<td><span :title="snapMode[1]">{{shapeMode[0]}}</span></td>
</tr>
<tr>
<td>Score Mode: </td>
<td>{{snapMode[0]}}</td>
<td><span :title="snapMode[1]">{{snapMode[0]}}</span></td>
</tr>
</table>
</div>

View file

@ -1,9 +1,9 @@
import GameCommon from './../common/GameCommon'
import { Change, Game, Input, ScoreMode, ShapeMode, SnapMode, Timestamp } from './../common/Types'
import { Change, Game, Input, ScoreMode, ShapeMode, SnapMode,ImageInfo, Timestamp } from './../common/Types'
import Util, { logger } from './../common/Util'
import { Rng } from './../common/Rng'
import GameLog from './GameLog'
import { createPuzzle, PuzzleCreationImageInfo } from './Puzzle'
import { createPuzzle } from './Puzzle'
import Protocol from './../common/Protocol'
import GameStorage from './GameStorage'
@ -12,7 +12,7 @@ const log = logger('Game.ts')
async function createGameObject(
gameId: string,
targetTiles: number,
image: PuzzleCreationImageInfo,
image: ImageInfo,
ts: Timestamp,
scoreMode: ScoreMode,
shapeMode: ShapeMode,
@ -35,7 +35,7 @@ async function createGameObject(
async function createGame(
gameId: string,
targetTiles: number,
image: PuzzleCreationImageInfo,
image: ImageInfo,
ts: Timestamp,
scoreMode: ScoreMode,
shapeMode: ShapeMode,

View file

@ -100,4 +100,6 @@ export default {
exists,
log: _log,
get,
filename,
idxname,
}

View file

@ -7,30 +7,10 @@ import {UPLOAD_DIR, UPLOAD_URL} from './Dirs'
import Db, { OrderBy, WhereRaw } from './Db'
import { Dim } from '../common/Geometry'
import { logger } from '../common/Util'
import { Timestamp } from '../common/Types'
import { Tag, ImageInfo } from '../common/Types'
const log = logger('Images.ts')
interface Tag
{
id: number
slug: string
title: string
}
interface ImageInfo
{
id: number
filename: string
file: string
url: string
title: string
tags: Tag[]
created: Timestamp
width: number
height: number
}
const resizeImage = async (filename: string): Promise<void> => {
if (!filename.toLowerCase().match(/\.(jpe?g|webp|png)$/)) {
return
@ -106,7 +86,6 @@ const imageFromDb = (db: Db, imageId: number): ImageInfo => {
return {
id: i.id,
filename: i.filename,
file: `${UPLOAD_DIR}/${i.filename}`,
url: `${UPLOAD_URL}/${encodeURIComponent(i.filename)}`,
title: i.title,
tags: getTags(db, i.id),
@ -152,7 +131,6 @@ inner join images i on i.id = ixc.image_id ${where.sql};
return images.map(i => ({
id: i.id as number,
filename: i.filename,
file: `${UPLOAD_DIR}/${i.filename}`,
url: `${UPLOAD_URL}/${encodeURIComponent(i.filename)}`,
title: i.title,
tags: getTags(db, i.id),
@ -174,7 +152,6 @@ const allImagesFromDisk = (
.map(f => ({
id: 0,
filename: f,
file: `${UPLOAD_DIR}/${f}`,
url: `${UPLOAD_URL}/${encodeURIComponent(f)}`,
title: f.replace(/\.[a-z]+$/, ''),
tags: [] as Tag[],
@ -186,13 +163,13 @@ const allImagesFromDisk = (
switch (sort) {
case 'alpha_asc':
images = images.sort((a, b) => {
return a.file > b.file ? 1 : -1
return a.filename > b.filename ? 1 : -1
})
break;
case 'alpha_desc':
images = images.sort((a, b) => {
return a.file < b.file ? 1 : -1
return a.filename < b.filename ? 1 : -1
})
break;

View file

@ -1,14 +1,9 @@
import Util from './../common/Util'
import { Rng } from './../common/Rng'
import Images from './Images'
import { EncodedPiece, EncodedPieceShape, PieceShape, Puzzle, ShapeMode } from '../common/Types'
import { EncodedPiece, EncodedPieceShape, PieceShape, Puzzle, ShapeMode, ImageInfo } from '../common/Types'
import { Dim, Point } from '../common/Geometry'
export interface PuzzleCreationImageInfo {
file: string
url: string
title: string
}
import { UPLOAD_DIR } from './Dirs'
export interface PuzzleCreationInfo {
width: number
@ -28,11 +23,11 @@ const TILE_SIZE = 64
async function createPuzzle(
rng: Rng,
targetTiles: number,
image: PuzzleCreationImageInfo,
image: ImageInfo,
ts: number,
shapeMode: ShapeMode
): Promise<Puzzle> {
const imagePath = image.file
const imagePath = `${UPLOAD_DIR}/${image.filename}`
const imageUrl = image.url
// determine puzzle information from the image dimensions
@ -140,8 +135,8 @@ async function createPuzzle(
},
// information that was used to create the puzzle
targetTiles: targetTiles,
imageUrl,
imageTitle: image.title || '',
imageUrl, // todo: remove
image: image,
width: info.width, // actual puzzle width (same as bitmap.width)
height: info.height, // actual puzzle height (same as bitmap.height)

View file

@ -19,7 +19,7 @@ import {
UPLOAD_DIR,
} from './Dirs'
import GameCommon from '../common/GameCommon'
import { ServerEvent, Game as GameType, GameSettings, ScoreMode, ShapeMode, SnapMode } from '../common/Types'
import { ServerEvent, Game as GameType, GameSettings } from '../common/Types'
import GameStorage from './GameStorage'
import Db from './Db'
@ -87,7 +87,7 @@ app.get('/api/replay-data', async (req, res): Promise<void> => {
game = await Game.createGameObject(
gameId,
log[0][2],
log[0][3],
log[0][3], // must be ImageInfo
log[0][4],
log[0][5],
log[0][6],