Add server, DB, and image processing modules
Introduce initial project scaffold: Express webhook server (server.js) for Immich uploads, DB pool (modules/database.js) using pg, immich repository (repositories/immichRepo.js) with get/update functions, image processing via sharp (modules/image_processor.js), a small execution helper (modules/execution_helper.js), and package.json + package-lock. Server validates a bearer token, filters non-image assets, waits briefly, compresses the image and updates DB path. Requires env vars: DB_HOST, DB_USER, DB_PORT, DB_NAME, DB_PASS, PASS_CODE, IMMICH_DATA_PATH, IMG_COMPRESSION_OUTPUT_FORMAT, IMG_COMPRESSION_OUTPUT_QUALITY, PORT.
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
import { Pool } from "pg"
|
||||||
|
|
||||||
|
export const pool = new Pool({
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
port: process.env.DB_PORT,
|
||||||
|
database: process.env.DB_NAME,
|
||||||
|
password: process.env.DB_PASS,
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function connect() {
|
||||||
|
await pool.connect()
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
pool,
|
||||||
|
connect
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
sleep
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import path from "node:path"
|
||||||
|
import fs from "node:fs/promises"
|
||||||
|
import sharp from "sharp"
|
||||||
|
|
||||||
|
const IMAGE_EXTENSIONS = new Set([ ".jpg", ".jpeg", ".png", ".heic", ".heif", ".gif", ".tiff", ".bmp", ".svg", ".avif", ".raw", ".dng" ])
|
||||||
|
const VIDEO_EXTENSIONS = new Set([ ".mp4", ".mov", ".avi", ".mkv", ".webm", ".flv", ".wmv", ".m4v", ".3gp", ".ts" ])
|
||||||
|
|
||||||
|
function getMediaType(filePath) {
|
||||||
|
if (!filePath) return "unknown"
|
||||||
|
|
||||||
|
const ext = path.extname(filePath).toLowerCase()
|
||||||
|
|
||||||
|
if (IMAGE_EXTENSIONS.has(ext)) return "image"
|
||||||
|
if (VIDEO_EXTENSIONS.has(ext)) return "video"
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeExtension(filePath, newExt) {
|
||||||
|
const formattedExt = newExt.startsWith(".") ? newExt : `.${newExt}`
|
||||||
|
|
||||||
|
const parsed = path.parse(filePath)
|
||||||
|
parsed.base = `${parsed.name}${formattedExt}`
|
||||||
|
parsed.ext = formattedExt
|
||||||
|
|
||||||
|
return path.format(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processCompression($file) {
|
||||||
|
const file = $file.replace("/data", process.env.IMMICH_DATA_PATH)
|
||||||
|
const newFilePath = changeExtension(file, process.env.IMG_COMPRESSION_OUTPUT_FORMAT)
|
||||||
|
const compressedFile = await sharp(file)
|
||||||
|
.toFormat(process.env.IMG_COMPRESSION_OUTPUT_FORMAT,
|
||||||
|
{ quality: parseInt(process.env.IMG_COMPRESSION_OUTPUT_QUALITY) || 80 }
|
||||||
|
)
|
||||||
|
.toFile(newFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getMediaType,
|
||||||
|
changeExtension,
|
||||||
|
processCompression
|
||||||
|
}
|
||||||
Generated
+1993
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "immich-photo-compressor",
|
||||||
|
"version": "0.0.1-alpha",
|
||||||
|
"description": "a webhook tool to compress file post uploaded on immich",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"email": "gilleslazure04@gmail.com",
|
||||||
|
"name": "azures04",
|
||||||
|
"url": "https://azures.fr"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": "^17.4.2",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"nodemon": "^3.1.14",
|
||||||
|
"pg": "^8.23.0",
|
||||||
|
"sharp": "^0.35.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { pool } from "../modules/database.js"
|
||||||
|
|
||||||
|
async function getMediaInDatabase(mediaId) {
|
||||||
|
try {
|
||||||
|
const query = "SELECT * FROM asset WHERE id = $1"
|
||||||
|
const values = [mediaId]
|
||||||
|
const response = await pool.query(query, values)
|
||||||
|
return response.rows
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur dans getMediaInDatabase:", error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateMediaPathInDatabase(mediaId, newPath) {
|
||||||
|
try {
|
||||||
|
const query = "UPDATE asset SET \"originalPath\" = $1 WHERE id = $2"
|
||||||
|
const values = [newPath, mediaId]
|
||||||
|
const response = await pool.query(query, values)
|
||||||
|
return response.rows
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur dans updateMediaPathInDatabase:", error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getMediaInDatabase,
|
||||||
|
updateMediaPathInDatabase
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import "dotenv/config"
|
||||||
|
import express from "express"
|
||||||
|
import database from "./modules/database.js"
|
||||||
|
import immichDb from "./repositories/immichRepo.js"
|
||||||
|
import execHelper from "./modules/execution_helper.js"
|
||||||
|
import imageProcessor from "./modules/image_processor.js"
|
||||||
|
|
||||||
|
await database.connect()
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
app.use(express.json())
|
||||||
|
app.use(express.urlencoded({ extended: true }))
|
||||||
|
|
||||||
|
app.post("/", async (req, res) => {
|
||||||
|
const authorization = req.headers.authorization
|
||||||
|
if (authorization != `Bearer ${process.env.PASS_CODE}`) {
|
||||||
|
res.status(401).send()
|
||||||
|
}
|
||||||
|
const assetType = req.body.data.asset.type
|
||||||
|
if (assetType != "IMAGE") {
|
||||||
|
res.end()
|
||||||
|
}
|
||||||
|
const assetFilePath = req.body.data.asset.originalPath
|
||||||
|
if (imageProcessor.getMediaType(assetFilePath) != "IMAGE") {
|
||||||
|
res.end()
|
||||||
|
}
|
||||||
|
const assetId = req.body.data.asset.id
|
||||||
|
await execHelper.sleep(550)
|
||||||
|
const asset = await immichDb.getMediaInDatabase(assetId)
|
||||||
|
await imageProcessor.processCompression(asset[0].originalPath)
|
||||||
|
immichDb.updateMediaPathInDatabase(assetId, imageProcessor.changeExtension(asset[0].originalPath, process.env.IMG_COMPRESSION_OUTPUT_FORMAT))
|
||||||
|
return res.status(200).send()
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(process.env.PORT, () => {
|
||||||
|
return console.log(`Server listening at port : ${process.env.PORT}`)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user