diff --git a/routes/api/export/gallery/zip/[gid].ts b/routes/api/export/gallery/zip/[gid].ts index ed7e00c..6b8dfcf 100644 --- a/routes/api/export/gallery/zip/[gid].ts +++ b/routes/api/export/gallery/zip/[gid].ts @@ -1,7 +1,7 @@ import { Handlers } from "$fresh/server.ts"; import { get_task_manager } from "../../../../../server.ts"; import { get_export_zip_response } from "../../../../../server/export_zip.ts"; -import { parse_bool } from "../../../../../server/parse_form.ts"; +import { parse_bool, parse_int } from "../../../../../server/parse_form.ts"; import { ExportZipConfig } from "../../../../../tasks/export_zip.ts"; export const handler: Handlers = { @@ -13,6 +13,7 @@ export const handler: Handlers = { const params = new URL(req.url).searchParams; const cfg: ExportZipConfig = {}; cfg.jpn_title = await parse_bool(params.get("jpn_title"), false); + cfg.max_length = await parse_int(params.get("max_length"), 0); const m = get_task_manager(); return get_export_zip_response(gid, m.db, cfg); }, diff --git a/server/export_zip.ts b/server/export_zip.ts index 1d403ff..3153ab1 100644 --- a/server/export_zip.ts +++ b/server/export_zip.ts @@ -1,7 +1,7 @@ import { Uint8ArrayReader, ZipWriter } from "zipjs/index.js"; import { EhDb, PMeta } from "../db.ts"; import { ExportZipConfig } from "../tasks/export_zip.ts"; -import { addZero, configureZipJs } from "../utils.ts"; +import { addZero, configureZipJs, limitFilename } from "../utils.ts"; export function get_export_zip_response( gid: number, @@ -47,12 +47,13 @@ export function get_export_zip_response( let closed = false; const signalc = new AbortController(); const signal = signalc.signal; + const maxLength = cfg.max_length || 0; const download_task = async (p: PMeta) => { const f = db.get_files(p.token); if (f.length) { const r = await Deno.readFile(f[0].path, { signal }); await zip_writer.add( - `${addZero(p.index, l)}_${p.name}`, + limitFilename(`${addZero(p.index, l)}_${p.name}`, maxLength), new Uint8ArrayReader(r), { signal }, ); diff --git a/server/parse_form.ts b/server/parse_form.ts index cb96ee0..9990120 100644 --- a/server/parse_form.ts +++ b/server/parse_form.ts @@ -12,3 +12,14 @@ export async function parse_bool( return n !== 0; } } + +export async function parse_int( + value: FormDataEntryValue | null, + def: T, +): Promise { + if (value === null) return def; + const v = typeof value === "string" ? value : await value.text(); + const n = parseInt(v); + if (isNaN(n)) return def; + return n; +} diff --git a/server/parse_form_test.ts b/server/parse_form_test.ts index f462f35..3c9331c 100644 --- a/server/parse_form_test.ts +++ b/server/parse_form_test.ts @@ -1,5 +1,5 @@ import { assertEquals } from "std/testing/asserts.ts"; -import { parse_bool } from "./parse_form.ts"; +import { parse_bool, parse_int } from "./parse_form.ts"; Deno.test("parse_bool_test", async () => { const f = new FormData(); @@ -15,3 +15,15 @@ Deno.test("parse_bool_test", async () => { f.append("e", "tRUE", "a.png"); assertEquals(await parse_bool(f.get("e"), null), true); }); + +Deno.test("parse_int_test", async () => { + const f = new FormData(); + f.append("a", "d"); + assertEquals(await parse_int(f.get("a"), null), null); + assertEquals(await parse_int(f.get("a"), 1), 1); + f.append("c", "1"); + assertEquals(await parse_int(f.get("c"), null), 1); + assertEquals(await parse_int(f.get("c"), 2), 1); + f.append("d", "-1"); + assertEquals(await parse_int(f.get("d"), null), -1); +}); diff --git a/tasks/export_zip.ts b/tasks/export_zip.ts index 0e95dc7..4bcc5d1 100644 --- a/tasks/export_zip.ts +++ b/tasks/export_zip.ts @@ -6,6 +6,7 @@ import { asyncForEach, configureZipJs, filterFilename, + limitFilename, } from "../utils.ts"; import { Config } from "../config.ts"; import { Task, TaskExportZipProgress, TaskType } from "../task.ts"; @@ -14,6 +15,7 @@ import { TaskManager } from "../task_manager.ts"; export type ExportZipConfig = { output?: string; jpn_title?: boolean; + max_length?: number; }; export const DEFAULT_EXPORT_ZIP_CONFIG: ExportZipConfig = {}; @@ -47,6 +49,7 @@ export async function export_zip( const output = ecfg.output === undefined ? join(cfg.base, filterFilename(title + ".zip")) : ecfg.output; + const maxLength = ecfg.max_length || 0; const f = await Deno.open(output, { create: true, write: true, @@ -66,7 +69,10 @@ export async function export_zip( if (f.length) { const r = await Deno.readFile(f[0].path, { signal }); await z.add( - `${addZero(p.index, l)}_${p.name}`, + limitFilename( + `${addZero(p.index, l)}_${p.name}`, + maxLength, + ), new Uint8ArrayReader(r), { signal }, ); diff --git a/utils.ts b/utils.ts index c454dcb..51b1d49 100644 --- a/utils.ts +++ b/utils.ts @@ -141,8 +141,13 @@ export function filterFilename(p: string, maxLength = 256) { } else if (Deno.build.os == "linux") { p = p.replace(/[!\$\"]/g, "_"); } - if (p.length > maxLength) { + return limitFilename(p, maxLength); +} + +export function limitFilename(p: string, maxLength: number) { + if (maxLength > 0 && p.length > maxLength) { const ext = extname(p); + if (maxLength < ext.length) return ext; return p.slice(0, maxLength - ext.length) + ext; } return p; diff --git a/utils_test.ts b/utils_test.ts index f1b0241..d175247 100644 --- a/utils_test.ts +++ b/utils_test.ts @@ -82,6 +82,7 @@ Deno.test("filterFilename_test", () => { if (Deno.build.os == "windows") { assertEquals(filterFilename("d|?ad.ts"), "d__ad.ts"); } + assertEquals(filterFilename("t|.ts", 0), "t_.ts"); }); Deno.test("add_suffix_to_path_test", () => {