Compare commits

...
35 Commits
Author SHA1 Message Date
MasutaYunikon 15f5847c97 Merge pull request #30 from Masutayunikon/release-please--branches--main
chore(main): release 1.10.2
2026-03-13 04:25:54 +01:00
MasutaYunikon 8736b58d8c chore(main): release 1.10.2 2026-03-13 04:25:39 +01:00
MasutaYunikon 12e8e2f93a Merge remote-tracking branch 'origin/main' 2026-03-13 04:25:17 +01:00
MasutaYunikon ed2306bb70 fix: add target node 2026-03-13 04:25:09 +01:00
MasutaYunikon b3fd57c2c8 Merge pull request #29 from Masutayunikon/release-please--branches--main
chore(main): release 1.10.1
2026-03-13 04:22:24 +01:00
MasutaYunikon c2da12409a chore(main): release 1.10.1 2026-03-13 04:22:06 +01:00
MasutaYunikon 178a57c444 Merge remote-tracking branch 'origin/main' 2026-03-13 04:21:42 +01:00
MasutaYunikon 1afe13c555 fix: add target node 2026-03-13 04:21:34 +01:00
MasutaYunikon c8963cc4cf Merge pull request #28 from Masutayunikon/release-please--branches--main
chore(main): release 1.10.0
2026-03-13 04:19:01 +01:00
MasutaYunikon 391b793e39 chore(main): release 1.10.0 2026-03-13 04:18:23 +01:00
MasutaYunikon 9a1519e6cd Merge pull request #27 from Masutayunikon/release-please--branches--main
chore(main): release 1.9.5
2026-03-13 04:17:27 +01:00
MasutaYunikon a12237af68 chore(main): release 1.9.5 2026-03-13 04:16:50 +01:00
MasutaYunikon d304aa6a26 Merge remote-tracking branch 'origin/main' 2026-03-13 04:16:29 +01:00
MasutaYunikon 27092b3968 fix: worker outside the exe 2026-03-13 04:16:07 +01:00
MasutaYunikon c1c36018b1 Merge pull request #26 from Masutayunikon/release-please--branches--main
chore(main): release 1.9.4
2026-03-13 04:12:38 +01:00
MasutaYunikon b462ec717b chore(main): release 1.9.4 2026-03-13 04:11:56 +01:00
MasutaYunikon 65518861ac Merge remote-tracking branch 'origin/main' 2026-03-13 04:11:36 +01:00
MasutaYunikon ed75280e70 fix: worker outside the exe 2026-03-13 04:11:27 +01:00
MasutaYunikon 9f53a9fef5 Merge pull request #25 from Masutayunikon/release-please--branches--main
chore(main): release 1.9.3
2026-03-13 04:02:39 +01:00
MasutaYunikon 3e5dc8d1d4 chore(main): release 1.9.3 2026-03-13 04:02:06 +01:00
MasutaYunikon bcb7f656a1 Merge remote-tracking branch 'origin/main' 2026-03-13 04:01:43 +01:00
MasutaYunikon 372359d040 fix: use bcryptjs for compilation 2026-03-13 04:01:34 +01:00
MasutaYunikon 63847c583a Merge pull request #24 from Masutayunikon/release-please--branches--main
chore(main): release 1.9.2
2026-03-13 03:52:42 +01:00
MasutaYunikon e90f85af70 chore(main): release 1.9.2 2026-03-13 03:52:30 +01:00
MasutaYunikon 0c683d4e37 Merge remote-tracking branch 'origin/main' 2026-03-13 03:52:10 +01:00
MasutaYunikon 16b28004cf fix: workflow directory 2026-03-13 03:52:02 +01:00
MasutaYunikon 558f4e2a1b Merge pull request #23 from Masutayunikon/release-please--branches--main
chore(main): release 1.9.1
2026-03-13 03:47:53 +01:00
MasutaYunikon 352c18029f chore(main): release 1.9.1 2026-03-13 03:47:33 +01:00
MasutaYunikon 465d3dba8b fix: workflow directory 2026-03-13 03:47:09 +01:00
MasutaYunikon 7b82d60c69 Merge remote-tracking branch 'origin/main' 2026-03-13 03:43:01 +01:00
MasutaYunikon af03aecf0d fix: workflow permmisions 2026-03-13 03:42:50 +01:00
MasutaYunikon f878298cef Merge pull request #22 from Masutayunikon/release-please--branches--main
chore(main): release 1.9.0
2026-03-13 03:39:58 +01:00
MasutaYunikon fc5dd1d36d chore(main): release 1.9.0 2026-03-13 03:39:04 +01:00
MasutaYunikon 1b73e7d0a3 Merge remote-tracking branch 'origin/main' 2026-03-13 03:38:43 +01:00
MasutaYunikon 8030bf14f9 feat: add windows / linux / macos binaries on build 2026-03-13 03:38:34 +01:00
12 changed files with 333 additions and 60 deletions

No files matched your search

+119
View File
@@ -0,0 +1,119 @@
name: Build Binaries
on:
release:
types: [created]
workflow_dispatch:
jobs:
build-frontend:
name: Build Frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: latest
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Vue frontend
run: pnpm build
- name: Upload frontend dist
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: dist/client/
retention-days: 1
build-binaries:
name: Build ${{ matrix.target }}
needs: build-frontend
runs-on: ${{ matrix.os }}
permissions:
contents: write
strategy:
matrix:
include:
- target: windows-x64
os: windows-latest
bun-target: bun-windows-x64
binary: binaries/fankarr.exe
archive: fankarr-windows-x64.zip
- target: linux-x64
os: ubuntu-latest
bun-target: bun-linux-x64
binary: binaries/fankarr
archive: fankarr-linux-x64.zip
- target: macos
os: macos-latest
bun-target: bun-darwin-arm64
binary: binaries/fankarr
archive: fankarr-macos.zip
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: pnpm/action-setup@v3
with:
version: latest
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download frontend dist
uses: actions/download-artifact@v4
with:
name: frontend-dist
path: public/
- name: Build binary (Unix)
if: matrix.target != 'windows-x64'
run: |
mkdir -p binaries
bun build server/index.ts --compile --target ${{ matrix.bun-target }} --outfile ${{ matrix.binary }}
- name: Build binary (Windows)
if: matrix.target == 'windows-x64'
run: |
New-Item -ItemType Directory -Force -Path binaries
bun build server/index.ts --compile --target ${{ matrix.bun-target }} --outfile ${{ matrix.binary }}
shell: pwsh
- name: Build worker
run: bun build server/organize-worker.ts --target node --outfile organize-worker.js
- name: Package archive (Windows)
if: matrix.target == 'windows-x64'
run: Compress-Archive -Path ${{ matrix.binary }}, organize-worker.js, public -DestinationPath ${{ matrix.archive }}
shell: pwsh
- name: Package archive (Unix)
if: matrix.target != 'windows-x64'
run: |
chmod +x ${{ matrix.binary }}
zip -r ${{ matrix.archive }} ${{ matrix.binary }} organize-worker.js public/
- name: Upload archive to Release
uses: softprops/action-gh-release@v2
with:
files: ${{ matrix.archive }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1
View File
@@ -11,6 +11,7 @@ node_modules
dist dist
dist-ssr dist-ssr
*.local *.local
binaries
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*
+102
View File
@@ -1,5 +1,107 @@
# Changelog # Changelog
## [1.10.2](https://github.com/Masutayunikon/FanKarr/compare/v1.10.1...v1.10.2) (2026-03-13)
### Bug Fixes
* add target node ([58b09f2](https://github.com/Masutayunikon/FanKarr/commit/58b09f2267345c60ed589c160f7e47698e9be9a4))
## [1.10.1](https://github.com/Masutayunikon/FanKarr/compare/v1.10.0...v1.10.1) (2026-03-13)
### Bug Fixes
* add target node ([6c629e0](https://github.com/Masutayunikon/FanKarr/commit/6c629e0f6b4d38453cfb22fd315e02d9e8750c45))
## [1.10.0](https://github.com/Masutayunikon/FanKarr/compare/v1.9.5...v1.10.0) (2026-03-13)
### Features
* add favicon ([dc4a2d4](https://github.com/Masutayunikon/FanKarr/commit/dc4a2d48f5c0cddd8e431ce33a69fc9146143fb6))
* add README.md ([4eb487a](https://github.com/Masutayunikon/FanKarr/commit/4eb487a96beed8c82cb19d9a9b63c3b69d841386))
* add windows / linux / macos binaries on build ([f17a73b](https://github.com/Masutayunikon/FanKarr/commit/f17a73b9591fb49a75bd5fcdadf1970585877dab))
* **auth:** génération automatique du JWT secret au démarrage ([eaa52e9](https://github.com/Masutayunikon/FanKarr/commit/eaa52e90900ff0e198beae190d7ad0298a702482))
* cache images ([da460de](https://github.com/Masutayunikon/FanKarr/commit/da460de60c4eec6851ea58ce0ae646a1208f6018))
* initial project ([eaf01cf](https://github.com/Masutayunikon/FanKarr/commit/eaf01cfc591dd72f9b093b533ced6c12a6c490a1))
* make the organizer threaded to not consumme api main thread ([14c0e56](https://github.com/Masutayunikon/FanKarr/commit/14c0e567525e179da82c6b58d9886cc3d544a98d))
* **organize:** scan initial mediaPath + logs fichier centralisés ([807f786](https://github.com/Masutayunikon/FanKarr/commit/807f786b038579ca9d7cd136998f759cecae2255))
* **organize:** worker thread + badges statut + notifications ([b7242bc](https://github.com/Masutayunikon/FanKarr/commit/b7242bc95f59d259ab1a636aa304bf149f895041))
* **serie:** badges état organisation sur épisodes et saisons ([4d3a6bd](https://github.com/Masutayunikon/FanKarr/commit/4d3a6bd276907f793452429d62693fafc1d804db))
* **ui:** états catalogue + logs + badges erreurs downloads ([5326828](https://github.com/Masutayunikon/FanKarr/commit/5326828d89aa10a53bbe1ac4016a5eee4d5dbc97))
* UX improvements, folder picker, download state & NFO support ([20771fc](https://github.com/Masutayunikon/FanKarr/commit/20771fc31c187fd1fea8aa9b07093b15621a5b85))
### Bug Fixes
* add log for debug ([6876c5e](https://github.com/Masutayunikon/FanKarr/commit/6876c5ee6c7734e664a6da4e4bc721cc7c5abdf7))
* add log for debug ([d8cdeae](https://github.com/Masutayunikon/FanKarr/commit/d8cdeae675bc73b5842d18d01eff9d5bb0e0a7e8))
* add user permissions when the scripts is launched ([501bbfc](https://github.com/Masutayunikon/FanKarr/commit/501bbfcf18c236f7ac382c50d90482b1f51244c1))
* CatalogView.vue badge deleted ([d897192](https://github.com/Masutayunikon/FanKarr/commit/d8971920462efd8d1d99eb867016c96713dfef58))
* docker errors ([2702764](https://github.com/Masutayunikon/FanKarr/commit/27027642eb55b75adb04446a88e5007e88f75948))
* docker is able to change directory ([af08f7d](https://github.com/Masutayunikon/FanKarr/commit/af08f7d9d4fb2e23c5feac6d33b2ef495f9d91b2))
* empty docker-compose.yml ([303dfd8](https://github.com/Masutayunikon/FanKarr/commit/303dfd85b5d033e2b247fd064ae1ae612fdb49e1))
* logger third args ([5abac1e](https://github.com/Masutayunikon/FanKarr/commit/5abac1e11236ed1e8f7f0de7f0cff881d1b60f10))
* nfo settings and add json file when not existing ([0b6bfa6](https://github.com/Masutayunikon/FanKarr/commit/0b6bfa6a81d99353be877ed6d99687ad3d55adbc))
* organize file ([d6550e9](https://github.com/Masutayunikon/FanKarr/commit/d6550e9961789073be1583e03d599afb6d295004))
* organize use torrent hash and not name ([61901f9](https://github.com/Masutayunikon/FanKarr/commit/61901f9014a759e2950311cd96266ea8201fa902))
* path ([314f817](https://github.com/Masutayunikon/FanKarr/commit/314f817280f39d10eb21c4784f2aea41b9a6d32c))
* remove user creation in entrypoint.sh ([92d9fa6](https://github.com/Masutayunikon/FanKarr/commit/92d9fa6b01f1e2de1ceb13528afd309b21f9d4cb))
* rename if the torrent name is not the same as the api ([b8fcf71](https://github.com/Masutayunikon/FanKarr/commit/b8fcf71f54ab14e5572a7553d8554b617b4981b7))
* season fallback ([e9b5091](https://github.com/Masutayunikon/FanKarr/commit/e9b5091242d73be77bf6fb525ce2865554e336cc))
* skip excluded folders ([edf806d](https://github.com/Masutayunikon/FanKarr/commit/edf806d9bc48d4de1c3609a4b0405442305d546e))
* title readme align ([8ba3b37](https://github.com/Masutayunikon/FanKarr/commit/8ba3b370d1e2cd1565f9c2453be8706e3c198243))
* use bcryptjs for compilation ([6a6981e](https://github.com/Masutayunikon/FanKarr/commit/6a6981e10a3a9019f49982d7680e562872ee95a5))
* worker outside the exe ([c2ab58e](https://github.com/Masutayunikon/FanKarr/commit/c2ab58e7b2c34aae09b50dbdfde3277da4c276ff))
* worker outside the exe ([a47c3a4](https://github.com/Masutayunikon/FanKarr/commit/a47c3a43fb5dbbedd87c9ac2d584c3290fcc6097))
* workflow directory ([baebd8f](https://github.com/Masutayunikon/FanKarr/commit/baebd8fae146db70ee1cce0c876d2d3bc0f9bf4e))
* workflow directory ([d8405c1](https://github.com/Masutayunikon/FanKarr/commit/d8405c1589f277cd8fdd3e1fd15cc02d98834aa6))
* workflow permmisions ([3620bfb](https://github.com/Masutayunikon/FanKarr/commit/3620bfb71d8c2774b23c097e5e01326099c2f180))
## [1.9.5](https://github.com/Masutayunikon/FanKarr/compare/v1.9.4...v1.9.5) (2026-03-13)
### Bug Fixes
* worker outside the exe ([c2ab58e](https://github.com/Masutayunikon/FanKarr/commit/c2ab58e7b2c34aae09b50dbdfde3277da4c276ff))
## [1.9.4](https://github.com/Masutayunikon/FanKarr/compare/v1.9.3...v1.9.4) (2026-03-13)
### Bug Fixes
* worker outside the exe ([a47c3a4](https://github.com/Masutayunikon/FanKarr/commit/a47c3a43fb5dbbedd87c9ac2d584c3290fcc6097))
## [1.9.3](https://github.com/Masutayunikon/FanKarr/compare/v1.9.2...v1.9.3) (2026-03-13)
### Bug Fixes
* use bcryptjs for compilation ([6a6981e](https://github.com/Masutayunikon/FanKarr/commit/6a6981e10a3a9019f49982d7680e562872ee95a5))
## [1.9.2](https://github.com/Masutayunikon/FanKarr/compare/v1.9.1...v1.9.2) (2026-03-13)
### Bug Fixes
* workflow directory ([baebd8f](https://github.com/Masutayunikon/FanKarr/commit/baebd8fae146db70ee1cce0c876d2d3bc0f9bf4e))
## [1.9.1](https://github.com/Masutayunikon/FanKarr/compare/v1.9.0...v1.9.1) (2026-03-13)
### Bug Fixes
* workflow directory ([d8405c1](https://github.com/Masutayunikon/FanKarr/commit/d8405c1589f277cd8fdd3e1fd15cc02d98834aa6))
* workflow permmisions ([3620bfb](https://github.com/Masutayunikon/FanKarr/commit/3620bfb71d8c2774b23c097e5e01326099c2f180))
## [1.9.0](https://github.com/Masutayunikon/FanKarr/compare/v1.8.2...v1.9.0) (2026-03-13)
### Features
* add windows / linux / macos binaries on build ([f17a73b](https://github.com/Masutayunikon/FanKarr/commit/f17a73b9591fb49a75bd5fcdadf1970585877dab))
## [1.8.2](https://github.com/Masutayunikon/FanKarr/compare/v1.8.1...v1.8.2) (2026-03-13) ## [1.8.2](https://github.com/Masutayunikon/FanKarr/compare/v1.8.1...v1.8.2) (2026-03-13)
+36 -15
View File
@@ -69,14 +69,9 @@ Fankarr récupère ce fichier au démarrage et via le bouton **Mettre à jour**
## Installation ## Installation
### Prérequis ### Docker Compose (recommandé)
- Docker + Docker Compose **Prérequis** : Docker + Docker Compose, qBittorrent accessible en réseau.
- qBittorrent accessible en réseau
- Un dossier de téléchargements complétés (`/downloads/complete` ou équivalent)
- Un dossier médiathèque pour Jellyfin (`/media/Kai` ou équivalent)
### Docker Compose
```yaml ```yaml
services: services:
@@ -88,8 +83,8 @@ services:
- PGID=1000 # GID de votre utilisateur (id -g) - PGID=1000 # GID de votre utilisateur (id -g)
- TZ=Europe/Paris # Votre timezone - TZ=Europe/Paris # Votre timezone
volumes: volumes:
- ./data:/app/data # Config, logs, base de données - ./data:/app/data # Config, logs, base de données
- /votre/chemin/media:/media/Kai # Destination Jellyfin - /votre/chemin/media:/media/Kai # Destination Jellyfin
- /votre/chemin/complete:/downloads/complete # Dossier complétés qBittorrent - /votre/chemin/complete:/downloads/complete # Dossier complétés qBittorrent
ports: ports:
- 3001:3001 - 3001:3001
@@ -100,15 +95,41 @@ services:
docker compose up -d docker compose up -d
``` ```
Fankarr sera accessible sur `http://localhost:3001`. ### Binaire autonome (Windows / Linux / macOS)
Téléchargez l'archive correspondant à votre système depuis les [Releases GitHub](https://github.com/masutayunikon/fankarr/releases/latest) :
| Système | Archive |
|---|---|
| Windows x64 | `fankarr-windows-x64.zip` |
| Linux x64 | `fankarr-linux-x64.zip` |
| macOS (Apple Silicon) | `fankarr-macos.zip` |
Extrayez l'archive — elle contient le binaire **et** le dossier `public/` qui doivent rester ensemble :
```
fankarr-windows-x64/
├── fankarr.exe ← lancer ce fichier
└── public/ ← ne pas déplacer séparément
```
**Linux / macOS** : rendre le binaire exécutable avant le premier lancement :
```bash
chmod +x fankarr
./fankarr
```
Fankarr sera accessible sur `http://localhost:3001`. Les données (config, logs, base de données) sont sauvegardées dans un dossier `data/` créé automatiquement à côté du binaire.
### Variables d'environnement ### Variables d'environnement
| Variable | Défaut | Description | | Variable | Défaut | Description |
|---|---|---| |---|---|---|
| `PUID` | `1000` | UID utilisateur pour les permissions fichiers | | `PUID` | `1000` | UID utilisateur pour les permissions fichiers (Docker) |
| `PGID` | `1000` | GID utilisateur pour les permissions fichiers | | `PGID` | `1000` | GID utilisateur pour les permissions fichiers (Docker) |
| `TZ` | — | Timezone (ex: `Europe/Paris`) | | `TZ` | — | Timezone (ex: `Europe/Paris`) |
| `PORT` | `3001` | Port d'écoute du serveur |
| `JWT_SECRET` | auto-généré | Secret JWT — généré automatiquement dans `data/secret.key` si absent | | `JWT_SECRET` | auto-généré | Secret JWT — généré automatiquement dans `data/secret.key` si absent |
| `GITHUB_RAW_URL` | repo scraper | URL du `torrent_final.json` à utiliser | | `GITHUB_RAW_URL` | repo scraper | URL du `torrent_final.json` à utiliser |
@@ -130,7 +151,7 @@ Fankarr organise automatiquement les fichiers terminés toutes les **30 secondes
``` ```
/media/Kai/ /media/Kai/
└── Black Lagoon Henshū/ └── Black Lagoon Henshū/
└── Saison 001/ └── Saison 1/
├── Black Lagoon Henshū S01E01.mkv ├── Black Lagoon Henshū S01E01.mkv
├── Black Lagoon Henshū S01E02.mkv ├── Black Lagoon Henshū S01E02.mkv
└── ... └── ...
@@ -157,8 +178,8 @@ Fankarr est disponible dans l'appstore personnalisé. Pour l'installer :
| Frontend | Vue 3 + Vite + Tailwind CSS | | Frontend | Vue 3 + Vite + Tailwind CSS |
| Backend | Express 5 + TypeScript | | Backend | Express 5 + TypeScript |
| Auth | JWT + bcrypt | | Auth | JWT + bcrypt |
| Packaging | Docker (`node:20-slim`) + pnpm | | Packaging Docker | `node:20-slim` + pnpm + gosu (PUID/PGID) |
| Permissions | gosu (PUID/PGID) | | Packaging binaire | Bun (self-contained, pas de Node requis) |
--- ---
+10 -5
View File
@@ -7,12 +7,16 @@
"dev": "concurrently \"vite\" \"tsx watch server/index.ts\"", "dev": "concurrently \"vite\" \"tsx watch server/index.ts\"",
"dev:server": "tsx watch server/index.ts", "dev:server": "tsx watch server/index.ts",
"dev:client": "vite", "dev:client": "vite",
"build": "vite build --outDir dist/client && tsc -p tsconfig.server.json", "build": "vite build && tsc -p tsconfig.server.json",
"build:bun:linux": "bun build server/index.ts --compile --target bun-linux-x64 --outfile binaries/fankarr-linux-x64 && bun build server/organize-worker.ts --target node --outfile binaries/organize-worker.js",
"build:bun:windows": "bun build server/index.ts --compile --target bun-windows-x64 --outfile binaries/fankarr-windows-x64.exe && bun build server/organize-worker.ts --target node --outfile binaries/organize-worker.js",
"build:bun:macos": "bun build server/index.ts --compile --target bun-darwin-arm64 --outfile binaries/fankarr-macos && bun build server/organize-worker.ts --target node --outfile binaries/organize-worker.js",
"build:bun:all": "pnpm build:bun:linux && pnpm build:bun:windows && pnpm build:bun:macos",
"start": "node dist/server/index.js",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.2.1", "@tailwindcss/vite": "^4.2.1",
"bcrypt": "^6.0.0",
"cookie-parser": "^1.4.7", "cookie-parser": "^1.4.7",
"express": "^5.2.1", "express": "^5.2.1",
"jsonwebtoken": "^9.0.3", "jsonwebtoken": "^9.0.3",
@@ -20,10 +24,10 @@
"pinia": "^3.0.4", "pinia": "^3.0.4",
"tailwindcss": "^4.2.1", "tailwindcss": "^4.2.1",
"vue": "^3.5.25", "vue": "^3.5.25",
"vue-router": "^5.0.3" "vue-router": "^5.0.3",
"bcryptjs": "^3.0.3"
}, },
"devDependencies": { "devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/cookie-parser": "^1.4.8", "@types/cookie-parser": "^1.4.8",
"@types/express": "^5.0.6", "@types/express": "^5.0.6",
"@types/jsonwebtoken": "^9.0.9", "@types/jsonwebtoken": "^9.0.9",
@@ -34,6 +38,7 @@
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"vite": "^7.3.1", "vite": "^7.3.1",
"vue-tsc": "^3.1.5" "vue-tsc": "^3.1.5",
"@types/bcrypt": "^5.0.2"
} }
} }
+7 -22
View File
@@ -11,9 +11,9 @@ importers:
'@tailwindcss/vite': '@tailwindcss/vite':
specifier: ^4.2.1 specifier: ^4.2.1
version: 4.2.1(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2)) version: 4.2.1(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(tsx@4.21.0)(yaml@2.8.2))
bcrypt: bcryptjs:
specifier: ^6.0.0 specifier: ^3.0.3
version: 6.0.0 version: 3.0.3
cookie-parser: cookie-parser:
specifier: ^1.4.7 specifier: ^1.4.7
version: 1.4.7 version: 1.4.7
@@ -650,9 +650,9 @@ packages:
resolution: {integrity: sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg==} resolution: {integrity: sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
bcrypt@6.0.0: bcryptjs@3.0.3:
resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==} resolution: {integrity: sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==}
engines: {node: '>= 18'} hasBin: true
birpc@2.9.0: birpc@2.9.0:
resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
@@ -1087,10 +1087,6 @@ packages:
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
node-addon-api@8.6.0:
resolution: {integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==}
engines: {node: ^18 || ^20 || >= 21}
node-domexception@1.0.0: node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'} engines: {node: '>=10.5.0'}
@@ -1100,10 +1096,6 @@ packages:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
node-gyp-build@4.8.4:
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
hasBin: true
object-inspect@1.13.4: object-inspect@1.13.4:
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -1919,10 +1911,7 @@ snapshots:
'@babel/parser': 7.29.0 '@babel/parser': 7.29.0
ast-kit: 2.2.0 ast-kit: 2.2.0
bcrypt@6.0.0: bcryptjs@3.0.3: {}
dependencies:
node-addon-api: 8.6.0
node-gyp-build: 4.8.4
birpc@2.9.0: {} birpc@2.9.0: {}
@@ -2351,8 +2340,6 @@ snapshots:
negotiator@1.0.0: {} negotiator@1.0.0: {}
node-addon-api@8.6.0: {}
node-domexception@1.0.0: {} node-domexception@1.0.0: {}
node-fetch@3.3.2: node-fetch@3.3.2:
@@ -2361,8 +2348,6 @@ snapshots:
fetch-blob: 3.2.0 fetch-blob: 3.2.0
formdata-polyfill: 4.0.10 formdata-polyfill: 4.0.10
node-gyp-build@4.8.4: {}
object-inspect@1.13.4: {} object-inspect@1.13.4: {}
on-finished@2.4.1: on-finished@2.4.1:
+1 -1
View File
@@ -1,6 +1,6 @@
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import bcrypt from 'bcrypt' import bcrypt from 'bcryptjs'
import jwt from 'jsonwebtoken' import jwt from 'jsonwebtoken'
import type { Request, Response, NextFunction } from 'express' import type { Request, Response, NextFunction } from 'express'
import { JWT_SECRET } from './secret.js' import { JWT_SECRET } from './secret.js'
+12 -3
View File
@@ -15,10 +15,19 @@ import { logger, readLogs, clearLogs, logsFileSize } from './logger.js'
registerDriver(qbittorrentDriver) registerDriver(qbittorrentDriver)
// ── Base directory ─────────────────────────────────────────────
// Binaire Bun compilé : data/ et public/ sont à côté de l'exécutable
// Docker / dev : relatif au cwd (comportement inchangé)
const _isBunBinary = typeof (globalThis as any).Bun !== 'undefined'
&& path.dirname((process as any).execPath) !== process.cwd()
export const BASE_DIR = _isBunBinary
? path.dirname((process as any).execPath)
: process.cwd()
const app = express() const app = express()
const PORT = 3001 const PORT = Number(process.env.PORT) || 3001
const FANKAI_API = 'https://metadata.fankai.fr' const FANKAI_API = 'https://metadata.fankai.fr'
const TORRENTS_PATH = path.join(process.cwd(), 'data', 'torrent_final.json') const TORRENTS_PATH = path.join(BASE_DIR, 'data', 'torrent_final.json')
const GITHUB_RAW_URL = process.env.GITHUB_RAW_URL const GITHUB_RAW_URL = process.env.GITHUB_RAW_URL
?? 'https://raw.githubusercontent.com/masutayunikon/fankarr-scraper/main/data/torrent_final.json' ?? 'https://raw.githubusercontent.com/masutayunikon/fankarr-scraper/main/data/torrent_final.json'
@@ -26,7 +35,7 @@ app.use(express.json())
app.use(cookieParser()) app.use(cookieParser())
// ── Static frontend (prod) ───────────────────────────────────── // ── Static frontend (prod) ─────────────────────────────────────
const PUBLIC_PATH = path.join(process.cwd(), 'public') const PUBLIC_PATH = path.join(BASE_DIR, 'public')
if (fs.existsSync(PUBLIC_PATH)) { if (fs.existsSync(PUBLIC_PATH)) {
app.use(express.static(PUBLIC_PATH)) app.use(express.static(PUBLIC_PATH))
} }
+9 -2
View File
@@ -16,8 +16,15 @@ import fsp from 'fs/promises'
import path from 'path' import path from 'path'
import { parentPort } from 'worker_threads' import { parentPort } from 'worker_threads'
const TORRENTS_PATH = path.join(process.cwd(), 'data', 'torrent_final.json') // Base directory : binaire Bun ou cwd (Docker/dev)
const ORGANIZED_PATH = path.join(process.cwd(), 'data', 'organized.json') const _isBunBinary = typeof (globalThis as any).Bun !== 'undefined'
&& path.dirname((process as any).execPath) !== process.cwd()
const BASE_DIR = _isBunBinary
? path.dirname((process as any).execPath)
: process.cwd()
const TORRENTS_PATH = path.join(BASE_DIR, 'data', 'torrent_final.json')
const ORGANIZED_PATH = path.join(BASE_DIR, 'data', 'organized.json')
// ─── Utils log ──────────────────────────────────────────────── // ─── Utils log ────────────────────────────────────────────────
// Le worker tourne dans un thread séparé — il passe les logs au thread principal // Le worker tourne dans un thread séparé — il passe les logs au thread principal
+27 -11
View File
@@ -13,6 +13,18 @@ import { logger } from './logger.js'
// Un seul worker à la fois // Un seul worker à la fois
let workerRunning = false let workerRunning = false
// Résolution du chemin du worker
// En binaire Bun compilé : le worker est à côté de l'exécutable
// En Docker/dev : à côté du fichier courant
function resolveWorkerPath(): string {
const _isBunBinary = typeof (globalThis as any).Bun !== 'undefined'
&& path.dirname((process as any).execPath) !== process.cwd()
if (_isBunBinary) {
return path.join(path.dirname((process as any).execPath), 'organize-worker.js')
}
return path.join(path.dirname(new URL(import.meta.url).pathname), 'organize-worker.js')
}
export interface OrganizeResult { export interface OrganizeResult {
total : number total : number
skipped : number skipped : number
@@ -112,7 +124,7 @@ export async function autoOrganizeAll(
onResult?: (r: { hash: string; name: string; done: number; skipped: number; errors: number; errorFiles: { file: string; error: string }[] }) => void onResult?: (r: { hash: string; name: string; done: number; skipped: number; errors: number; errorFiles: { file: string; error: string }[] }) => void
): Promise<void> { ): Promise<void> {
if (workerRunning) { if (workerRunning) {
logger.debug('organize', 'Worker déjà en cours, skip') console.log('[organize] Worker déjà en cours, skip')
return return
} }
@@ -120,7 +132,7 @@ export async function autoOrganizeAll(
try { try {
torrents = await listFn() torrents = await listFn()
} catch (err) { } catch (err) {
logger.error('organize', "Impossible de récupérer la liste des torrents: " + (err instanceof Error ? err.message : String(err))) console.error('[organize] Impossible de récupérer la liste des torrents:', err)
return return
} }
@@ -129,18 +141,20 @@ export async function autoOrganizeAll(
const seedingCount = torrents.filter(t => t.state === 'seeding').length const seedingCount = torrents.filter(t => t.state === 'seeding').length
if (seedingCount === 0) return if (seedingCount === 0) return
logger.info('organize', `Lancement worker (${seedingCount} torrents en seeding)`) console.log(`[organize] Lancement worker (${seedingCount} torrents en seeding)`)
workerRunning = true workerRunning = true
const workerPath = path.join(path.dirname(new URL(import.meta.url).pathname), 'organize-worker.js') const workerPath = resolveWorkerPath()
const worker = new Worker(workerPath) const worker = new Worker(workerPath)
worker.on('message', (msg: any) => { worker.on('message', (msg: any) => {
if (msg.type === 'log') { if (msg.type === 'log') {
logger[msg.level as 'debug'|'info'|'warn'|'error']('organize', msg.msg) if (msg.level === 'error') console.error(msg.msg)
else if (msg.level === 'warn') console.warn(msg.msg)
else console.log(msg.msg)
} else if (msg.type === 'result') { } else if (msg.type === 'result') {
logger.info('organize', `${msg.name} → ${msg.done} OK, ${msg.skipped} skippés, ${msg.errors.length} erreurs`) console.log(`[organize] ${msg.name} → ${msg.done} OK, ${msg.skipped} skippés, ${msg.errors.length} erreurs`)
onResult?.({ onResult?.({
hash : msg.hash, hash : msg.hash,
name : msg.name, name : msg.name,
@@ -150,19 +164,19 @@ export async function autoOrganizeAll(
errorFiles : msg.errors, errorFiles : msg.errors,
}) })
} else if (msg.type === 'done') { } else if (msg.type === 'done') {
logger.debug('organize', 'Worker terminé') console.log('[organize] Worker terminé')
workerRunning = false workerRunning = false
worker.terminate() worker.terminate()
} }
}) })
worker.on('error', (err) => { worker.on('error', (err) => {
logger.error('organize', `Worker erreur: ${err instanceof Error ? err.message : err}`) console.error('[organize] Worker erreur:', err)
workerRunning = false workerRunning = false
}) })
worker.on('exit', (code) => { worker.on('exit', (code) => {
if (code !== 0) logger.error('organize', `Worker exit code ${code}`) if (code !== 0) console.error(`[organize] Worker exit code ${code}`)
workerRunning = false workerRunning = false
}) })
@@ -180,7 +194,7 @@ export async function organizeTorrent(
savePath: string savePath: string
): Promise<OrganizeResult> { ): Promise<OrganizeResult> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const workerPath = path.join(path.dirname(new URL(import.meta.url).pathname), 'organize-worker.js') const workerPath = resolveWorkerPath()
const worker = new Worker(workerPath) const worker = new Worker(workerPath)
// On envoie un seul torrent "fictif seeding" // On envoie un seul torrent "fictif seeding"
@@ -189,7 +203,9 @@ export async function organizeTorrent(
worker.on('message', (msg: any) => { worker.on('message', (msg: any) => {
if (msg.type === 'log') { if (msg.type === 'log') {
logger[msg.level as 'debug'|'info'|'warn'|'error']('organize', msg.msg) if (msg.level === 'error') console.error(msg.msg)
else if (msg.level === 'warn') console.warn(msg.msg)
else console.log(msg.msg)
} else if (msg.type === 'result') { } else if (msg.type === 'result') {
result.total = msg.total result.total = msg.total
result.skipped = msg.skipped result.skipped = msg.skipped
+6 -1
View File
@@ -1,7 +1,12 @@
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
const DATA_PATH = path.join(process.cwd(), 'data', 'settings.json') const _isBunBinary = typeof (globalThis as any).Bun !== 'undefined'
&& path.dirname((process as any).execPath) !== process.cwd()
const BASE_DIR = _isBunBinary
? path.dirname((process as any).execPath)
: process.cwd()
const DATA_PATH = path.join(BASE_DIR, 'data', 'settings.json')
export interface Settings { export interface Settings {
mediaPath : string mediaPath : string
+3
View File
@@ -10,6 +10,9 @@ export default defineConfig({
'@': fileURLToPath(new URL('./src', import.meta.url)) '@': fileURLToPath(new URL('./src', import.meta.url))
} }
}, },
build: {
outDir: 'dist/client'
},
server: { server: {
port: 3000, port: 3000,
proxy: { proxy: {