image operations via Images.js, directory fun via Dirs.js
This commit is contained in:
parent
0cbc8e69a3
commit
1089f70d5e
9 changed files with 122 additions and 166 deletions
|
|
@ -1,7 +1,5 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
cd "$RUN_DIR/server"
|
|
||||||
|
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
if [ ! -e "$RUN_DIR/server/config.js" ]; then
|
if [ ! -e "$RUN_DIR/server/config.js" ]; then
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,9 @@
|
||||||
#!/bin/env node
|
#!/bin/env node
|
||||||
|
|
||||||
import fs from 'fs'
|
import Images from './../server/Images.js'
|
||||||
import sharp from 'sharp'
|
|
||||||
import exif from 'exif'
|
|
||||||
|
|
||||||
async function getExifOrientation(imagePath) {
|
const images = Images.allImages()
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
new exif.ExifImage({ image: imagePath }, function (error, exifData) {
|
|
||||||
if (error) {
|
|
||||||
resolve(0)
|
|
||||||
} else {
|
|
||||||
resolve(exifData.image.Orientation)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const dir = `./../data/uploads`
|
|
||||||
const images = fs.readdirSync(dir)
|
|
||||||
images.forEach(async (image) => {
|
images.forEach(async (image) => {
|
||||||
if (!image.toLowerCase().match(/\.(jpe?g|webp|png)$/)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
console.log(image)
|
console.log(image)
|
||||||
|
Images.resizeImage(image.filename)
|
||||||
const imagePath = `${dir}/${image}`
|
|
||||||
const iamgeOutPath = `${dir}/r/${image}`
|
|
||||||
const orientation = await getExifOrientation(imagePath)
|
|
||||||
|
|
||||||
let sharpImg = sharp(imagePath, { failOnError: false })
|
|
||||||
// when image is rotated to the left or right, switch width/height
|
|
||||||
// https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
|
|
||||||
if (orientation === 6) {
|
|
||||||
sharpImg = sharpImg.rotate()
|
|
||||||
} else if (orientation === 3) {
|
|
||||||
sharpImg = sharpImg.rotate().rotate()
|
|
||||||
} else if (orientation === 8) {
|
|
||||||
sharpImg = sharpImg.rotate().rotate().rotate()
|
|
||||||
}
|
|
||||||
const sizes = [
|
|
||||||
[150, 100],
|
|
||||||
[375, 210],
|
|
||||||
]
|
|
||||||
sizes.forEach(([w, h]) => {
|
|
||||||
sharpImg.resize(w, h, {fit: 'contain'}).toFile(`${iamgeOutPath}-${w}x${h}.webp`)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import Protocol from '../common/Protocol.js'
|
import Protocol from '../common/Protocol.js'
|
||||||
import { logger } from '../common/Util.js'
|
import { logger } from '../common/Util.js'
|
||||||
|
import { DATA_DIR } from '../server/Dirs.js'
|
||||||
|
|
||||||
const log = logger('rewrite_logs')
|
const log = logger('rewrite_logs')
|
||||||
|
|
||||||
const DATA_DIR = '../data'
|
|
||||||
|
|
||||||
const filename = (gameId) => `${DATA_DIR}/log_${gameId}.log`
|
const filename = (gameId) => `${DATA_DIR}/log_${gameId}.log`
|
||||||
|
|
||||||
const rewrite = (gameId) => {
|
const rewrite = (gameId) => {
|
||||||
|
|
|
||||||
14
server/Dirs.js
Normal file
14
server/Dirs.js
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
import { dirname } from 'path'
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
|
const __dirname = dirname(__filename)
|
||||||
|
|
||||||
|
const BASE_DIR = `${__dirname}/..`
|
||||||
|
|
||||||
|
export const DATA_DIR = `${BASE_DIR}/data`
|
||||||
|
export const UPLOAD_DIR = `${BASE_DIR}/data/uploads`
|
||||||
|
export const UPLOAD_URL = `/uploads`
|
||||||
|
export const COMMON_DIR = `${BASE_DIR}/common/`
|
||||||
|
export const GAME_DIR = `${BASE_DIR}/game/`
|
||||||
|
export const TEMPLATE_DIR = `${BASE_DIR}/game/templates`
|
||||||
|
|
@ -5,11 +5,10 @@ import { Rng } from '../common/Rng.js'
|
||||||
import GameLog from './GameLog.js'
|
import GameLog from './GameLog.js'
|
||||||
import { createPuzzle } from './Puzzle.js'
|
import { createPuzzle } from './Puzzle.js'
|
||||||
import Protocol from '../common/Protocol.js'
|
import Protocol from '../common/Protocol.js'
|
||||||
|
import { DATA_DIR } from './Dirs.js'
|
||||||
|
|
||||||
const log = logger('Game.js')
|
const log = logger('Game.js')
|
||||||
|
|
||||||
const DATA_DIR = './../data'
|
|
||||||
|
|
||||||
function loadAllGames() {
|
function loadAllGames() {
|
||||||
const files = fs.readdirSync(DATA_DIR)
|
const files = fs.readdirSync(DATA_DIR)
|
||||||
for (const f of files) {
|
for (const f of files) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { logger } from '../common/Util.js'
|
import { logger } from '../common/Util.js'
|
||||||
|
import { DATA_DIR } from '../server/Dirs.js'
|
||||||
|
|
||||||
const log = logger('GameLog.js')
|
const log = logger('GameLog.js')
|
||||||
|
|
||||||
const DATA_DIR = './../data'
|
|
||||||
|
|
||||||
const filename = (gameId) => `${DATA_DIR}/log_${gameId}.log`
|
const filename = (gameId) => `${DATA_DIR}/log_${gameId}.log`
|
||||||
|
|
||||||
const create = (gameId) => {
|
const create = (gameId) => {
|
||||||
|
|
|
||||||
81
server/Images.js
Normal file
81
server/Images.js
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
import sizeOf from 'image-size'
|
||||||
|
import fs from 'fs'
|
||||||
|
import exif from 'exif'
|
||||||
|
import sharp from 'sharp'
|
||||||
|
import {UPLOAD_DIR, UPLOAD_URL} from './Dirs.js'
|
||||||
|
|
||||||
|
const resizeImage = async (filename) => {
|
||||||
|
if (!filename.toLowerCase().match(/\.(jpe?g|webp|png)$/)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const imagePath = `${UPLOAD_DIR}/${filename}`
|
||||||
|
const imageOutPath = `${UPLOAD_DIR}/r/${filename}`
|
||||||
|
const orientation = await getExifOrientation(imagePath)
|
||||||
|
|
||||||
|
let sharpImg = sharp(imagePath, { failOnError: false })
|
||||||
|
// when image is rotated to the left or right, switch width/height
|
||||||
|
// https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
|
||||||
|
if (orientation === 6) {
|
||||||
|
sharpImg = sharpImg.rotate()
|
||||||
|
} else if (orientation === 3) {
|
||||||
|
sharpImg = sharpImg.rotate().rotate()
|
||||||
|
} else if (orientation === 8) {
|
||||||
|
sharpImg = sharpImg.rotate().rotate().rotate()
|
||||||
|
}
|
||||||
|
const sizes = [
|
||||||
|
[150, 100],
|
||||||
|
[375, 210],
|
||||||
|
]
|
||||||
|
for (let [w,h] of sizes) {
|
||||||
|
console.log(w, h, imagePath)
|
||||||
|
await sharpImg.resize(w, h, { fit: 'contain' }).toFile(`${imageOutPath}-${w}x${h}.webp`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getExifOrientation(imagePath) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
new exif.ExifImage({ image: imagePath }, function (error, exifData) {
|
||||||
|
if (error) {
|
||||||
|
resolve(0)
|
||||||
|
} else {
|
||||||
|
resolve(exifData.image.Orientation)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const allImages = () => {
|
||||||
|
const images = fs.readdirSync(UPLOAD_DIR)
|
||||||
|
.filter(f => f.toLowerCase().match(/\.(jpe?g|webp|png)$/))
|
||||||
|
.map(f => ({
|
||||||
|
filename: f,
|
||||||
|
file: `${UPLOAD_DIR}/${f}`,
|
||||||
|
url: `${UPLOAD_URL}/${f}`,
|
||||||
|
}))
|
||||||
|
.sort((a, b) => {
|
||||||
|
return fs.statSync(b.file).mtime.getTime() -
|
||||||
|
fs.statSync(a.file).mtime.getTime()
|
||||||
|
})
|
||||||
|
return images
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDimensions(imagePath) {
|
||||||
|
let dimensions = sizeOf(imagePath)
|
||||||
|
const orientation = await getExifOrientation(imagePath)
|
||||||
|
// when image is rotated to the left or right, switch width/height
|
||||||
|
// https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
|
||||||
|
if (orientation === 6 || orientation === 8) {
|
||||||
|
return {
|
||||||
|
width: dimensions.height,
|
||||||
|
height: dimensions.width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dimensions
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
allImages,
|
||||||
|
resizeImage,
|
||||||
|
getDimensions,
|
||||||
|
}
|
||||||
|
|
@ -1,40 +1,11 @@
|
||||||
import sizeOf from 'image-size'
|
|
||||||
import Util from '../common/Util.js'
|
import Util from '../common/Util.js'
|
||||||
import exif from 'exif'
|
|
||||||
import { Rng } from '../common/Rng.js'
|
import { Rng } from '../common/Rng.js'
|
||||||
|
import Images from './Images.js'
|
||||||
|
|
||||||
// cut size of each puzzle tile in the
|
// cut size of each puzzle tile in the
|
||||||
// final resized version of the puzzle image
|
// final resized version of the puzzle image
|
||||||
const TILE_SIZE = 64
|
const TILE_SIZE = 64
|
||||||
|
|
||||||
async function getDimensions(imagePath) {
|
|
||||||
let dimensions = sizeOf(imagePath)
|
|
||||||
try {
|
|
||||||
const orientation = await getExifOrientation(imagePath)
|
|
||||||
// when image is rotated to the left or right, switch width/height
|
|
||||||
// https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
|
|
||||||
if (orientation === 6 || orientation === 8) {
|
|
||||||
return {
|
|
||||||
width: dimensions.height,
|
|
||||||
height: dimensions.width,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
return dimensions
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getExifOrientation(imagePath) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
new exif.ExifImage({ image: imagePath }, function (error, exifData) {
|
|
||||||
if (error) {
|
|
||||||
reject(error)
|
|
||||||
} else {
|
|
||||||
resolve(exifData.image.Orientation)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createPuzzle(
|
async function createPuzzle(
|
||||||
/** @type Rng */ rng,
|
/** @type Rng */ rng,
|
||||||
targetTiles,
|
targetTiles,
|
||||||
|
|
@ -44,10 +15,8 @@ async function createPuzzle(
|
||||||
const imagePath = image.file
|
const imagePath = image.file
|
||||||
const imageUrl = image.url
|
const imageUrl = image.url
|
||||||
|
|
||||||
// load bitmap, to determine the original size of the image
|
// determine puzzle information from the image dimensions
|
||||||
const dim = await getDimensions(imagePath)
|
const dim = await Images.getDimensions(imagePath)
|
||||||
|
|
||||||
// determine puzzle information from the bitmap
|
|
||||||
const info = determinePuzzleInfo(dim.width, dim.height, targetTiles)
|
const info = determinePuzzleInfo(dim.width, dim.height, targetTiles)
|
||||||
|
|
||||||
let tiles = new Array(info.tiles)
|
let tiles = new Array(info.tiles)
|
||||||
|
|
@ -58,7 +27,7 @@ async function createPuzzle(
|
||||||
|
|
||||||
let positions = new Array(info.tiles)
|
let positions = new Array(info.tiles)
|
||||||
for (let tile of tiles) {
|
for (let tile of tiles) {
|
||||||
let coord = Util.coordByTileIdx(info, tile.idx)
|
const coord = Util.coordByTileIdx(info, tile.idx)
|
||||||
positions[tile.idx] ={
|
positions[tile.idx] ={
|
||||||
// instead of info.tileSize, we use info.tileDrawSize
|
// instead of info.tileSize, we use info.tileDrawSize
|
||||||
// to spread the tiles a bit
|
// to spread the tiles a bit
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import WebSocketServer from './WebSocketServer.js'
|
import WebSocketServer from './WebSocketServer.js'
|
||||||
|
|
||||||
import fs from 'fs'
|
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import multer from 'multer'
|
import multer from 'multer'
|
||||||
import config from './../config.js'
|
import config from './../config.js'
|
||||||
|
|
@ -13,96 +12,33 @@ import v8 from 'v8'
|
||||||
import GameLog from './GameLog.js'
|
import GameLog from './GameLog.js'
|
||||||
import GameSockets from './GameSockets.js'
|
import GameSockets from './GameSockets.js'
|
||||||
import Time from '../common/Time.js'
|
import Time from '../common/Time.js'
|
||||||
import exif from 'exif'
|
import Images from './Images.js'
|
||||||
import sharp from 'sharp'
|
import {
|
||||||
|
UPLOAD_DIR,
|
||||||
|
UPLOAD_URL,
|
||||||
|
COMMON_DIR,
|
||||||
|
GAME_DIR,
|
||||||
|
TEMPLATE_DIR,
|
||||||
|
} from './Dirs.js'
|
||||||
|
|
||||||
const log = logger('index.js')
|
const log = logger('index.js')
|
||||||
|
|
||||||
async function getExifOrientation(imagePath) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
new exif.ExifImage({ image: imagePath }, function (error, exifData) {
|
|
||||||
if (error) {
|
|
||||||
resolve(0)
|
|
||||||
|
|
||||||
async function getExifOrientation(imagePath) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
new exif.ExifImage({ image: imagePath }, function (error, exifData) {
|
|
||||||
if (error) {
|
|
||||||
resolve(0)
|
|
||||||
} else {
|
|
||||||
resolve(exifData.image.Orientation)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resolve(exifData.image.Orientation)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const resizeImage = async (filename) => {
|
|
||||||
const dir = `./../data/uploads/`
|
|
||||||
if (!filename.toLowerCase().match(/\.(jpe?g|webp|png)$/)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
console.log(filename)
|
|
||||||
|
|
||||||
const imagePath = `${dir}/${filename}`
|
|
||||||
const iamgeOutPath = `${dir}/r/${filename}`
|
|
||||||
const orientation = await getExifOrientation(imagePath)
|
|
||||||
|
|
||||||
let sharpImg = sharp(imagePath, { failOnError: false })
|
|
||||||
// when image is rotated to the left or right, switch width/height
|
|
||||||
// https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
|
|
||||||
if (orientation === 6) {
|
|
||||||
sharpImg = sharpImg.rotate()
|
|
||||||
} else if (orientation === 3) {
|
|
||||||
sharpImg = sharpImg.rotate().rotate()
|
|
||||||
} else if (orientation === 8) {
|
|
||||||
sharpImg = sharpImg.rotate().rotate().rotate()
|
|
||||||
}
|
|
||||||
const sizes = [
|
|
||||||
[150, 100],
|
|
||||||
[375, 210],
|
|
||||||
]
|
|
||||||
sizes.forEach(([w, h]) => {
|
|
||||||
sharpImg.resize(w, h, {fit: 'contain'}).toFile(`${iamgeOutPath}-${w}x${h}.webp`)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const allImages = () => {
|
|
||||||
const images = fs.readdirSync('./../data/uploads/')
|
|
||||||
.filter(f => f.toLowerCase().match(/\.(jpe?g|webp|png)$/))
|
|
||||||
.map(f => ({
|
|
||||||
file: `./../data/uploads/${f}`,
|
|
||||||
url: `/uploads/${f}`,
|
|
||||||
}))
|
|
||||||
.sort((a, b) => {
|
|
||||||
return fs.statSync(b.file).mtime.getTime() -
|
|
||||||
fs.statSync(a.file).mtime.getTime()
|
|
||||||
})
|
|
||||||
return images
|
|
||||||
}
|
|
||||||
|
|
||||||
const port = config.http.port
|
const port = config.http.port
|
||||||
const hostname = config.http.hostname
|
const hostname = config.http.hostname
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
const uploadDir = './../data/uploads'
|
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
destination: uploadDir,
|
destination: UPLOAD_DIR,
|
||||||
filename: function (req, file, cb) {
|
filename: function (req, file, cb) {
|
||||||
cb(null , file.originalname);
|
cb(null , file.originalname);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const upload = multer({storage}).single('file');
|
const upload = multer({storage}).single('file');
|
||||||
|
|
||||||
const statics = express.static('./../game/')
|
const statics = express.static(GAME_DIR)
|
||||||
|
|
||||||
const render = async (template, data) => {
|
const render = async (template, data) => {
|
||||||
const loader = new twing.TwingLoaderFilesystem('./../game/templates')
|
const loader = new twing.TwingLoaderFilesystem(TEMPLATE_DIR)
|
||||||
const env = new twing.TwingEnvironment(loader)
|
const env = new twing.TwingEnvironment(loader)
|
||||||
return env.render(template, data)
|
return env.render(template, data)
|
||||||
}
|
}
|
||||||
|
|
@ -129,7 +65,7 @@ app.post('/upload', (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await resizeImage(req.file.filename)
|
await Images.resizeImage(req.file.filename)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.log(err)
|
log.log(err)
|
||||||
res.status(400).send("Something went wrong!");
|
res.status(400).send("Something went wrong!");
|
||||||
|
|
@ -137,8 +73,8 @@ app.post('/upload', (req, res) => {
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
image: {
|
image: {
|
||||||
file: './../data/uploads/' + req.file.filename,
|
file: `${UPLOAD_DIR}/${req.file.filename}`,
|
||||||
url: '/uploads/' + req.file.filename,
|
url: `${UPLOAD_URL}/${req.file.filename}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -154,8 +90,8 @@ app.post('/newgame', bodyParser.json(), async (req, res) => {
|
||||||
res.send({ url: `/g/${gameId}` })
|
res.send({ url: `/g/${gameId}` })
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use('/common/', express.static('./../common/'))
|
app.use('/common/', express.static(COMMON_DIR))
|
||||||
app.use('/uploads/', express.static('./../data/uploads/'))
|
app.use('/uploads/', express.static(UPLOAD_DIR))
|
||||||
app.use('/', async (req, res, next) => {
|
app.use('/', async (req, res, next) => {
|
||||||
if (req.path === '/') {
|
if (req.path === '/') {
|
||||||
const ts = Time.timestamp()
|
const ts = Time.timestamp()
|
||||||
|
|
@ -175,7 +111,7 @@ app.use('/', async (req, res, next) => {
|
||||||
res.send(await render('index.html.twig', {
|
res.send(await render('index.html.twig', {
|
||||||
gamesRunning: games.filter(g => !g.finished),
|
gamesRunning: games.filter(g => !g.finished),
|
||||||
gamesFinished: games.filter(g => !!g.finished),
|
gamesFinished: games.filter(g => !!g.finished),
|
||||||
images: allImages(),
|
images: Images.allImages(),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
statics(req, res, next)
|
statics(req, res, next)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue