Compare commits
489 Commits
feature-po
...
v0.13.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7914194856 | ||
|
|
2dac7f1362 | ||
|
|
a17e5fd614 | ||
|
|
21994fb6a2 | ||
|
|
a5eaaa422a | ||
|
|
ff2ef74135 | ||
|
|
70705c1850 | ||
|
|
fd9c151e01 | ||
|
|
4f0573963f | ||
|
|
6bb6bce8a4 | ||
|
|
448557bece | ||
|
|
bdbd4a122c | ||
|
|
cb9d0ec680 | ||
|
|
fb60ef66f5 | ||
|
|
c1ae43075f | ||
|
|
377f69ae8d | ||
|
|
cb131cd0a0 | ||
|
|
fcc83c5ea8 | ||
|
|
96d4717d13 | ||
|
|
4d73bf9760 | ||
|
|
725a94bc95 | ||
|
|
0a366b447a | ||
|
|
4a27a7bc03 | ||
|
|
3ca5803bda | ||
|
|
239041294c | ||
|
|
31fdd8f214 | ||
|
|
c3319c09eb | ||
|
|
d460e94d52 | ||
|
|
4b5c732380 | ||
|
|
f42665ca40 | ||
|
|
bed52cef17 | ||
|
|
9d1c93155c | ||
|
|
794cc7c474 | ||
|
|
d7d584e497 | ||
|
|
f5320df86e | ||
|
|
056fd4ba93 | ||
|
|
5b6e70eb3a | ||
|
|
f437a8e7e2 | ||
|
|
cdae798fcf | ||
|
|
bcc827a81b | ||
|
|
84274b9c55 | ||
|
|
20c6f8249e | ||
|
|
8f0ea2a592 | ||
|
|
a29e4a930a | ||
|
|
4549c96ae3 | ||
|
|
bc64094c04 | ||
|
|
fa58827ad5 | ||
|
|
8f27be0e3d | ||
|
|
df43df1178 | ||
|
|
c2beb4a227 | ||
|
|
9277c27a50 | ||
|
|
171ecd6884 | ||
|
|
dca29f7e5a | ||
|
|
318acc20bd | ||
|
|
f433493d57 | ||
|
|
19970fc132 | ||
|
|
24394ca3c5 | ||
|
|
10ff0b464a | ||
|
|
9263d17609 | ||
|
|
c1b75a13fd | ||
|
|
a8ed60d48f | ||
|
|
dc82a438d4 | ||
|
|
cc54bdcbe7 | ||
|
|
ae4bbc8baa | ||
|
|
ad98499da0 | ||
|
|
db60f355b2 | ||
|
|
eb91d8b298 | ||
|
|
b8312be4b7 | ||
|
|
326a8e3404 | ||
|
|
f017e13ac1 | ||
|
|
67a5fe353e | ||
|
|
51d49d7ff3 | ||
|
|
d42b820b36 | ||
|
|
07d32776d3 | ||
|
|
ef027e81b5 | ||
|
|
a75e4b495d | ||
|
|
fba5e212e8 | ||
|
|
62f44fb052 | ||
|
|
6b9254047c | ||
|
|
decfea5dc9 | ||
|
|
eacded6848 | ||
|
|
279ca72c64 | ||
|
|
b8fc9383ca | ||
|
|
bec58ac59f | ||
|
|
83d7126820 | ||
|
|
f0e9c6d794 | ||
|
|
0e61051fc6 | ||
|
|
480ba77ebe | ||
|
|
16f27c13bb | ||
|
|
afe5c50d66 | ||
|
|
72ea859ebb | ||
|
|
8edf3834c4 | ||
|
|
e595014fcd | ||
|
|
8bebf7e569 | ||
|
|
c825ec06e2 | ||
|
|
8c75f273fb | ||
|
|
0ba776c129 | ||
|
|
2bbbd03554 | ||
|
|
0a5d0487b1 | ||
|
|
583cd2dd3b | ||
|
|
e1f7fc1ecb | ||
|
|
961a55cbe5 | ||
|
|
cdf9bad903 | ||
|
|
6769fa2f83 | ||
|
|
3b7ea88b73 | ||
|
|
59310c095d | ||
|
|
c47f0c12fe | ||
|
|
ca71a40485 | ||
|
|
d4e8f376c1 | ||
|
|
14c6ea1e6b | ||
|
|
d6e4d8fbd6 | ||
|
|
460bda62d5 | ||
|
|
d2702ab673 | ||
|
|
e2581f42f5 | ||
|
|
f0fcfc159f | ||
|
|
538c5b60c9 | ||
|
|
2fabb7bbb2 | ||
|
|
e7c34a9c94 | ||
|
|
618f9fce5a | ||
|
|
95dbc9f678 | ||
|
|
aa87bc5c51 | ||
|
|
815de531ed | ||
|
|
cf2b026dc4 | ||
|
|
9ce46aefba | ||
|
|
98b2db7818 | ||
|
|
0229851bf9 | ||
|
|
9e15114fe8 | ||
|
|
7f66a76bb0 | ||
|
|
e9cc8392bb | ||
|
|
d0b89ce74f | ||
|
|
f537c81db7 | ||
|
|
03d3edfff6 | ||
|
|
447b4c5e5c | ||
|
|
cb143209ae | ||
|
|
9c24fe73b5 | ||
|
|
19ae85424b | ||
|
|
22fad99552 | ||
|
|
8144bbef74 | ||
|
|
aad6da0ae8 | ||
|
|
c5f8162a22 | ||
|
|
f0f30224b5 | ||
|
|
d0d888e356 | ||
|
|
2c64122224 | ||
|
|
3b2eee96a9 | ||
|
|
465aacbf9b | ||
|
|
d1a2a66170 | ||
|
|
4c05fd72bb | ||
|
|
f04fe760e3 | ||
|
|
834d19bcc6 | ||
|
|
6808c4642c | ||
|
|
d0ce307f94 | ||
|
|
f3740e9ded | ||
|
|
b485bc9445 | ||
|
|
2d14c1bb26 | ||
|
|
1a442d6e69 | ||
|
|
2386543e5c | ||
|
|
58e220e82d | ||
|
|
24bea6e4d2 | ||
|
|
43497ad8d1 | ||
|
|
f22b61fe4c | ||
|
|
5b08f4cd19 | ||
|
|
1589f8d24e | ||
|
|
7d1db72cf5 | ||
|
|
53a8f66414 | ||
|
|
36cb6cc589 | ||
|
|
f3a4aece46 | ||
|
|
580a6a869a | ||
|
|
008eaac493 | ||
|
|
b450623bb4 | ||
|
|
8ac2ecb673 | ||
|
|
0a10a56ae3 | ||
|
|
9378ba9208 | ||
|
|
0c586e324b | ||
|
|
91c4a64284 | ||
|
|
c599e98d9d | ||
|
|
d2cd6706c9 | ||
|
|
e8ed10dde8 | ||
|
|
5fe0b79802 | ||
|
|
34a6722a68 | ||
|
|
5b0d769c63 | ||
|
|
718401a28b | ||
|
|
3112cd57f6 | ||
|
|
410fc777a7 | ||
|
|
8eed99e732 | ||
|
|
663b1d4171 | ||
|
|
c3067ca12d | ||
|
|
4561ca3760 | ||
|
|
698cce58ce | ||
|
|
339b79f786 | ||
|
|
4f98f778f0 | ||
|
|
8479b33a47 | ||
|
|
78844d7bd5 | ||
|
|
64e4a271e1 | ||
|
|
5fb8c3575b | ||
|
|
a6b8bcecae | ||
|
|
bc9c820820 | ||
|
|
ee9207a7f4 | ||
|
|
a34e215202 | ||
|
|
b4e53dbb8e | ||
|
|
b5e8d82bfa | ||
|
|
5d9000bb33 | ||
|
|
ccb065ef0f | ||
|
|
883fad806b | ||
|
|
feacd1b816 | ||
|
|
094e7a0d1c | ||
|
|
72636c5059 | ||
|
|
291cfc80c6 | ||
|
|
ae1dfafc9d | ||
|
|
6caa583c35 | ||
|
|
2057167576 | ||
|
|
1c9e67fc32 | ||
|
|
d3af9688c6 | ||
|
|
7d0cbb9844 | ||
|
|
88173891ba | ||
|
|
2b4b8f9551 | ||
|
|
63a4328d4a | ||
|
|
413f5dc7b4 | ||
|
|
ebccdf9169 | ||
|
|
47139a550b | ||
|
|
fa5446c446 | ||
|
|
8772e582b0 | ||
|
|
45922ed3a3 | ||
|
|
4c747e8908 | ||
|
|
e573997aa9 | ||
|
|
c57b69991c | ||
|
|
eee983a56a | ||
|
|
22f823c535 | ||
|
|
ed59cd7aa4 | ||
|
|
b28977ffe2 | ||
|
|
a47bb682a5 | ||
|
|
a17eca0a09 | ||
|
|
ea9250543e | ||
|
|
317c932c2a | ||
|
|
5b1703db68 | ||
|
|
60ba7c93fb | ||
|
|
22227130dd | ||
|
|
5daf66f5d0 | ||
|
|
aee1962607 | ||
|
|
0d42762b36 | ||
|
|
b97b12b449 | ||
|
|
bdf651df82 | ||
|
|
267ef14789 | ||
|
|
905adc5e1c | ||
|
|
52ed7274e9 | ||
|
|
a29238c265 | ||
|
|
48c6fb79fc | ||
|
|
8358396656 | ||
|
|
b30e5800c3 | ||
|
|
21a1b50ed8 | ||
|
|
e6a94fb21d | ||
|
|
bef1710e33 | ||
|
|
16b322d4e6 | ||
|
|
9bf64e42d5 | ||
|
|
5988fe8212 | ||
|
|
5df9c0b751 | ||
|
|
136a8b2d74 | ||
|
|
ccfb574d5d | ||
|
|
ad6eedea69 | ||
|
|
c3082db8f7 | ||
|
|
a1f8cbae66 | ||
|
|
bb34bdee87 | ||
|
|
11fcbc3f96 | ||
|
|
f7344e4c65 | ||
|
|
781310f3dc | ||
|
|
3f063644f2 | ||
|
|
081634b610 | ||
|
|
cf3da08c73 | ||
|
|
5f7234d6c1 | ||
|
|
6597c1d7ca | ||
|
|
ecb2c75008 | ||
|
|
d5eeef9f68 | ||
|
|
7456174022 | ||
|
|
bc4ad49285 | ||
|
|
f0d0e43929 | ||
|
|
8ca4f1587d | ||
|
|
1535377bfe | ||
|
|
83bf78fd57 | ||
|
|
4d9c4d64aa | ||
|
|
53fff594fc | ||
|
|
fe4aeaff03 | ||
|
|
2078cb0ee0 | ||
|
|
86a61d35d7 | ||
|
|
96fa7e2f55 | ||
|
|
7d2af46b0b | ||
|
|
57e2999866 | ||
|
|
6fb8ca4d82 | ||
|
|
c295e546bd | ||
|
|
f7abb9389c | ||
|
|
d7de154eda | ||
|
|
20bd111765 | ||
|
|
eadd0da291 | ||
|
|
52294465fb | ||
|
|
049e9163ce | ||
|
|
d466d2dbbc | ||
|
|
3f79ccaa2a | ||
|
|
1e9bde18c7 | ||
|
|
9af23346bf | ||
|
|
d310341fca | ||
|
|
d88a755c13 | ||
|
|
7c6085c685 | ||
|
|
7ed1ad21f2 | ||
|
|
8a2237fbd9 | ||
|
|
0e363f0731 | ||
|
|
4074647b67 | ||
|
|
c84968be50 | ||
|
|
0e53a99d43 | ||
|
|
bdd0cf556f | ||
|
|
2483274388 | ||
|
|
4c5129910a | ||
|
|
fe13a1b736 | ||
|
|
f1ac71b397 | ||
|
|
1b1067a03f | ||
|
|
8674557e42 | ||
|
|
87052ce105 | ||
|
|
98ee26f6e2 | ||
|
|
96e2c88465 | ||
|
|
d55ba218ff | ||
|
|
ae2455e73e | ||
|
|
b9fe32053c | ||
|
|
5cf3d74e03 | ||
|
|
2b92778f37 | ||
|
|
27d4da8941 | ||
|
|
2384e22c22 | ||
|
|
6690caeb1e | ||
|
|
c714ade3e2 | ||
|
|
e9e95c61e9 | ||
|
|
b1e0e68d9c | ||
|
|
5ce3706550 | ||
|
|
57e47e95c0 | ||
|
|
6d6bc6cfdd | ||
|
|
b44eb22e77 | ||
|
|
6edfbaa27d | ||
|
|
d669baeff4 | ||
|
|
ec1a7bc015 | ||
|
|
0805241a19 | ||
|
|
83f041daa2 | ||
|
|
55331a4496 | ||
|
|
b53f07e7a7 | ||
|
|
0eb89ae712 | ||
|
|
7dd153b02c | ||
|
|
6ccafeb3b0 | ||
|
|
b703903b22 | ||
|
|
9e66eab0a2 | ||
|
|
b272bf9504 | ||
|
|
56632f3500 | ||
|
|
2d9d8f8b4f | ||
|
|
65d4e0fbbe | ||
|
|
8182d12ea0 | ||
|
|
1c241d4cad | ||
|
|
874ff6ee00 | ||
|
|
e9f1219ad9 | ||
|
|
4811452aec | ||
|
|
382ebad35a | ||
|
|
85945256e7 | ||
|
|
c504692569 | ||
|
|
64a16036be | ||
|
|
b9f038386f | ||
|
|
945775e52b | ||
|
|
e7f3466736 | ||
|
|
ee80eeb18d | ||
|
|
34c7e0bd25 | ||
|
|
492dbd5617 | ||
|
|
0935bf66ce | ||
|
|
7389e0a059 | ||
|
|
c512b45f91 | ||
|
|
3ae2db5d9b | ||
|
|
0945b40a9c | ||
|
|
20b958e547 | ||
|
|
e7e146c6c9 | ||
|
|
005ad2d66b | ||
|
|
e5c3a8acc4 | ||
|
|
87ecbabd1f | ||
|
|
991c4e4ba8 | ||
|
|
87ccd8b44c | ||
|
|
83e6699ca6 | ||
|
|
c91523c038 | ||
|
|
1f73f036b2 | ||
|
|
1223fabfca | ||
|
|
8a42a39e69 | ||
|
|
22023bad25 | ||
|
|
db2f2d8f0a | ||
|
|
d0fa9ac408 | ||
|
|
776a97289b | ||
|
|
95340dd0eb | ||
|
|
7dcd74cc5f | ||
|
|
c5efac9423 | ||
|
|
cceca9a924 | ||
|
|
4d4c13a8d8 | ||
|
|
f8f90ebd69 | ||
|
|
648d5070e2 | ||
|
|
801cf28d1e | ||
|
|
fae2ba9c54 | ||
|
|
10d20a8786 | ||
|
|
c9bc1e237e | ||
|
|
48a76a46b3 | ||
|
|
4dcb796e1b | ||
|
|
5952103bcd | ||
|
|
fd05ee5cd5 | ||
|
|
7e7d238c7a | ||
|
|
755a4170f2 | ||
|
|
263ba415f5 | ||
|
|
317260098a | ||
|
|
5304e94b4e | ||
|
|
aab2b311cf | ||
|
|
baa7ea40e6 | ||
|
|
481a11b610 | ||
|
|
c09fe296b1 | ||
|
|
f023aae753 | ||
|
|
5cd9544b55 | ||
|
|
97c23ba65c | ||
|
|
0ffda40ac8 | ||
|
|
cb639907ee | ||
|
|
0166842b78 | ||
|
|
277a35b5df | ||
|
|
42124e08b2 | ||
|
|
13169574f0 | ||
|
|
6fb07f0d13 | ||
|
|
8121114ccb | ||
|
|
81881af1c1 | ||
|
|
bbb4117e9d | ||
|
|
d7ec8179d8 | ||
|
|
cef60afee3 | ||
|
|
ccf116acde | ||
|
|
f609984c90 | ||
|
|
663e654c80 | ||
|
|
31050dbf66 | ||
|
|
8918c418f4 | ||
|
|
56266e0da8 | ||
|
|
a3f5b5153a | ||
|
|
6b4c7a16e0 | ||
|
|
7c7756713b | ||
|
|
15d4233a82 | ||
|
|
a1411a7559 | ||
|
|
853a4c4f32 | ||
|
|
b05b0a14b0 | ||
|
|
ff680cb295 | ||
|
|
31e1a3124c | ||
|
|
70278ef0b6 | ||
|
|
0a33fb32e7 | ||
|
|
26f52a5122 | ||
|
|
8f8de4295a | ||
|
|
660b342f2e | ||
|
|
6306a99740 | ||
|
|
5ca6f45809 | ||
|
|
3ea3e1dd01 | ||
|
|
2fddfbe24a | ||
|
|
25df58ba82 | ||
|
|
249bccdc7d | ||
|
|
ec0e2db0e9 | ||
|
|
ef9b68e0da | ||
|
|
31789738fc | ||
|
|
5fa349a80e | ||
|
|
5dfd0f6f44 | ||
|
|
bfa6301570 | ||
|
|
3ea52c4faf | ||
|
|
391e62bfee | ||
|
|
4d1da58f74 | ||
|
|
6dec9ae93b | ||
|
|
b466a6de99 | ||
|
|
186681ef44 | ||
|
|
1e2273b7c4 | ||
|
|
8d17f59a58 | ||
|
|
d8fcd15aeb | ||
|
|
8cc0eee254 | ||
|
|
e4b69023d9 | ||
|
|
7d40890636 | ||
|
|
3ecd2c62ae | ||
|
|
16cabab0d0 | ||
|
|
3e1c9e147f | ||
|
|
5e7a0f5634 | ||
|
|
61b02206c0 | ||
|
|
e19a32fc6b | ||
|
|
1712fea1d3 | ||
|
|
1ac4808a64 | ||
|
|
84fd5367ce | ||
|
|
337cfdc15b | ||
|
|
b979bd4f13 | ||
|
|
3cab902752 | ||
|
|
1d0dd2a69f | ||
|
|
17f439210a | ||
|
|
9970fd3f89 | ||
|
|
8b7bcceb7b | ||
|
|
93ebdabf6f | ||
|
|
0b278c989b | ||
|
|
518e771afe | ||
|
|
6578a02478 | ||
|
|
e76b8a97f0 | ||
|
|
54d9aecbf9 | ||
|
|
f2befc5edb |
@@ -1,16 +1,19 @@
|
|||||||
node_modules
|
.dockerignore
|
||||||
Dockerfile*
|
.editorconfig
|
||||||
docker-compose*
|
.env
|
||||||
.dockerignore
|
.git
|
||||||
.git
|
.idea
|
||||||
.gitignore
|
.vscode
|
||||||
README.md
|
CHANGELOG.md
|
||||||
LICENSE
|
coverage*
|
||||||
.vscode
|
data
|
||||||
Makefile
|
docker-compose*
|
||||||
helm-charts
|
Dockerfile*
|
||||||
.env
|
eslint.config.js
|
||||||
.editorconfig
|
helm-charts
|
||||||
.idea
|
LICENSE
|
||||||
coverage*
|
Makefile
|
||||||
data
|
node_modules
|
||||||
|
prettier.config.js
|
||||||
|
README.md
|
||||||
|
renovate.json
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
/** @type {import("eslint").Linter.Config} */
|
|
||||||
const config = {
|
|
||||||
root: true,
|
|
||||||
parser: "@typescript-eslint/parser",
|
|
||||||
plugins: ["isaacscript", "import"],
|
|
||||||
extends: [
|
|
||||||
"plugin:@typescript-eslint/recommended-type-checked",
|
|
||||||
"plugin:@typescript-eslint/stylistic-type-checked",
|
|
||||||
"plugin:prettier/recommended",
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
ecmaVersion: "latest",
|
|
||||||
sourceType: "module",
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
project: [
|
|
||||||
"./tsconfig.json",
|
|
||||||
"./cli/tsconfig.eslint.json", // separate eslint config for the CLI since we want to lint and typecheck differently due to template files
|
|
||||||
"./upgrade/tsconfig.json",
|
|
||||||
"./www/tsconfig.json",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
// Template files don't have reliable type information
|
|
||||||
{
|
|
||||||
files: ["./cli/template/**/*.{ts,tsx}"],
|
|
||||||
extends: ["plugin:@typescript-eslint/disable-type-checked"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
// These off/not-configured-the-way-we-want lint rules we like & opt into
|
|
||||||
"@typescript-eslint/no-explicit-any": "error",
|
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"error",
|
|
||||||
{ argsIgnorePattern: "^_", destructuredArrayIgnorePattern: "^_" },
|
|
||||||
],
|
|
||||||
"@typescript-eslint/consistent-type-imports": [
|
|
||||||
"error",
|
|
||||||
{ prefer: "type-imports", fixStyle: "inline-type-imports" },
|
|
||||||
],
|
|
||||||
"import/consistent-type-specifier-style": ["error", "prefer-inline"],
|
|
||||||
|
|
||||||
// For educational purposes we format our comments/jsdoc nicely
|
|
||||||
"isaacscript/complete-sentences-jsdoc": "warn",
|
|
||||||
"isaacscript/format-jsdoc-comments": "warn",
|
|
||||||
|
|
||||||
// These lint rules don't make sense for us but are enabled in the preset configs
|
|
||||||
"@typescript-eslint/no-confusing-void-expression": "off",
|
|
||||||
"@typescript-eslint/restrict-template-expressions": "off",
|
|
||||||
|
|
||||||
// This rule doesn't seem to be working properly
|
|
||||||
"@typescript-eslint/prefer-nullish-coalescing": "off",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = config;
|
|
||||||
15
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [C4illin] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
|
patreon: # Replace with a single Patreon username
|
||||||
|
open_collective: # Replace with a single Open Collective username
|
||||||
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
|
liberapay: # Replace with a single Liberapay username
|
||||||
|
issuehunt: # Replace with a single IssueHunt username
|
||||||
|
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||||
|
polar: # Replace with a single Polar username
|
||||||
|
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||||
|
thanks_dev: # Replace with a single thanks.dev username
|
||||||
|
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||||
23
.github/dependabot.yml
vendored
@@ -1,23 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
# Maintain dependencies for npm
|
|
||||||
- package-ecosystem: "npm" # See documentation for possible values
|
|
||||||
versioning-strategy: increase
|
|
||||||
directory: "/" # Location of package manifests
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
# Maintain dependencies for GitHub Actions
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
# Maintain dependencies for Docker
|
|
||||||
- package-ecosystem: "docker"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
28
.github/workflows/bun-dependabot.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
name: 'Dependabot: Update bun.lockb'
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "package.json"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update-bun-lockb:
|
|
||||||
name: "Update bun.lockb"
|
|
||||||
if: github.actor == 'dependabot[bot]'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: oven-sh/setup-bun@v1
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
|
||||||
- run: |
|
|
||||||
bun install
|
|
||||||
git add bun.lockb
|
|
||||||
git config --global user.name 'dependabot[bot]'
|
|
||||||
git config --global user.email 'dependabot[bot]@users.noreply.github.com'
|
|
||||||
git commit --amend --no-edit
|
|
||||||
git push --force
|
|
||||||
148
.github/workflows/docker-publish.yml
vendored
@@ -1,68 +1,80 @@
|
|||||||
name: Docker
|
name: Docker
|
||||||
|
|
||||||
# This workflow uses actions that are not certified by GitHub.
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
# They are provided by a third-party and are governed by
|
# They are provided by a third-party and are governed by
|
||||||
# separate terms of service, privacy policy, and support
|
# separate terms of service, privacy policy, and support
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
# Publish semver tags as releases.
|
# Publish semver tags as releases.
|
||||||
tags: [ 'v*.*.*' ]
|
tags: [ 'v*.*.*' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main" ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Use docker.io for Docker Hub if empty
|
# Use docker.io for Docker Hub if empty
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
# github.repository as <account>/<repo>
|
# github.repository as <account>/<repo>
|
||||||
IMAGE_NAME: ${{ github.repository }}
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
|
DOCKERHUB_USERNAME: c4illin
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
jobs:
|
||||||
runs-on: ubuntu-latest
|
build:
|
||||||
permissions:
|
runs-on: ubuntu-latest
|
||||||
contents: read
|
permissions:
|
||||||
packages: write
|
contents: read
|
||||||
|
packages: write
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
steps:
|
||||||
uses: actions/checkout@v4
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
# Workaround: https://github.com/docker/build-push-action/issues/461
|
|
||||||
- name: Setup Docker buildx
|
# Workaround: https://github.com/docker/build-push-action/issues/461
|
||||||
uses: docker/setup-buildx-action@v3
|
- name: Setup Docker buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
# Login against a Docker registry except on PR
|
|
||||||
# https://github.com/docker/login-action
|
# Login against a Docker registry except on PR
|
||||||
- name: Log into registry ${{ env.REGISTRY }}
|
# https://github.com/docker/login-action
|
||||||
if: github.event_name != 'pull_request'
|
- name: Log into registry ${{ env.REGISTRY }}
|
||||||
uses: docker/login-action@v3
|
if: github.event_name != 'pull_request'
|
||||||
with:
|
uses: docker/login-action@v3
|
||||||
registry: ${{ env.REGISTRY }}
|
with:
|
||||||
username: ${{ github.actor }}
|
registry: ${{ env.REGISTRY }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
# Extract metadata (tags, labels) for Docker
|
|
||||||
# https://github.com/docker/metadata-action
|
- name: Login to Docker Hub
|
||||||
- name: Extract Docker metadata
|
if: github.event_name != 'pull_request'
|
||||||
id: meta
|
uses: docker/login-action@v3
|
||||||
uses: docker/metadata-action@v5
|
with:
|
||||||
with:
|
username: ${{ env.DOCKERHUB_USERNAME }}
|
||||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
# Build and push Docker image with Buildx (don't push on PR)
|
# Extract metadata (tags, labels) for Docker
|
||||||
# https://github.com/docker/build-push-action
|
# https://github.com/docker/metadata-action
|
||||||
- name: Build and push Docker image
|
- name: Extract Docker metadata
|
||||||
id: build-and-push
|
id: meta
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
images: |
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
${{ env.IMAGE_NAME }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
cache-from: type=gha
|
# Build and push Docker image with Buildx (don't push on PR)
|
||||||
cache-to: type=gha,mode=max
|
# https://github.com/docker/build-push-action
|
||||||
|
- name: Build and push Docker image
|
||||||
|
id: build-and-push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
27
.github/workflows/dockerhub-description.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Update Docker Hub Description
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
|
DOCKERHUB_USERNAME: c4illin
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- README.md
|
||||||
|
- .github/workflows/dockerhub-description.yml
|
||||||
|
jobs:
|
||||||
|
dockerHubDescription:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Docker Hub Description
|
||||||
|
uses: peter-evans/dockerhub-description@v4
|
||||||
|
with:
|
||||||
|
username: ${{ env.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
repository: ${{ env.IMAGE_NAME }}
|
||||||
|
short-description: ${{ github.event.repository.description }}
|
||||||
|
enable-url-completion: true
|
||||||
25
.github/workflows/release-please.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
name: release-please
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-please:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: googleapis/release-please-action@v4
|
||||||
|
with:
|
||||||
|
# this assumes that you have created a personal access token
|
||||||
|
# (PAT) and configured it as a GitHub action secret named
|
||||||
|
# `MY_RELEASE_PLEASE_TOKEN` (this secret name is not important).
|
||||||
|
token: ${{ secrets.MY_RELEASE_PLEASE_TOKEN }}
|
||||||
|
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# this is a built-in strategy in release-please, see "Action Inputs"
|
||||||
|
# for more options
|
||||||
|
release-type: node
|
||||||
21
.github/workflows/remove-docker-tag.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Remove Docker Tag
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
remove-docker-tag:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
|
||||||
|
# (required)
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Remove Docker Tag
|
||||||
|
uses: ArchieAtkinson/remove-dockertag-action@v0.0
|
||||||
|
with:
|
||||||
|
tag_name: master
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
4
.gitignore
vendored
@@ -46,4 +46,6 @@ package-lock.json
|
|||||||
/output
|
/output
|
||||||
/db
|
/db
|
||||||
/data
|
/data
|
||||||
/Bruno
|
/Bruno
|
||||||
|
/tsconfig.tsbuildinfo
|
||||||
|
/public/generated.css
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||||
|
}
|
||||||
246
CHANGELOG.md
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [0.13.0](https://github.com/C4illin/ConvertX/compare/v0.12.1...v0.13.0) (2025-05-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add HIDE_HISTORY option to control visibility of history page ([bed52ce](https://github.com/C4illin/ConvertX/commit/bed52cef17ff68ec5e8770705a1fdf038e02e607))
|
||||||
|
* add HIDE_HISTORY option to control visibility of history page ([9d1c931](https://github.com/C4illin/ConvertX/commit/9d1c93155cc33ed6c83f9e5122afff8f28d0e4bf))
|
||||||
|
* add potrace converter ([bdbd4a1](https://github.com/C4illin/ConvertX/commit/bdbd4a122c09559b089b985ea12c5f3e085107da))
|
||||||
|
* Add support for .HIF files ([a5eaaa4](https://github.com/C4illin/ConvertX/commit/a5eaaa422a64506dd16d90d48a240556de33bc93))
|
||||||
|
* Add support for .HIF files ([70705c1](https://github.com/C4illin/ConvertX/commit/70705c1850d470296df85958c02a01fb5bc3a25f))
|
||||||
|
* add support for drag/drop of images ([ff2ef74](https://github.com/C4illin/ConvertX/commit/ff2ef7413542cf10ba7a6e246763bcecd6829ec1))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add timezone support ([4b5c732](https://github.com/C4illin/ConvertX/commit/4b5c732380bc844dccf340ea1eb4f8bfe3bb44a5)), closes [#258](https://github.com/C4illin/ConvertX/issues/258)
|
||||||
|
|
||||||
|
## [0.12.1](https://github.com/C4illin/ConvertX/compare/v0.12.0...v0.12.1) (2025-03-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* rollback to bun 1.2.2 ([cdae798](https://github.com/C4illin/ConvertX/commit/cdae798fcf5879e4adea87386a38748b9a1e1ddc))
|
||||||
|
|
||||||
|
## [0.12.0](https://github.com/C4illin/ConvertX/compare/v0.11.1...v0.12.0) (2025-03-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* added progress bar for file upload ([db60f35](https://github.com/C4illin/ConvertX/commit/db60f355b2973f43f8e5990e6fe4e351b959b659))
|
||||||
|
* made every upload file independent ([cc54bdc](https://github.com/C4illin/ConvertX/commit/cc54bdcbe764c41cc3273485d072fd3178ad2dca))
|
||||||
|
* replace exec with execFile ([9263d17](https://github.com/C4illin/ConvertX/commit/9263d17609dc4b2b367eb7fee67b3182e283b3a3))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add libheif ([6b92540](https://github.com/C4illin/ConvertX/commit/6b9254047c0598963aee1d99e20ba1650a0368bf))
|
||||||
|
* add libheif ([decfea5](https://github.com/C4illin/ConvertX/commit/decfea5dc9627b216bb276a9e1578c32cfa1deb6)), closes [#202](https://github.com/C4illin/ConvertX/issues/202)
|
||||||
|
* added onerror log ([ae4bbc8](https://github.com/C4illin/ConvertX/commit/ae4bbc8baacbaf67763c62ea44140bb21cc17230))
|
||||||
|
* refactored uploadFile to only accept a single file instead of multiple ([dc82a43](https://github.com/C4illin/ConvertX/commit/dc82a438d4104b79ff423d502a6779a43928968a))
|
||||||
|
* update libheif to 1.19.5 ([fba5e21](https://github.com/C4illin/ConvertX/commit/fba5e212e8d0eaba8971e239e35aeb521f3cd813)), closes [#202](https://github.com/C4illin/ConvertX/issues/202)
|
||||||
|
|
||||||
|
## [0.11.1](https://github.com/C4illin/ConvertX/compare/v0.11.0...v0.11.1) (2025-02-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* mobile view overflow ([bec58ac](https://github.com/C4illin/ConvertX/commit/bec58ac59f9600e35385b9e21d174f3ab1b42b1d))
|
||||||
|
|
||||||
|
## [0.11.0](https://github.com/C4illin/ConvertX/compare/v0.10.1...v0.11.0) (2025-02-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add deps for vaapi ([2bbbd03](https://github.com/C4illin/ConvertX/commit/2bbbd03554d384a4488143f29e5fc863cfdf333b)), closes [#192](https://github.com/C4illin/ConvertX/issues/192)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* don't crash if file is not found ([16f27c1](https://github.com/C4illin/ConvertX/commit/16f27c13bbc1c0e5fa2316f3db11d0918524053b))
|
||||||
|
* install numpy for inkscape ([0e61051](https://github.com/C4illin/ConvertX/commit/0e61051fc6be188164c3865b4fb579c140859fdc))
|
||||||
|
|
||||||
|
## [0.10.1](https://github.com/C4illin/ConvertX/compare/v0.10.0...v0.10.1) (2025-01-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ffmpeg works without ffmpeg_args ([3b7ea88](https://github.com/C4illin/ConvertX/commit/3b7ea88b7382f7c21b120bdc9bda5bb10547f55d)), closes [#212](https://github.com/C4illin/ConvertX/issues/212)
|
||||||
|
|
||||||
|
## [0.10.0](https://github.com/C4illin/ConvertX/compare/v0.9.0...v0.10.0) (2025-01-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add calibre ([03d3edf](https://github.com/C4illin/ConvertX/commit/03d3edfff65c252dd4b8922fc98257c089c1ff74)), closes [#191](https://github.com/C4illin/ConvertX/issues/191)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add FFMPEG_ARGS env variable ([f537c81](https://github.com/C4illin/ConvertX/commit/f537c81db7815df8017f834e3162291197e1c40f)), closes [#190](https://github.com/C4illin/ConvertX/issues/190)
|
||||||
|
* add qt6-qtbase-private-dev from community repo ([95dbc9f](https://github.com/C4illin/ConvertX/commit/95dbc9f678bec7e6e2c03587e1473fb8ff708ea3))
|
||||||
|
* skip account setup when ALLOW_UNAUTHENTICATED is true ([538c5b6](https://github.com/C4illin/ConvertX/commit/538c5b60c9e27a8184740305475245da79bae143))
|
||||||
|
|
||||||
|
## [0.9.0](https://github.com/C4illin/ConvertX/compare/v0.8.1...v0.9.0) (2024-11-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add inkscape for vector images ([f3740e9](https://github.com/C4illin/ConvertX/commit/f3740e9ded100b8500f3613517960248bbd3c210))
|
||||||
|
* Allow to chose webroot ([36cb6cc](https://github.com/C4illin/ConvertX/commit/36cb6cc589d80d0a87fa8dbe605db71a9a2570f9)), closes [#180](https://github.com/C4illin/ConvertX/issues/180)
|
||||||
|
* disable convert when uploading ([58e220e](https://github.com/C4illin/ConvertX/commit/58e220e82d7f9c163d6ea4dc31092c08a3e254f4)), closes [#177](https://github.com/C4illin/ConvertX/issues/177)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* treat unknown as m4a ([1a442d6](https://github.com/C4illin/ConvertX/commit/1a442d6e69606afef63b1e7df36aa83d111fa23d)), closes [#178](https://github.com/C4illin/ConvertX/issues/178)
|
||||||
|
* wait for both upload and selection ([4c05fd7](https://github.com/C4illin/ConvertX/commit/4c05fd72bbbf91ee02327f6fcbf749b78272376b)), closes [#177](https://github.com/C4illin/ConvertX/issues/177)
|
||||||
|
|
||||||
|
## [0.8.1](https://github.com/C4illin/ConvertX/compare/v0.8.0...v0.8.1) (2024-10-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* disable convert button when input is empty ([78844d7](https://github.com/C4illin/ConvertX/commit/78844d7bd55990789ed07c81e49043e688cbe656)), closes [#151](https://github.com/C4illin/ConvertX/issues/151)
|
||||||
|
* resize to fit for ico ([b4e53db](https://github.com/C4illin/ConvertX/commit/b4e53dbb8e70b3a95b44e5b756759d16117a87e1)), closes [#157](https://github.com/C4illin/ConvertX/issues/157)
|
||||||
|
* treat jfif as jpeg ([339b79f](https://github.com/C4illin/ConvertX/commit/339b79f786131deb93f0d5683e03178fdcab1ef5)), closes [#163](https://github.com/C4illin/ConvertX/issues/163)
|
||||||
|
|
||||||
|
## [0.8.0](https://github.com/C4illin/ConvertX/compare/v0.7.0...v0.8.0) (2024-09-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add light theme, fixes [#156](https://github.com/C4illin/ConvertX/issues/156) ([72636c5](https://github.com/C4illin/ConvertX/commit/72636c5059ebf09c8fece2e268293650b2f8ccf6))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add support for usd for assimp, [#144](https://github.com/C4illin/ConvertX/issues/144) ([2057167](https://github.com/C4illin/ConvertX/commit/20571675766209ad1251f07e687d29a6791afc8b))
|
||||||
|
* cleanup formats and add opus, fixes [#159](https://github.com/C4illin/ConvertX/issues/159) ([ae1dfaf](https://github.com/C4illin/ConvertX/commit/ae1dfafc9d9116a57b08c2f7fc326990e00824b0))
|
||||||
|
* support .awb and clean up, fixes [#153](https://github.com/C4illin/ConvertX/issues/153), [#92](https://github.com/C4illin/ConvertX/issues/92) ([1c9e67f](https://github.com/C4illin/ConvertX/commit/1c9e67fc3201e0e5dee91e8981adf34daaabf33a))
|
||||||
|
|
||||||
|
## [0.7.0](https://github.com/C4illin/ConvertX/compare/v0.6.0...v0.7.0) (2024-09-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add support for 3d assets through assimp converter ([63a4328](https://github.com/C4illin/ConvertX/commit/63a4328d4a1e01df3e0ec4a877bad8c8ffe71129))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* wrong layout on search with few options ([8817389](https://github.com/C4illin/ConvertX/commit/88173891ba2d69da46eda46f3f598a9b54f26f96))
|
||||||
|
|
||||||
|
## [0.6.0](https://github.com/C4illin/ConvertX/compare/v0.5.0...v0.6.0) (2024-09-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* ui remake with tailwind ([22f823c](https://github.com/C4illin/ConvertX/commit/22f823c535b20382981f86a13616b830a1f3392f))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* rename css file to force update cache, fixes [#141](https://github.com/C4illin/ConvertX/issues/141) ([47139a5](https://github.com/C4illin/ConvertX/commit/47139a550bd3d847da288c61bf8f88953b79c673))
|
||||||
|
|
||||||
|
## [0.5.0](https://github.com/C4illin/ConvertX/compare/v0.4.1...v0.5.0) (2024-09-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add option to customize how often files are automatically deleted ([317c932](https://github.com/C4illin/ConvertX/commit/317c932c2a26280bf37ed3d3bf9b879413590f5a))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* improve file name replacement logic ([60ba7c9](https://github.com/C4illin/ConvertX/commit/60ba7c93fbdc961f3569882fade7cc13dee7a7a5))
|
||||||
|
|
||||||
|
## [0.4.1](https://github.com/C4illin/ConvertX/compare/v0.4.0...v0.4.1) (2024-09-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* allow non lowercase true and false values, fixes [#122](https://github.com/C4illin/ConvertX/issues/122) ([bef1710](https://github.com/C4illin/ConvertX/commit/bef1710e3376baa7e25c107ded20a40d18b8c6b0))
|
||||||
|
|
||||||
|
## [0.4.0](https://github.com/C4illin/ConvertX/compare/v0.3.3...v0.4.0) (2024-08-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add option for unauthenticated file conversions [#114](https://github.com/C4illin/ConvertX/issues/114) ([f0d0e43](https://github.com/C4illin/ConvertX/commit/f0d0e4392983c3e4c530304ea88e023fda9bcac0))
|
||||||
|
* add resvg converter ([d5eeef9](https://github.com/C4illin/ConvertX/commit/d5eeef9f6884b2bb878508bed97ea9ceaa662995))
|
||||||
|
* add robots.txt ([6597c1d](https://github.com/C4illin/ConvertX/commit/6597c1d7caeb4dfb6bc47b442e4dfc9840ad12b7))
|
||||||
|
* Add search bar for formats ([53fff59](https://github.com/C4illin/ConvertX/commit/53fff594fc4d69306abcb2a5cad890fcd0953a58))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* keep unauthenticated user logged in if allowed [#114](https://github.com/C4illin/ConvertX/issues/114) ([bc4ad49](https://github.com/C4illin/ConvertX/commit/bc4ad492852fad8cb832a0c03485cccdd7f7b117))
|
||||||
|
* pdf support in vips ([8ca4f15](https://github.com/C4illin/ConvertX/commit/8ca4f1587df7f358893941c656d78d75f04dac93))
|
||||||
|
* Slow click on conversion popup does not work ([4d9c4d6](https://github.com/C4illin/ConvertX/commit/4d9c4d64aa0266f3928935ada68d91ac81f638aa))
|
||||||
|
|
||||||
|
## [0.3.3](https://github.com/C4illin/ConvertX/compare/v0.3.2...v0.3.3) (2024-07-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* downgrade @elysiajs/html dependency to version 1.0.2 ([c714ade](https://github.com/C4illin/ConvertX/commit/c714ade3e23865ba6cfaf76c9e7259df1cda222c))
|
||||||
|
|
||||||
|
## [0.3.2](https://github.com/C4illin/ConvertX/compare/v0.3.1...v0.3.2) (2024-07-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* increase max request body to support large uploads ([3ae2db5](https://github.com/C4illin/ConvertX/commit/3ae2db5d9b36fe3dcd4372ddcd32aa573ea59aa6)), closes [#64](https://github.com/C4illin/ConvertX/issues/64)
|
||||||
|
|
||||||
|
## [0.3.1](https://github.com/C4illin/ConvertX/compare/v0.3.0...v0.3.1) (2024-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* release releases ([4d4c13a](https://github.com/C4illin/ConvertX/commit/4d4c13a8d85ec7c9209ad41cdbea7d4380b0edbf))
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/C4illin/ConvertX/compare/v0.2.0...v0.3.0) (2024-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add version number to log ([4dcb796](https://github.com/C4illin/ConvertX/commit/4dcb796e1bd27badc078d0638076cd9f1e81c4a4)), closes [#44](https://github.com/C4illin/ConvertX/issues/44)
|
||||||
|
* change to xelatex ([fae2ba9](https://github.com/C4illin/ConvertX/commit/fae2ba9c54461dccdccd1bfb5e76398540d11d0b))
|
||||||
|
* print version of installed converters to log ([801cf28](https://github.com/C4illin/ConvertX/commit/801cf28d1e5edac9353b0b16be75a4fb48470b8a))
|
||||||
|
|
||||||
|
## [0.2.0](https://github.com/C4illin/ConvertX/compare/v0.1.2...v0.2.0) (2024-06-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add libjxl for jpegxl conversion ([ff680cb](https://github.com/C4illin/ConvertX/commit/ff680cb29534a25c3148a90fd064bb86c71fb482))
|
||||||
|
* change from debian to alpine ([1316957](https://github.com/C4illin/ConvertX/commit/13169574f0134ae236f8d41287bb73930b575e82)), closes [#34](https://github.com/C4illin/ConvertX/issues/34)
|
||||||
|
|
||||||
|
## [0.1.2](https://github.com/C4illin/ConvertX/compare/v0.1.1...v0.1.2) (2024-06-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix incorrect redirect ([25df58b](https://github.com/C4illin/ConvertX/commit/25df58ba82321aaa6617811a6995cb96c2a00a40)), closes [#23](https://github.com/C4illin/ConvertX/issues/23)
|
||||||
|
|
||||||
|
## [0.1.1](https://github.com/C4illin/ConvertX/compare/v0.1.0...v0.1.1) (2024-05-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* :bug: make sure all redirects are 302 ([9970fd3](https://github.com/C4illin/ConvertX/commit/9970fd3f89190af96f8762edc3817d1e03082b3a)), closes [#12](https://github.com/C4illin/ConvertX/issues/12)
|
||||||
|
|
||||||
|
## 0.1.0 (2024-05-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* remove file from file list in index.html ([787ff97](https://github.com/C4illin/ConvertX/commit/787ff9741ecbbf4fb4c02b43bd22a214a173fd7b))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous Chores
|
||||||
|
|
||||||
|
* release 0.1.0 ([54d9aec](https://github.com/C4illin/ConvertX/commit/54d9aecbf949689b12aa7e5e8e9be7b9032f4431))
|
||||||
64
Dockerfile
@@ -1,51 +1,71 @@
|
|||||||
FROM oven/bun:1-debian as base
|
FROM oven/bun:1.2.2-alpine AS base
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/C4illin/ConvertX"
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# install dependencies into temp directory
|
# install dependencies into temp directory
|
||||||
# this will cache them and speed up future builds
|
# this will cache them and speed up future builds
|
||||||
FROM base AS install
|
FROM base AS install
|
||||||
RUN mkdir -p /temp/dev
|
RUN mkdir -p /temp/dev
|
||||||
COPY package.json bun.lockb /temp/dev/
|
COPY package.json bun.lock /temp/dev/
|
||||||
RUN cd /temp/dev && bun install --frozen-lockfile
|
RUN cd /temp/dev && bun install --frozen-lockfile
|
||||||
|
|
||||||
# install with --production (exclude devDependencies)
|
# install with --production (exclude devDependencies)
|
||||||
RUN mkdir -p /temp/prod
|
RUN mkdir -p /temp/prod
|
||||||
COPY package.json bun.lockb /temp/prod/
|
COPY package.json bun.lock /temp/prod/
|
||||||
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
||||||
|
|
||||||
# copy node_modules from temp directory
|
FROM base AS builder
|
||||||
# then copy all (non-ignored) project files into the image
|
RUN apk --no-cache add curl gcc
|
||||||
# FROM base AS prerelease
|
ENV PATH=/root/.cargo/bin:$PATH
|
||||||
# COPY --from=install /temp/dev/node_modules node_modules
|
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
# COPY . .
|
RUN cargo install resvg
|
||||||
|
|
||||||
|
FROM base AS prerelease
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=install /temp/dev/node_modules node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
# # [optional] tests & build
|
|
||||||
# ENV NODE_ENV=production
|
# ENV NODE_ENV=production
|
||||||
# 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
|
||||||
LABEL maintainer="Emrik Östling (C4illin)"
|
|
||||||
LABEL description="ConvertX: self-hosted online file converter supporting 700+ file formats."
|
RUN apk --no-cache add libheif-tools --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community/
|
||||||
LABEL repo="https://github.com/C4illin/ConvertX"
|
|
||||||
|
|
||||||
# install additional dependencies
|
# install additional dependencies
|
||||||
RUN rm -rf /var/lib/apt/lists/partial && apt-get update -o Acquire::CompressionTypes::Order::=gz \
|
RUN apk --no-cache add \
|
||||||
&& apt-get install -y \
|
|
||||||
pandoc \
|
pandoc \
|
||||||
texlive-latex-recommended \
|
texlive \
|
||||||
texlive-fonts-recommended \
|
texlive-xetex \
|
||||||
texlive-latex-extra \
|
texmf-dist-latexextra \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
graphicsmagick \
|
graphicsmagick \
|
||||||
ghostscript \
|
ghostscript \
|
||||||
libvips-tools
|
vips-tools \
|
||||||
|
vips-poppler \
|
||||||
|
vips-jxl \
|
||||||
|
vips-heif \
|
||||||
|
vips-magick \
|
||||||
|
libjxl-tools \
|
||||||
|
assimp \
|
||||||
|
inkscape \
|
||||||
|
poppler-utils \
|
||||||
|
gcompat \
|
||||||
|
libva-utils \
|
||||||
|
py3-numpy \
|
||||||
|
potrace
|
||||||
|
|
||||||
|
# RUN apk --no-cache add calibre@testing --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main/
|
||||||
|
|
||||||
|
# this might be needed for some latex use cases, will add it if needed.
|
||||||
|
# texmf-dist-fontsextra \
|
||||||
|
|
||||||
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=builder /root/.cargo/bin/resvg /usr/local/bin/resvg
|
||||||
# COPY --from=prerelease /app/package.json .
|
COPY --from=prerelease /app/public/generated.css /app/public/
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
EXPOSE 3000/tcp
|
EXPOSE 3000/tcp
|
||||||
|
ENV NODE_ENV=production
|
||||||
ENTRYPOINT [ "bun", "run", "./src/index.tsx" ]
|
ENTRYPOINT [ "bun", "run", "./src/index.tsx" ]
|
||||||
233
README.md
@@ -1,78 +1,155 @@
|
|||||||

|

|
||||||
# ConvertX
|
|
||||||
[](https://github.com/C4illin/ConvertX/actions/workflows/docker-publish.yml)
|
# ConvertX
|
||||||
|
|
||||||
A self-hosted online file converter. Supports 831 different formats. Written with Typescript, Bun and Elysia.
|
[](https://github.com/C4illin/ConvertX/actions/workflows/docker-publish.yml)
|
||||||
|
[](https://github.com/C4illin/ConvertX/pkgs/container/ConvertX)
|
||||||
## Features
|
[](https://hub.docker.com/r/c4illin/convertx)
|
||||||
|
[](https://github.com/C4illin/ConvertX/pkgs/container/convertx)
|
||||||
- Convert files to different formats
|

|
||||||
- Password protection
|

|
||||||
- Multiple accounts
|

|
||||||
|
<!--  -->
|
||||||
## Converters supported
|
|
||||||
|
A self-hosted online file converter. Supports over a thousand different formats. Written with TypeScript, Bun and Elysia.
|
||||||
| Converter | Use case | Converts from | Converts to |
|
|
||||||
|----------------|---------------|---------------|-------------|
|
## Features
|
||||||
| Vips | Images (fast) | 45 | 23 |
|
|
||||||
| PDFLaTeX | Documents | 1 | 1 |
|
- Convert files to different formats
|
||||||
| Pandoc | Documents | 43 | 65 |
|
- Process multiple files at once
|
||||||
| GraphicsMagick | Images | 166 | 133 |
|
- Password protection
|
||||||
| FFmpeg | Video | ~473 | ~280 |
|
- Multiple accounts
|
||||||
|
|
||||||
<!-- many ffmpeg fileformats are duplicates -->
|
## Converters supported
|
||||||
|
|
||||||
## Deployment
|
| Converter | Use case | Converts from | Converts to |
|
||||||
|
|------------------------------------------------------------------------------|---------------|---------------|-------------|
|
||||||
```yml
|
| [libjxl](https://github.com/libjxl/libjxl) | JPEG XL | 11 | 11 |
|
||||||
# docker-compose.yml
|
| [resvg](https://github.com/RazrFalcon/resvg) | SVG | 1 | 1 |
|
||||||
services:
|
| [Vips](https://github.com/libvips/libvips) | Images | 45 | 23 |
|
||||||
convertx:
|
| [libheif](https://github.com/strukturag/libheif) | HEIF | 2 | 4 |
|
||||||
image: ghcr.io/c4illin/convertx:main
|
| [XeLaTeX](https://tug.org/xetex/) | LaTeX | 1 | 1 |
|
||||||
ports:
|
| [Calibre](https://calibre-ebook.com/) | E-books | 26 | 19 |
|
||||||
- "3000:3000"
|
| [Pandoc](https://pandoc.org/) | Documents | 43 | 65 |
|
||||||
environment: # Defaults are listed below. All are optional.
|
| [GraphicsMagick](http://www.graphicsmagick.org/) | Images | 167 | 130 |
|
||||||
- ACCOUNT_REGISTRATION=false # true or false, doesn't matter for the first account (e.g. keep this to false if you only want one account)
|
| [Inkscape](https://inkscape.org/) | Vector images | 7 | 17 |
|
||||||
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() by default
|
| [Assimp](https://github.com/assimp/assimp) | 3D Assets | 77 | 23 |
|
||||||
- HTTP_ALLOWED=false # setting this to true is unsafe, only set this to true locally
|
| [FFmpeg](https://ffmpeg.org/) | Video | ~472 | ~199 |
|
||||||
volumes:
|
|
||||||
- convertx:/app/data
|
<!-- many ffmpeg fileformats are duplicates -->
|
||||||
```
|
|
||||||
|
Any missing converter? Open an issue or pull request!
|
||||||
<!-- or
|
|
||||||
|
## Deployment
|
||||||
```bash
|
|
||||||
docker run ghcr.io/c4illin/convertx:master -p 3000:3000 -e ACCOUNT_REGISTRATION=false -v /path/you/want:/app/data
|
> [!WARNING]
|
||||||
``` -->
|
> If you can't login, make sure you are accessing the service over localhost or https otherwise set HTTP_ALLOWED=true
|
||||||
|
|
||||||
Then visit `http://localhost:3000` in your browser and create your account. Don't leave it unconfigured and open, as anyone can register the first account.
|
```yml
|
||||||
|
# docker-compose.yml
|
||||||
If you get unable to open database file run `chown -R $USER:$USER path` on the path you choose.
|
services:
|
||||||
|
convertx:
|
||||||
### Tutorial
|
image: ghcr.io/c4illin/convertx
|
||||||
|
container_name: convertx
|
||||||
Tutorial in french: https://belginux.com/installer-convertx-avec-docker/
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
## Todo
|
- "3000:3000"
|
||||||
- [x] Add messages for errors in converters
|
environment:
|
||||||
- [ ] Add options for converters
|
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() if unset
|
||||||
- [ ] Add more converters
|
volumes:
|
||||||
- [ ] Divide index.tsx into smaller components
|
- ./data:/app/data
|
||||||
- [ ] Add tests
|
```
|
||||||
- [ ] Add searchable list of formats
|
|
||||||
|
or
|
||||||
## Contributors
|
|
||||||
|
```bash
|
||||||
<a href="https://github.com/C4illin/ConvertX/graphs/contributors">
|
docker run -p 3000:3000 -v ./data:/app/data ghcr.io/c4illin/convertx
|
||||||
<img src="https://contrib.rocks/image?repo=C4illin/ConvertX" />
|
```
|
||||||
</a>
|
|
||||||
|
Then visit `http://localhost:3000` in your browser and create your account. Don't leave it unconfigured and open, as anyone can register the first account.
|
||||||
## Star History
|
|
||||||
|
If you get unable to open database file run `chown -R $USER:$USER path` on the path you choose.
|
||||||
<a href="https://github.com/C4illin/ConvertX/stargazers">
|
|
||||||
<picture>
|
### Environment variables
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=C4illin/ConvertX&type=Date&theme=dark" />
|
|
||||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=C4illin/ConvertX&type=Date" />
|
All are optional, JWT_SECRET is recommended to be set.
|
||||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=C4illin/ConvertX&type=Date" />
|
|
||||||
</picture>
|
| Name | Default | Description |
|
||||||
</a>
|
|---------------------------|---------|-------------|
|
||||||
|
| JWT_SECRET | when unset it will use the value from randomUUID() | A long and secret string used to sign the JSON Web Token |
|
||||||
|
| ACCOUNT_REGISTRATION | false | Allow users to register accounts |
|
||||||
|
| HTTP_ALLOWED | false | Allow HTTP connections, only set this to true locally |
|
||||||
|
| ALLOW_UNAUTHENTICATED | false | Allow unauthenticated users to use the service, only set this to true locally |
|
||||||
|
| AUTO_DELETE_EVERY_N_HOURS | 24 | Checks every n hours for files older then n hours and deletes them, set to 0 to disable |
|
||||||
|
| WEBROOT | | The address to the root path setting this to "/convert" will serve the website on "example.com/convert/" |
|
||||||
|
| FFMPEG_ARGS | | Arguments to pass to ffmpeg, e.g. `-preset veryfast` |
|
||||||
|
| HIDE_HISTORY | false | Hide the history page |
|
||||||
|
|
||||||
|
### Docker images
|
||||||
|
|
||||||
|
There is a `:latest` tag that is updated with every release and a `:main` tag that is updated with every push to the main branch. `:latest` is recommended for normal use.
|
||||||
|
|
||||||
|
The image is available on [GitHub Container Registry](https://github.com/C4illin/ConvertX/pkgs/container/ConvertX) and [Docker Hub](https://hub.docker.com/r/c4illin/convertx).
|
||||||
|
|
||||||
|
| Image | What it is |
|
||||||
|
|-------|------------|
|
||||||
|
| `image: ghcr.io/c4illin/convertx` | The latest release on ghcr |
|
||||||
|
| `image: ghcr.io/c4illin/convertx:main` | The latest commit on ghcr |
|
||||||
|
| `image: c4illin/convertx` | The latest release on docker hub |
|
||||||
|
| `image: c4illin/convertx:main` | The latest commit on docker hub |
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
<!-- Dockerhub was introduced in 0.9.0 and older releases -->
|
||||||
|
|
||||||
|
### Tutorial
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> These are written by other people, and may be outdated, incorrect or wrong.
|
||||||
|
|
||||||
|
Tutorial in french: <https://belginux.com/installer-convertx-avec-docker/>
|
||||||
|
|
||||||
|
Tutorial in chinese: <https://xzllll.com/24092901/>
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
0. Install [Bun](https://bun.sh/) and Git
|
||||||
|
1. Clone the repository
|
||||||
|
2. `bun install`
|
||||||
|
3. `bun run dev`
|
||||||
|
|
||||||
|
Pull requests are welcome! See below and open issues for the list of todos.
|
||||||
|
|
||||||
|
## Todo
|
||||||
|
|
||||||
|
- [x] Add messages for errors in converters
|
||||||
|
- [x] Add searchable list of formats
|
||||||
|
- [ ] Add options for converters
|
||||||
|
- [ ] Divide index.tsx into smaller components
|
||||||
|
- [ ] Add tests
|
||||||
|
- [ ] Make the upload button nicer and more easy to drop files on. Support copy paste as well if possible.
|
||||||
|
- [ ] Make errors logs visible from the web ui
|
||||||
|
- [ ] Add more converters:
|
||||||
|
- [ ] [deark](https://github.com/jsummers/deark)
|
||||||
|
- [ ] LibreOffice
|
||||||
|
- [ ] [dvisvgm](https://github.com/mgieseki/dvisvgm)
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
<a href="https://github.com/C4illin/ConvertX/graphs/contributors">
|
||||||
|
<img src="https://contrib.rocks/image?repo=C4illin/ConvertX" alt="Image with all contributors"/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
<a href="https://github.com/C4illin/ConvertX/stargazers">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=C4illin/ConvertX&type=Date&theme=dark" />
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=C4illin/ConvertX&type=Date" />
|
||||||
|
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=C4illin/ConvertX&type=Date" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|||||||
9
SECURITY.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Only the latest release is supported
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/C4illin/ConvertX/security/advisories/new) tab.
|
||||||
23
biome.json
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
|
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
|
||||||
"formatter": {
|
"formatter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"formatWithErrors": true,
|
"formatWithErrors": true,
|
||||||
@@ -9,7 +9,15 @@
|
|||||||
"lineWidth": 80,
|
"lineWidth": 80,
|
||||||
"attributePosition": "auto"
|
"attributePosition": "auto"
|
||||||
},
|
},
|
||||||
"organizeImports": { "enabled": true },
|
"files": {
|
||||||
|
"ignore": [
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/pico.lime.min.css"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"organizeImports": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
@@ -22,7 +30,11 @@
|
|||||||
"useLiteralKeys": "error",
|
"useLiteralKeys": "error",
|
||||||
"useOptionalChain": "error"
|
"useOptionalChain": "error"
|
||||||
},
|
},
|
||||||
"correctness": { "noPrecisionLoss": "error", "noUnusedVariables": "off" },
|
"correctness": {
|
||||||
|
"noPrecisionLoss": "error",
|
||||||
|
"noUnusedVariables": "off",
|
||||||
|
"useJsxKeyInIterable": "off"
|
||||||
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"noInferrableTypes": "error",
|
"noInferrableTypes": "error",
|
||||||
"noNamespace": "error",
|
"noNamespace": "error",
|
||||||
@@ -42,6 +54,9 @@
|
|||||||
"noUnsafeDeclarationMerging": "error",
|
"noUnsafeDeclarationMerging": "error",
|
||||||
"useAwait": "error",
|
"useAwait": "error",
|
||||||
"useNamespaceKeyword": "error"
|
"useNamespaceKeyword": "error"
|
||||||
|
},
|
||||||
|
"nursery": {
|
||||||
|
"useSortedClasses": "error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -57,4 +72,4 @@
|
|||||||
"attributePosition": "auto"
|
"attributePosition": "auto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
29
compose.yaml
@@ -1,11 +1,18 @@
|
|||||||
services:
|
services:
|
||||||
convertx:
|
convertx:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
volumes:
|
# dockerfile: Debian.Dockerfile
|
||||||
- ./data:/app/data
|
volumes:
|
||||||
environment:
|
- ./data:/app/data
|
||||||
- ACCOUNT_REGISTRATION=true
|
environment: # Defaults are listed below. All are optional.
|
||||||
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234
|
- ACCOUNT_REGISTRATION=true # true or false, doesn't matter for the first account (e.g. keep this to false if you only want one account)
|
||||||
ports:
|
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() by default
|
||||||
- 3000:3000
|
- HTTP_ALLOWED=true # setting this to true is unsafe, only set this to true locally
|
||||||
|
- ALLOW_UNAUTHENTICATED=true # allows anyone to use the service without logging in, only set this to true locally
|
||||||
|
- AUTO_DELETE_EVERY_N_HOURS=1 # checks every n hours for files older then n hours and deletes them, set to 0 to disable
|
||||||
|
# - FFMPEG_ARGS=-hwaccel vulkan # additional arguments to pass to ffmpeg
|
||||||
|
# - WEBROOT=/convertx # the root path of the web interface, leave empty to disable
|
||||||
|
# - HIDE_HISTORY=true # hides the history tab in the web interface, defaults to false
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
|||||||
57
eslint.config.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import eslintParserTypeScript from "@typescript-eslint/parser";
|
||||||
|
import type { Linter } from "eslint";
|
||||||
|
import eslintPluginReadableTailwind from "eslint-plugin-readable-tailwind";
|
||||||
|
import simpleImportSortPlugin from "eslint-plugin-simple-import-sort";
|
||||||
|
import globals from "globals";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
js.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
// ...tailwind.configs["flat/recommended"],
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
"simple-import-sort": simpleImportSortPlugin,
|
||||||
|
"readable-tailwind": eslintPluginReadableTailwind,
|
||||||
|
},
|
||||||
|
ignores: ["**/node_modules/**"],
|
||||||
|
languageOptions: {
|
||||||
|
parser: eslintParserTypeScript,
|
||||||
|
parserOptions: {
|
||||||
|
project: true,
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
files: ["**/*.{js,mjs,cjs,jsx,tsx,ts}"],
|
||||||
|
rules: {
|
||||||
|
...eslintPluginReadableTailwind.configs.warning.rules,
|
||||||
|
// "tailwindcss/classnames-order": "off",
|
||||||
|
"readable-tailwind/multiline": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
group: "newLine",
|
||||||
|
printWidth: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// "tailwindcss/no-custom-classname": [
|
||||||
|
// "warn",
|
||||||
|
// {
|
||||||
|
// whitelist: [
|
||||||
|
// "select_container",
|
||||||
|
// "convert_to_popup",
|
||||||
|
// "convert_to_group",
|
||||||
|
// "target",
|
||||||
|
// "convert_to_target",
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as Linter.Config[];
|
||||||
BIN
images/preview.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
65
package.json
@@ -1,39 +1,54 @@
|
|||||||
{
|
{
|
||||||
"name": "convertx-frontend",
|
"name": "convertx-frontend",
|
||||||
"version": "1.0.50",
|
"version": "0.13.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run --watch src/index.tsx",
|
"dev": "bun run --watch src/index.tsx",
|
||||||
"hot": "bun run --hot src/index.tsx",
|
"hot": "bun run --hot src/index.tsx",
|
||||||
"format": "biome format --write ./src",
|
"format": "eslint --fix .",
|
||||||
"css": "cpy 'node_modules/@picocss/pico/css/pico.lime.min.css' 'src/public/' --flat"
|
"build": "bunx @tailwindcss/cli -i ./src/main.css -o ./public/generated.css",
|
||||||
|
"lint": "run-p 'lint:*'",
|
||||||
|
"lint:tsc": "tsc --noEmit",
|
||||||
|
"lint:knip": "knip",
|
||||||
|
"lint:eslint": "eslint ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@elysiajs/cookie": "^0.8.0",
|
"@elysiajs/html": "^1.3.0",
|
||||||
"@elysiajs/html": "^1.0.2",
|
"@elysiajs/jwt": "^1.3.0",
|
||||||
"@elysiajs/jwt": "^1.0.2",
|
"@elysiajs/static": "^1.3.0",
|
||||||
"@elysiajs/static": "^1.0.3",
|
"@kitajs/html": "^4.2.9",
|
||||||
"elysia": "^1.0.22"
|
"elysia": "^1.3.1",
|
||||||
|
"sanitize-filename": "^1.6.3"
|
||||||
},
|
},
|
||||||
"module": "src/index.tsx",
|
"module": "src/index.tsx",
|
||||||
|
"type": "module",
|
||||||
"bun-create": {
|
"bun-create": {
|
||||||
"start": "bun run src/index.tsx"
|
"start": "bun run src/index.tsx"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.7.3",
|
"@eslint/js": "^9.26.0",
|
||||||
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
|
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
|
||||||
"@kitajs/ts-html-plugin": "^4.0.1",
|
"@kitajs/ts-html-plugin": "^4.1.1",
|
||||||
"@picocss/pico": "^2.0.6",
|
"@tailwindcss/cli": "^4.1.6",
|
||||||
"@total-typescript/ts-reset": "^0.5.1",
|
"@tailwindcss/postcss": "^4.1.6",
|
||||||
"@types/bun": "^1.1.3",
|
"@total-typescript/ts-reset": "^0.6.1",
|
||||||
"@types/eslint": "^8.56.10",
|
"@types/bun": "^1.2.13",
|
||||||
"@types/node": "^20.12.13",
|
"@types/eslint-plugin-tailwindcss": "^3.17.0",
|
||||||
"@types/ws": "^8.5.10",
|
"@types/node": "^22.15.17",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
"autoprefixer": "^10.4.21",
|
||||||
"@typescript-eslint/parser": "^7.11.0",
|
"cssnano": "^7.0.7",
|
||||||
"cpy-cli": "^5.0.0",
|
"eslint": "^9.26.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-plugin-readable-tailwind": "^2.1.1",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
"prettier": "^3.2.5",
|
"eslint-plugin-tailwindcss": "4.0.0-alpha.0",
|
||||||
"typescript": "^5.4.5"
|
"globals": "^16.1.0",
|
||||||
|
"knip": "^5.55.1",
|
||||||
|
"npm-run-all2": "^8.0.1",
|
||||||
|
"postcss": "^8.5.3",
|
||||||
|
"postcss-cli": "^11.0.1",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"tailwind-scrollbar": "^4.0.2",
|
||||||
|
"tailwindcss": "^4.1.6",
|
||||||
|
"typescript": "^5.8.3",
|
||||||
|
"typescript-eslint": "^8.32.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
5
postcss.config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
"@tailwindcss/postcss": {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 476 B After Width: | Height: | Size: 476 B |
|
Before Width: | Height: | Size: 960 B After Width: | Height: | Size: 960 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,3 +1,5 @@
|
|||||||
|
const webroot = document.querySelector("meta[name='webroot']").content;
|
||||||
|
|
||||||
window.downloadAll = function () {
|
window.downloadAll = function () {
|
||||||
// Get all download links
|
// Get all download links
|
||||||
const downloadLinks = document.querySelectorAll("a[download]");
|
const downloadLinks = document.querySelectorAll("a[download]");
|
||||||
@@ -18,7 +20,7 @@ let progressElem = document.querySelector("progress");
|
|||||||
const refreshData = () => {
|
const refreshData = () => {
|
||||||
// console.log("Refreshing data...", progressElem.value, progressElem.max);
|
// console.log("Refreshing data...", progressElem.value, progressElem.max);
|
||||||
if (progressElem.value !== progressElem.max) {
|
if (progressElem.value !== progressElem.max) {
|
||||||
fetch(`/progress/${jobId}`, {
|
fetch(`${webroot}/progress/${jobId}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
})
|
})
|
||||||
.then((res) => res.text())
|
.then((res) => res.text())
|
||||||
2
public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow: /
|
||||||
254
public/script.js
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
const webroot = document.querySelector("meta[name='webroot']").content;
|
||||||
|
const fileInput = document.querySelector('input[type="file"]');
|
||||||
|
const dropZone = document.getElementById("dropzone");
|
||||||
|
const convertButton = document.querySelector("input[type='submit']");
|
||||||
|
const fileNames = [];
|
||||||
|
let fileType;
|
||||||
|
let pendingFiles = 0;
|
||||||
|
let formatSelected = false;
|
||||||
|
|
||||||
|
dropZone.addEventListener("dragover", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
dropZone.classList.add("dragover");
|
||||||
|
});
|
||||||
|
|
||||||
|
dropZone.addEventListener("dragleave", () => {
|
||||||
|
dropZone.classList.remove("dragover");
|
||||||
|
});
|
||||||
|
|
||||||
|
dropZone.addEventListener("drop", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
dropZone.classList.remove("dragover");
|
||||||
|
|
||||||
|
const files = e.dataTransfer.files;
|
||||||
|
|
||||||
|
if (files.length === 0) {
|
||||||
|
console.warn("No files dropped — likely a URL or unsupported source.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
console.log("Handling dropped file:", file.name);
|
||||||
|
handleFile(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extracted handleFile function for reusability in drag-and-drop and file input
|
||||||
|
function handleFile(file) {
|
||||||
|
const fileList = document.querySelector("#file-list");
|
||||||
|
|
||||||
|
const row = document.createElement("tr");
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>${file.name}</td>
|
||||||
|
<td><progress max="100"></progress></td>
|
||||||
|
<td>${(file.size / 1024).toFixed(2)} kB</td>
|
||||||
|
<td><a onclick="deleteRow(this)">Remove</a></td>
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (!fileType) {
|
||||||
|
fileType = file.name.split(".").pop();
|
||||||
|
fileInput.setAttribute("accept", `.${fileType}`);
|
||||||
|
setTitle();
|
||||||
|
|
||||||
|
fetch(`${webroot}/conversions`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ fileType }),
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
})
|
||||||
|
.then((res) => res.text())
|
||||||
|
.then((html) => {
|
||||||
|
selectContainer.innerHTML = html;
|
||||||
|
updateSearchBar();
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileList.appendChild(row);
|
||||||
|
file.htmlRow = row;
|
||||||
|
fileNames.push(file.name);
|
||||||
|
uploadFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectContainer = document.querySelector("form .select_container");
|
||||||
|
|
||||||
|
const updateSearchBar = () => {
|
||||||
|
const convertToInput = document.querySelector(
|
||||||
|
"input[name='convert_to_search']",
|
||||||
|
);
|
||||||
|
const convertToPopup = document.querySelector(".convert_to_popup");
|
||||||
|
const convertToGroupElements = document.querySelectorAll(".convert_to_group");
|
||||||
|
const convertToGroups = {};
|
||||||
|
const convertToElement = document.querySelector("select[name='convert_to']");
|
||||||
|
|
||||||
|
const showMatching = (search) => {
|
||||||
|
for (const [targets, groupElement] of Object.values(convertToGroups)) {
|
||||||
|
let matchingTargetsFound = 0;
|
||||||
|
for (const target of targets) {
|
||||||
|
if (target.dataset.target.includes(search)) {
|
||||||
|
matchingTargetsFound++;
|
||||||
|
target.classList.remove("hidden");
|
||||||
|
target.classList.add("flex");
|
||||||
|
} else {
|
||||||
|
target.classList.add("hidden");
|
||||||
|
target.classList.remove("flex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingTargetsFound === 0) {
|
||||||
|
groupElement.classList.add("hidden");
|
||||||
|
groupElement.classList.remove("flex");
|
||||||
|
} else {
|
||||||
|
groupElement.classList.remove("hidden");
|
||||||
|
groupElement.classList.add("flex");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const groupElement of convertToGroupElements) {
|
||||||
|
const groupName = groupElement.dataset.converter;
|
||||||
|
|
||||||
|
const targetElements = groupElement.querySelectorAll(".target");
|
||||||
|
const targets = Array.from(targetElements);
|
||||||
|
|
||||||
|
for (const target of targets) {
|
||||||
|
target.onmousedown = () => {
|
||||||
|
convertToElement.value = target.dataset.value;
|
||||||
|
convertToInput.value = `${target.dataset.target} using ${target.dataset.converter}`;
|
||||||
|
formatSelected = true;
|
||||||
|
if (pendingFiles === 0 && fileNames.length > 0) {
|
||||||
|
convertButton.disabled = false;
|
||||||
|
}
|
||||||
|
showMatching("");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToGroups[groupName] = [targets, groupElement];
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToInput.addEventListener("input", (e) => {
|
||||||
|
showMatching(e.target.value.toLowerCase());
|
||||||
|
});
|
||||||
|
|
||||||
|
convertToInput.addEventListener("search", () => {
|
||||||
|
// when the user clears the search bar using the 'x' button
|
||||||
|
convertButton.disabled = true;
|
||||||
|
formatSelected = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
convertToInput.addEventListener("blur", (e) => {
|
||||||
|
// Keep the popup open even when clicking on a target button
|
||||||
|
// for a split second to allow the click to go through
|
||||||
|
if (e?.relatedTarget?.classList?.contains("target")) {
|
||||||
|
convertToPopup.classList.add("hidden");
|
||||||
|
convertToPopup.classList.remove("flex");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToPopup.classList.add("hidden");
|
||||||
|
convertToPopup.classList.remove("flex");
|
||||||
|
});
|
||||||
|
|
||||||
|
convertToInput.addEventListener("focus", () => {
|
||||||
|
convertToPopup.classList.remove("hidden");
|
||||||
|
convertToPopup.classList.add("flex");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add a 'change' event listener to the file input element
|
||||||
|
fileInput.addEventListener("change", (e) => {
|
||||||
|
const files = e.target.files;
|
||||||
|
for (const file of files) {
|
||||||
|
handleFile(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const setTitle = () => {
|
||||||
|
const title = document.querySelector("h1");
|
||||||
|
title.textContent = `Convert ${fileType ? `.${fileType}` : ""}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add a onclick for the delete button
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const deleteRow = (target) => {
|
||||||
|
const filename = target.parentElement.parentElement.children[0].textContent;
|
||||||
|
const row = target.parentElement.parentElement;
|
||||||
|
row.remove();
|
||||||
|
|
||||||
|
// remove from fileNames
|
||||||
|
const index = fileNames.indexOf(filename);
|
||||||
|
fileNames.splice(index, 1);
|
||||||
|
|
||||||
|
// reset fileInput
|
||||||
|
fileInput.value = "";
|
||||||
|
|
||||||
|
// if fileNames is empty, reset fileType
|
||||||
|
if (fileNames.length === 0) {
|
||||||
|
fileType = null;
|
||||||
|
fileInput.removeAttribute("accept");
|
||||||
|
convertButton.disabled = true;
|
||||||
|
setTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`${webroot}/delete`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ filename: filename }),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadFile = (file) => {
|
||||||
|
convertButton.disabled = true;
|
||||||
|
convertButton.textContent = "Uploading...";
|
||||||
|
pendingFiles += 1;
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file, file.name);
|
||||||
|
|
||||||
|
let xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.open("POST", `${webroot}/upload`, true);
|
||||||
|
|
||||||
|
xhr.onload = () => {
|
||||||
|
let data = JSON.parse(xhr.responseText);
|
||||||
|
|
||||||
|
pendingFiles -= 1;
|
||||||
|
if (pendingFiles === 0) {
|
||||||
|
if (formatSelected) {
|
||||||
|
convertButton.disabled = false;
|
||||||
|
}
|
||||||
|
convertButton.textContent = "Convert";
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove the progress bar when upload is done
|
||||||
|
let progressbar = file.htmlRow.getElementsByTagName("progress");
|
||||||
|
progressbar[0].parentElement.remove();
|
||||||
|
console.log(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.upload.onprogress = (e) => {
|
||||||
|
let sent = e.loaded;
|
||||||
|
let total = e.total;
|
||||||
|
console.log(`upload progress (${file.name}):`, (100 * sent) / total);
|
||||||
|
|
||||||
|
let progressbar = file.htmlRow.getElementsByTagName("progress");
|
||||||
|
progressbar[0].value = ((100 * sent) / total);
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = (e) => {
|
||||||
|
console.log(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send(formData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const formConvert = document.querySelector(`form[action='${webroot}/convert']`);
|
||||||
|
|
||||||
|
formConvert.addEventListener("submit", () => {
|
||||||
|
const hiddenInput = document.querySelector("input[name='file_names']");
|
||||||
|
hiddenInput.value = JSON.stringify(fileNames);
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSearchBar();
|
||||||
11
renovate.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"config:recommended",
|
||||||
|
":disableDependencyDashboard"
|
||||||
|
],
|
||||||
|
"lockFileMaintenance": {
|
||||||
|
"enabled": true,
|
||||||
|
"automerge": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +1,40 @@
|
|||||||
export const BaseHtml = ({ children, title = "ConvertX" }) => (
|
import { Html } from "@elysiajs/html";
|
||||||
<html lang="en">
|
|
||||||
<head>
|
export const BaseHtml = ({
|
||||||
<meta charset="UTF-8" />
|
children,
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
title = "ConvertX",
|
||||||
<title safe>{title}</title>
|
webroot = "",
|
||||||
<link rel="stylesheet" href="/pico.lime.min.css" />
|
}: {
|
||||||
<link rel="stylesheet" href="/style.css" />
|
children: JSX.Element;
|
||||||
<link
|
title?: string;
|
||||||
rel="apple-touch-icon"
|
webroot?: string;
|
||||||
sizes="180x180"
|
}) => (
|
||||||
href="/apple-touch-icon.png"
|
<html lang="en">
|
||||||
/>
|
<head>
|
||||||
<link
|
<meta charset="UTF-8" />
|
||||||
rel="icon"
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
type="image/png"
|
<meta name="webroot" content={webroot} />
|
||||||
sizes="32x32"
|
<title safe>{title}</title>
|
||||||
href="/favicon-32x32.png"
|
<link rel="stylesheet" href={`${webroot}/generated.css`} />
|
||||||
/>
|
<link
|
||||||
<link
|
rel="apple-touch-icon"
|
||||||
rel="icon"
|
sizes="180x180"
|
||||||
type="image/png"
|
href={`${webroot}/apple-touch-icon.png`}
|
||||||
sizes="16x16"
|
/>
|
||||||
href="/favicon-16x16.png"
|
<link
|
||||||
/>
|
rel="icon"
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
type="image/png"
|
||||||
</head>
|
sizes="32x32"
|
||||||
<body>{children}</body>
|
href={`${webroot}/favicon-32x32.png`}
|
||||||
</html>
|
/>
|
||||||
);
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href={`${webroot}/favicon-16x16.png`}
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href={`${webroot}/site.webmanifest`} />
|
||||||
|
</head>
|
||||||
|
<body class="w-full bg-neutral-900 text-neutral-200">{children}</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,48 +1,88 @@
|
|||||||
|
import { Html } from "@kitajs/html";
|
||||||
|
|
||||||
export const Header = ({
|
export const Header = ({
|
||||||
loggedIn,
|
loggedIn,
|
||||||
accountRegistration,
|
accountRegistration,
|
||||||
}: { loggedIn?: boolean; accountRegistration?: boolean }) => {
|
allowUnauthenticated,
|
||||||
|
hideHistory,
|
||||||
|
webroot = "",
|
||||||
|
}: {
|
||||||
|
loggedIn?: boolean;
|
||||||
|
accountRegistration?: boolean;
|
||||||
|
allowUnauthenticated?: boolean;
|
||||||
|
hideHistory?: boolean;
|
||||||
|
webroot?: string;
|
||||||
|
}) => {
|
||||||
let rightNav: JSX.Element;
|
let rightNav: JSX.Element;
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
rightNav = (
|
rightNav = (
|
||||||
<ul>
|
<ul class="flex gap-4">
|
||||||
<li>
|
{!hideHistory && (
|
||||||
<a href="/history">History</a>
|
<li>
|
||||||
</li>
|
<a
|
||||||
<li>
|
class={`
|
||||||
<a href="/logoff">Logout</a>
|
text-accent-600 transition-all
|
||||||
</li>
|
hover:text-accent-500 hover:underline
|
||||||
|
`}
|
||||||
|
href={`${webroot}/history`}
|
||||||
|
>
|
||||||
|
History
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
{!allowUnauthenticated ? (
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
class={`
|
||||||
|
text-accent-600 transition-all
|
||||||
|
hover:text-accent-500 hover:underline
|
||||||
|
`}
|
||||||
|
href={`${webroot}/logoff`}
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
) : null}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
rightNav = (
|
rightNav = (
|
||||||
<ul>
|
<ul class="flex gap-4">
|
||||||
<li>
|
<li>
|
||||||
<a href="/login">Login</a>
|
<a
|
||||||
|
class={`
|
||||||
|
text-accent-600 transition-all
|
||||||
|
hover:text-accent-500 hover:underline
|
||||||
|
`}
|
||||||
|
href={`${webroot}/login`}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{accountRegistration && (
|
{accountRegistration ? (
|
||||||
<li>
|
<li>
|
||||||
<a href="/register">Register</a>
|
<a
|
||||||
|
class={`
|
||||||
|
text-accent-600 transition-all
|
||||||
|
hover:text-accent-500 hover:underline
|
||||||
|
`}
|
||||||
|
href={`${webroot}/register`}
|
||||||
|
>
|
||||||
|
Register
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
)}
|
) : null}
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="container">
|
<header class="w-full p-4">
|
||||||
<nav>
|
<nav class="mx-auto flex max-w-4xl justify-between rounded-sm bg-neutral-900 p-4">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<strong>
|
<strong>
|
||||||
<a
|
<a href={`${webroot}/`}>ConvertX</a>
|
||||||
href="/"
|
|
||||||
style={{
|
|
||||||
textDecoration: "none",
|
|
||||||
color: "inherit",
|
|
||||||
}}>
|
|
||||||
ConvertX
|
|
||||||
</a>
|
|
||||||
</strong>
|
</strong>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -50,4 +90,4 @@ export const Header = ({
|
|||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
139
src/converters/assimp.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
|
export const properties = {
|
||||||
|
from: {
|
||||||
|
object: [
|
||||||
|
"3d",
|
||||||
|
"3ds",
|
||||||
|
"3mf",
|
||||||
|
"ac",
|
||||||
|
"ac3d",
|
||||||
|
"acc",
|
||||||
|
"amf",
|
||||||
|
"amj",
|
||||||
|
"ase",
|
||||||
|
"ask",
|
||||||
|
"assbin",
|
||||||
|
"b3d",
|
||||||
|
"blend",
|
||||||
|
"bsp",
|
||||||
|
"bvh",
|
||||||
|
"cob",
|
||||||
|
"csm",
|
||||||
|
"dae",
|
||||||
|
"dxf",
|
||||||
|
"enff",
|
||||||
|
"fbx",
|
||||||
|
"glb",
|
||||||
|
"gltf",
|
||||||
|
"hmb",
|
||||||
|
"hmp",
|
||||||
|
"ifc",
|
||||||
|
"ifczip",
|
||||||
|
"iqm",
|
||||||
|
"irr",
|
||||||
|
"irrmesh",
|
||||||
|
"lwo",
|
||||||
|
"lws",
|
||||||
|
"lxo",
|
||||||
|
"m3d",
|
||||||
|
"md2",
|
||||||
|
"md3",
|
||||||
|
"md5anim",
|
||||||
|
"md5camera",
|
||||||
|
"md5mesh",
|
||||||
|
"mdc",
|
||||||
|
"mdl",
|
||||||
|
"mesh.xml",
|
||||||
|
"mesh",
|
||||||
|
"mot",
|
||||||
|
"ms3d",
|
||||||
|
"ndo",
|
||||||
|
"nff",
|
||||||
|
"obj",
|
||||||
|
"off",
|
||||||
|
"ogex",
|
||||||
|
"pk3",
|
||||||
|
"ply",
|
||||||
|
"pmx",
|
||||||
|
"prj",
|
||||||
|
"q3o",
|
||||||
|
"q3s",
|
||||||
|
"raw",
|
||||||
|
"scn",
|
||||||
|
"sib",
|
||||||
|
"smd",
|
||||||
|
"step",
|
||||||
|
"stl",
|
||||||
|
"stp",
|
||||||
|
"ter",
|
||||||
|
"uc",
|
||||||
|
"usd",
|
||||||
|
"usda",
|
||||||
|
"usdc",
|
||||||
|
"usdz",
|
||||||
|
"vta",
|
||||||
|
"x",
|
||||||
|
"x3d",
|
||||||
|
"x3db",
|
||||||
|
"xgl",
|
||||||
|
"xml",
|
||||||
|
"zae",
|
||||||
|
"zgl",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
object: [
|
||||||
|
"3ds",
|
||||||
|
"3mf",
|
||||||
|
"assbin",
|
||||||
|
"assjson",
|
||||||
|
"assxml",
|
||||||
|
"collada",
|
||||||
|
"dae",
|
||||||
|
"fbx",
|
||||||
|
"fbxa",
|
||||||
|
"glb",
|
||||||
|
"glb2",
|
||||||
|
"gltf",
|
||||||
|
"gltf2",
|
||||||
|
"json",
|
||||||
|
"obj",
|
||||||
|
"objnomtl",
|
||||||
|
"pbrt",
|
||||||
|
"ply",
|
||||||
|
"plyb",
|
||||||
|
"stl",
|
||||||
|
"stlb",
|
||||||
|
"stp",
|
||||||
|
"x",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function convert(
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
convertTo: string,
|
||||||
|
targetPath: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
options?: unknown,
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execFile("assimp", ["export", filePath, targetPath], (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
reject(`error: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
84
src/converters/calibre.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
|
export const properties = {
|
||||||
|
from: {
|
||||||
|
document: [
|
||||||
|
"azw4",
|
||||||
|
"chm",
|
||||||
|
"cbr",
|
||||||
|
"cbz",
|
||||||
|
"cbt",
|
||||||
|
"cba",
|
||||||
|
"cb7",
|
||||||
|
"djvu",
|
||||||
|
"docx",
|
||||||
|
"epub",
|
||||||
|
"fb2",
|
||||||
|
"htlz",
|
||||||
|
"html",
|
||||||
|
"lit",
|
||||||
|
"lrf",
|
||||||
|
"mobi",
|
||||||
|
"odt",
|
||||||
|
"pdb",
|
||||||
|
"pdf",
|
||||||
|
"pml",
|
||||||
|
"rb",
|
||||||
|
"rtf",
|
||||||
|
"recipe",
|
||||||
|
"snb",
|
||||||
|
"tcr",
|
||||||
|
"txt",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
document: [
|
||||||
|
"azw3",
|
||||||
|
"docx",
|
||||||
|
"epub",
|
||||||
|
"fb2",
|
||||||
|
"html",
|
||||||
|
"htmlz",
|
||||||
|
"lit",
|
||||||
|
"lrf",
|
||||||
|
"mobi",
|
||||||
|
"oeb",
|
||||||
|
"pdb",
|
||||||
|
"pdf",
|
||||||
|
"pml",
|
||||||
|
"rb",
|
||||||
|
"rtf",
|
||||||
|
"snb",
|
||||||
|
"tcr",
|
||||||
|
"txt",
|
||||||
|
"txtz",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function convert(
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
convertTo: string,
|
||||||
|
targetPath: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
options?: unknown,
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execFile("ebook-convert", [filePath, targetPath], (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
reject(`error: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,339 +1,340 @@
|
|||||||
import { exec } from "node:child_process";
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
export const properties = {
|
export const properties = {
|
||||||
from: {
|
from: {
|
||||||
image: [
|
image: [
|
||||||
"3fr",
|
"3fr",
|
||||||
"8bim",
|
"8bim",
|
||||||
"8bimtext",
|
"8bimtext",
|
||||||
"8bimwtext",
|
"8bimwtext",
|
||||||
"app1",
|
"app1",
|
||||||
"app1jpeg",
|
"app1jpeg",
|
||||||
"art",
|
"art",
|
||||||
"arw",
|
"arw",
|
||||||
"avs",
|
"avs",
|
||||||
"b",
|
"b",
|
||||||
"bie",
|
"bie",
|
||||||
"bigtiff",
|
"bigtiff",
|
||||||
"bmp",
|
"bmp",
|
||||||
"c",
|
"c",
|
||||||
"cals",
|
"cals",
|
||||||
"caption",
|
"caption",
|
||||||
"cin",
|
"cin",
|
||||||
"cmyk",
|
"cmyk",
|
||||||
"cmyka",
|
"cmyka",
|
||||||
"cr2",
|
"cr2",
|
||||||
"crw",
|
"crw",
|
||||||
"cur",
|
"cur",
|
||||||
"cut",
|
"cut",
|
||||||
"dcm",
|
"dcm",
|
||||||
"dcr",
|
"dcr",
|
||||||
"dcx",
|
"dcx",
|
||||||
"dng",
|
"dng",
|
||||||
"dpx",
|
"dpx",
|
||||||
"epdf",
|
"epdf",
|
||||||
"epi",
|
"epi",
|
||||||
"eps",
|
"eps",
|
||||||
"epsf",
|
"epsf",
|
||||||
"epsi",
|
"epsi",
|
||||||
"ept",
|
"ept",
|
||||||
"ept2",
|
"ept2",
|
||||||
"ept3",
|
"ept3",
|
||||||
"erf",
|
"erf",
|
||||||
"exif",
|
"exif",
|
||||||
"fax",
|
"fax",
|
||||||
"file",
|
"file",
|
||||||
"fits",
|
"fits",
|
||||||
"fractal",
|
"fractal",
|
||||||
"ftp",
|
"ftp",
|
||||||
"g",
|
"g",
|
||||||
"gif",
|
"gif",
|
||||||
"gif87",
|
"gif87",
|
||||||
"gradient",
|
"gradient",
|
||||||
"gray",
|
"gray",
|
||||||
"graya",
|
"graya",
|
||||||
"heic",
|
"heic",
|
||||||
"heif",
|
"heif",
|
||||||
"hrz",
|
"hrz",
|
||||||
"http",
|
"http",
|
||||||
"icb",
|
"icb",
|
||||||
"icc",
|
"icc",
|
||||||
"icm",
|
"icm",
|
||||||
"ico",
|
"ico",
|
||||||
"icon",
|
"icon",
|
||||||
"identity",
|
"identity",
|
||||||
"image",
|
"image",
|
||||||
"iptc",
|
"iptc",
|
||||||
"iptctext",
|
"iptctext",
|
||||||
"iptcwtext",
|
"iptcwtext",
|
||||||
"jbg",
|
"jbg",
|
||||||
"jbig",
|
"jbig",
|
||||||
"jng",
|
"jng",
|
||||||
"jnx",
|
"jnx",
|
||||||
"jpeg",
|
"jpeg",
|
||||||
"jpg",
|
"jpg",
|
||||||
"k",
|
"k",
|
||||||
"k25",
|
"k25",
|
||||||
"kdc",
|
"kdc",
|
||||||
"label",
|
"label",
|
||||||
"m",
|
"m",
|
||||||
"mac",
|
"mac",
|
||||||
"map",
|
"map",
|
||||||
"mat",
|
"mat",
|
||||||
"mef",
|
"mef",
|
||||||
"miff",
|
"miff",
|
||||||
"mng",
|
"mng",
|
||||||
"mono",
|
"mono",
|
||||||
"mpc",
|
"mpc",
|
||||||
"mrw",
|
"mrw",
|
||||||
"msl",
|
"msl",
|
||||||
"mtv",
|
"mtv",
|
||||||
"mvg",
|
"mvg",
|
||||||
"nef",
|
"nef",
|
||||||
"null",
|
"null",
|
||||||
"o",
|
"o",
|
||||||
"orf",
|
"orf",
|
||||||
"otb",
|
"otb",
|
||||||
"p7",
|
"p7",
|
||||||
"pal",
|
"pal",
|
||||||
"palm",
|
"palm",
|
||||||
"pam",
|
"pam",
|
||||||
"pbm",
|
"pbm",
|
||||||
"pcd",
|
"pcd",
|
||||||
"pcds",
|
"pcds",
|
||||||
"pct",
|
"pct",
|
||||||
"pcx",
|
"pcx",
|
||||||
"pdb",
|
"pdb",
|
||||||
"pdf",
|
"pdf",
|
||||||
"pef",
|
"pef",
|
||||||
"pfa",
|
"pfa",
|
||||||
"pfb",
|
"pfb",
|
||||||
"pgm",
|
"pgm",
|
||||||
"picon",
|
"picon",
|
||||||
"pict",
|
"pict",
|
||||||
"pix",
|
"pix",
|
||||||
"plasma",
|
"plasma",
|
||||||
"png",
|
"png",
|
||||||
"png00",
|
"png00",
|
||||||
"png24",
|
"png24",
|
||||||
"png32",
|
"png32",
|
||||||
"png48",
|
"png48",
|
||||||
"png64",
|
"png64",
|
||||||
"png8",
|
"png8",
|
||||||
"pnm",
|
"pnm",
|
||||||
"ppm",
|
"ppm",
|
||||||
"ps",
|
"ps",
|
||||||
"ptif",
|
"ptif",
|
||||||
"pwp",
|
"pwp",
|
||||||
"r",
|
"r",
|
||||||
"raf",
|
"raf",
|
||||||
"ras",
|
"ras",
|
||||||
"rgb",
|
"rgb",
|
||||||
"rgba",
|
"rgba",
|
||||||
"rla",
|
"rla",
|
||||||
"rle",
|
"rle",
|
||||||
"sct",
|
"sct",
|
||||||
"sfw",
|
"sfw",
|
||||||
"sgi",
|
"sgi",
|
||||||
"sr2",
|
"sr2",
|
||||||
"srf",
|
"srf",
|
||||||
"stegano",
|
"stegano",
|
||||||
"sun",
|
"sun",
|
||||||
"svg",
|
"svg",
|
||||||
"svgz",
|
"svgz",
|
||||||
"text",
|
"text",
|
||||||
"tga",
|
"tga",
|
||||||
"tiff",
|
"tif",
|
||||||
"tile",
|
"tiff",
|
||||||
"tim",
|
"tile",
|
||||||
"topol",
|
"tim",
|
||||||
"ttf",
|
"topol",
|
||||||
"txt",
|
"ttf",
|
||||||
"uyvy",
|
"txt",
|
||||||
"vda",
|
"uyvy",
|
||||||
"vicar",
|
"vda",
|
||||||
"vid",
|
"vicar",
|
||||||
"viff",
|
"vid",
|
||||||
"vst",
|
"viff",
|
||||||
"wbmp",
|
"vst",
|
||||||
"webp",
|
"wbmp",
|
||||||
"wmf",
|
"webp",
|
||||||
"wpg",
|
"wmf",
|
||||||
"x3f",
|
"wpg",
|
||||||
"xbm",
|
"x3f",
|
||||||
"xc",
|
"xbm",
|
||||||
"xcf",
|
"xc",
|
||||||
"xmp",
|
"xcf",
|
||||||
"xpm",
|
"xmp",
|
||||||
"xv",
|
"xpm",
|
||||||
"xwd",
|
"xv",
|
||||||
"y",
|
"xwd",
|
||||||
"yuv",
|
"y",
|
||||||
],
|
"yuv",
|
||||||
},
|
],
|
||||||
to: {
|
},
|
||||||
image: [
|
to: {
|
||||||
"8bim",
|
image: [
|
||||||
"8bimtext",
|
"8bim",
|
||||||
"8bimwtext",
|
"8bimtext",
|
||||||
"app1",
|
"8bimwtext",
|
||||||
"app1jpeg",
|
"app1",
|
||||||
"art",
|
"app1jpeg",
|
||||||
"avs",
|
"art",
|
||||||
"b",
|
"avs",
|
||||||
"bie",
|
"b",
|
||||||
"bigtiff",
|
"bie",
|
||||||
"bmp",
|
"bigtiff",
|
||||||
"bmp2",
|
"bmp",
|
||||||
"bmp3",
|
"bmp2",
|
||||||
"brf",
|
"bmp3",
|
||||||
"c",
|
"brf",
|
||||||
"cals",
|
"c",
|
||||||
"cin",
|
"cals",
|
||||||
"cmyk",
|
"cin",
|
||||||
"cmyka",
|
"cmyk",
|
||||||
"dcx",
|
"cmyka",
|
||||||
"dpx",
|
"dcx",
|
||||||
"epdf",
|
"dpx",
|
||||||
"epi",
|
"epdf",
|
||||||
"eps",
|
"epi",
|
||||||
"eps2",
|
"eps",
|
||||||
"eps3",
|
"eps2",
|
||||||
"epsf",
|
"eps3",
|
||||||
"epsi",
|
"epsf",
|
||||||
"ept",
|
"epsi",
|
||||||
"ept2",
|
"ept",
|
||||||
"ept3",
|
"ept2",
|
||||||
"exif",
|
"ept3",
|
||||||
"fax",
|
"exif",
|
||||||
"fits",
|
"fax",
|
||||||
"g",
|
"fits",
|
||||||
"gif",
|
"g",
|
||||||
"gif87",
|
"gif",
|
||||||
"gray",
|
"gif87",
|
||||||
"graya",
|
"gray",
|
||||||
"histogram",
|
"graya",
|
||||||
"html",
|
"histogram",
|
||||||
"icb",
|
"html",
|
||||||
"icc",
|
"icb",
|
||||||
"icm",
|
"icc",
|
||||||
"info",
|
"icm",
|
||||||
"iptc",
|
"info",
|
||||||
"iptctext",
|
"iptc",
|
||||||
"iptcwtext",
|
"iptctext",
|
||||||
"isobrl",
|
"iptcwtext",
|
||||||
"isobrl6",
|
"isobrl",
|
||||||
"jbg",
|
"isobrl6",
|
||||||
"jbig",
|
"jbg",
|
||||||
"jng",
|
"jbig",
|
||||||
"jpeg",
|
"jng",
|
||||||
"jpg",
|
"jpeg",
|
||||||
"k",
|
"k",
|
||||||
"m",
|
"m",
|
||||||
"m2v",
|
"m2v",
|
||||||
"map",
|
"map",
|
||||||
"mat",
|
"mat",
|
||||||
"matte",
|
"matte",
|
||||||
"miff",
|
"miff",
|
||||||
"mng",
|
"mng",
|
||||||
"mono",
|
"mono",
|
||||||
"mpc",
|
"mpc",
|
||||||
"mpeg",
|
"mpeg",
|
||||||
"mpg",
|
"mpg",
|
||||||
"msl",
|
"msl",
|
||||||
"mtv",
|
"mtv",
|
||||||
"mvg",
|
"mvg",
|
||||||
"null",
|
"null",
|
||||||
"o",
|
"o",
|
||||||
"otb",
|
"otb",
|
||||||
"p7",
|
"p7",
|
||||||
"pal",
|
"pal",
|
||||||
"pam",
|
"pam",
|
||||||
"pbm",
|
"pbm",
|
||||||
"pcd",
|
"pcd",
|
||||||
"pcds",
|
"pcds",
|
||||||
"pcl",
|
"pcl",
|
||||||
"pct",
|
"pct",
|
||||||
"pcx",
|
"pcx",
|
||||||
"pdb",
|
"pdb",
|
||||||
"pdf",
|
"pdf",
|
||||||
"pgm",
|
"pgm",
|
||||||
"picon",
|
"picon",
|
||||||
"pict",
|
"pict",
|
||||||
"png",
|
"png",
|
||||||
"png00",
|
"png00",
|
||||||
"png24",
|
"png24",
|
||||||
"png32",
|
"png32",
|
||||||
"png48",
|
"png48",
|
||||||
"png64",
|
"png64",
|
||||||
"png8",
|
"png8",
|
||||||
"pnm",
|
"pnm",
|
||||||
"ppm",
|
"ppm",
|
||||||
"preview",
|
"preview",
|
||||||
"ps",
|
"ps",
|
||||||
"ps2",
|
"ps2",
|
||||||
"ps3",
|
"ps3",
|
||||||
"ptif",
|
"ptif",
|
||||||
"r",
|
"r",
|
||||||
"ras",
|
"ras",
|
||||||
"rgb",
|
"rgb",
|
||||||
"rgba",
|
"rgba",
|
||||||
"sgi",
|
"sgi",
|
||||||
"shtml",
|
"shtml",
|
||||||
"sun",
|
"sun",
|
||||||
"text",
|
"text",
|
||||||
"tga",
|
"tga",
|
||||||
"tiff",
|
"tiff",
|
||||||
"txt",
|
"txt",
|
||||||
"ubrl",
|
"ubrl",
|
||||||
"ubrl6",
|
"ubrl6",
|
||||||
"uil",
|
"uil",
|
||||||
"uyvy",
|
"uyvy",
|
||||||
"vda",
|
"vda",
|
||||||
"vicar",
|
"vicar",
|
||||||
"vid",
|
"vid",
|
||||||
"viff",
|
"viff",
|
||||||
"vst",
|
"vst",
|
||||||
"wbmp",
|
"wbmp",
|
||||||
"webp",
|
"webp",
|
||||||
"x",
|
"x",
|
||||||
"xbm",
|
"xbm",
|
||||||
"xmp",
|
"xmp",
|
||||||
"xpm",
|
"xpm",
|
||||||
"xv",
|
"xv",
|
||||||
"xwd",
|
"xwd",
|
||||||
"y",
|
"y",
|
||||||
"yuv",
|
"yuv",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
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>
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
options?: any,
|
options?: unknown,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
exec(
|
execFile(
|
||||||
`gm convert "${filePath}" "${targetPath}"`,
|
"gm",
|
||||||
(error, stdout, stderr) => {
|
["convert", filePath, targetPath],
|
||||||
if (error) {
|
(error, stdout, stderr) => {
|
||||||
reject(`error: ${error}`);
|
if (error) {
|
||||||
}
|
reject(`error: ${error}`);
|
||||||
|
}
|
||||||
if (stdout) {
|
|
||||||
console.log(`stdout: ${stdout}`);
|
if (stdout) {
|
||||||
}
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
if (stderr) {
|
|
||||||
console.error(`stderr: ${stderr}`);
|
if (stderr) {
|
||||||
}
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
resolve("success");
|
|
||||||
},
|
resolve("Done");
|
||||||
);
|
},
|
||||||
});
|
);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|||||||
59
src/converters/inkscape.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
|
export const properties = {
|
||||||
|
from: {
|
||||||
|
images: ["svg", "pdf", "eps", "ps", "wmf", "emf", "png"],
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
images: [
|
||||||
|
"dxf",
|
||||||
|
"emf",
|
||||||
|
"eps",
|
||||||
|
"fxg",
|
||||||
|
"gpl",
|
||||||
|
"hpgl",
|
||||||
|
"html",
|
||||||
|
"odg",
|
||||||
|
"pdf",
|
||||||
|
"png",
|
||||||
|
"pov",
|
||||||
|
"ps",
|
||||||
|
"sif",
|
||||||
|
"svg",
|
||||||
|
"svgz",
|
||||||
|
"tex",
|
||||||
|
"wmf",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function convert(
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
convertTo: string,
|
||||||
|
targetPath: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
options?: unknown,
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execFile(
|
||||||
|
"inkscape",
|
||||||
|
[filePath, "-o", targetPath],
|
||||||
|
(error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
reject(`error: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
53
src/converters/libheif.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { execFile } from "child_process";
|
||||||
|
|
||||||
|
export const properties = {
|
||||||
|
from: {
|
||||||
|
images: [
|
||||||
|
"avci",
|
||||||
|
"avcs",
|
||||||
|
"avif",
|
||||||
|
"h264",
|
||||||
|
"heic",
|
||||||
|
"heics",
|
||||||
|
"heif",
|
||||||
|
"heifs",
|
||||||
|
"hif",
|
||||||
|
"mkv",
|
||||||
|
"mp4",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
images: ["jpeg", "png", "y4m"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function convert(
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
convertTo: string,
|
||||||
|
targetPath: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
options?: unknown,
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execFile(
|
||||||
|
"heif-convert",
|
||||||
|
[filePath, targetPath],
|
||||||
|
(error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
reject(`error: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
71
src/converters/libjxl.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
|
// declare possible conversions
|
||||||
|
export const properties = {
|
||||||
|
from: {
|
||||||
|
jxl: ["jxl"],
|
||||||
|
images: [
|
||||||
|
"apng",
|
||||||
|
"exr",
|
||||||
|
"gif",
|
||||||
|
"jpeg",
|
||||||
|
"pam",
|
||||||
|
"pfm",
|
||||||
|
"pgm",
|
||||||
|
"pgx",
|
||||||
|
"png",
|
||||||
|
"ppm",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
jxl: [
|
||||||
|
"apng",
|
||||||
|
"exr",
|
||||||
|
"gif",
|
||||||
|
"jpeg",
|
||||||
|
"pam",
|
||||||
|
"pfm",
|
||||||
|
"pgm",
|
||||||
|
"pgx",
|
||||||
|
"png",
|
||||||
|
"ppm",
|
||||||
|
],
|
||||||
|
images: ["jxl"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function convert(
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
convertTo: string,
|
||||||
|
targetPath: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
options?: unknown,
|
||||||
|
): Promise<string> {
|
||||||
|
let tool = "";
|
||||||
|
if (fileType === "jxl") {
|
||||||
|
tool = "djxl";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convertTo === "jxl") {
|
||||||
|
tool = "cjxl";
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execFile(tool, [filePath, targetPath], (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
reject(`error: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,63 +1,72 @@
|
|||||||
import { convert as convertImage, properties as propertiesImage } from "./vips";
|
|
||||||
|
|
||||||
import {
|
|
||||||
convert as convertPandoc,
|
|
||||||
properties as propertiesPandoc,
|
|
||||||
} from "./pandoc";
|
|
||||||
|
|
||||||
import {
|
|
||||||
convert as convertFFmpeg,
|
|
||||||
properties as propertiesFFmpeg,
|
|
||||||
} from "./ffmpeg";
|
|
||||||
|
|
||||||
import {
|
|
||||||
convert as convertGraphicsmagick,
|
|
||||||
properties as propertiesGraphicsmagick,
|
|
||||||
} from "./graphicsmagick";
|
|
||||||
|
|
||||||
import {
|
|
||||||
convert as convertPdflatex,
|
|
||||||
properties as propertiesPdflatex,
|
|
||||||
} from "./pdflatex";
|
|
||||||
|
|
||||||
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
||||||
|
import { convert as convertassimp, properties as propertiesassimp } from "./assimp";
|
||||||
|
import { convert as convertFFmpeg, properties as propertiesFFmpeg } from "./ffmpeg";
|
||||||
|
import { convert as convertGraphicsmagick, properties as propertiesGraphicsmagick } from "./graphicsmagick";
|
||||||
|
import { convert as convertInkscape, properties as propertiesInkscape } from "./inkscape";
|
||||||
|
import { convert as convertLibjxl, properties as propertiesLibjxl } from "./libjxl";
|
||||||
|
import { convert as convertPandoc, properties as propertiesPandoc } from "./pandoc";
|
||||||
|
import { convert as convertresvg, properties as propertiesresvg } from "./resvg";
|
||||||
|
import { convert as convertImage, properties as propertiesImage } from "./vips";
|
||||||
|
import { convert as convertxelatex, properties as propertiesxelatex } from "./xelatex";
|
||||||
|
// import { convert as convertCalibre, properties as propertiesCalibre } from "./calibre";
|
||||||
|
import { convert as convertLibheif, properties as propertiesLibheif } from "./libheif";
|
||||||
|
import { convert as convertpotrace, properties as propertiespotrace } from "./potrace";
|
||||||
|
|
||||||
|
|
||||||
// This should probably be reconstructed so that the functions are not imported instead the functions hook into this to make the converters more modular
|
// This should probably be reconstructed so that the functions are not imported instead the functions hook into this to make the converters more modular
|
||||||
|
|
||||||
const properties: {
|
const properties: Record<
|
||||||
[key: string]: {
|
string,
|
||||||
|
{
|
||||||
properties: {
|
properties: {
|
||||||
from: { [key: string]: string[] };
|
from: Record<string, string[]>;
|
||||||
to: { [key: string]: string[] };
|
to: Record<string, string[]>;
|
||||||
options?: {
|
options?: Record<
|
||||||
[key: string]: {
|
string,
|
||||||
[key: string]: {
|
Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
description: string;
|
description: string;
|
||||||
type: string;
|
type: string;
|
||||||
default: number;
|
default: number;
|
||||||
};
|
}
|
||||||
};
|
>
|
||||||
};
|
>;
|
||||||
};
|
};
|
||||||
converter: (
|
converter: (
|
||||||
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?: unknown,
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
) => unknown;
|
||||||
) => any;
|
}
|
||||||
};
|
> = {
|
||||||
} = {
|
libjxl: {
|
||||||
|
properties: propertiesLibjxl,
|
||||||
|
converter: convertLibjxl,
|
||||||
|
},
|
||||||
|
resvg: {
|
||||||
|
properties: propertiesresvg,
|
||||||
|
converter: convertresvg,
|
||||||
|
},
|
||||||
vips: {
|
vips: {
|
||||||
properties: propertiesImage,
|
properties: propertiesImage,
|
||||||
converter: convertImage,
|
converter: convertImage,
|
||||||
},
|
},
|
||||||
pdflatex: {
|
libheif: {
|
||||||
properties: propertiesPdflatex,
|
properties: propertiesLibheif,
|
||||||
converter: convertPdflatex,
|
converter: convertLibheif,
|
||||||
},
|
},
|
||||||
|
xelatex: {
|
||||||
|
properties: propertiesxelatex,
|
||||||
|
converter: convertxelatex,
|
||||||
|
},
|
||||||
|
// calibre: {
|
||||||
|
// properties: propertiesCalibre,
|
||||||
|
// converter: convertCalibre,
|
||||||
|
// },
|
||||||
pandoc: {
|
pandoc: {
|
||||||
properties: propertiesPandoc,
|
properties: propertiesPandoc,
|
||||||
converter: convertPandoc,
|
converter: convertPandoc,
|
||||||
@@ -66,33 +75,40 @@ const properties: {
|
|||||||
properties: propertiesGraphicsmagick,
|
properties: propertiesGraphicsmagick,
|
||||||
converter: convertGraphicsmagick,
|
converter: convertGraphicsmagick,
|
||||||
},
|
},
|
||||||
|
inkscape: {
|
||||||
|
properties: propertiesInkscape,
|
||||||
|
converter: convertInkscape,
|
||||||
|
},
|
||||||
|
assimp: {
|
||||||
|
properties: propertiesassimp,
|
||||||
|
converter: convertassimp,
|
||||||
|
},
|
||||||
ffmpeg: {
|
ffmpeg: {
|
||||||
properties: propertiesFFmpeg,
|
properties: propertiesFFmpeg,
|
||||||
converter: convertFFmpeg,
|
converter: convertFFmpeg,
|
||||||
},
|
},
|
||||||
|
potrace: {
|
||||||
|
properties: propertiespotrace,
|
||||||
|
converter: convertpotrace,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function mainConverter(
|
export async function mainConverter(
|
||||||
inputFilePath: string,
|
inputFilePath: string,
|
||||||
fileTypeOriginal: string,
|
fileTypeOriginal: string,
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
convertTo: string,
|
||||||
convertTo: any,
|
|
||||||
targetPath: string,
|
targetPath: string,
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
options?: unknown,
|
||||||
options?: any,
|
|
||||||
converterName?: string,
|
converterName?: string,
|
||||||
) {
|
) {
|
||||||
const fileType = normalizeFiletype(fileTypeOriginal);
|
const fileType = normalizeFiletype(fileTypeOriginal);
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
let converterFunc: typeof properties["libjxl"]["converter"] | undefined;
|
||||||
let converterFunc: any;
|
|
||||||
// let converterName = converterName;
|
|
||||||
|
|
||||||
if (converterName) {
|
if (converterName) {
|
||||||
converterFunc = properties[converterName]?.converter;
|
converterFunc = properties[converterName]?.converter;
|
||||||
} else {
|
} else {
|
||||||
// Iterate over each converter in properties
|
// Iterate over each converter in properties
|
||||||
// biome-ignore lint/style/noParameterAssign: <explanation>
|
|
||||||
for (converterName in properties) {
|
for (converterName in properties) {
|
||||||
const converterObj = properties[converterName];
|
const converterObj = properties[converterName];
|
||||||
|
|
||||||
@@ -102,9 +118,8 @@ export async function mainConverter(
|
|||||||
|
|
||||||
for (const key in converterObj.properties.from) {
|
for (const key in converterObj.properties.from) {
|
||||||
if (
|
if (
|
||||||
// HOW??
|
converterObj?.properties?.from[key]?.includes(fileType) &&
|
||||||
converterObj.properties.from[key].includes(fileType) &&
|
converterObj?.properties?.to[key]?.includes(convertTo)
|
||||||
converterObj.properties.to[key].includes(convertTo)
|
|
||||||
) {
|
) {
|
||||||
converterFunc = converterObj.converter;
|
converterFunc = converterObj.converter;
|
||||||
break;
|
break;
|
||||||
@@ -121,7 +136,7 @@ export async function mainConverter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await converterFunc(
|
const result = await converterFunc(
|
||||||
inputFilePath,
|
inputFilePath,
|
||||||
fileType,
|
fileType,
|
||||||
convertTo,
|
convertTo,
|
||||||
@@ -131,7 +146,13 @@ export async function mainConverter(
|
|||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully using ${converterName}.`,
|
`Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully using ${converterName}.`,
|
||||||
|
result,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (typeof result === "string") {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
return "Done";
|
return "Done";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -142,7 +163,7 @@ export async function mainConverter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const possibleTargets: { [key: string]: { [key: string]: string[] } } = {};
|
const possibleTargets: Record<string, Record<string, string[]>> = {};
|
||||||
|
|
||||||
for (const converterName in properties) {
|
for (const converterName in properties) {
|
||||||
const converterProperties = properties[converterName]?.properties;
|
const converterProperties = properties[converterName]?.properties;
|
||||||
@@ -167,9 +188,7 @@ for (const converterName in properties) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPossibleTargets = (
|
export const getPossibleTargets = (from: string): Record<string, string[]> => {
|
||||||
from: string,
|
|
||||||
): { [key: string]: string[] } => {
|
|
||||||
const fromClean = normalizeFiletype(from);
|
const fromClean = normalizeFiletype(from);
|
||||||
|
|
||||||
return possibleTargets[fromClean] || {};
|
return possibleTargets[fromClean] || {};
|
||||||
@@ -193,11 +212,12 @@ for (const converterName in properties) {
|
|||||||
}
|
}
|
||||||
possibleInputs.sort();
|
possibleInputs.sort();
|
||||||
|
|
||||||
export const getPossibleInputs = () => {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const getPossibleInputs = () => {
|
||||||
return possibleInputs;
|
return possibleInputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
const allTargets: { [key: string]: string[] } = {};
|
const allTargets: Record<string, string[]> = {};
|
||||||
|
|
||||||
for (const converterName in properties) {
|
for (const converterName in properties) {
|
||||||
const converterProperties = properties[converterName]?.properties;
|
const converterProperties = properties[converterName]?.properties;
|
||||||
@@ -208,9 +228,9 @@ for (const converterName in properties) {
|
|||||||
|
|
||||||
for (const key in converterProperties.to) {
|
for (const key in converterProperties.to) {
|
||||||
if (allTargets[converterName]) {
|
if (allTargets[converterName]) {
|
||||||
allTargets[converterName].push(...converterProperties.to[key]);
|
allTargets[converterName].push(...(converterProperties.to[key] || []));
|
||||||
} else {
|
} else {
|
||||||
allTargets[converterName] = converterProperties.to[key];
|
allTargets[converterName] = converterProperties.to[key] || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,7 +239,7 @@ export const getAllTargets = () => {
|
|||||||
return allTargets;
|
return allTargets;
|
||||||
};
|
};
|
||||||
|
|
||||||
const allInputs: { [key: string]: string[] } = {};
|
const allInputs: Record<string, string[]> = {};
|
||||||
for (const converterName in properties) {
|
for (const converterName in properties) {
|
||||||
const converterProperties = properties[converterName]?.properties;
|
const converterProperties = properties[converterName]?.properties;
|
||||||
|
|
||||||
@@ -229,9 +249,9 @@ for (const converterName in properties) {
|
|||||||
|
|
||||||
for (const key in converterProperties.from) {
|
for (const key in converterProperties.from) {
|
||||||
if (allInputs[converterName]) {
|
if (allInputs[converterName]) {
|
||||||
allInputs[converterName].push(...converterProperties.from[key]);
|
allInputs[converterName].push(...(converterProperties.from[key] || []));
|
||||||
} else {
|
} else {
|
||||||
allInputs[converterName] = converterProperties.from[key];
|
allInputs[converterName] = converterProperties.from[key] || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,4 +284,4 @@ export const getAllInputs = (converter: string) => {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// // print the number of unique Inputs and Outputs
|
// // print the number of unique Inputs and Outputs
|
||||||
// console.log(`Unique Formats: ${uniqueFormats.size}`);
|
// console.log(`Unique Formats: ${uniqueFormats.size}`);
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
import sharp from "sharp";
|
|
||||||
import type { FormatEnum } from "sharp";
|
|
||||||
|
|
||||||
// declare possible conversions
|
|
||||||
export const properties = {
|
|
||||||
from: {
|
|
||||||
images: [
|
|
||||||
"avif",
|
|
||||||
"bif",
|
|
||||||
"csv",
|
|
||||||
"exr",
|
|
||||||
"fits",
|
|
||||||
"gif",
|
|
||||||
"hdr.gz",
|
|
||||||
"hdr",
|
|
||||||
"heic",
|
|
||||||
"heif",
|
|
||||||
"img.gz",
|
|
||||||
"img",
|
|
||||||
"j2c",
|
|
||||||
"j2k",
|
|
||||||
"jp2",
|
|
||||||
"jpeg",
|
|
||||||
"jpx",
|
|
||||||
"jxl",
|
|
||||||
"mat",
|
|
||||||
"mrxs",
|
|
||||||
"ndpi",
|
|
||||||
"nia.gz",
|
|
||||||
"nia",
|
|
||||||
"nii.gz",
|
|
||||||
"nii",
|
|
||||||
"pdf",
|
|
||||||
"pfm",
|
|
||||||
"pgm",
|
|
||||||
"pic",
|
|
||||||
"png",
|
|
||||||
"ppm",
|
|
||||||
"raw",
|
|
||||||
"scn",
|
|
||||||
"svg",
|
|
||||||
"svs",
|
|
||||||
"svslide",
|
|
||||||
"szi",
|
|
||||||
"tif",
|
|
||||||
"tiff",
|
|
||||||
"v",
|
|
||||||
"vips",
|
|
||||||
"vms",
|
|
||||||
"vmu",
|
|
||||||
"webp",
|
|
||||||
"zip",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
images: [
|
|
||||||
"avif",
|
|
||||||
"dzi",
|
|
||||||
"fits",
|
|
||||||
"gif",
|
|
||||||
"hdr.gz",
|
|
||||||
"heic",
|
|
||||||
"heif",
|
|
||||||
"img.gz",
|
|
||||||
"j2c",
|
|
||||||
"j2k",
|
|
||||||
"jp2",
|
|
||||||
"jpeg",
|
|
||||||
"jpx",
|
|
||||||
"jxl",
|
|
||||||
"mat",
|
|
||||||
"nia.gz",
|
|
||||||
"nia",
|
|
||||||
"nii.gz",
|
|
||||||
"nii",
|
|
||||||
"png",
|
|
||||||
"tiff",
|
|
||||||
"vips",
|
|
||||||
"webp",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
svg: {
|
|
||||||
scale: {
|
|
||||||
description: "Scale the image up or down",
|
|
||||||
type: "number",
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
if (!metadata || !metadata.width || !metadata.height) {
|
|
||||||
throw new Error("Could not get metadata from image");
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await sharp(filePath).toFormat(convertTo).toFile(targetPath);
|
|
||||||
}
|
|
||||||
@@ -1,150 +1,162 @@
|
|||||||
import { exec } from "node:child_process";
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
export const properties = {
|
export const properties = {
|
||||||
from: {
|
from: {
|
||||||
text: [
|
text: [
|
||||||
"textile",
|
"textile",
|
||||||
"tikiwiki",
|
"tikiwiki",
|
||||||
"tsv",
|
"tsv",
|
||||||
"twiki",
|
"twiki",
|
||||||
"typst",
|
"typst",
|
||||||
"vimwiki",
|
"vimwiki",
|
||||||
"biblatex",
|
"biblatex",
|
||||||
"bibtex",
|
"bibtex",
|
||||||
"bits",
|
"bits",
|
||||||
"commonmark",
|
"commonmark",
|
||||||
"commonmark_x",
|
"commonmark_x",
|
||||||
"creole",
|
"creole",
|
||||||
"csljson",
|
"csljson",
|
||||||
"csv",
|
"csv",
|
||||||
"djot",
|
"djot",
|
||||||
"docbook",
|
"docbook",
|
||||||
"docx",
|
"docx",
|
||||||
"dokuwiki",
|
"dokuwiki",
|
||||||
"endnotexml",
|
"endnotexml",
|
||||||
"epub",
|
"epub",
|
||||||
"fb2",
|
"fb2",
|
||||||
"gfm",
|
"gfm",
|
||||||
"haddock",
|
"haddock",
|
||||||
"html",
|
"html",
|
||||||
"ipynb",
|
"ipynb",
|
||||||
"jats",
|
"jats",
|
||||||
"jira",
|
"jira",
|
||||||
"json",
|
"json",
|
||||||
"latex",
|
"latex",
|
||||||
"man",
|
"man",
|
||||||
"markdown",
|
"markdown",
|
||||||
"markdown_mmd",
|
"markdown_mmd",
|
||||||
"markdown_phpextra",
|
"markdown_phpextra",
|
||||||
"markdown_strict",
|
"markdown_strict",
|
||||||
"mediawiki",
|
"mediawiki",
|
||||||
"muse",
|
"muse",
|
||||||
"pandoc native",
|
"pandoc native",
|
||||||
"opml",
|
"opml",
|
||||||
"org",
|
"org",
|
||||||
"ris",
|
"ris",
|
||||||
"rst",
|
"rst",
|
||||||
"rtf",
|
"rtf",
|
||||||
"t2t",
|
"t2t",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
text: [
|
text: [
|
||||||
"tei",
|
"tei",
|
||||||
"texinfo",
|
"texinfo",
|
||||||
"textile",
|
"textile",
|
||||||
"typst",
|
"typst",
|
||||||
"xwiki",
|
"xwiki",
|
||||||
"zimwiki",
|
"zimwiki",
|
||||||
"asciidoc",
|
"asciidoc",
|
||||||
"asciidoc_legacy",
|
"asciidoc_legacy",
|
||||||
"asciidoctor",
|
"asciidoctor",
|
||||||
"beamer",
|
"beamer",
|
||||||
"biblatex",
|
"biblatex",
|
||||||
"bibtex",
|
"bibtex",
|
||||||
"chunkedhtml",
|
"chunkedhtml",
|
||||||
"commonmark",
|
"commonmark",
|
||||||
"commonmark_x",
|
"commonmark_x",
|
||||||
"context",
|
"context",
|
||||||
"csljson",
|
"csljson",
|
||||||
"djot",
|
"djot",
|
||||||
"docbook",
|
"docbook",
|
||||||
"docbook4",
|
"docbook4",
|
||||||
"docbook5",
|
"docbook5",
|
||||||
"docx",
|
"docx",
|
||||||
"dokuwiki",
|
"dokuwiki",
|
||||||
"dzslides",
|
"dzslides",
|
||||||
"epub",
|
"epub",
|
||||||
"epub2",
|
"epub2",
|
||||||
"epub3",
|
"epub3",
|
||||||
"fb2",
|
"fb2",
|
||||||
"gfm",
|
"gfm",
|
||||||
"haddock",
|
"haddock",
|
||||||
"html",
|
"html",
|
||||||
"html4",
|
"html4",
|
||||||
"html5",
|
"html5",
|
||||||
"icml",
|
"icml",
|
||||||
"ipynb",
|
"ipynb",
|
||||||
"jats",
|
"jats",
|
||||||
"jats_archiving",
|
"jats_archiving",
|
||||||
"jats_articleauthoring",
|
"jats_articleauthoring",
|
||||||
"jats_publishing",
|
"jats_publishing",
|
||||||
"jira",
|
"jira",
|
||||||
"json",
|
"json",
|
||||||
"latex",
|
"latex",
|
||||||
"man",
|
"man",
|
||||||
"markdown",
|
"markdown",
|
||||||
"markdown_mmd",
|
"markdown_mmd",
|
||||||
"markdown_phpextra",
|
"markdown_phpextra",
|
||||||
"markdown_strict",
|
"markdown_strict",
|
||||||
"markua",
|
"markua",
|
||||||
"mediawiki",
|
"mediawiki",
|
||||||
"ms",
|
"ms",
|
||||||
"muse",
|
"muse",
|
||||||
"pandoc native",
|
"pandoc native",
|
||||||
"odt",
|
"odt",
|
||||||
"opendocument",
|
"opendocument",
|
||||||
"opml",
|
"opml",
|
||||||
"org",
|
"org",
|
||||||
"pdf",
|
"pdf",
|
||||||
"plain",
|
"plain",
|
||||||
"pptx",
|
"pptx",
|
||||||
"revealjs",
|
"revealjs",
|
||||||
"rst",
|
"rst",
|
||||||
"rtf",
|
"rtf",
|
||||||
"s5",
|
"s5",
|
||||||
"slideous",
|
"slideous",
|
||||||
"slidy",
|
"slidy",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
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>
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
options?: any,
|
options?: unknown,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
// set xelatex here
|
||||||
exec(
|
const xelatex = ["pdf", "latex"];
|
||||||
`pandoc "${filePath}" -f ${fileType} -t ${convertTo} -o "${targetPath}"`,
|
|
||||||
(error, stdout, stderr) => {
|
// Build arguments array
|
||||||
if (error) {
|
const args: string[] = [];
|
||||||
reject(`error: ${error}`);
|
|
||||||
}
|
if (xelatex.includes(convertTo)) {
|
||||||
|
args.push("--pdf-engine=xelatex");
|
||||||
if (stdout) {
|
}
|
||||||
console.log(`stdout: ${stdout}`);
|
|
||||||
}
|
args.push(filePath);
|
||||||
|
args.push("-f", fileType);
|
||||||
if (stderr) {
|
args.push("-t", convertTo);
|
||||||
console.error(`stderr: ${stderr}`);
|
args.push("-o", targetPath);
|
||||||
}
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
resolve("success");
|
execFile("pandoc", args, (error, stdout, stderr) => {
|
||||||
},
|
if (error) {
|
||||||
);
|
reject(`error: ${error}`);
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
37
src/converters/potrace.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
|
export const properties = {
|
||||||
|
from: {
|
||||||
|
images: ["pnm", "pbm", "pgm", "bmp"],
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
images: ["svg", "pdf", "pdfpage", "eps", "postscript", "ps", "dxf", "geojson", "pgm", "gimppath", "xfig"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function convert(
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
convertTo: string,
|
||||||
|
targetPath: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
options?: unknown,
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execFile("potrace", [filePath, "-o", targetPath, "-b", convertTo], (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
reject(`error: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
37
src/converters/resvg.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
|
export const properties = {
|
||||||
|
from: {
|
||||||
|
images: ["svg"],
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
images: ["png"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function convert(
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
convertTo: string,
|
||||||
|
targetPath: string,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
options?: unknown,
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
execFile("resvg", [filePath, targetPath], (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
reject(`error: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,134 +1,142 @@
|
|||||||
import { exec } from "node:child_process";
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
// declare possible conversions
|
// declare possible conversions
|
||||||
export const properties = {
|
export const properties = {
|
||||||
from: {
|
from: {
|
||||||
images: [
|
images: [
|
||||||
"avif",
|
"avif",
|
||||||
"bif",
|
"bif",
|
||||||
"csv",
|
"csv",
|
||||||
"exr",
|
"exr",
|
||||||
"fits",
|
"fits",
|
||||||
"gif",
|
"gif",
|
||||||
"hdr.gz",
|
"hdr.gz",
|
||||||
"hdr",
|
"hdr",
|
||||||
"heic",
|
"heic",
|
||||||
"heif",
|
"heif",
|
||||||
"img.gz",
|
"img.gz",
|
||||||
"img",
|
"img",
|
||||||
"j2c",
|
"j2c",
|
||||||
"j2k",
|
"j2k",
|
||||||
"jp2",
|
"jp2",
|
||||||
"jpeg",
|
"jpeg",
|
||||||
"jpx",
|
"jpx",
|
||||||
"jxl",
|
"jxl",
|
||||||
"mat",
|
"mat",
|
||||||
"mrxs",
|
"mrxs",
|
||||||
"ndpi",
|
"ndpi",
|
||||||
"nia.gz",
|
"nia.gz",
|
||||||
"nia",
|
"nia",
|
||||||
"nii.gz",
|
"nii.gz",
|
||||||
"nii",
|
"nii",
|
||||||
"pdf",
|
"pdf",
|
||||||
"pfm",
|
"pfm",
|
||||||
"pgm",
|
"pgm",
|
||||||
"pic",
|
"pic",
|
||||||
"png",
|
"png",
|
||||||
"ppm",
|
"ppm",
|
||||||
"raw",
|
"raw",
|
||||||
"scn",
|
"scn",
|
||||||
"svg",
|
"svg",
|
||||||
"svs",
|
"svs",
|
||||||
"svslide",
|
"svslide",
|
||||||
"szi",
|
"szi",
|
||||||
"tif",
|
"tif",
|
||||||
"tiff",
|
"tiff",
|
||||||
"v",
|
"v",
|
||||||
"vips",
|
"vips",
|
||||||
"vms",
|
"vms",
|
||||||
"vmu",
|
"vmu",
|
||||||
"webp",
|
"webp",
|
||||||
"zip",
|
"zip",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
images: [
|
images: [
|
||||||
"avif",
|
"avif",
|
||||||
"dzi",
|
"dzi",
|
||||||
"fits",
|
"fits",
|
||||||
"gif",
|
"gif",
|
||||||
"hdr.gz",
|
"hdr.gz",
|
||||||
"heic",
|
"heic",
|
||||||
"heif",
|
"heif",
|
||||||
"img.gz",
|
"img.gz",
|
||||||
"j2c",
|
"j2c",
|
||||||
"j2k",
|
"j2k",
|
||||||
"jp2",
|
"jp2",
|
||||||
"jpeg",
|
"jpeg",
|
||||||
"jpx",
|
"jpx",
|
||||||
"jxl",
|
"jxl",
|
||||||
"mat",
|
"mat",
|
||||||
"nia.gz",
|
"nia.gz",
|
||||||
"nia",
|
"nia",
|
||||||
"nii.gz",
|
"nii.gz",
|
||||||
"nii",
|
"nii",
|
||||||
"png",
|
"png",
|
||||||
"tiff",
|
"tiff",
|
||||||
"vips",
|
"vips",
|
||||||
"webp",
|
"webp",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
svg: {
|
svg: {
|
||||||
scale: {
|
scale: {
|
||||||
description: "Scale the image up or down",
|
description: "Scale the image up or down",
|
||||||
type: "number",
|
type: "number",
|
||||||
default: 1,
|
default: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
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>
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
options?: any,
|
options?: unknown,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
// 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();
|
||||||
|
|
||||||
// if (!metadata || !metadata.width || !metadata.height) {
|
// if (!metadata || !metadata.width || !metadata.height) {
|
||||||
// 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)
|
||||||
// .toFormat(convertTo)
|
// .toFormat(convertTo)
|
||||||
// .toFile(targetPath);
|
// .toFile(targetPath);
|
||||||
// }
|
// }
|
||||||
|
let action = "copy";
|
||||||
return new Promise((resolve, reject) => {
|
if (fileType === "pdf") {
|
||||||
exec(`vips copy "${filePath}" "${targetPath}"`, (error, stdout, stderr) => {
|
action = "pdfload";
|
||||||
if (error) {
|
}
|
||||||
reject(`error: ${error}`);
|
|
||||||
}
|
return new Promise((resolve, reject) => {
|
||||||
|
execFile(
|
||||||
if (stdout) {
|
"vips",
|
||||||
console.log(`stdout: ${stdout}`);
|
[action, filePath, targetPath],
|
||||||
}
|
(error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
if (stderr) {
|
reject(`error: ${error}`);
|
||||||
console.error(`stderr: ${stderr}`);
|
}
|
||||||
}
|
|
||||||
|
if (stdout) {
|
||||||
resolve("success");
|
console.log(`stdout: ${stdout}`);
|
||||||
});
|
}
|
||||||
});
|
|
||||||
}
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,42 +1,53 @@
|
|||||||
import { exec } from "node:child_process";
|
import { execFile } from "node:child_process";
|
||||||
|
|
||||||
export const properties = {
|
export const properties = {
|
||||||
from: {
|
from: {
|
||||||
text: ["tex", "latex"],
|
text: ["tex", "latex"],
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
text: ["pdf"],
|
text: ["pdf"],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
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>
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
options?: any,
|
options?: unknown,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// const fileName: string = (targetPath.split("/").pop() as string).replace(".pdf", "")
|
// const fileName: string = (targetPath.split("/").pop() as string).replace(".pdf", "")
|
||||||
const outputPath = targetPath.split("/").slice(0, -1).join("/").replace("./", "")
|
const outputPath = targetPath
|
||||||
exec(
|
.split("/")
|
||||||
`pdflatex -interaction=nonstopmode -output-directory="${outputPath}" "${filePath}"`,
|
.slice(0, -1)
|
||||||
(error, stdout, stderr) => {
|
.join("/")
|
||||||
if (error) {
|
.replace("./", "");
|
||||||
reject(`error: ${error}`);
|
|
||||||
}
|
execFile(
|
||||||
|
"latexmk",
|
||||||
if (stdout) {
|
[
|
||||||
console.log(`stdout: ${stdout}`);
|
"-xelatex",
|
||||||
}
|
"-interaction=nonstopmode",
|
||||||
|
`-output-directory=${outputPath}`,
|
||||||
if (stderr) {
|
filePath,
|
||||||
console.error(`stderr: ${stderr}`);
|
],
|
||||||
}
|
(error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
resolve("success");
|
reject(`error: ${error}`);
|
||||||
},
|
}
|
||||||
);
|
|
||||||
});
|
if (stdout) {
|
||||||
}
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("Done");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,31 +1,37 @@
|
|||||||
export const normalizeFiletype = (filetype: string): string => {
|
export const normalizeFiletype = (filetype: string): string => {
|
||||||
const lowercaseFiletype = filetype.toLowerCase();
|
const lowercaseFiletype = filetype.toLowerCase();
|
||||||
|
|
||||||
switch (lowercaseFiletype) {
|
switch (lowercaseFiletype) {
|
||||||
case "jpg":
|
case "jfif":
|
||||||
return "jpeg";
|
case "jpg":
|
||||||
case "htm":
|
return "jpeg";
|
||||||
return "html";
|
case "htm":
|
||||||
case "tex":
|
return "html";
|
||||||
return "latex";
|
case "tex":
|
||||||
case "md":
|
return "latex";
|
||||||
return "markdown";
|
case "md":
|
||||||
default:
|
return "markdown";
|
||||||
return lowercaseFiletype;
|
case "unknown":
|
||||||
}
|
return "m4a";
|
||||||
};
|
default:
|
||||||
|
return lowercaseFiletype;
|
||||||
export const normalizeOutputFiletype = (filetype: string): string => {
|
}
|
||||||
const lowercaseFiletype = filetype.toLowerCase();
|
};
|
||||||
|
|
||||||
switch (lowercaseFiletype) {
|
export const normalizeOutputFiletype = (filetype: string): string => {
|
||||||
case "jpeg":
|
const lowercaseFiletype = filetype.toLowerCase();
|
||||||
return "jpg";
|
|
||||||
case "latex":
|
switch (lowercaseFiletype) {
|
||||||
return "tex";
|
case "jpeg":
|
||||||
case "markdown":
|
return "jpg";
|
||||||
return "md";
|
case "latex":
|
||||||
default:
|
return "tex";
|
||||||
return lowercaseFiletype;
|
case "markdown_phpextra":
|
||||||
}
|
case "markdown_strict":
|
||||||
};
|
case "markdown_mmd":
|
||||||
|
case "markdown":
|
||||||
|
return "md";
|
||||||
|
default:
|
||||||
|
return lowercaseFiletype;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
146
src/helpers/printVersions.ts
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import { exec } from "node:child_process";
|
||||||
|
import { version } from "../../package.json";
|
||||||
|
|
||||||
|
console.log(`ConvertX v${version}`);
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "production") {
|
||||||
|
exec("cat /etc/os-release", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Not running on docker, this is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split('PRETTY_NAME="')[1]?.split('"')[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("pandoc -v", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Pandoc is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("ffmpeg -version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("FFmpeg is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("vips -v", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Vips is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("gm version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("GraphicsMagick is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("inkscape --version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Inkscape is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("djxl --version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("libjxl-tools is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("xelatex -version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Tex Live with XeTeX is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("resvg -V", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("resvg is not installed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`resvg v${stdout.split("\n")[0]}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("assimp version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("assimp is not installed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`assimp ${stdout.split("\n")[5]}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("ebook-convert --version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("ebook-convert (calibre) is not installed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("heif-info -v", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("libheif is not installed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`libheif v${stdout.split("\n")[0]}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("potrace -v", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("potrace is not installed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("bun -v", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Bun is not installed. wait what");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`Bun v${stdout.split("\n")[0]}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
15
src/helpers/tailwind.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import tailwind from "@tailwindcss/postcss";
|
||||||
|
import postcss from "postcss";
|
||||||
|
|
||||||
|
export const generateTailwind = async () => {
|
||||||
|
const result = await Bun.file("./src/main.css")
|
||||||
|
.text()
|
||||||
|
.then((sourceText) => {
|
||||||
|
return postcss([tailwind]).process(sourceText, {
|
||||||
|
from: "./src/main.css",
|
||||||
|
to: "./public/generated.css",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
1421
src/index.tsx
78
src/main.css
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@plugin 'tailwind-scrollbar';
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--color-contrast: rgba(var(--contrast));
|
||||||
|
--color-neutral-900: rgba(var(--neutral-900));
|
||||||
|
--color-neutral-800: rgba(var(--neutral-800));
|
||||||
|
--color-neutral-700: rgba(var(--neutral-700));
|
||||||
|
--color-neutral-600: rgba(var(--neutral-600));
|
||||||
|
--color-neutral-500: rgba(var(--neutral-500));
|
||||||
|
--color-neutral-400: rgba(var(--neutral-400));
|
||||||
|
--color-neutral-300: rgba(var(--neutral-300));
|
||||||
|
--color-neutral-200: rgba(var(--neutral-200));
|
||||||
|
--color-neutral-100: rgba(var(--neutral-100));
|
||||||
|
--color-accent-600: rgba(var(--accent-600));
|
||||||
|
--color-accent-500: rgba(var(--accent-500));
|
||||||
|
--color-accent-400: rgba(var(--accent-400));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
||||||
|
so we've added these compatibility styles to make sure everything still
|
||||||
|
looks the same as it did with Tailwind CSS v3.
|
||||||
|
|
||||||
|
If we ever want to remove these styles, we need to add an explicit border
|
||||||
|
color utility to any element that depends on these defaults.
|
||||||
|
*/
|
||||||
|
@layer base {
|
||||||
|
*,
|
||||||
|
::after,
|
||||||
|
::before,
|
||||||
|
::backdrop,
|
||||||
|
::file-selector-button {
|
||||||
|
border-color: var(--color-gray-200, currentColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility article {
|
||||||
|
@apply px-2 sm:px-4 py-4 mb-4 bg-neutral-800/40 w-full mx-auto max-w-4xl rounded-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility btn-primary {
|
||||||
|
@apply bg-accent-500 text-contrast rounded-sm p-2 sm:p-4 hover:bg-accent-400 cursor-pointer transition-colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--contrast: 255, 255, 255;
|
||||||
|
--neutral-900: 243, 244, 246;
|
||||||
|
--neutral-800: 229, 231, 235;
|
||||||
|
--neutral-700: 209, 213, 219;
|
||||||
|
--neutral-600: 156, 163, 175;
|
||||||
|
--neutral-500: 180, 180, 180;
|
||||||
|
--neutral-400: 75, 85, 99;
|
||||||
|
--neutral-300: 55, 65, 81;
|
||||||
|
--neutral-200: 31, 41, 55;
|
||||||
|
--neutral-100: 17, 24, 39;
|
||||||
|
--accent-400: 132, 204, 22;
|
||||||
|
--accent-500: 101, 163, 13;
|
||||||
|
--accent-600: 77, 124, 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--contrast: 0, 0, 0;
|
||||||
|
--neutral-900: 17, 24, 39;
|
||||||
|
--neutral-800: 31, 41, 55;
|
||||||
|
--neutral-700: 55, 65, 81;
|
||||||
|
--neutral-600: 75, 85, 99;
|
||||||
|
--neutral-500: 107, 114, 128;
|
||||||
|
--neutral-300: 209, 213, 219;
|
||||||
|
--neutral-400: 156, 163, 175;
|
||||||
|
--neutral-200: 229, 231, 235;
|
||||||
|
--accent-600: 101, 163, 13;
|
||||||
|
--accent-500: 132, 204, 22;
|
||||||
|
--accent-400: 163, 230, 53;
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/public/pico.lime.min.css
vendored
@@ -1,125 +0,0 @@
|
|||||||
// Select the file input element
|
|
||||||
const fileInput = document.querySelector('input[type="file"]');
|
|
||||||
const fileNames = [];
|
|
||||||
let fileType;
|
|
||||||
|
|
||||||
const selectContainer = document.querySelector("form > article");
|
|
||||||
|
|
||||||
// const convertFromSelect = document.querySelector("select[name='convert_from']");
|
|
||||||
|
|
||||||
// Add a 'change' event listener to the file input element
|
|
||||||
fileInput.addEventListener("change", (e) => {
|
|
||||||
// console.log(e.target.files);
|
|
||||||
// Get the selected files from the event target
|
|
||||||
const files = e.target.files;
|
|
||||||
|
|
||||||
// Select the file-list table
|
|
||||||
const fileList = document.querySelector("#file-list");
|
|
||||||
|
|
||||||
// Loop through the selected files
|
|
||||||
for (const file of files) {
|
|
||||||
// Create a new table row for each file
|
|
||||||
const row = document.createElement("tr");
|
|
||||||
row.innerHTML = `
|
|
||||||
<td>${file.name}</td>
|
|
||||||
<td>${(file.size / 1024).toFixed(2)} kB</td>
|
|
||||||
<td><a class="secondary" onclick="deleteRow(this)" style="cursor: pointer">Remove</a></td>
|
|
||||||
`;
|
|
||||||
|
|
||||||
if (!fileType) {
|
|
||||||
fileType = file.name.split(".").pop();
|
|
||||||
fileInput.setAttribute("accept", `.${fileType}`);
|
|
||||||
setTitle();
|
|
||||||
|
|
||||||
// choose the option that matches the file type
|
|
||||||
// for (const option of convertFromSelect.children) {
|
|
||||||
// console.log(option.value);
|
|
||||||
// if (option.value === fileType) {
|
|
||||||
// option.selected = true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
fetch("/conversions", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({ fileType: fileType }),
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((res) => res.text())
|
|
||||||
.then((html) => {
|
|
||||||
selectContainer.innerHTML = html;
|
|
||||||
})
|
|
||||||
.catch((err) => console.log(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the row to the file-list table
|
|
||||||
fileList.appendChild(row);
|
|
||||||
|
|
||||||
// Append the file to the hidden input
|
|
||||||
fileNames.push(file.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadFiles(files);
|
|
||||||
});
|
|
||||||
|
|
||||||
const setTitle = () => {
|
|
||||||
const title = document.querySelector("h1");
|
|
||||||
title.textContent = `Convert ${fileType ? `.${fileType}` : ""}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add a onclick for the delete button
|
|
||||||
const deleteRow = (target) => {
|
|
||||||
const filename = target.parentElement.parentElement.children[0].textContent;
|
|
||||||
const row = target.parentElement.parentElement;
|
|
||||||
row.remove();
|
|
||||||
|
|
||||||
// remove from fileNames
|
|
||||||
const index = fileNames.indexOf(filename);
|
|
||||||
fileNames.splice(index, 1);
|
|
||||||
|
|
||||||
// if fileNames is empty, reset fileType
|
|
||||||
if (fileNames.length === 0) {
|
|
||||||
fileType = null;
|
|
||||||
fileInput.removeAttribute("accept");
|
|
||||||
setTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch("/delete", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify({ filename: filename }),
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then((data) => {
|
|
||||||
console.log(data);
|
|
||||||
})
|
|
||||||
.catch((err) => console.log(err));
|
|
||||||
};
|
|
||||||
|
|
||||||
const uploadFiles = (files) => {
|
|
||||||
const formData = new FormData();
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
formData.append("file", file, file.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch("/upload", {
|
|
||||||
method: "POST",
|
|
||||||
body: formData,
|
|
||||||
})
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then((data) => {
|
|
||||||
console.log(data);
|
|
||||||
})
|
|
||||||
.catch((err) => console.log(err));
|
|
||||||
};
|
|
||||||
|
|
||||||
const formConvert = document.querySelector("form[action='/convert']");
|
|
||||||
|
|
||||||
formConvert.addEventListener("submit", (e) => {
|
|
||||||
const hiddenInput = document.querySelector("input[name='file_names']");
|
|
||||||
hiddenInput.value = JSON.stringify(fileNames);
|
|
||||||
});
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
article {
|
|
||||||
/* height: 300px; */
|
|
||||||
/* width: 300px; */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
div.icon {
|
|
||||||
height: 100px;
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
button[type="submit"] {
|
|
||||||
width: 50%
|
|
||||||
}
|
|
||||||
|
|
||||||
div.center {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["ESNext"],
|
"lib": ["ESNext"],
|
||||||
"module": "esnext",
|
"module": "ESNext",
|
||||||
"target": "esnext",
|
"target": "ES2021",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
@@ -17,9 +17,6 @@
|
|||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"types": [
|
|
||||||
"bun-types" // add Bun global
|
|
||||||
],
|
|
||||||
// non bun init
|
// non bun init
|
||||||
"plugins": [{ "name": "@kitajs/ts-html-plugin" }],
|
"plugins": [{ "name": "@kitajs/ts-html-plugin" }],
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
@@ -30,4 +27,4 @@
|
|||||||
"noImplicitOverride": true
|
"noImplicitOverride": true
|
||||||
// "noImplicitReturns": true
|
// "noImplicitReturns": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||