mirror of
https://github.com/C4illin/ConvertX.git
synced 2026-06-30 07:55:48 +00:00
added ffmpeg
This commit is contained in:
21
Dockerfile
21
Dockerfile
@@ -15,29 +15,26 @@ RUN mkdir -p /temp/prod
|
||||
COPY package.json bun.lockb /temp/prod/
|
||||
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
||||
|
||||
# install pandoc
|
||||
RUN apt-get update && apt-get install -y pandoc
|
||||
|
||||
# copy node_modules from temp directory
|
||||
# then copy all (non-ignored) project files into the image
|
||||
FROM base AS prerelease
|
||||
COPY --from=install /temp/dev/node_modules node_modules
|
||||
COPY . .
|
||||
# FROM base AS prerelease
|
||||
# COPY --from=install /temp/dev/node_modules node_modules
|
||||
# COPY . .
|
||||
|
||||
# [optional] tests & build
|
||||
ENV NODE_ENV=production
|
||||
# # [optional] tests & build
|
||||
# ENV NODE_ENV=production
|
||||
# RUN bun test
|
||||
# RUN bun run build
|
||||
|
||||
# copy production dependencies and source code into final image
|
||||
FROM base AS release
|
||||
COPY --from=install /temp/prod/node_modules node_modules
|
||||
COPY --from=prerelease /app/src/index.tsx /app/src/
|
||||
COPY --from=prerelease /app/package.json .
|
||||
# COPY --from=prerelease /app/src/index.tsx /app/src/
|
||||
# COPY --from=prerelease /app/package.json .
|
||||
COPY . .
|
||||
|
||||
# copy pandoc
|
||||
COPY --from=install /usr/bin/pandoc /usr/bin/pandoc
|
||||
# install additional dependencies
|
||||
RUN apt-get update && apt-get install -y pandoc texlive-latex-recommended ffmpeg
|
||||
|
||||
# run the app
|
||||
USER bun
|
||||
|
||||
673
src/converters/ffmpeg.ts
Normal file
673
src/converters/ffmpeg.ts
Normal file
@@ -0,0 +1,673 @@
|
||||
import { exec } from "node:child_process";
|
||||
|
||||
// This could be done dynamically by running `ffmpeg -formats` and parsing the output
|
||||
export const properties = {
|
||||
from: {
|
||||
video: [
|
||||
"012v",
|
||||
"4xm",
|
||||
"8bps",
|
||||
"aasc",
|
||||
"agm",
|
||||
"aic",
|
||||
"alias_pix",
|
||||
"amv",
|
||||
"anm",
|
||||
"ansi",
|
||||
"apng",
|
||||
"arbc",
|
||||
"argo",
|
||||
"asv1",
|
||||
"asv2",
|
||||
"aura",
|
||||
"aura2",
|
||||
"av1",
|
||||
"avrn",
|
||||
"avrp",
|
||||
"avs",
|
||||
"avui",
|
||||
"ayuv",
|
||||
"bethsoftvid",
|
||||
"bfi",
|
||||
"binkvideo",
|
||||
"bintext",
|
||||
"bitpacked",
|
||||
"bmp",
|
||||
"bmv_video",
|
||||
"brender_pix",
|
||||
"c93",
|
||||
"cavs",
|
||||
"cdgraphics",
|
||||
"cdtoons",
|
||||
"cdxl",
|
||||
"cfhd",
|
||||
"cinepak",
|
||||
"clearvideo",
|
||||
"cljr",
|
||||
"cllc",
|
||||
"cmv",
|
||||
"cpia",
|
||||
"cri",
|
||||
"cscd",
|
||||
"cyuv",
|
||||
"dds",
|
||||
"dfa",
|
||||
"dirac",
|
||||
"dnxhd",
|
||||
"dpx",
|
||||
"dsicinvideo",
|
||||
"dvvideo",
|
||||
"dxa",
|
||||
"dxtory",
|
||||
"dxv",
|
||||
"escape124",
|
||||
"escape130",
|
||||
"exr",
|
||||
"ffv1",
|
||||
"ffvhuff",
|
||||
"fic",
|
||||
"fits",
|
||||
"flashsv",
|
||||
"flashsv2",
|
||||
"flic",
|
||||
"flv1",
|
||||
"fmvc",
|
||||
"fraps",
|
||||
"frwu",
|
||||
"g2m",
|
||||
"gdv",
|
||||
"gif",
|
||||
"h261",
|
||||
"h263",
|
||||
"h263i",
|
||||
"h263p",
|
||||
"h264",
|
||||
"hap",
|
||||
"hevc",
|
||||
"hnm4video",
|
||||
"hq_hqa",
|
||||
"hqx",
|
||||
"huffyuv",
|
||||
"hymt",
|
||||
"idcin",
|
||||
"idf",
|
||||
"iff_ilbm",
|
||||
"imm4",
|
||||
"imm5",
|
||||
"indeo2",
|
||||
"indeo3",
|
||||
"indeo4",
|
||||
"indeo5",
|
||||
"interplayvideo",
|
||||
"ipu",
|
||||
"jpeg2000",
|
||||
"jpegls",
|
||||
"jv",
|
||||
"kgv1",
|
||||
"kmvc",
|
||||
"lagarith",
|
||||
"loco",
|
||||
"lscr",
|
||||
"m101",
|
||||
"mad",
|
||||
"magicyuv",
|
||||
"mdec",
|
||||
"mimic",
|
||||
"mjpeg",
|
||||
"mjpegb",
|
||||
"mmvideo",
|
||||
"mobiclip",
|
||||
"motionpixels",
|
||||
"mpeg1video",
|
||||
"mpeg2video",
|
||||
"mpeg4",
|
||||
"msa1",
|
||||
"mscc",
|
||||
"msmpeg4v1",
|
||||
"msmpeg4v2",
|
||||
"msmpeg4v3",
|
||||
"msp2",
|
||||
"msrle",
|
||||
"mss1",
|
||||
"mss2",
|
||||
"msvideo1",
|
||||
"mszh",
|
||||
"mts2",
|
||||
"mv30",
|
||||
"mvc1",
|
||||
"mvc2",
|
||||
"mvdv",
|
||||
"mvha",
|
||||
"mwsc",
|
||||
"mxpeg",
|
||||
"notchlc",
|
||||
"nuv",
|
||||
"paf_video",
|
||||
"pam",
|
||||
"pbm",
|
||||
"pcx",
|
||||
"pfm",
|
||||
"pgm",
|
||||
"pgmyuv",
|
||||
"pgx",
|
||||
"photocd",
|
||||
"pictor",
|
||||
"pixlet",
|
||||
"png",
|
||||
"ppm",
|
||||
"prores",
|
||||
"prosumer",
|
||||
"psd",
|
||||
"ptx",
|
||||
"qdraw",
|
||||
"qpeg",
|
||||
"qtrle",
|
||||
"r10k",
|
||||
"r210",
|
||||
"rasc",
|
||||
"rawvideo",
|
||||
"rl2",
|
||||
"roq",
|
||||
"rpza",
|
||||
"rscc",
|
||||
"rv10",
|
||||
"rv20",
|
||||
"rv30",
|
||||
"rv40",
|
||||
"sanm",
|
||||
"scpr",
|
||||
"screenpresso",
|
||||
"sga",
|
||||
"sgi",
|
||||
"sgirle",
|
||||
"sheervideo",
|
||||
"simbiosis_imx",
|
||||
"smackvideo",
|
||||
"smc",
|
||||
"smvjpeg",
|
||||
"snow",
|
||||
"sp5x",
|
||||
"speedhq",
|
||||
"srgc",
|
||||
"sunrast",
|
||||
"svg",
|
||||
"svq1",
|
||||
"svq3",
|
||||
"targa",
|
||||
"targa_y216",
|
||||
"tdsc",
|
||||
"tgq",
|
||||
"tgv",
|
||||
"theora",
|
||||
"thp",
|
||||
"tiertexseqvideo",
|
||||
"tiff",
|
||||
"tmv",
|
||||
"tqi",
|
||||
"truemotion1",
|
||||
"truemotion2",
|
||||
"truemotion2rt",
|
||||
"tscc",
|
||||
"tscc2",
|
||||
"txd",
|
||||
"ulti",
|
||||
"utvideo",
|
||||
"v210",
|
||||
"v210x",
|
||||
"v308",
|
||||
"v408",
|
||||
"v410",
|
||||
"vb",
|
||||
"vble",
|
||||
"vc1",
|
||||
"vc1image",
|
||||
"vcr1",
|
||||
"vixl",
|
||||
"vmdvideo",
|
||||
"vmnc",
|
||||
"vp3",
|
||||
"vp4",
|
||||
"vp5",
|
||||
"vp6",
|
||||
"vp6a",
|
||||
"vp6f",
|
||||
"vp7",
|
||||
"vp8",
|
||||
"vp9",
|
||||
"wcmv",
|
||||
"webp",
|
||||
"wmv1",
|
||||
"wmv2",
|
||||
"wmv3",
|
||||
"wmv3image",
|
||||
"wnv1",
|
||||
"wrapped_avframe",
|
||||
"ws_vqa",
|
||||
"xan_wc3",
|
||||
"xan_wc4",
|
||||
"xbin",
|
||||
"xbm",
|
||||
"xface",
|
||||
"xpm",
|
||||
"xwd",
|
||||
"y41p",
|
||||
"ylc",
|
||||
"yop",
|
||||
"yuv4",
|
||||
"zerocodec",
|
||||
"zlib",
|
||||
"zmbv",
|
||||
],
|
||||
audio: [
|
||||
"8svx_exp",
|
||||
"8svx_fib",
|
||||
"aac",
|
||||
"aac_latm",
|
||||
"ac3",
|
||||
"acelp.kelvin",
|
||||
"adpcm_4xm",
|
||||
"adpcm_adx",
|
||||
"adpcm_afc",
|
||||
"adpcm_agm",
|
||||
"adpcm_aica",
|
||||
"adpcm_argo",
|
||||
"adpcm_ct",
|
||||
"adpcm_dtk",
|
||||
"adpcm_ea",
|
||||
"adpcm_ea_maxis_xa",
|
||||
"adpcm_ea_r1",
|
||||
"adpcm_ea_r2",
|
||||
"adpcm_ea_r3",
|
||||
"adpcm_ea_xas",
|
||||
"adpcm_g722",
|
||||
"adpcm_g726",
|
||||
"adpcm_g726le",
|
||||
"adpcm_ima_alp",
|
||||
"adpcm_ima_amv",
|
||||
"adpcm_ima_apc",
|
||||
"adpcm_ima_apm",
|
||||
"adpcm_ima_cunning",
|
||||
"adpcm_ima_dat4",
|
||||
"adpcm_ima_dk3",
|
||||
"adpcm_ima_dk4",
|
||||
"adpcm_ima_ea_eacs",
|
||||
"adpcm_ima_ea_sead",
|
||||
"adpcm_ima_iss",
|
||||
"adpcm_ima_moflex",
|
||||
"adpcm_ima_mtf",
|
||||
"adpcm_ima_oki",
|
||||
"adpcm_ima_qt",
|
||||
"adpcm_ima_rad",
|
||||
"adpcm_ima_smjpeg",
|
||||
"adpcm_ima_ssi",
|
||||
"adpcm_ima_wav",
|
||||
"adpcm_ima_ws",
|
||||
"adpcm_ms",
|
||||
"adpcm_mtaf",
|
||||
"adpcm_psx",
|
||||
"adpcm_sbpro_2",
|
||||
"adpcm_sbpro_3",
|
||||
"adpcm_sbpro_4",
|
||||
"adpcm_swf",
|
||||
"adpcm_thp",
|
||||
"adpcm_thp_le",
|
||||
"adpcm_vima",
|
||||
"adpcm_xa",
|
||||
"adpcm_yamaha",
|
||||
"adpcm_zork",
|
||||
"alac",
|
||||
"amr_nb",
|
||||
"amr_wb",
|
||||
"ape",
|
||||
"aptx",
|
||||
"aptx_hd",
|
||||
"atrac1",
|
||||
"atrac3",
|
||||
"atrac3al",
|
||||
"atrac3p",
|
||||
"atrac3pal",
|
||||
"atrac9",
|
||||
"avc",
|
||||
"binkaudio_dct",
|
||||
"binkaudio_rdft",
|
||||
"bmv_audio",
|
||||
"codec2",
|
||||
"comfortnoise",
|
||||
"cook",
|
||||
"derf_dpcm",
|
||||
"dolby_e",
|
||||
"dsd_lsbf",
|
||||
"dsd_lsbf_planar",
|
||||
"dsd_msbf",
|
||||
"dsd_msbf_planar",
|
||||
"dsicinaudio",
|
||||
"dss_sp",
|
||||
"dst",
|
||||
"dts",
|
||||
"dvaudio",
|
||||
"eac3",
|
||||
"evrc",
|
||||
"fastaudio",
|
||||
"flac",
|
||||
"g723_1",
|
||||
"g729",
|
||||
"gremlin_dpcm",
|
||||
"gsm",
|
||||
"gsm_ms",
|
||||
"hca",
|
||||
"hcom",
|
||||
"iac",
|
||||
"ilbc",
|
||||
"imc",
|
||||
"interplay_dpcm",
|
||||
"interplayacm",
|
||||
"mace3",
|
||||
"mace6",
|
||||
"metasound",
|
||||
"mlp",
|
||||
"mp1",
|
||||
"mp2",
|
||||
"mp3",
|
||||
"mp3adu",
|
||||
"mp3on4",
|
||||
"mp4als",
|
||||
"musepack7",
|
||||
"musepack8",
|
||||
"nellymoser",
|
||||
"opus",
|
||||
"paf_audio",
|
||||
"pcm_alaw",
|
||||
"pcm_bluray",
|
||||
"pcm_dvd",
|
||||
"pcm_f16le",
|
||||
"pcm_f24le",
|
||||
"pcm_f32be",
|
||||
"pcm_f32le",
|
||||
"pcm_f64be",
|
||||
"pcm_f64le",
|
||||
"pcm_lxf",
|
||||
"pcm_mulaw",
|
||||
"pcm_s16be",
|
||||
"pcm_s16be_planar",
|
||||
"pcm_s16le",
|
||||
"pcm_s16le_planar",
|
||||
"pcm_s24be",
|
||||
"pcm_s24daud",
|
||||
"pcm_s24le",
|
||||
"pcm_s24le_planar",
|
||||
"pcm_s32be",
|
||||
"pcm_s32le",
|
||||
"pcm_s32le_planar",
|
||||
"pcm_s64be",
|
||||
"pcm_s64le",
|
||||
"pcm_s8",
|
||||
"pcm_s8_planar",
|
||||
"pcm_sga",
|
||||
"pcm_u16be",
|
||||
"pcm_u16le",
|
||||
"pcm_u24be",
|
||||
"pcm_u24le",
|
||||
"pcm_u32be",
|
||||
"pcm_u32le",
|
||||
"pcm_u8",
|
||||
"pcm_vidc",
|
||||
"qcelp",
|
||||
"qdm2",
|
||||
"qdmc",
|
||||
"ra_144",
|
||||
"ra_288",
|
||||
"ralf",
|
||||
"roq_dpcm",
|
||||
"s302m",
|
||||
"sbc",
|
||||
"sdx2_dpcm",
|
||||
"shorten",
|
||||
"sipr",
|
||||
"siren",
|
||||
"smackaudio",
|
||||
"sol_dpcm",
|
||||
"sonic",
|
||||
"speex",
|
||||
"tak",
|
||||
"truehd",
|
||||
"truespeech",
|
||||
"tta",
|
||||
"twinvq",
|
||||
"vmdaudio",
|
||||
"vorbis",
|
||||
"wavesynth",
|
||||
"wavpack",
|
||||
"westwood_snd1",
|
||||
"wmalossless",
|
||||
"wmapro",
|
||||
"wmav1",
|
||||
"wmav2",
|
||||
"wmavoice",
|
||||
"xan_dpcm",
|
||||
"xma1",
|
||||
"xma2",
|
||||
],
|
||||
subtitles: [
|
||||
"ass",
|
||||
"dvb_subtitle",
|
||||
"dvb_teletext",
|
||||
"dvd_subtitle",
|
||||
"eia_608",
|
||||
"hdmv_pgs_subtitle",
|
||||
"jacosub",
|
||||
"microdvd",
|
||||
"mov_text",
|
||||
"mpl2",
|
||||
"pjs",
|
||||
"realtext",
|
||||
"sami",
|
||||
"stl",
|
||||
"subrip",
|
||||
"subviewer",
|
||||
"subviewer1",
|
||||
"text",
|
||||
"vplayer",
|
||||
"webvtt",
|
||||
"xsub",
|
||||
],
|
||||
},
|
||||
to: {
|
||||
video: [
|
||||
"a64_multi",
|
||||
"a64_multi5",
|
||||
"ljpeg",
|
||||
"alias_pix",
|
||||
"amv",
|
||||
"apng",
|
||||
"asv1",
|
||||
"asv2",
|
||||
"av1",
|
||||
"avrp",
|
||||
"avui",
|
||||
"ayuv",
|
||||
"bmp",
|
||||
"cfhd",
|
||||
"cinepak",
|
||||
"cljr",
|
||||
"dirac",
|
||||
"dnxhd",
|
||||
"dpx",
|
||||
"dvvideo",
|
||||
"exr",
|
||||
"ffv1",
|
||||
"ffvhuff",
|
||||
"fits",
|
||||
"flashsv",
|
||||
"flashsv2",
|
||||
"flv1",
|
||||
"gif",
|
||||
"h261",
|
||||
"h263",
|
||||
"h263p",
|
||||
"h264",
|
||||
"hap",
|
||||
"hevc",
|
||||
"huffyuv",
|
||||
"jpeg2000",
|
||||
"jpegls",
|
||||
"magicyuv",
|
||||
"mjpeg",
|
||||
"mpeg1video",
|
||||
"mpeg2video",
|
||||
"mpeg4",
|
||||
"msmpeg4v2",
|
||||
"msmpeg4v3",
|
||||
"msvideo1",
|
||||
"pam",
|
||||
"pbm",
|
||||
"pcx",
|
||||
"pfm",
|
||||
"pgm",
|
||||
"pgmyuv",
|
||||
"png",
|
||||
"ppm",
|
||||
"prores",
|
||||
"qtrle",
|
||||
"r10k",
|
||||
"r210",
|
||||
"rawvideo",
|
||||
"roq",
|
||||
"rpza",
|
||||
"rv10",
|
||||
"rv20",
|
||||
"sgi",
|
||||
"snow",
|
||||
"speedhq",
|
||||
"sunrast",
|
||||
"svq1",
|
||||
"targa",
|
||||
"theora",
|
||||
"tiff",
|
||||
"utvideo",
|
||||
"v210",
|
||||
"v308",
|
||||
"v408",
|
||||
"v410",
|
||||
"vp8",
|
||||
"vp9",
|
||||
"webp",
|
||||
"wmv1",
|
||||
"wmv2",
|
||||
"wrapped_avframe",
|
||||
"xbm",
|
||||
"xface",
|
||||
"xwd",
|
||||
"y41p",
|
||||
"yuv4",
|
||||
"zlib",
|
||||
"zmbv",
|
||||
],
|
||||
audio: [
|
||||
"aac",
|
||||
"ac3",
|
||||
"adpcm_adx",
|
||||
"adpcm_argo",
|
||||
"adpcm_g722",
|
||||
"adpcm_g726",
|
||||
"adpcm_g726le",
|
||||
"adpcm_ima_alp",
|
||||
"adpcm_ima_amv",
|
||||
"adpcm_ima_apm",
|
||||
"adpcm_ima_qt",
|
||||
"adpcm_ima_ssi",
|
||||
"adpcm_ima_wav",
|
||||
"adpcm_ms",
|
||||
"adpcm_swf",
|
||||
"adpcm_yamaha",
|
||||
"alac",
|
||||
"aptx",
|
||||
"aptx_hd",
|
||||
"codec2",
|
||||
"comfortnoise",
|
||||
"dts",
|
||||
"eac3",
|
||||
"flac",
|
||||
"g723_1",
|
||||
"gsm",
|
||||
"gsm_ms",
|
||||
"mlp",
|
||||
"mp2",
|
||||
"mp3",
|
||||
"nellymoser",
|
||||
"opus",
|
||||
"pcm_alaw",
|
||||
"pcm_dvd",
|
||||
"pcm_f32be",
|
||||
"pcm_f32le",
|
||||
"pcm_f64be",
|
||||
"pcm_f64le",
|
||||
"pcm_mulaw",
|
||||
"pcm_s16be",
|
||||
"pcm_s16be_planar",
|
||||
"pcm_s16le",
|
||||
"pcm_s16le_planar",
|
||||
"pcm_s24be",
|
||||
"pcm_s24daud",
|
||||
"pcm_s24le",
|
||||
"pcm_s24le_planar",
|
||||
"pcm_s32be",
|
||||
"pcm_s32le",
|
||||
"pcm_s32le_planar",
|
||||
"pcm_s64be",
|
||||
"pcm_s64le",
|
||||
"pcm_s8",
|
||||
"pcm_s8_planar",
|
||||
"pcm_u16be",
|
||||
"pcm_u16le",
|
||||
"pcm_u24be",
|
||||
"pcm_u24le",
|
||||
"pcm_u32be",
|
||||
"pcm_u32le",
|
||||
"pcm_u8",
|
||||
"pcm_vidc",
|
||||
"ra_144",
|
||||
"roq_dpcm",
|
||||
"s302m",
|
||||
"sbc",
|
||||
"sonic",
|
||||
"speex",
|
||||
"truehd",
|
||||
"tta",
|
||||
"vorbis",
|
||||
"wavpack",
|
||||
"wmav1",
|
||||
"wmav2",
|
||||
],
|
||||
subtitles: [
|
||||
"ass",
|
||||
"dvb_subtitle",
|
||||
"dvd_subtitle",
|
||||
"mov_text",
|
||||
"subrip",
|
||||
"text",
|
||||
"webvtt",
|
||||
"xsub",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export async function convert(
|
||||
filePath: string,
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
options?: any,
|
||||
) {
|
||||
return exec(
|
||||
`ffmpeg -f "${fileType}" -i "${filePath}" -f "${convertTo}" "${targetPath}"`,
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`exec error: ${error}`);
|
||||
return;
|
||||
}
|
||||
console.log(`stdout: ${stdout}`);
|
||||
console.error(`stderr: ${stderr}`);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -8,78 +8,164 @@ import {
|
||||
convert as convertPandoc,
|
||||
} from "./pandoc";
|
||||
|
||||
import {
|
||||
properties as propertiesFfmpeg,
|
||||
convert as convertFfmpeg,
|
||||
} from "./ffmpeg";
|
||||
|
||||
const properties: {
|
||||
[key: string]: {
|
||||
properties: {
|
||||
from: string[] | { [key: string]: string[] };
|
||||
to: string[] | { [key: string]: string[] };
|
||||
options?: {
|
||||
[key: string]: {
|
||||
[key: string]: {
|
||||
description: string;
|
||||
type: string;
|
||||
default: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
converter: (
|
||||
filePath: string,
|
||||
fileType: string,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
convertTo: any,
|
||||
targetPath: string,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
options?: any,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
) => any;
|
||||
};
|
||||
} = {
|
||||
sharp: {
|
||||
properties: propertiesImage,
|
||||
converter: convertImage,
|
||||
},
|
||||
pandoc: {
|
||||
properties: propertiesPandoc,
|
||||
converter: convertPandoc,
|
||||
},
|
||||
ffmpeg: {
|
||||
properties: propertiesFfmpeg,
|
||||
converter: convertFfmpeg,
|
||||
},
|
||||
};
|
||||
|
||||
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
||||
|
||||
export async function mainConverter(
|
||||
inputFilePath: string,
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
fileTypeOriginal: string,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
convertTo: any,
|
||||
targetPath: string,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
options?: any,
|
||||
converter?: string,
|
||||
) {
|
||||
// Check if the fileType and convertTo are supported by the sharp converter
|
||||
if (
|
||||
propertiesImage.from.includes(fileType) &&
|
||||
propertiesImage.to.includes(convertTo)
|
||||
) {
|
||||
// Use the sharp converter
|
||||
try {
|
||||
await convertImage(
|
||||
inputFilePath,
|
||||
fileType,
|
||||
convertTo,
|
||||
targetPath,
|
||||
options,
|
||||
);
|
||||
console.log(
|
||||
`Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully.`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to convert ${inputFilePath} from ${fileType} to ${convertTo}.`,
|
||||
error,
|
||||
);
|
||||
const fileType = normalizeFiletype(fileTypeOriginal);
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
let converterFunc: any;
|
||||
|
||||
if (converter) {
|
||||
converterFunc = properties[converter];
|
||||
} else {
|
||||
// Iterate over each converter in properties
|
||||
for (const converterName in properties) {
|
||||
const converterObj = properties[converterName];
|
||||
|
||||
if (!converterObj) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if converter properties.from is an object loop thorugh the keys otherwise use the array
|
||||
// for example ffmpeg is an object eg from: {video: ["mp4", "webm"], audio: ["mp3"]}
|
||||
if (Array.isArray(converterObj.properties.from) && Array.isArray(converterObj.properties.to)) {
|
||||
if (
|
||||
converterObj.properties.from.includes(fileType) &&
|
||||
converterObj.properties.to.includes(convertTo)
|
||||
) {
|
||||
converterFunc = converterObj.converter;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (const key in converterObj.properties.from) {
|
||||
if (
|
||||
converterObj.properties.from[key].includes(fileType) &&
|
||||
converterObj.properties.to[key].includes(convertTo)
|
||||
) {
|
||||
converterFunc = converterObj.converter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if the fileType and convertTo are supported by the pandoc converter
|
||||
else if (
|
||||
propertiesPandoc.from.includes(fileType) &&
|
||||
propertiesPandoc.to.includes(convertTo)
|
||||
) {
|
||||
// Use the pandoc converter
|
||||
try {
|
||||
await convertPandoc(
|
||||
inputFilePath,
|
||||
fileType,
|
||||
convertTo,
|
||||
targetPath,
|
||||
options,
|
||||
);
|
||||
console.log(
|
||||
`Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully.`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to convert ${inputFilePath} from ${fileType} to ${convertTo}.`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (!converterFunc) {
|
||||
console.log(
|
||||
`Neither the sharp nor pandoc converter support converting from ${fileType} to ${convertTo}.`,
|
||||
`No available converter supports converting from ${fileType} to ${convertTo}.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await converterFunc(
|
||||
inputFilePath,
|
||||
fileType,
|
||||
convertTo,
|
||||
targetPath,
|
||||
options,
|
||||
);
|
||||
console.log(
|
||||
`Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully using ${converter}.`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to convert ${inputFilePath} from ${fileType} to ${convertTo} using ${converter}.`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const possibleConversions: { [key: string]: string[] } = {};
|
||||
|
||||
for (const from of propertiesImage.from) {
|
||||
possibleConversions[from] = propertiesImage.to;
|
||||
for (const converterName in properties) {
|
||||
const converterProperties = properties[converterName]?.properties;
|
||||
|
||||
if (!converterProperties) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(converterProperties.from)) {
|
||||
for (const extension of converterProperties.from) {
|
||||
possibleConversions[extension] = converterProperties.to;
|
||||
}
|
||||
} else {
|
||||
for (const key in converterProperties.from) {
|
||||
if (!converterProperties.from[key] || !converterProperties.to[key]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const extension of converterProperties.from[key]) {
|
||||
possibleConversions[extension] = converterProperties.to[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const from of propertiesPandoc.from) {
|
||||
possibleConversions[from] = propertiesPandoc.to;
|
||||
}
|
||||
// // save all possible conversions to a file
|
||||
// import fs from "fs";
|
||||
// import path from "path";
|
||||
// import { FormatEnum } from "sharp";
|
||||
// fs.writeFileSync(
|
||||
// path.join(__dirname, ".", "possibleConversions.json"),
|
||||
// JSON.stringify(possibleConversions),
|
||||
// );
|
||||
|
||||
export const getPossibleConversions = (from: string): string[] => {
|
||||
const fromClean = normalizeFiletype(from);
|
||||
@@ -87,6 +173,20 @@ export const getPossibleConversions = (from: string): string[] => {
|
||||
return possibleConversions[fromClean] || ([] as string[]);
|
||||
};
|
||||
|
||||
let allTargets: string[] = [];
|
||||
|
||||
for (const converterName in properties) {
|
||||
const converterProperties = properties[converterName].properties;
|
||||
|
||||
if (Array.isArray(converterProperties.from)) {
|
||||
allTargets = allTargets.concat(converterProperties.to);
|
||||
} else {
|
||||
for (const key in converterProperties.to) {
|
||||
allTargets = allTargets.concat(converterProperties.to[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getAllTargets = () => {
|
||||
return [...propertiesImage.to, ...propertiesPandoc.to];
|
||||
return allTargets;
|
||||
};
|
||||
|
||||
@@ -115,15 +115,23 @@ export const properties = {
|
||||
],
|
||||
};
|
||||
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
export function convert(
|
||||
filePath: string,
|
||||
fileType: string,
|
||||
convertTo: string,
|
||||
targetPath: string,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
options?: any,
|
||||
) {
|
||||
return exec(
|
||||
`pandoc "${filePath}" -f ${fileType} -t ${convertTo} -o "${targetPath}"`,
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`exec error: ${error}`);
|
||||
return;
|
||||
}
|
||||
console.log(`stdout: ${stdout}`);
|
||||
console.error(`stderr: ${stderr}`);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
1
src/converters/possibleConversions.json
Normal file
1
src/converters/possibleConversions.json
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,5 @@
|
||||
import sharp from "sharp";
|
||||
import type { FormatEnum } from "sharp";
|
||||
|
||||
// declare possible conversions
|
||||
export const properties = {
|
||||
@@ -11,11 +12,18 @@ export const properties = {
|
||||
type: "number",
|
||||
default: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export async function convert(filePath: string, fileType: string, convertTo: string, targetPath: string, options?: any) {
|
||||
export async function convert(
|
||||
filePath: string,
|
||||
fileType: string,
|
||||
convertTo: keyof FormatEnum,
|
||||
targetPath: string,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||
options?: any,
|
||||
) {
|
||||
if (fileType === "svg") {
|
||||
const scale = options.scale || 1;
|
||||
const metadata = await sharp(filePath).metadata();
|
||||
@@ -24,14 +32,14 @@ export async function convert(filePath: string, fileType: string, convertTo: str
|
||||
throw new Error("Could not get metadata from image");
|
||||
}
|
||||
|
||||
const newWidth = Math.round(metadata.width * scale)
|
||||
const newHeight = Math.round(metadata.height * scale)
|
||||
const newWidth = Math.round(metadata.width * scale);
|
||||
const newHeight = Math.round(metadata.height * scale);
|
||||
|
||||
return await sharp(filePath)
|
||||
.resize(newWidth, newHeight)
|
||||
.toFormat(convertTo)
|
||||
.toFile(targetPath);
|
||||
.resize(newWidth, newHeight)
|
||||
.toFormat(convertTo)
|
||||
.toFile(targetPath);
|
||||
}
|
||||
|
||||
return await sharp(filePath).toFormat(convertTo).toFile(targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ const app = new Elysia()
|
||||
})
|
||||
.post(
|
||||
"/register",
|
||||
async function handler({ body, set, jwt, cookie: { auth } }) {
|
||||
async ({ body, set, jwt, cookie: { auth } }) => {
|
||||
const existingUser = await db
|
||||
.query("SELECT * FROM users WHERE email = ?")
|
||||
.get(body.email);
|
||||
@@ -177,6 +177,7 @@ const app = new Elysia()
|
||||
Location: "/",
|
||||
};
|
||||
},
|
||||
{ body: t.Object({ email: t.String(), password: t.String() }) },
|
||||
)
|
||||
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
|
||||
console.log("login handler");
|
||||
@@ -276,17 +277,15 @@ const app = new Elysia()
|
||||
sameSite: "strict",
|
||||
});
|
||||
|
||||
// redirect to home
|
||||
set.status = 302;
|
||||
set.headers = {
|
||||
Location: "/",
|
||||
};
|
||||
redirect("/");
|
||||
},
|
||||
{ body: t.Object({ email: t.String(), password: t.String() }) },
|
||||
)
|
||||
.get("/logout", ({ redirect, cookie: { auth } }) => {
|
||||
if (auth?.value) {
|
||||
auth.remove();
|
||||
}
|
||||
|
||||
return redirect("/login");
|
||||
})
|
||||
.post("/logout", ({ redirect, cookie: { auth } }) => {
|
||||
@@ -518,8 +517,9 @@ const app = new Elysia()
|
||||
return redirect(`/results/${jobId.value}`);
|
||||
},
|
||||
)
|
||||
.get("/history", async ({ jwt, redirect, cookie: { auth } }) => {
|
||||
.get("/hist", async ({ body, jwt, redirect, cookie: { auth } }) => {
|
||||
console.log("results page");
|
||||
|
||||
if (!auth?.value) {
|
||||
console.log("no auth value");
|
||||
return redirect("/login");
|
||||
@@ -533,14 +533,7 @@ const app = new Elysia()
|
||||
|
||||
const userJobs = db
|
||||
.query("SELECT * FROM jobs WHERE user_id = ?")
|
||||
.all(user.id) as {
|
||||
id: number;
|
||||
user_id: number;
|
||||
date_created: string;
|
||||
status: string;
|
||||
num_files: number;
|
||||
finished_files: number;
|
||||
}[];
|
||||
.all(user.id) as IJobs[];
|
||||
|
||||
for (const job of userJobs) {
|
||||
const files = db
|
||||
@@ -631,6 +624,7 @@ const app = new Elysia()
|
||||
<button
|
||||
type="button"
|
||||
style={{ width: "10rem", float: "right" }}
|
||||
onclick="downloadAll()"
|
||||
>
|
||||
Download All
|
||||
</button>
|
||||
@@ -671,6 +665,7 @@ const app = new Elysia()
|
||||
</table>
|
||||
</article>
|
||||
</main>
|
||||
<script src="/downloadAll.js" defer />
|
||||
</BaseHtml>
|
||||
);
|
||||
},
|
||||
@@ -703,31 +698,34 @@ const app = new Elysia()
|
||||
return Bun.file(filePath);
|
||||
},
|
||||
)
|
||||
.get("/zip/:userId/:jobId", async ({ params, jwt, redirect, cookie: { auth } }) => {
|
||||
// TODO: Implement zip download
|
||||
if (!auth?.value) {
|
||||
return redirect("/login");
|
||||
}
|
||||
.get(
|
||||
"/zip/:userId/:jobId",
|
||||
async ({ params, jwt, redirect, cookie: { auth } }) => {
|
||||
// TODO: Implement zip download
|
||||
if (!auth?.value) {
|
||||
return redirect("/login");
|
||||
}
|
||||
|
||||
const user = await jwt.verify(auth.value);
|
||||
if (!user) {
|
||||
return redirect("/login");
|
||||
}
|
||||
const user = await jwt.verify(auth.value);
|
||||
if (!user) {
|
||||
return redirect("/login");
|
||||
}
|
||||
|
||||
const job = await db
|
||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||
.get(user.id, params.jobId);
|
||||
const job = await db
|
||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||
.get(user.id, params.jobId);
|
||||
|
||||
if (!job) {
|
||||
return redirect("/results");
|
||||
}
|
||||
if (!job) {
|
||||
return redirect("/results");
|
||||
}
|
||||
|
||||
const userId = decodeURIComponent(params.userId);
|
||||
const jobId = decodeURIComponent(params.jobId);
|
||||
const outputPath = `${outputDir}${userId}/${jobId}/`;
|
||||
const userId = decodeURIComponent(params.userId);
|
||||
const jobId = decodeURIComponent(params.jobId);
|
||||
const outputPath = `${outputDir}${userId}/${jobId}/`;
|
||||
|
||||
// return Bun.zip(outputPath);
|
||||
})
|
||||
// return Bun.zip(outputPath);
|
||||
},
|
||||
)
|
||||
.onError(({ code, error, request }) => {
|
||||
// log.error(` ${request.method} ${request.url}`, code, error);
|
||||
console.error(error);
|
||||
|
||||
13
src/public/downloadAll.js
Normal file
13
src/public/downloadAll.js
Normal file
@@ -0,0 +1,13 @@
|
||||
window.downloadAll = function () {
|
||||
// Get all download links
|
||||
const downloadLinks = document.querySelectorAll("a[download]");
|
||||
|
||||
// Trigger download for each link
|
||||
downloadLinks.forEach((link, index) => {
|
||||
// We add a delay for each download to prevent them from starting at the same time
|
||||
setTimeout(() => {
|
||||
const event = new MouseEvent("click");
|
||||
link.dispatchEvent(event);
|
||||
}, index * 100);
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user