feat: compression 6

This commit is contained in:
2026-09-24 14:39:49 +01:00
parent 890d04e393
commit 1b63992d0c
4 changed files with 24 additions and 29 deletions
+1 -1
View File
@@ -145,7 +145,7 @@ export const listCompressionVideos = async (
): Promise<IAsset[]> => {
const params: Record<string, string | number> = {
page: filters.page ?? 1,
limit: filters.limit ?? 200,
limit: filters.limit ?? 50,
sortBy: filters.sortBy ?? "date",
sortOrder: filters.sortOrder ?? "desc",
};
+1 -1
View File
@@ -12,7 +12,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (req.method !== "GET") return res.status(405).json({ message: "Method not allowed" });
const page = Math.max(1, Number(req.query.page) || 1);
const limit = Math.max(1, Number(req.query.limit) || 200);
const limit = Math.min(100, Math.max(1, Number(req.query.limit) || 50));
const minSizeMb = Number(req.query.minSizeMb);
const maxSizeMb = Number(req.query.maxSizeMb);
const sortBy = req.query.sortBy === "size" ? "size" : "date";
+21 -26
View File
@@ -1,11 +1,12 @@
import { ENV } from '@/config/environment';
import { getCurrentUser } from '@/handlers/serverUtils/user.utils';
import { getUserHeaders } from '@/helpers/user.helper';
import { Readable } from 'node:stream';
import { pipeline } from 'node:stream/promises';
import { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const proxyPath = req.url?.replace('/api/immich-proxy', '');
const targetUrl = `${ENV.IMMICH_URL}/api${proxyPath}`;
const currentUser = await getCurrentUser(req);
@@ -19,47 +20,41 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
body: req.method !== 'GET' && req.method !== 'HEAD' ? JSON.stringify(req.body) : null,
})
// Forward the status code
res.status(response.status)
// Forward the headers, but filter out problematic ones
const responseHeaders = response.headers
responseHeaders.forEach((value, key) => {
// Skip headers that can cause decoding issues
response.headers.forEach((value, key) => {
const skipHeaders = [
'content-encoding',
'content-length',
'transfer-encoding',
'connection',
'keep-alive'
'keep-alive',
]
if (!skipHeaders.includes(key.toLowerCase())) {
res.setHeader(key, value)
}
})
// Handle the response body properly
if (response.body) {
// For binary content or when we need to preserve encoding
if (response.headers.get('content-type')?.includes('image/') ||
response.headers.get('content-type')?.includes('video/') ||
response.headers.get('content-type')?.includes('audio/') ||
response.headers.get('content-type')?.includes('application/octet-stream')) {
const arrayBuffer = await response.arrayBuffer()
res.send(Buffer.from(arrayBuffer))
} else {
// For text/JSON content, let Next.js handle the encoding
const text = await response.text()
res.send(text)
}
} else {
if (!response.body || req.method === 'HEAD') {
res.end()
return
}
// Never materialize thumbnails/videos in the Node heap. The previous
// arrayBuffer()/Buffer path could keep many concurrent asset responses in
// memory when a grid page became visible.
await pipeline(
Readable.fromWeb(response.body as any),
res,
)
} catch (error: any) {
console.error('Proxy error:', error)
res.status(500).json({ error: error?.message })
if (!res.headersSent) {
res.status(500).json({ error: error?.message })
} else {
res.destroy(error)
}
}
}
@@ -67,4 +62,4 @@ export const config = {
api: {
responseLimit: false,
},
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ import { ChevronLeft, ChevronRight, ExternalLink, Trash2, Video } from 'lucide-r
import { useEffect, useMemo, useState } from 'react'
import toast from 'react-hot-toast'
const PAGE_SIZE = 200
const PAGE_SIZE = 50
const QUEUE_STORAGE_KEY = 'videoCompressionQueue'
const formatMB = (bytes?: number | null) => {