Compare commits

...
17 Commits
Author SHA1 Message Date
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
11 changed files with 227 additions and 46 deletions

No files matched your search

+116
View File
@@ -0,0 +1,116 @@
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: Package archive (Windows)
if: matrix.target == 'windows-x64'
run: Compress-Archive -Path ${{ matrix.binary }}, 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 }} 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/*
+29
View File
@@ -1,5 +1,34 @@
# Changelog # Changelog
## [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) |
--- ---
+7 -2
View File
@@ -7,12 +7,17 @@
"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",
"build:bun:windows": "bun build server/index.ts --compile --target bun-windows-x64 --outfile binaries/fankarr-windows-x64.exe",
"build:bun:macos": "bun build server/index.ts --compile --target bun-darwin-arm64 --outfile binaries/fankarr-macos",
"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", "bcryptjs": "^3.0.3",
"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",
+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
+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: {