added ffmpeg
This commit is contained in:
parent
e55b8abaa0
commit
4aeeaa5060
8 changed files with 911 additions and 113 deletions
21
Dockerfile
21
Dockerfile
|
|
@ -15,29 +15,26 @@ RUN mkdir -p /temp/prod
|
||||||
COPY package.json bun.lockb /temp/prod/
|
COPY package.json bun.lockb /temp/prod/
|
||||||
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
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
|
# copy node_modules from temp directory
|
||||||
# then copy all (non-ignored) project files into the image
|
# then copy all (non-ignored) project files into the image
|
||||||
FROM base AS prerelease
|
# FROM base AS prerelease
|
||||||
COPY --from=install /temp/dev/node_modules node_modules
|
# COPY --from=install /temp/dev/node_modules node_modules
|
||||||
COPY . .
|
# COPY . .
|
||||||
|
|
||||||
# [optional] tests & build
|
# # [optional] tests & build
|
||||||
ENV NODE_ENV=production
|
# ENV NODE_ENV=production
|
||||||
# RUN bun test
|
# RUN bun test
|
||||||
# RUN bun run build
|
# RUN bun run build
|
||||||
|
|
||||||
# copy production dependencies and source code into final image
|
# copy production dependencies and source code into final image
|
||||||
FROM base AS release
|
FROM base AS release
|
||||||
COPY --from=install /temp/prod/node_modules node_modules
|
COPY --from=install /temp/prod/node_modules node_modules
|
||||||
COPY --from=prerelease /app/src/index.tsx /app/src/
|
# COPY --from=prerelease /app/src/index.tsx /app/src/
|
||||||
COPY --from=prerelease /app/package.json .
|
# COPY --from=prerelease /app/package.json .
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# copy pandoc
|
# install additional dependencies
|
||||||
COPY --from=install /usr/bin/pandoc /usr/bin/pandoc
|
RUN apt-get update && apt-get install -y pandoc texlive-latex-recommended ffmpeg
|
||||||
|
|
||||||
# run the app
|
# run the app
|
||||||
USER bun
|
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,
|
convert as convertPandoc,
|
||||||
} from "./pandoc";
|
} 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";
|
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
||||||
|
|
||||||
export async function mainConverter(
|
export async function mainConverter(
|
||||||
inputFilePath: string,
|
inputFilePath: string,
|
||||||
fileType: string,
|
fileTypeOriginal: string,
|
||||||
convertTo: string,
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
|
convertTo: any,
|
||||||
targetPath: string,
|
targetPath: string,
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
options?: any,
|
options?: any,
|
||||||
|
converter?: string,
|
||||||
) {
|
) {
|
||||||
// Check if the fileType and convertTo are supported by the sharp converter
|
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 (
|
if (
|
||||||
propertiesImage.from.includes(fileType) &&
|
converterObj.properties.from.includes(fileType) &&
|
||||||
propertiesImage.to.includes(convertTo)
|
converterObj.properties.to.includes(convertTo)
|
||||||
) {
|
) {
|
||||||
// Use the sharp converter
|
converterFunc = converterObj.converter;
|
||||||
try {
|
break;
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 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 {
|
} 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!converterFunc) {
|
||||||
console.log(
|
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[] } = {};
|
const possibleConversions: { [key: string]: string[] } = {};
|
||||||
|
|
||||||
for (const from of propertiesImage.from) {
|
for (const converterName in properties) {
|
||||||
possibleConversions[from] = propertiesImage.to;
|
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) {
|
// // save all possible conversions to a file
|
||||||
possibleConversions[from] = propertiesPandoc.to;
|
// 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[] => {
|
export const getPossibleConversions = (from: string): string[] => {
|
||||||
const fromClean = normalizeFiletype(from);
|
const fromClean = normalizeFiletype(from);
|
||||||
|
|
@ -87,6 +173,20 @@ export const getPossibleConversions = (from: string): string[] => {
|
||||||
return possibleConversions[fromClean] || ([] as 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 = () => {
|
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(
|
export function convert(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
fileType: string,
|
fileType: string,
|
||||||
convertTo: string,
|
convertTo: string,
|
||||||
targetPath: string,
|
targetPath: string,
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
options?: any,
|
options?: any,
|
||||||
) {
|
) {
|
||||||
return exec(
|
return exec(
|
||||||
`pandoc "${filePath}" -f ${fileType} -t ${convertTo} -o "${targetPath}"`,
|
`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 sharp from "sharp";
|
||||||
|
import type { FormatEnum } from "sharp";
|
||||||
|
|
||||||
// declare possible conversions
|
// declare possible conversions
|
||||||
export const properties = {
|
export const properties = {
|
||||||
|
|
@ -11,11 +12,18 @@ export const properties = {
|
||||||
type: "number",
|
type: "number",
|
||||||
default: 1,
|
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") {
|
if (fileType === "svg") {
|
||||||
const scale = options.scale || 1;
|
const scale = options.scale || 1;
|
||||||
const metadata = await sharp(filePath).metadata();
|
const metadata = await sharp(filePath).metadata();
|
||||||
|
|
@ -24,8 +32,8 @@ export async function convert(filePath: string, fileType: string, convertTo: str
|
||||||
throw new Error("Could not get metadata from image");
|
throw new Error("Could not get metadata from image");
|
||||||
}
|
}
|
||||||
|
|
||||||
const newWidth = Math.round(metadata.width * scale)
|
const newWidth = Math.round(metadata.width * scale);
|
||||||
const newHeight = Math.round(metadata.height * scale)
|
const newHeight = Math.round(metadata.height * scale);
|
||||||
|
|
||||||
return await sharp(filePath)
|
return await sharp(filePath)
|
||||||
.resize(newWidth, newHeight)
|
.resize(newWidth, newHeight)
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ const app = new Elysia()
|
||||||
})
|
})
|
||||||
.post(
|
.post(
|
||||||
"/register",
|
"/register",
|
||||||
async function handler({ body, set, jwt, cookie: { auth } }) {
|
async ({ body, set, jwt, cookie: { auth } }) => {
|
||||||
const existingUser = await db
|
const existingUser = await db
|
||||||
.query("SELECT * FROM users WHERE email = ?")
|
.query("SELECT * FROM users WHERE email = ?")
|
||||||
.get(body.email);
|
.get(body.email);
|
||||||
|
|
@ -177,6 +177,7 @@ const app = new Elysia()
|
||||||
Location: "/",
|
Location: "/",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
{ body: t.Object({ email: t.String(), password: t.String() }) },
|
||||||
)
|
)
|
||||||
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
|
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
|
||||||
console.log("login handler");
|
console.log("login handler");
|
||||||
|
|
@ -276,17 +277,15 @@ const app = new Elysia()
|
||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
});
|
});
|
||||||
|
|
||||||
// redirect to home
|
redirect("/");
|
||||||
set.status = 302;
|
|
||||||
set.headers = {
|
|
||||||
Location: "/",
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
{ body: t.Object({ email: t.String(), password: t.String() }) },
|
||||||
)
|
)
|
||||||
.get("/logout", ({ redirect, cookie: { auth } }) => {
|
.get("/logout", ({ redirect, cookie: { auth } }) => {
|
||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
auth.remove();
|
auth.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect("/login");
|
return redirect("/login");
|
||||||
})
|
})
|
||||||
.post("/logout", ({ redirect, cookie: { auth } }) => {
|
.post("/logout", ({ redirect, cookie: { auth } }) => {
|
||||||
|
|
@ -518,8 +517,9 @@ const app = new Elysia()
|
||||||
return redirect(`/results/${jobId.value}`);
|
return redirect(`/results/${jobId.value}`);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.get("/history", async ({ jwt, redirect, cookie: { auth } }) => {
|
.get("/hist", async ({ body, jwt, redirect, cookie: { auth } }) => {
|
||||||
console.log("results page");
|
console.log("results page");
|
||||||
|
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
console.log("no auth value");
|
console.log("no auth value");
|
||||||
return redirect("/login");
|
return redirect("/login");
|
||||||
|
|
@ -533,14 +533,7 @@ const app = new Elysia()
|
||||||
|
|
||||||
const userJobs = db
|
const userJobs = db
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ?")
|
.query("SELECT * FROM jobs WHERE user_id = ?")
|
||||||
.all(user.id) as {
|
.all(user.id) as IJobs[];
|
||||||
id: number;
|
|
||||||
user_id: number;
|
|
||||||
date_created: string;
|
|
||||||
status: string;
|
|
||||||
num_files: number;
|
|
||||||
finished_files: number;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
for (const job of userJobs) {
|
for (const job of userJobs) {
|
||||||
const files = db
|
const files = db
|
||||||
|
|
@ -631,6 +624,7 @@ const app = new Elysia()
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
style={{ width: "10rem", float: "right" }}
|
style={{ width: "10rem", float: "right" }}
|
||||||
|
onclick="downloadAll()"
|
||||||
>
|
>
|
||||||
Download All
|
Download All
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -671,6 +665,7 @@ const app = new Elysia()
|
||||||
</table>
|
</table>
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
|
<script src="/downloadAll.js" defer />
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -703,7 +698,9 @@ const app = new Elysia()
|
||||||
return Bun.file(filePath);
|
return Bun.file(filePath);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.get("/zip/:userId/:jobId", async ({ params, jwt, redirect, cookie: { auth } }) => {
|
.get(
|
||||||
|
"/zip/:userId/:jobId",
|
||||||
|
async ({ params, jwt, redirect, cookie: { auth } }) => {
|
||||||
// TODO: Implement zip download
|
// TODO: Implement zip download
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login");
|
||||||
|
|
@ -727,7 +724,8 @@ const app = new Elysia()
|
||||||
const outputPath = `${outputDir}${userId}/${jobId}/`;
|
const outputPath = `${outputDir}${userId}/${jobId}/`;
|
||||||
|
|
||||||
// return Bun.zip(outputPath);
|
// return Bun.zip(outputPath);
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.onError(({ code, error, request }) => {
|
.onError(({ code, error, request }) => {
|
||||||
// log.error(` ${request.method} ${request.url}`, code, error);
|
// log.error(` ${request.method} ${request.url}`, code, error);
|
||||||
console.error(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);
|
||||||
|
});
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue