Replace image and video compression with unified optimize handler

This commit is contained in:
SrGooglo 2025-04-24 11:18:43 +00:00
parent 4f82ef46d0
commit 56c9ee7b19
6 changed files with 91 additions and 58 deletions

View File

@ -1,43 +0,0 @@
import fs from "node:fs"
import path from "node:path"
import Sharp from "sharp"
const imageProcessingConf = {
sizeThreshold: 10 * 1024 * 1024,
imageQuality: 80,
}
const imageTypeToConfig = {
png: {
compressionLevel: Math.floor(imageProcessingConf.imageQuality / 100),
},
default: {
quality: imageProcessingConf.imageQuality,
},
}
export default async ({ filePath, workPath }) => {
const stat = await fs.promises.stat(file.filepath)
// ignore if too small
if (stat.size < imageProcessingConf.sizeThreshold) {
return file
}
let image = await Sharp(filePath)
const { format } = await image.metadata()
image = await image[format](
imageTypeToConfig[format] ?? imageTypeToConfig.default,
).withMetadata()
filePath = path.resolve(workPath, `${path.basename(filePath)}_ff`)
await image.toFile(outputFilepath)
return {
filePath: filePath,
}
}

View File

@ -0,0 +1,63 @@
import fs from "node:fs"
import path from "node:path"
import { fileTypeFromBuffer } from "file-type"
import readChunk from "@shared-utils/readChunk"
import Sharp from "sharp"
const thresholds = {
size: 10 * 1024 * 1024,
}
const sharpConfigs = {
png: {
compressionLevel: 6,
//quality: 80,
},
jpeg: {
quality: 80,
mozjpeg: true,
},
default: {
quality: 80,
},
}
export default async ({ filePath, workPath, onProgress }) => {
const stat = await fs.promises.stat(filePath)
const firstBuffer = await readChunk(filePath, {
length: 4100,
})
const fileType = await fileTypeFromBuffer(firstBuffer)
// first check if size over threshold
if (stat.size < thresholds.size) {
return {
outputFile: filePath,
}
}
// get the type of the file mime
const type = fileType.mime.split("/")[0]
switch (type) {
case "image": {
let image = Sharp(filePath)
const metadata = await image.metadata()
const config = sharpConfigs[metadata.format] ?? sharpConfigs.default
image = await image[metadata.format](config).withMetadata()
filePath = path.resolve(workPath, `${path.basename(filePath)}_ff`)
await image.toFile(filePath)
}
}
return {
outputFile: filePath,
}
}

View File

@ -1,6 +0,0 @@
export default async ({ filePath, workPath }) => {
// TODO: Implement video compression logic
return {
filePath: filePath,
}
}

View File

@ -1,8 +1,7 @@
const Handlers = { const Handlers = {
"a-dash": require("./handlers/a-dash").default, "a-dash": require("./handlers/a-dash").default,
"mq-hls": require("./handlers/mq-hls").default, "mq-hls": require("./handlers/mq-hls").default,
"img-compress": require("./handlers/img-compress").default, optimize: require("./handlers/optimize").default,
"video-compress": require("./handlers/video-compress").default,
} }
export type TransformationPayloadType = { export type TransformationPayloadType = {

View File

@ -15,6 +15,7 @@ export type FileHandlePayload = {
targetPath?: string // mostly provided by processed results targetPath?: string // mostly provided by processed results
//uploadId?: string //uploadId?: string
transformations?: Array<string> transformations?: Array<string>
useCompression?: boolean
s3Provider?: string s3Provider?: string
onProgress?: Function onProgress?: Function
} }
@ -29,11 +30,26 @@ export type S3UploadPayload = {
export default class Upload { export default class Upload {
static fileHandle = async (payload: FileHandlePayload) => { static fileHandle = async (payload: FileHandlePayload) => {
// process if (!payload.transformations) {
const processed = await Upload.process(payload) payload.transformations = []
}
// overwrite filePath // if compression is enabled and no transformations are provided, add basic transformations for images or videos
payload.filePath = processed.filePath if (
payload.useCompression === true &&
payload.transformations.length === 0
) {
payload.transformations.push("optimize")
}
// process file upload if transformations are provided
if (payload.transformations.length > 0) {
// process
const processed = await Upload.transform(payload)
// overwrite filePath
payload.filePath = processed.filePath
}
// upload // upload
const result = await Upload.toS3({ const result = await Upload.toS3({
@ -50,7 +66,7 @@ export default class Upload {
return result return result
} }
static process = async (payload: FileHandlePayload) => { static transform = async (payload: FileHandlePayload) => {
if (Array.isArray(payload.transformations)) { if (Array.isArray(payload.transformations)) {
for await (const transformation of payload.transformations) { for await (const transformation of payload.transformations) {
const transformationResult = await Transformation.transform({ const transformationResult = await Transformation.transform({
@ -91,7 +107,7 @@ export default class Upload {
let uploadPath = path.join(basePath, metadata["File-Hash"]) let uploadPath = path.join(basePath, metadata["File-Hash"])
if (isDirectory) { if (isDirectory) {
uploadPath = path.join(basePath, nanoid()) uploadPath = path.join(basePath, global.nanoid())
} }
if (typeof onProgress === "function") { if (typeof onProgress === "function") {

View File

@ -86,10 +86,14 @@ export default {
workPath: workPath, workPath: workPath,
transformations: transformations, transformations: transformations,
s3Provider: config.useProvider, s3Provider: config.useProvider,
useCompression: config.useCompression,
} }
// if has transformations, use background job // if has transformations, use background job
if (transformations && transformations.length > 0) { if (
(transformations && transformations.length > 0) ||
config.useCompression
) {
const job = await global.queues.createJob( const job = await global.queues.createJob(
"file-process", "file-process",
payload, payload,