123 Commits

Author SHA1 Message Date
Emrik Östling
59310c095d Merge pull request #192 from C4illin/release-please--branches--main--components--convertx-frontend
chore(main): release 0.10.0
2025-01-18 12:46:01 +01:00
Emrik Östling
c47f0c12fe chore(main): release 0.10.0 2025-01-18 12:01:01 +01:00
Emrik Östling
ca71a40485 Merge pull request #211 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.45
2025-01-18 12:00:28 +01:00
renovate[bot]
d4e8f376c1 chore(deps): update oven/bun docker tag to v1.1.45 2025-01-17 17:41:37 +00:00
Emrik Östling
14c6ea1e6b Merge pull request #210 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.44
2025-01-17 11:14:27 +01:00
renovate[bot]
d6e4d8fbd6 chore(deps): update oven/bun docker tag to v1.1.44 2025-01-17 09:03:11 +00:00
C4illin
460bda62d5 chore: update deps 2025-01-16 21:30:10 +01:00
Emrik Östling
d2702ab673 Merge pull request #208 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.43
2025-01-08 23:35:11 +01:00
renovate[bot]
e2581f42f5 chore(deps): update oven/bun docker tag to v1.1.43 2025-01-08 19:03:18 +00:00
Emrik Östling
f0fcfc159f Merge pull request #203 from tejashah88/fix-unauthed-users-1
Fix: skip account setup when ALLOW_UNAUTHENTICATED is true
2025-01-04 17:36:04 +01:00
Tejas Shah
538c5b60c9 fix: skip account setup when ALLOW_UNAUTHENTICATED is true 2025-01-04 17:05:03 +01:00
Emrik Östling
2fabb7bbb2 Merge pull request #197 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.42
2025-01-04 14:27:59 +01:00
renovate[bot]
e7c34a9c94 chore(deps): update oven/bun docker tag to v1.1.42 2025-01-04 12:43:57 +00:00
Emrik Östling
618f9fce5a Merge pull request #204 from C4illin/fix-calibre-build-error
fix: add qt6-qtbase-private-dev from community repo
2025-01-04 13:43:30 +01:00
C4illin
95dbc9f678 fix: add qt6-qtbase-private-dev from community repo 2025-01-04 12:54:22 +01:00
Emrik Östling
aa87bc5c51 Merge pull request #200 from tejashah88/main
Fixed example docker-compose.yml file
2024-12-27 03:34:01 +01:00
Tejas Shah
815de531ed Fixed example docker-compose.yml file 2024-12-26 07:04:26 -08:00
Emrik Östling
cf2b026dc4 Merge pull request #196 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.41
2024-12-20 16:44:02 +01:00
renovate[bot]
9ce46aefba chore(deps): update oven/bun docker tag to v1.1.41 2024-12-20 15:33:31 +00:00
Emrik Östling
98b2db7818 Merge pull request #195 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.40
2024-12-18 13:20:35 +01:00
renovate[bot]
0229851bf9 chore(deps): update oven/bun docker tag to v1.1.40 2024-12-18 09:07:21 +00:00
C4illin
9e15114fe8 chore: update deps 2024-12-17 21:56:24 +01:00
Emrik Östling
7f66a76bb0 Merge pull request #194 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.39
2024-12-17 18:04:50 +01:00
renovate[bot]
e9cc8392bb chore(deps): update oven/bun docker tag to v1.1.39 2024-12-17 16:08:25 +00:00
C4illin
d0b89ce74f chore: add ffmpeg args and calibre to readme 2024-12-11 11:09:03 +01:00
C4illin
f537c81db7 fix: add FFMPEG_ARGS env variable
issue #190
2024-12-11 11:01:39 +01:00
C4illin
03d3edfff6 feat: add calibre
issue #191
2024-12-07 02:38:30 +01:00
Emrik Östling
447b4c5e5c Merge pull request #189 from C4illin/renovate/oven-bun-1.x 2024-12-01 12:22:47 +01:00
renovate[bot]
cb143209ae chore(deps): update oven/bun docker tag to v1.1.38 2024-11-29 14:10:18 +00:00
C4illin
9c24fe73b5 chore: add table overview for images 2024-11-27 17:06:35 +01:00
C4illin
19ae85424b chore: add dockerhub pulls badge 2024-11-27 16:51:31 +01:00
C4illin
22fad99552 chore: update converter count 2024-11-26 17:18:48 +01:00
Emrik Östling
8144bbef74 Merge pull request #188 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.37
2024-11-26 09:31:12 +01:00
renovate[bot]
aad6da0ae8 chore(deps): update oven/bun docker tag to v1.1.37 2024-11-26 04:13:39 +00:00
C4illin
c5f8162a22 chore: update deps 2024-11-25 23:22:18 +01:00
Emrik Östling
f0f30224b5 Merge pull request #183 from C4illin/release-please--branches--main--components--convertx-frontend
chore(main): release 0.9.0
2024-11-24 14:47:34 +01:00
C4illin
d0d888e356 chore: update postcss to ejs 2024-11-21 23:08:30 +01:00
C4illin
2c64122224 chore: update deps 2024-11-21 23:08:16 +01:00
C4illin
3b2eee96a9 chore: update deps 2024-11-21 22:50:45 +01:00
C4illin
465aacbf9b chore: update formats 2024-11-21 22:50:38 +01:00
Emrik Östling
d1a2a66170 chore(main): release 0.9.0 2024-11-21 22:44:51 +01:00
C4illin
4c05fd72bb fix: wait for both upload and selection
issue #177
2024-11-21 22:44:13 +01:00
Emrik Östling
f04fe760e3 Merge pull request #187 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.36
2024-11-20 10:12:40 +01:00
renovate[bot]
834d19bcc6 chore(deps): update oven/bun docker tag to v1.1.36 2024-11-20 02:03:06 +00:00
Emrik Östling
6808c4642c Update README.md 2024-11-18 17:00:04 +01:00
Emrik Östling
d0ce307f94 Merge pull request #186 from C10udburst/main
feat: add inkscape for vector images
2024-11-16 14:13:43 +01:00
Cloudburst
f3740e9ded feat: add inkscape for vector images 2024-11-16 11:34:47 +01:00
C4illin
b485bc9445 chore: add download stats 2024-11-14 11:26:24 +01:00
Emrik Östling
2d14c1bb26 Merge pull request #184 from C4illin/feature/#177/disable-convert-when-uploading 2024-11-13 14:59:31 +01:00
C4illin
1a442d6e69 fix: treat unknown as m4a
issue #178
2024-11-13 13:08:40 +01:00
C4illin
2386543e5c chore: update deps 2024-11-13 13:08:00 +01:00
C4illin
58e220e82d feat: disable convert when uploading
issue #177
2024-11-12 22:30:16 +01:00
C4illin
24bea6e4d2 chore: add tutorial link 2024-11-12 22:20:32 +01:00
C4illin
43497ad8d1 ci: split docker hub description to separate workflow 2024-11-12 12:35:05 +01:00
C4illin
f22b61fe4c ci: sync docker hub description with readme 2024-11-12 12:27:39 +01:00
C4illin
5b08f4cd19 ci: support dockerhub 2024-11-12 12:17:45 +01:00
Emrik Östling
1589f8d24e Merge pull request #182 from C4illin/feature/#180/add-webroot-env-variable 2024-11-06 22:56:00 +01:00
C4illin
7d1db72cf5 chore: fix default value for webroot 2024-11-06 14:14:11 +01:00
C4illin
53a8f66414 chore: update deps 2024-11-06 14:04:57 +01:00
C4illin
36cb6cc589 feat: Allow to chose webroot
issue #180
2024-11-06 08:49:53 +01:00
Emrik Östling
f3a4aece46 Merge pull request #181 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.34
2024-11-04 11:21:48 +01:00
renovate[bot]
580a6a869a chore(deps): update oven/bun docker tag to v1.1.34 2024-11-02 06:59:17 +00:00
Emrik Östling
008eaac493 Merge pull request #176 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.33
2024-10-28 22:56:14 +01:00
renovate[bot]
b450623bb4 chore(deps): update oven/bun docker tag to v1.1.33 2024-10-24 10:42:28 +00:00
Emrik Östling
8ac2ecb673 Merge pull request #175 from C4illin/renovate/npm-run-all2-7.x
chore(deps): update dependency npm-run-all2 to v7
2024-10-22 09:04:39 +02:00
Emrik Östling
0a10a56ae3 Merge pull request #174 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.32
2024-10-22 09:04:14 +02:00
renovate[bot]
9378ba9208 chore(deps): update dependency npm-run-all2 to v7 2024-10-22 01:09:18 +00:00
renovate[bot]
0c586e324b chore(deps): update oven/bun docker tag to v1.1.32 2024-10-21 23:14:26 +00:00
Emrik Östling
91c4a64284 chore: add dev image size 2024-10-18 20:07:44 +02:00
C4illin
c599e98d9d chore: ignore more files 2024-10-18 20:03:19 +02:00
C4illin
d2cd6706c9 chore: update @elysiajs/static 2024-10-18 19:32:42 +02:00
C4illin
e8ed10dde8 chore(deps): update @elysiajs/html to version 1.1.1 2024-10-18 18:53:45 +02:00
C4illin
5fe0b79802 chore(deps): update dependencies to latest versions 2024-10-18 18:43:51 +02:00
Emrik Östling
34a6722a68 Merge pull request #172 from C4illin/renovate/oven-bun-1.x 2024-10-18 11:52:36 +02:00
renovate[bot]
5b0d769c63 chore(deps): update oven/bun docker tag to v1.1.31 2024-10-18 07:56:43 +00:00
Emrik Östling
718401a28b Merge pull request #169 from C4illin/renovate/oven-bun-1.x
chore(deps): update oven/bun docker tag to v1.1.30
2024-10-08 18:09:13 +02:00
renovate[bot]
3112cd57f6 chore(deps): update oven/bun docker tag to v1.1.30 2024-10-08 14:13:13 +00:00
C4illin
410fc777a7 Merge branch 'main' of https://github.com/C4illin/ConvertX 2024-10-07 10:47:40 +02:00
C4illin
8eed99e732 chore: fix drop done style 2024-10-07 10:47:37 +02:00
Emrik Östling
663b1d4171 Merge pull request #167 from C4illin/release-please--branches--main--components--convertx-frontend 2024-10-06 23:36:13 +02:00
Emrik Östling
c3067ca12d chore(main): release 0.8.1 2024-10-06 00:49:06 +02:00
Emrik Östling
4561ca3760 Merge pull request #164 from C4illin/fix/#163/add-jfif-support 2024-10-06 00:48:46 +02:00
Emrik Östling
698cce58ce Merge pull request #165 from C4illin/fix/#157/resize-when-converting-to-ico 2024-10-06 00:46:22 +02:00
C4illin
339b79f786 fix: treat jfif as jpeg
issue #163
2024-10-06 00:45:08 +02:00
C4illin
4f98f778f0 chore: add message when resizing image 2024-10-06 00:40:34 +02:00
Emrik Östling
8479b33a47 Merge pull request #166 from C4illin/fix/#151/disable-convert-button 2024-10-05 23:29:39 +02:00
C4illin
78844d7bd5 fix: disable convert button when input is empty
issue #151
2024-10-05 01:20:23 +02:00
Emrik Östling
64e4a271e1 Merge branch 'main' into fix/#157/resize-when-converting-to-ico 2024-10-05 01:02:48 +02:00
C4illin
5fb8c3575b chore: add eslint-plugin-readable-tailwind 2024-10-05 01:01:00 +02:00
C4illin
a6b8bcecae chore: disable dependency dashboard in Renovate configuration 2024-10-05 00:47:34 +02:00
C4illin
bc9c820820 chore: remove DeepSource configuration file 2024-10-05 00:45:34 +02:00
C4illin
ee9207a7f4 chore: fix eslint rules 2024-10-05 00:43:24 +02:00
C4illin
a34e215202 chore: remove biome 2024-10-05 00:01:39 +02:00
C4illin
b4e53dbb8e fix: resize to fit for ico
issue #157
2024-10-04 23:55:39 +02:00
C4illin
b5e8d82bfa chore(eslint): add browser globals to ESLint configuration 2024-10-04 23:44:18 +02:00
Emrik Östling
5d9000bb33 Merge pull request #162 from C4illin/renovate/biomejs-biome-1.x
chore(deps): update dependency @biomejs/biome to v1.9.3
2024-10-01 22:01:56 +02:00
renovate[bot]
ccb065ef0f chore(deps): update dependency @biomejs/biome to v1.9.3 2024-10-01 15:11:07 +00:00
Emrik Östling
883fad806b Merge pull request #155 from C4illin/release-please--branches--main--components--convertx-frontend 2024-10-01 14:17:30 +02:00
Emrik Östling
feacd1b816 chore(main): release 0.8.0 2024-09-30 22:01:44 +02:00
Emrik Östling
094e7a0d1c Merge pull request #160 from C4illin/feature-light-theme
feat: add light theme
2024-09-30 22:01:19 +02:00
C4illin
72636c5059 feat: add light theme, fixes #156 2024-09-30 17:14:44 +02:00
C4illin
291cfc80c6 chore: update deps 2024-09-30 15:56:33 +02:00
C4illin
ae1dfafc9d fix: cleanup formats and add opus, fixes #159 2024-09-30 15:51:11 +02:00
Emrik Östling
6caa583c35 Merge pull request #154 from C4illin/fix/#153/clean-up-ffmpeg-formats
fix: support .awb and clean up, fixes #153, #92
2024-09-28 13:36:46 +02:00
C4illin
2057167576 fix: add support for usd for assimp, #144 2024-09-28 13:13:48 +02:00
C4illin
1c9e67fc32 fix: support .awb and clean up, fixes #153, #92 2024-09-28 13:02:17 +02:00
Emrik Östling
d3af9688c6 Merge pull request #149 from C4illin/release-please--branches--main--components--convertx-frontend
chore(main): release 0.7.0
2024-09-28 11:11:11 +02:00
Emrik Östling
7d0cbb9844 chore(main): release 0.7.0 2024-09-26 23:37:45 +02:00
C4illin
88173891ba fix: wrong layout on search with few options 2024-09-26 23:37:19 +02:00
Emrik Östling
2b4b8f9551 Merge pull request #148 from Aymendje/feat-assimp 2024-09-26 23:01:33 +02:00
Aymen Djellal
63a4328d4a feat: Add support for 3d assets through assimp converter
This is a start for #144
It does not support all the 3d formats, but its a good few
2024-09-26 22:55:42 +02:00
Emrik Östling
413f5dc7b4 Merge pull request #138 from C4illin/release-please--branches--main--components--convertx-frontend 2024-09-25 23:56:29 +02:00
Emrik Östling
ebccdf9169 chore(main): release 0.6.0 2024-09-25 23:47:47 +02:00
C4illin
47139a550b fix: rename css file to force update cache, fixes #141 2024-09-25 23:47:18 +02:00
C4illin
fa5446c446 chore: fix eslint config 2024-09-25 23:46:21 +02:00
Emrik Östling
8772e582b0 Merge pull request #142 from Aymendje/patch-2 2024-09-25 23:31:23 +02:00
Aymen Djellal
45922ed3a3 [FIX] Fix broken CSS
This is a fix for #141 
The CSS was broken due to the import in the tailwind.config.js, it should be inside the module.exports and not outside. Thats why bun was giving a warning before :

```
# bun run dev
warn - The `content` option in your Tailwind CSS configuration is missing or empty.
warn - Configure your content sources or your generated CSS will be missing styles.
warn - https://tailwindcss.com/docs/content-configuration
```

it is now fixed
2024-09-25 09:12:54 -04:00
C4illin
4c747e8908 chore: format and update deps 2024-09-24 23:49:14 +02:00
Emrik Östling
e573997aa9 Merge pull request #140 from Aymendje/patch-1
Fix UNAUTHENTICATED mode
2024-09-24 09:12:28 +02:00
Aymen Djellal
c57b69991c Fix UNAUTHENTICATED mode
the function used here, randmInt(Min, Max) has an issue.
When running the code, I get a 500 error, with the error being 

```
 |       const newUserId = String(randomInt(2 ** 24,  Number.MAX_SAFE_INTEGER));
      ^
RangeError: The "max - min" is out of range. It must be <= 281474976710655. Received 9007199237963775
 code: "ERR_OUT_OF_RANGE"

      at randomInt (native:1:1)
      at /.../ConvertX/src/index.tsx:460:32
      at /.../ConvertX/src/index.tsx:594:29
      at file:///.../ConvertX/node_modules/elysia/dist/bun/index.js:76:22
```

When digging deeper in the implementation, it seems that the official node doc says : 
> The range (max - min) must be less than 2**48. min and max must be safe integers.

See : https://nodejs.org/api/crypto.html#cryptorandomintmin-max-callback

Feel free to close this PR and do the fix another way (it: by using a uuid instead of randomInt, etc.)
2024-09-23 11:44:37 -04:00
Emrik Östling
eee983a56a Merge pull request #137 from C4illin/feature-tailwind
feat: ui remake with tailwind
2024-09-23 10:20:01 +02:00
C4illin
22f823c535 feat: ui remake with tailwind 2024-09-23 03:58:29 +02:00
Emrik Östling
ed59cd7aa4 chore: clarify that batch processing is possible 2024-09-22 23:53:37 +02:00
51 changed files with 2472 additions and 1855 deletions

View File

@@ -1,7 +0,0 @@
version = 1
[[analyzers]]
name = "javascript"
[analyzers.meta]
environment = ["nodejs"]

View File

@@ -1,16 +1,20 @@
node_modules
Dockerfile*
docker-compose*
.dockerignore
.editorconfig
.env
.git
.gitignore
README.md
LICENSE
.vscode
Makefile
helm-charts
.env
.editorconfig
.idea
.vscode
CHANGELOG.md
coverage*
data
data
docker-compose*
Dockerfile*
eslint.config.js
helm-charts
LICENSE
Makefile
node_modules
prettier.config.js
README.md
renovate.json

View File

@@ -1,69 +1,80 @@
name: Docker
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
on:
push:
branches: [ "main" ]
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# Build and push Docker image with Buildx (don't push on PR)
# 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
name: Docker
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
on:
push:
branches: [ "main" ]
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
DOCKERHUB_USERNAME: c4illin
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ env.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
${{ env.IMAGE_NAME }}
# Build and push Docker image with Buildx (don't push on PR)
# 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

View 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

3
.gitignore vendored
View File

@@ -47,4 +47,5 @@ package-lock.json
/db
/data
/Bruno
/tsconfig.tsbuildinfo
/tsconfig.tsbuildinfo
/public/generated.css

View File

@@ -1,5 +1,81 @@
# Changelog
## [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)

View File

@@ -1,63 +0,0 @@
FROM oven/bun:1-debian as base
WORKDIR /app
# install dependencies into temp directory
# this will cache them and speed up future builds
FROM base AS install
RUN mkdir -p /temp/dev
COPY package.json bun.lockb /temp/dev/
RUN cd /temp/dev && bun install --frozen-lockfile
# install with --production (exclude devDependencies)
RUN mkdir -p /temp/prod
COPY package.json bun.lockb /temp/prod/
RUN cd /temp/prod && bun install --frozen-lockfile --production
# FROM base AS install-libjxl-tools
# download
# copy node_modules from temp directory
# then copy all (non-ignored) project files into the image
# FROM base AS prerelease
# COPY --from=install /temp/dev/node_modules node_modules
# COPY . .
# # [optional] tests & build
# ENV NODE_ENV=production
# RUN bun test
# RUN bun run build
# copy production dependencies and source code into final image
FROM base AS release
LABEL maintainer="Emrik Östling (C4illin)"
LABEL description="ConvertX: self-hosted online file converter supporting 700+ file formats."
LABEL repo="https://github.com/C4illin/ConvertX"
# install additional dependencies
RUN rm -rf /var/lib/apt/lists/partial && apt-get update -o Acquire::CompressionTypes::Order::=gz \
&& apt-get install -y \
pandoc \
texlive-latex-recommended \
texlive-fonts-recommended \
texlive-latex-extra \
ffmpeg \
graphicsmagick \
ghostscript \
libvips-tools
# # libjxl is not available in the official debian repositories
# RUN wget https://github.com/libjxl/libjxl/releases/download/v0.10.2/jxl-debs-amd64-debian-bullseye-v0.10.2.tar.gz -O /tmp/jxl-debs-amd64-debian-bullseye-v0.10.2.tar.gz \
# && mkdir -p /tmp/libjxl \
# && tar -xvf /tmp/jxl-debs-amd64-debian-bullseye-v0.10.2.tar.gz -C /tmp/libjxl \
# && dpkg -i /tmp/libjxl/libjxl_0.10.2_amd64.deb /tmp/libjxl/jxl_0.10.2_amd64.deb \
# && rm -rf /tmp/jxl-debs-amd64-debian-bullseye-v0.10.2.tar.gz /tmp/libjxl
COPY --from=install /temp/prod/node_modules node_modules
# COPY --from=prerelease /app/src/index.tsx /app/src/
# COPY --from=prerelease /app/package.json .
COPY . .
EXPOSE 3000/tcp
ENTRYPOINT [ "bun", "run", "./src/index.tsx" ]

View File

@@ -1,4 +1,4 @@
FROM oven/bun:1.1.29-alpine AS base
FROM oven/bun:1.1.45-alpine AS base
LABEL org.opencontainers.image.source="https://github.com/C4illin/ConvertX"
WORKDIR /app
@@ -22,14 +22,14 @@ RUN cargo install resvg
# copy node_modules from temp directory
# then copy all (non-ignored) project files into the image
# FROM base AS prerelease
# COPY --from=install /temp/dev/node_modules node_modules
# COPY . .
FROM base AS prerelease
COPY --from=install /temp/dev/node_modules node_modules
COPY . .
# # [optional] tests & build
# ENV NODE_ENV=production
ENV NODE_ENV=production
# RUN bun test
# RUN bun run build
RUN bun run build
# copy production dependencies and source code into final image
FROM base AS release
@@ -49,13 +49,21 @@ RUN apk --no-cache add \
vips-tools \
vips-poppler \
vips-jxl \
libjxl-tools
libjxl-tools \
assimp \
inkscape \
poppler-utils
RUN apk --no-cache add qt6-qtbase-private-dev --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community/
RUN apk --no-cache add calibre --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing/
# 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=builder /root/.cargo/bin/resvg /usr/local/bin/resvg
COPY --from=prerelease /app/public/generated.css /app/public/
# COPY --from=prerelease /app/src/index.tsx /app/src/
# COPY --from=prerelease /app/package.json .
COPY . .

257
README.md
View File

@@ -1,109 +1,148 @@
![ConvertX](images/logo.png)
# ConvertX
[![Docker](https://github.com/C4illin/ConvertX/actions/workflows/docker-publish.yml/badge.svg?branch=main)](https://github.com/C4illin/ConvertX/actions/workflows/docker-publish.yml)
[![GitHub Release](https://img.shields.io/github/v/release/C4illin/ConvertX)](https://github.com/C4illin/ConvertX/pkgs/container/convertx)
![GitHub commits since latest release](https://img.shields.io/github/commits-since/C4illin/ConvertX/latest)
![GitHub repo size](https://img.shields.io/github/repo-size/C4illin/ConvertX)
![Docker container size](https://ghcr-badge.egpl.dev/c4illin/convertx/size?color=%230375b6&tag=latest&label=image+size&trim=)
![GitHub top language](https://img.shields.io/github/languages/top/C4illin/ConvertX)
A self-hosted online file converter. Supports 831 different formats. Written with TypeScript, Bun and Elysia.
## Features
- Convert files to different formats
- Password protection
- Multiple accounts
## Converters supported
| Converter | Use case | Converts from | Converts to |
|------------------------------------------------------------------------------|---------------|---------------|-------------|
| [libjxl](https://github.com/libjxl/libjxl) | JPEG XL | 11 | 11 |
| [resvg](https://github.com/RazrFalcon/resvg) | SVG | 1 | 1 |
| [Vips](https://github.com/libvips/libvips) | Images | 45 | 23 |
| [XeLaTeX](https://tug.org/xetex/) | LaTeX | 1 | 1 |
| [Pandoc](https://pandoc.org/) | Documents | 43 | 65 |
| [GraphicsMagick](http://www.graphicsmagick.org/) | Images | 166 | 133 |
| [FFmpeg](https://ffmpeg.org/) | Video | ~473 | ~280 |
<!-- many ffmpeg fileformats are duplicates -->
Any missing converter? Open an issue or pull request!
## Deployment
```yml
# docker-compose.yml
services:
convertx:
image: ghcr.io/c4illin/convertx
container_name: convertx
restart: unless-stopped
ports:
- "3000:3000"
environment: # Defaults are listed below. All are optional.
- 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)
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() by default
- HTTP_ALLOWED=false # setting this to true is unsafe, only set this to true locally
- ALLOW_UNAUTHENTICATED=false # allows anyone to use the service without logging in, 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
volumes:
- convertx:/app/data
```
or
```bash
docker run -p 3000:3000 -v ./data:/app/data ghcr.io/c4illin/convertx
```
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.
If you get unable to open database file run `chown -R $USER:$USER path` on the path you choose.
### Tutorial
Tutorial in french: https://belginux.com/installer-convertx-avec-docker/
## Screenshots
![ConvertX Preview](images/preview.png)
## 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" />
</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>
![ConvertX](images/logo.png)
# ConvertX
[![Docker](https://github.com/C4illin/ConvertX/actions/workflows/docker-publish.yml/badge.svg?branch=main)](https://github.com/C4illin/ConvertX/actions/workflows/docker-publish.yml)
[![ghcr.io Pulls](https://img.shields.io/badge/dynamic/json?logo=github&url=https%3A%2F%2Fipitio.github.io%2Fbackage%2FC4illin%2FConvertX%2Fconvertx.json&query=%24.downloads&label=ghcr.io%20pulls&cacheSeconds=14400)](https://github.com/C4illin/ConvertX/pkgs/container/ConvertX)
[![Docker Pulls](https://img.shields.io/docker/pulls/c4illin/convertx?style=flat&logo=docker&label=dockerhub%20pulls&link=https%3A%2F%2Fhub.docker.com%2Frepository%2Fdocker%2Fc4illin%2Fconvertx%2Fgeneral)](https://hub.docker.com/r/c4illin/convertx)
[![GitHub Release](https://img.shields.io/github/v/release/C4illin/ConvertX)](https://github.com/C4illin/ConvertX/pkgs/container/convertx)
![GitHub commits since latest release](https://img.shields.io/github/commits-since/C4illin/ConvertX/latest)
![GitHub repo size](https://img.shields.io/github/repo-size/C4illin/ConvertX)
![Docker container size](https://ghcr-badge.egpl.dev/c4illin/convertx/size?color=%230375b6&tag=latest&label=image+size&trim=)
<!-- ![Dev image size](https://ghcr-badge.egpl.dev/c4illin/convertx/size?color=%230375b6&tag=main&label=dev+image&trim=) -->
A self-hosted online file converter. Supports over a thousand different formats. Written with TypeScript, Bun and Elysia.
## Features
- Convert files to different formats
- Process multiple files at once
- Password protection
- Multiple accounts
## Converters supported
| Converter | Use case | Converts from | Converts to |
|------------------------------------------------------------------------------|---------------|---------------|-------------|
| [libjxl](https://github.com/libjxl/libjxl) | JPEG XL | 11 | 11 |
| [resvg](https://github.com/RazrFalcon/resvg) | SVG | 1 | 1 |
| [Vips](https://github.com/libvips/libvips) | Images | 45 | 23 |
| [XeLaTeX](https://tug.org/xetex/) | LaTeX | 1 | 1 |
| [Calibre](https://calibre-ebook.com/) | E-books | 26 | 19 |
| [Pandoc](https://pandoc.org/) | Documents | 43 | 65 |
| [GraphicsMagick](http://www.graphicsmagick.org/) | Images | 167 | 130 |
| [Inkscape](https://inkscape.org/) | Vector images | 7 | 17 |
| [Assimp](https://github.com/assimp/assimp) | 3D Assets | 77 | 23 |
| [FFmpeg](https://ffmpeg.org/) | Video | ~472 | ~199 |
<!-- many ffmpeg fileformats are duplicates -->
Any missing converter? Open an issue or pull request!
## Deployment
```yml
# docker-compose.yml
services:
convertx:
image: ghcr.io/c4illin/convertx
container_name: convertx
restart: unless-stopped
ports:
- "3000:3000"
environment:
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() if unset
volumes:
- ./data:/app/data
```
or
```bash
docker run -p 3000:3000 -v ./data:/app/data ghcr.io/c4illin/convertx
```
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.
If you get unable to open database file run `chown -R $USER:$USER path` on the path you choose.
### Environment variables
All are optional, JWT_SECRET is recommended to be set.
| Name | Default | Description |
|---------------------------|---------|-------------|
| 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` |
> [!WARNING]
> If you can't login, make sure you are accessing the service over https or set HTTP_ALLOWED=true
### 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
Tutorial in french: <https://belginux.com/installer-convertx-avec-docker/>
Tutorial in chinese: <https://xzllll.com/24092901/>
## Screenshots
![ConvertX Preview](images/preview.png)
## 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>

View File

@@ -10,9 +10,14 @@
"attributePosition": "auto"
},
"files": {
"ignore": ["**/node_modules/**", "**/pico.lime.min.css"]
"ignore": [
"**/node_modules/**",
"**/pico.lime.min.css"
]
},
"organizeImports": {
"enabled": true
},
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": {
@@ -25,7 +30,11 @@
"useLiteralKeys": "error",
"useOptionalChain": "error"
},
"correctness": { "noPrecisionLoss": "error", "noUnusedVariables": "off" },
"correctness": {
"noPrecisionLoss": "error",
"noUnusedVariables": "off",
"useJsxKeyInIterable": "off"
},
"style": {
"noInferrableTypes": "error",
"noNamespace": "error",
@@ -45,6 +54,9 @@
"noUnsafeDeclarationMerging": "error",
"useAwait": "error",
"useNamespaceKeyword": "error"
},
"nursery": {
"useSortedClasses": "error"
}
}
},
@@ -60,4 +72,4 @@
"attributePosition": "auto"
}
}
}
}

BIN
bun.lockb

Binary file not shown.

View File

@@ -1,13 +1,17 @@
services:
convertx:
build:
context: .
# dockerfile: Debian.Dockerfile
volumes:
- ./data:/app/data
environment:
- ACCOUNT_REGISTRATION=true
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234
- ALLOW_UNAUTHENTICATED=true
ports:
- 3000:3000
services:
convertx:
build:
context: .
# dockerfile: Debian.Dockerfile
volumes:
- ./data:/app/data
environment: # Defaults are listed below. All are optional.
- 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)
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() by default
- 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
ports:
- 3000:3000

59
eslint.config.js Normal file
View File

@@ -0,0 +1,59 @@
import { fixupPluginRules } from "@eslint/compat";
import eslint from "@eslint/js";
import deprecationPlugin from "eslint-plugin-deprecation";
import eslintPluginReadableTailwind from "eslint-plugin-readable-tailwind";
import simpleImportSortPlugin from "eslint-plugin-simple-import-sort";
import tailwind from "eslint-plugin-tailwindcss";
import globals from "globals";
import tseslint from "typescript-eslint";
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
...tailwind.configs["flat/recommended"],
{
plugins: {
deprecation: fixupPluginRules(deprecationPlugin),
"simple-import-sort": simpleImportSortPlugin,
"readable-tailwind": eslintPluginReadableTailwind,
},
ignores: ["**/node_modules/**"],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
ecmaVersion: "latest",
sourceType: "module",
project: ["./tsconfig.json"],
},
globals: {
...globals.node,
...globals.browser,
},
},
files: ["**/*.{js,mjs,cjs,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",
],
},
],
},
},
);

View File

@@ -1,36 +0,0 @@
import { fixupPluginRules } from "@eslint/compat";
import tseslint from "typescript-eslint";
import eslint from "@eslint/js";
import deprecationPlugin from "eslint-plugin-deprecation";
import eslintCommentsPlugin from "eslint-plugin-eslint-comments";
import importPlugin from "eslint-plugin-import";
import simpleImportSortPlugin from "eslint-plugin-simple-import-sort";
export default tseslint.config(
{
plugins: {
"@typescript-eslint": tseslint.plugin,
deprecation: fixupPluginRules(deprecationPlugin),
"eslint-comments": eslintCommentsPlugin,
import: fixupPluginRules(importPlugin),
"simple-import-sort": simpleImportSortPlugin,
},
},
{
ignores: ["**/node_modules/**", "**/public/**"],
},
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
...tseslint.configs.stylisticTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
ecmaVersion: "latest",
sourceType: "module",
project: ["./tsconfig.json"],
},
},
},
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -1,22 +1,23 @@
{
"name": "convertx-frontend",
"version": "0.5.0",
"version": "0.10.0",
"scripts": {
"dev": "bun run --watch src/index.tsx",
"hot": "bun run --hot src/index.tsx",
"format": "biome format --write ./src",
"css": "cpy 'node_modules/@picocss/pico/css/pico.lime.min.css' 'src/public/' --flat",
"format": "eslint --fix .",
"build": "postcss ./src/main.css -o ./public/generated.css",
"lint": "run-p 'lint:*'",
"lint:tsc": "tsc --noEmit",
"lint:knip": "knip",
"lint:biome": "biome lint --error-on-warnings ./src"
"lint:eslint": "eslint ."
},
"dependencies": {
"@elysiajs/cookie": "^0.8.0",
"@elysiajs/html": "1.0.2",
"@elysiajs/jwt": "^1.1.1",
"@elysiajs/static": "1.0.3",
"elysia": "^1.1.12"
"@elysiajs/html": "^1.2.0",
"@elysiajs/jwt": "^1.2.0",
"@elysiajs/static": "^1.2.0",
"@kitajs/html": "^4.2.7",
"elysia": "^1.2.10"
},
"module": "src/index.tsx",
"type": "module",
@@ -24,34 +25,31 @@
"start": "bun run src/index.tsx"
},
"devDependencies": {
"@biomejs/biome": "1.9.2",
"@eslint/compat": "^1.1.1",
"@eslint/js": "^9.9.1",
"@ianvs/prettier-plugin-sort-imports": "^4.3.1",
"@kitajs/ts-html-plugin": "^4.0.2",
"@picocss/pico": "^2.0.6",
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.18.0",
"@ianvs/prettier-plugin-sort-imports": "^4.4.1",
"@kitajs/ts-html-plugin": "^4.1.1",
"@total-typescript/ts-reset": "^0.6.1",
"@types/bun": "^1.1.8",
"@types/eslint": "^9.6.1",
"@types/node": "^22.5.4",
"@typescript-eslint/eslint-plugin": "^8.4.0",
"@typescript-eslint/parser": "^8.4.0",
"cpy-cli": "^5.0.0",
"eslint": "^9.9.1",
"eslint-config-prettier": "^9.1.0",
"@types/bun": "^1.1.16",
"@types/eslint-plugin-tailwindcss": "^3.17.0",
"@types/eslint__js": "^8.42.3",
"@types/node": "^22.10.7",
"autoprefixer": "^10.4.20",
"cssnano": "^7.0.6",
"eslint": "^9.18.0",
"eslint-plugin-deprecation": "^3.0.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-isaacscript": "^4.0.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-readable-tailwind": "^1.8.2",
"eslint-plugin-simple-import-sort": "^12.1.1",
"knip": "^5.29.2",
"npm-run-all2": "^6.2.2",
"prettier": "^3.3.3",
"typescript": "^5.5.4",
"typescript-eslint": "^8.4.0"
},
"trustedDependencies": [
"@biomejs/biome"
]
}
"eslint-plugin-tailwindcss": "^3.17.5",
"globals": "^15.14.0",
"knip": "^5.42.1",
"npm-run-all2": "^7.0.2",
"postcss": "^8.5.1",
"postcss-cli": "^11.0.0",
"prettier": "^3.4.2",
"tailwind-scrollbar": "^3.1.0",
"tailwindcss": "^3.4.17",
"typescript": "^5.7.3",
"typescript-eslint": "^8.20.0"
}
}

8
postcss.config.js Normal file
View File

@@ -0,0 +1,8 @@
import autoprefixer from "autoprefixer";
import cssnano from "cssnano";
import tailwind from "tailwindcss";
import tailwindConfig from "./tailwind.config.js";
export default {
plugins: [autoprefixer, tailwind(tailwindConfig), cssnano],
};

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 476 B

View File

Before

Width:  |  Height:  |  Size: 960 B

After

Width:  |  Height:  |  Size: 960 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,3 +1,5 @@
const webroot = document.querySelector("meta[name='webroot']").content;
window.downloadAll = function () {
// Get all download links
const downloadLinks = document.querySelectorAll("a[download]");
@@ -18,7 +20,7 @@ let progressElem = document.querySelector("progress");
const refreshData = () => {
// console.log("Refreshing data...", progressElem.value, progressElem.max);
if (progressElem.value !== progressElem.max) {
fetch(`/progress/${jobId}`, {
fetch(`${webroot}/progress/${jobId}`, {
method: "POST",
})
.then((res) => res.text())

View File

@@ -1,194 +1,236 @@
// Select the file input element
const fileInput = document.querySelector('input[type="file"]');
const fileNames = [];
let fileType;
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.hidden = false;
} else {
target.hidden = true;
}
}
if (matchingTargetsFound === 0) {
groupElement.hidden = true;
} else {
groupElement.hidden = false;
}
}
};
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}`;
showMatching("");
};
}
convertToGroups[groupName] = [targets, groupElement];
}
convertToInput.addEventListener("input", (e) => {
showMatching(e.target.value.toLowerCase());
});
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.hidden = true;
return;
}
convertToPopup.hidden = true;
});
convertToInput.addEventListener("focus", () => {
convertToPopup.hidden = false;
});
};
// 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;
updateSearchBar();
})
.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);
});
updateSearchBar();
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", () => {
dropZone.classList.add("dragover");
});
dropZone.addEventListener("dragleave", () => {
dropZone.classList.remove("dragover");
});
dropZone.addEventListener("drop", () => {
dropZone.classList.remove("dragover");
});
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) => {
// 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 onclick="deleteRow(this)">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(`${webroot}/conversions`, {
method: "POST",
body: JSON.stringify({ fileType: fileType }),
headers: {
"Content-Type": "application/json",
},
})
.then((res) => res.text())
.then((html) => {
selectContainer.innerHTML = html;
updateSearchBar();
})
.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
// 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 uploadFiles = (files) => {
convertButton.disabled = true;
convertButton.textContent = "Uploading...";
pendingFiles += 1;
const formData = new FormData();
for (const file of files) {
formData.append("file", file, file.name);
}
fetch(`${webroot}/upload`, {
method: "POST",
body: formData,
})
.then((res) => res.json())
.then((data) => {
pendingFiles -= 1;
if (pendingFiles === 0) {
if (formatSelected) {
convertButton.disabled = false;
}
convertButton.textContent = "Convert";
}
console.log(data);
})
.catch((err) => console.log(err));
};
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();

View File

@@ -1,6 +1,11 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
]
}
"config:recommended",
":disableDependencyDashboard"
],
"lockFileMaintenance": {
"enabled": true,
"automerge": true
}
}

View File

@@ -1,33 +1,40 @@
import { Html } from "@elysiajs/html";
export const BaseHtml = ({
children,
title = "ConvertX",
}: { children: JSX.Element; title?: string }) => (
webroot = "",
}: {
children: JSX.Element;
title?: string;
webroot?: string;
}) => (
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="webroot" content={webroot} />
<title safe>{title}</title>
<link rel="stylesheet" href="/pico.lime.min.css" />
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href={`${webroot}/generated.css`} />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
href={`${webroot}/apple-touch-icon.png`}
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
href={`${webroot}/favicon-32x32.png`}
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
href={`${webroot}/favicon-16x16.png`}
/>
<link rel="manifest" href="/site.webmanifest" />
<link rel="manifest" href={`${webroot}/site.webmanifest`} />
</head>
<body>{children}</body>
<body class="w-full bg-neutral-900 text-neutral-200">{children}</body>
</html>
);

View File

@@ -1,48 +1,84 @@
import { Html } from "@kitajs/html";
export const Header = ({
loggedIn,
accountRegistration,
}: { loggedIn?: boolean; accountRegistration?: boolean }) => {
allowUnauthenticated,
webroot = "",
}: {
loggedIn?: boolean;
accountRegistration?: boolean;
allowUnauthenticated?: boolean;
webroot?: string;
}) => {
let rightNav: JSX.Element;
if (loggedIn) {
rightNav = (
<ul>
<ul class="flex gap-4">
<li>
<a href="/history">History</a>
</li>
<li>
<a href="/logoff">Logout</a>
<a
class={`
text-accent-600 transition-all
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>
);
} else {
rightNav = (
<ul>
<ul class="flex gap-4">
<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>
{accountRegistration && (
{accountRegistration ? (
<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>
)}
) : null}
</ul>
);
}
return (
<header class="container">
<nav>
<header class="w-full p-4">
<nav class="mx-auto flex max-w-4xl justify-between rounded bg-neutral-900 p-4">
<ul>
<li>
<strong>
<a
href="/"
style={{
textDecoration: "none",
color: "inherit",
}}>
ConvertX
</a>
<a href={`${webroot}/`}>ConvertX</a>
</strong>
</li>
</ul>

141
src/converters/assimp.ts Normal file
View File

@@ -0,0 +1,141 @@
import { exec } 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> {
const command = `assimp export "${filePath}" "${targetPath}"`;
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
reject(`error: ${error}`);
}
if (stdout) {
console.log(`stdout: ${stdout}`);
}
if (stderr) {
console.error(`stderr: ${stderr}`);
}
resolve("Done");
});
});
}

86
src/converters/calibre.ts Normal file
View File

@@ -0,0 +1,86 @@
import { exec } 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> {
const command = `ebook-convert "${filePath}" "${targetPath}"`;
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
reject(`error: ${error}`);
}
if (stdout) {
console.log(`stdout: ${stdout}`);
}
if (stderr) {
console.error(`stderr: ${stderr}`);
}
resolve("Done");
});
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -143,6 +143,7 @@ export const properties = {
"svgz",
"text",
"tga",
"tif",
"tiff",
"tile",
"tim",
@@ -227,7 +228,6 @@ export const properties = {
"jbig",
"jng",
"jpeg",
"jpg",
"k",
"m",
"m2v",
@@ -313,8 +313,8 @@ export function convert(
fileType: string,
convertTo: string,
targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
options?: any,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: unknown,
): Promise<string> {
return new Promise((resolve, reject) => {
exec(
@@ -332,7 +332,7 @@ export function convert(
console.error(`stderr: ${stderr}`);
}
resolve("success");
resolve("Done");
},
);
});

View File

@@ -0,0 +1,64 @@
import { exec } 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) => {
exec(`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");
});
});
}

View File

@@ -39,8 +39,8 @@ export function convert(
fileType: string,
convertTo: string,
targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
options?: any,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: unknown,
): Promise<string> {
let tool = "";
if (fileType === "jxl") {
@@ -65,7 +65,7 @@ export function convert(
console.error(`stderr: ${stderr}`);
}
resolve("success");
resolve("Done");
});
});
}

View File

@@ -1,65 +1,46 @@
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 convertxelatex,
properties as propertiesxelatex,
} from "./xelatex";
import {
convert as convertLibjxl,
properties as propertiesLibjxl,
} from "./libjxl";
import {
convert as convertresvg,
properties as propertiesresvg,
} from "./resvg";
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";
// 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: {
[key: string]: {
const properties: Record<
string,
{
properties: {
from: { [key: string]: string[] };
to: { [key: string]: string[] };
options?: {
[key: string]: {
[key: string]: {
from: Record<string, string[]>;
to: Record<string, string[]>;
options?: Record<
string,
Record<
string,
{
description: string;
type: string;
default: number;
};
};
};
}
>
>;
};
converter: (
filePath: string,
fileType: string,
convertTo: string,
targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
options?: any,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
) => any;
};
} = {
options?: unknown,
) => unknown;
}
> = {
libjxl: {
properties: propertiesLibjxl,
converter: convertLibjxl,
@@ -76,6 +57,10 @@ const properties: {
properties: propertiesxelatex,
converter: convertxelatex,
},
calibre: {
properties: propertiesCalibre,
converter: convertCalibre,
},
pandoc: {
properties: propertiesPandoc,
converter: convertPandoc,
@@ -84,6 +69,14 @@ const properties: {
properties: propertiesGraphicsmagick,
converter: convertGraphicsmagick,
},
inkscape: {
properties: propertiesInkscape,
converter: convertInkscape,
},
assimp: {
properties: propertiesassimp,
converter: convertassimp,
},
ffmpeg: {
properties: propertiesFFmpeg,
converter: convertFFmpeg,
@@ -93,24 +86,19 @@ const properties: {
export async function mainConverter(
inputFilePath: string,
fileTypeOriginal: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
convertTo: any,
convertTo: string,
targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
options?: any,
options?: unknown,
converterName?: string,
) {
const fileType = normalizeFiletype(fileTypeOriginal);
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
let converterFunc: any;
// let converterName = converterName;
let converterFunc: typeof properties["libjxl"]["converter"] | undefined;
if (converterName) {
converterFunc = properties[converterName]?.converter;
} else {
// Iterate over each converter in properties
// biome-ignore lint/style/noParameterAssign: <explanation>
for (converterName in properties) {
const converterObj = properties[converterName];
@@ -138,7 +126,7 @@ export async function mainConverter(
}
try {
await converterFunc(
const result = await converterFunc(
inputFilePath,
fileType,
convertTo,
@@ -148,7 +136,13 @@ export async function mainConverter(
console.log(
`Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully using ${converterName}.`,
result,
);
if (typeof result === "string") {
return result;
}
return "Done";
} catch (error) {
console.error(
@@ -159,7 +153,7 @@ export async function mainConverter(
}
}
const possibleTargets: { [key: string]: { [key: string]: string[] } } = {};
const possibleTargets: Record<string, Record<string, string[]>> = {};
for (const converterName in properties) {
const converterProperties = properties[converterName]?.properties;
@@ -184,9 +178,7 @@ for (const converterName in properties) {
}
}
export const getPossibleTargets = (
from: string,
): { [key: string]: string[] } => {
export const getPossibleTargets = (from: string): Record<string, string[]> => {
const fromClean = normalizeFiletype(from);
return possibleTargets[fromClean] || {};
@@ -210,11 +202,12 @@ for (const converterName in properties) {
}
possibleInputs.sort();
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const getPossibleInputs = () => {
return possibleInputs;
};
const allTargets: { [key: string]: string[] } = {};
const allTargets: Record<string, string[]> = {};
for (const converterName in properties) {
const converterProperties = properties[converterName]?.properties;
@@ -236,7 +229,7 @@ export const getAllTargets = () => {
return allTargets;
};
const allInputs: { [key: string]: string[] } = {};
const allInputs: Record<string, string[]> = {};
for (const converterName in properties) {
const converterProperties = properties[converterName]?.properties;
@@ -281,4 +274,4 @@ export const getAllInputs = (converter: string) => {
// }
// // print the number of unique Inputs and Outputs
// console.log(`Unique Formats: ${uniqueFormats.size}`);
// console.log(`Unique Formats: ${uniqueFormats.size}`);

View File

@@ -124,8 +124,8 @@ export function convert(
fileType: string,
convertTo: string,
targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
options?: any,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: unknown,
): Promise<string> {
// set xelatex here
const xelatex = ["pdf", "latex"];
@@ -149,7 +149,7 @@ export function convert(
console.error(`stderr: ${stderr}`);
}
resolve("success");
resolve("Done");
},
);
});

View File

@@ -14,8 +14,8 @@ export function convert(
fileType: string,
convertTo: string,
targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
options?: any,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: unknown,
): Promise<string> {
return new Promise((resolve, reject) => {
exec(`resvg "${filePath}" "${targetPath}"`, (error, stdout, stderr) => {
@@ -31,7 +31,7 @@ export function convert(
console.error(`stderr: ${stderr}`);
}
resolve("success");
resolve("Done");
});
});
}

View File

@@ -1,5 +1,6 @@
import { exec } from "node:child_process";
// declare possible conversions
export const properties = {
from: {
@@ -94,8 +95,8 @@ export function convert(
fileType: string,
convertTo: string,
targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
options?: any,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: unknown,
): Promise<string> {
// if (fileType === "svg") {
// const scale = options.scale || 1;
@@ -134,8 +135,8 @@ export function convert(
console.error(`stderr: ${stderr}`);
}
resolve("success");
resolve("Done");
},
);
});
}
}

View File

@@ -14,8 +14,8 @@ export function convert(
fileType: string,
convertTo: string,
targetPath: string,
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
options?: any,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: unknown,
): Promise<string> {
return new Promise((resolve, reject) => {
// const fileName: string = (targetPath.split("/").pop() as string).replace(".pdf", "")
@@ -39,7 +39,7 @@ export function convert(
console.error(`stderr: ${stderr}`);
}
resolve("success");
resolve("Done");
},
);
});

View File

@@ -1,31 +1,37 @@
export const normalizeFiletype = (filetype: string): string => {
const lowercaseFiletype = filetype.toLowerCase();
switch (lowercaseFiletype) {
case "jpg":
return "jpeg";
case "htm":
return "html";
case "tex":
return "latex";
case "md":
return "markdown";
default:
return lowercaseFiletype;
}
};
export const normalizeOutputFiletype = (filetype: string): string => {
const lowercaseFiletype = filetype.toLowerCase();
switch (lowercaseFiletype) {
case "jpeg":
return "jpg";
case "latex":
return "tex";
case "markdown":
return "md";
default:
return lowercaseFiletype;
}
};
export const normalizeFiletype = (filetype: string): string => {
const lowercaseFiletype = filetype.toLowerCase();
switch (lowercaseFiletype) {
case "jfif":
case "jpg":
return "jpeg";
case "htm":
return "html";
case "tex":
return "latex";
case "md":
return "markdown";
case "unknown":
return "m4a";
default:
return lowercaseFiletype;
}
};
export const normalizeOutputFiletype = (filetype: string): string => {
const lowercaseFiletype = filetype.toLowerCase();
switch (lowercaseFiletype) {
case "jpeg":
return "jpg";
case "latex":
return "tex";
case "markdown_phpextra":
case "markdown_strict":
case "markdown_mmd":
case "markdown":
return "md";
default:
return lowercaseFiletype;
}
};

View File

@@ -53,6 +53,16 @@ if (process.env.NODE_ENV === "production") {
}
});
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.");
@@ -83,6 +93,26 @@ if (process.env.NODE_ENV === "production") {
}
});
exec("assimp version", (error, stdout) => {
if (error) {
console.error("assimp is not installed");
}
if (stdout) {
console.log(`assimp v${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("bun -v", (error, stdout) => {
if (error) {
console.error("Bun is not installed. wait what");

17
src/helpers/tailwind.ts Normal file
View File

@@ -0,0 +1,17 @@
import tw from "tailwindcss";
import postcss from "postcss";
export const generateTailwind = async () => {
const result = await Bun.file("./src/main.css")
.text()
.then((sourceText) => {
const config = "./tailwind.config.js";
return postcss([tw(config)]).process(sourceText, {
from: "./src/main.css",
to: "./public/generated.css",
});
});
return result;
};

File diff suppressed because it is too large Load Diff

45
src/main.css Normal file
View File

@@ -0,0 +1,45 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.article {
@apply p-4 mb-4 bg-neutral-800/40 w-full mx-auto max-w-4xl rounded;
}
.btn-primary {
@apply bg-accent-500 text-contrast rounded 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;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,59 +0,0 @@
div.icon {
height: 100px;
width: 100px;
}
button[type="submit"] {
width: 50%;
}
div.center {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
@media (max-width: 99999999999px) {
.convert_to_popup {
width: 50vw !important;
height: 50vh;
}
}
@media (max-width: 850px) {
.convert_to_popup {
width: 60vw !important;
height: 60vh;
}
}
@media (max-width: 575px) {
.convert_to_popup {
width: 80vw !important;
height: 75vh;
}
}
@media (max-height: 1000px) {
.convert_to_popup {
height: 40vh;
}
}
@media (max-height: 650px) {
.convert_to_popup {
height: 30vh;
}
}
@media (max-height: 500px) {
.convert_to_popup {
height: 25vh;
}
}
@media (max-height: 400px) {
.convert_to_popup {
height: 15vh;
}
}

27
tailwind.config.js Normal file
View File

@@ -0,0 +1,27 @@
/** @type {import('tailwindcss').Config} */
import tailwindScrollbar from "tailwind-scrollbar";
export default {
content: ["./src/**/*.{html,js,tsx,jsx,cjs,mjs}"],
theme: {
extend: {
colors: {
contrast: "rgba(var(--contrast))",
"neutral-900": "rgba(var(--neutral-900))",
"neutral-800": "rgba(var(--neutral-800))",
"neutral-700": "rgba(var(--neutral-700))",
"neutral-600": "rgba(var(--neutral-600))",
"neutral-500": "rgba(var(--neutral-500))",
"neutral-400": "rgba(var(--neutral-400))",
"neutral-300": "rgba(var(--neutral-300))",
"neutral-200": "rgba(var(--neutral-200))",
"neutral-100": "rgba(var(--neutral-100))",
"accent-600": "rgba(var(--accent-600))",
"accent-500": "rgba(var(--accent-500))",
"accent-400": "rgba(var(--accent-400))",
},
},
},
plugins: [tailwindScrollbar],
};

View File

@@ -27,4 +27,4 @@
"noImplicitOverride": true
// "noImplicitReturns": true
}
}
}