Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Thumbs.db
logs/
uploads/
downloads/
secrets/
youtube-cookies.txt
bin/
build/
dist/
Expand Down
38 changes: 0 additions & 38 deletions .github/workflows/codeql.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
echo "minor=${MAJOR}.${MINOR}" >> "${GITHUB_OUTPUT}"

# `release=true` => on publie les tags de version IMMUABLES (X.Y.Z, X.Y).
# `release=true` => on publie X.Y.Z (immuable) et X.Y (canal mineur mobile).
# Vrai uniquement sur un tag Git, ou sur un push main qui modifie VERSION.
# Ainsi le cron nocturne et les commits sans bump ne réécrivent que `latest`.
RELEASE=false
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ cython_debug/
uploads/
downloads/
logs/
/secrets/
youtube-cookies.txt

# Tailwind — CSS généré (rebuild via Dockerfile ou `make tailwind-build`)
app/static/css/tailwind.css
Expand Down
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,45 @@ et ce projet adhère au [Versionnage Sémantique](https://semver.org/lang/fr/).

---

## [2.0.1] - 2026-08-08

### Informations légales

- Ajout d'une politique de confidentialité commune au site Web et à
l'application mobile, accessible sur `/confidentialite`.
- Ajout des conditions d'utilisation sur `/conditions-utilisation`, avec un
encadrement explicite des téléchargements et contenus tiers.
- Ajout d'alias stables `/privacy` et `/terms`, des liens dans le pied de page
et de tests de disponibilité.
- Documentation des traitements locaux, fichiers temporaires, journaux,
limitations de débit, services réseau tiers et droits RGPD.
- Identification de l'association Nerieum comme éditrice et exploitante du
service dans les pages légales, le pied de page et les métadonnées de l'image.

### Téléchargeur YouTube

- Ajout de Deno 2.9.5 et `yt-dlp-ejs` dans l'image pour résoudre les challenges
JavaScript récents de YouTube.
- Prise en charge optionnelle d'un export Netscape de cookies via
`YTDLP_COOKIES_FILE`, conservé hors du dépôt et monté en lecture seule.
- Cookie jar inscriptible conservé dans un volume Docker, initialisé depuis le
secret en lecture seule et partagé sous verrou entre les workers Gunicorn.
- Réinitialisation automatique du jar persistant lorsque l'empreinte SHA-256 de
l'export source change.
- Message explicite et statut `503` lorsque YouTube exige une session serveur.
- Correction du répertoire personnel de l'utilisateur non-root et désactivation
du control socket Gunicorn incompatible avec les permissions du conteneur.

### Distribution et documentation

- Références GitHub et GHCR alignées sur le nouvel emplacement
`Nerieum/ToolboxEverything` du dépôt.
- Publication de l'URL de l'instance officielle et documentation de la procédure
d'export, de montage et de renouvellement des cookies YouTube.
- Suppression du workflow CodeQL redondant avec l'analyse par défaut de GitHub.

---

## [2.0.0] - 2026-07-19

Refonte de fond en comble sur trois fronts (design, backend, outillage), à
Expand Down
41 changes: 26 additions & 15 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
# =====================================================
# Toolbox Everything — image Docker (multi-stage)
#
# 1. py-builder : installe les deps Python dans /opt/venv
# 2. css-builder : compile Tailwind (binaire Go standalone, pas de Node)
# 3. runtime : image finale, zéro outil de build
# 1. deno-bin : fournit le runtime JS recommandé par yt-dlp
# 2. py-builder : installe les deps Python dans /opt/venv
# 3. css-builder : compile Tailwind (binaire Go standalone, pas de Node)
# 4. runtime : image finale, zéro outil de build
# =====================================================
ARG TAILWIND_VERSION=4.3.3
ARG DENO_VERSION=2.9.5

# -----------------------------------------------------
# 1) Build des deps Python
# 1) Runtime JavaScript pour les challenges YouTube EJS
# -----------------------------------------------------
FROM denoland/deno:bin-${DENO_VERSION} AS deno-bin

# -----------------------------------------------------
# 2) Build des deps Python
# -----------------------------------------------------
FROM python:3.12-slim AS py-builder

Expand All @@ -19,7 +26,7 @@ COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# -----------------------------------------------------
# 2) Build du CSS Tailwind
# 3) Build du CSS Tailwind
# Même pipeline que le dev local : script Python + CLI standalone.
# Le binaire est téléchargé dans ce stage, puis jeté.
# -----------------------------------------------------
Expand All @@ -40,23 +47,26 @@ RUN python scripts/tailwind.py build && \
python scripts/tailwind.py check

# -----------------------------------------------------
# 3) Image finale
# 4) Image finale
# -----------------------------------------------------
FROM python:3.12-slim

LABEL maintainer="toolbox-everything"
LABEL maintainer="Association Nerieum"
LABEL description="Toolbox Everything - Une boite a outils web complete"
LABEL org.opencontainers.image.source="https://github.com/doalou/toolbox_everything"
LABEL org.opencontainers.image.documentation="https://github.com/doalou/toolbox_everything/README.md"
LABEL org.opencontainers.image.authors="Association Nerieum"
LABEL org.opencontainers.image.vendor="Association Nerieum"
LABEL org.opencontainers.image.source="https://github.com/Nerieum/ToolboxEverything"
LABEL org.opencontainers.image.documentation="https://github.com/Nerieum/ToolboxEverything/blob/main/README.md"

# FFmpeg est la seule dépendance système du runtime. Le healthcheck utilise
# urllib (stdlib), donc curl n'est plus nécessaire dans l'image.
# FFmpeg est la seule dépendance installée via APT. Le healthcheck utilise
# urllib (stdlib), et Deno est copié depuis son image binaire officielle.
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY --from=py-builder /opt/venv /opt/venv
COPY --from=deno-bin /deno /usr/local/bin/deno
ENV PATH="/opt/venv/bin:$PATH"

WORKDIR /app
Expand All @@ -71,16 +81,17 @@ ENV PYTHONUNBUFFERED=1
ENV FLASK_APP=run.py
ENV FLASK_ENV=production
ENV DOCKER_ENV=1
ENV HOME=/home/toolbox
EXPOSE 8000

RUN groupadd -r toolbox && \
useradd -r -g toolbox toolbox && \
mkdir -p uploads/temp logs && \
chown -R toolbox:toolbox /app
useradd -r -m -d /home/toolbox -g toolbox toolbox && \
mkdir -p uploads/temp logs /home/toolbox/.cache/deno /var/lib/toolbox/yt-dlp && \
chown -R toolbox:toolbox /app /home/toolbox /var/lib/toolbox
USER toolbox

# Une clé éphémère unique est créée au démarrage du conteneur si aucune clé
# persistante n'est fournie. Elle n'est ainsi jamais figée dans une couche.
CMD export SECRET_KEY="${SECRET_KEY:-$(python -c 'import secrets; print(secrets.token_hex(32))')}" && \
exec gunicorn --bind 0.0.0.0:8000 --workers 4 --threads 2 --timeout 900 \
--access-logfile - --error-logfile - run:app
--no-control-socket --access-logfile - --error-logfile - run:app
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ PIP := pip
FLASK_APP := run.py
PORT := 8000
VERSION := $(shell if [ -f VERSION ]; then tr -d '\r\n' < VERSION; else echo "0.0.0"; fi)
GHCR_IMAGE ?= ghcr.io/doalou/toolbox_everything
GHCR_IMAGE ?= ghcr.io/nerieum/toolboxeverything
TAILWIND_VERSION := 4.3.3
TAILWIND := $(PYTHON) scripts/tailwind.py

Expand Down
105 changes: 91 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# Toolbox Everything

> Une boîte à outils web, modulaire et sans prise de tête : télécharger une vidéo (YouTube, Vimeo, Dailymotion, TikTok), convertir un média, bidouiller un QR code ou un hash, et manipuler des PDF sans quitter son navigateur.
# Le projet est actuellement en cours de refonte, la description ci-dessous correspond à l'ancienne version et une nouvelle version sera bientôt proposée.

Stack : **Flask + Tailwind**, tout en Docker, prêt à être posé derrière un reverse proxy.
Interface **sobre et utilitaire** (v2.0.0) : couche de tokens CSS, thème clair/sombre
Interface **sobre et utilitaire** (v2.0.1) : couche de tokens CSS, thème clair/sombre
piloté par variables, Fraunces self-hébergée et zéro dépendance front tierce au runtime.

![version](https://img.shields.io/badge/version-2.0.0-blue)
Site public : <https://toolbox.doalo.fr>

Toolbox Everything est édité et exploité par l'**association Nerieum**.

![version](https://img.shields.io/badge/version-2.0.1-blue)
![python](https://img.shields.io/badge/python-3.12-3776AB?logo=python&logoColor=white)
![flask](https://img.shields.io/badge/flask-3.1-000000?logo=flask)
![license](https://img.shields.io/badge/license-MIT-green)
Expand All @@ -33,7 +36,7 @@ piloté par variables, Fraunces self-hébergée et zéro dépendance front tierc
Un **unique** `compose.yml` couvre les deux modes de déploiement, choix via `TOOLBOX_IMAGE` :

```bash
git clone https://github.com/doalou/toolbox_everything.git
git clone https://github.com/Nerieum/ToolboxEverything.git
cd toolbox_everything
Comment on lines +39 to 40
cp env.example .env # édite au moins SECRET_KEY
docker compose up -d --build # build local (dev / CI)
Expand All @@ -42,7 +45,7 @@ docker compose up -d --build # build local (dev / CI)
Ou en prod avec l'image publique GHCR :

```bash
export TOOLBOX_IMAGE=ghcr.io/doalou/toolbox_everything:2.0.0
export TOOLBOX_IMAGE=ghcr.io/nerieum/toolboxeverything:2.0.1
docker compose pull && docker compose up -d
```

Expand Down Expand Up @@ -90,19 +93,92 @@ Tout se passe dans `.env` (copié depuis `env.example`) :
| `FLASK_ENV` | `development` ou `production` | `production` |
| `MAX_CONTENT_LENGTH` | Taille max des uploads (octets) | `536870912` (512 MB) |
| `FFMPEG_PATH` | Chemin explicite vers FFmpeg | auto-détecté (`shutil.which`) |
| `YTDLP_COOKIES_FILE` | Fichier Netscape de cookies pour yt-dlp | désactivé |
| `YTDLP_COOKIES_STATE_DIR` | Cookie jar inscriptible et persistant | `/var/lib/toolbox/yt-dlp` |
| `YTDLP_USER_AGENT` | User-Agent associé aux cookies yt-dlp | défaut yt-dlp |
| `YTDLP_DENO_PATH` | Chemin du runtime Deno | auto-détecté (`shutil.which`) |
| `STIRLING_PDF_URL` | URL **interne** de Stirling PDF (healthcheck serveur) | `http://stirling-pdf:8080` |
| `STIRLING_PDF_PUBLIC_URL` | URL **publique** utilisée par l'iframe (navigateur) | `http://localhost:8080` |
| `LIBRESPEED_URL` | URL **interne** de LibreSpeed (healthcheck serveur) | `http://librespeed` |
| `LIBRESPEED_PUBLIC_URL` | URL **publique** utilisée par l'iframe (navigateur) | `http://localhost:8081` |
| `RATELIMIT_STORAGE_URI` | Backend du rate limiter (Redis en prod) | `redis://redis:6379/0` |
| `TOOLBOX_IMAGE` | Image Docker à tirer depuis GHCR | `ghcr.io/doalou/toolbox_everything:2.0.0` |
| `TOOLBOX_IMAGE` | Image Docker à tirer depuis GHCR | `ghcr.io/nerieum/toolboxeverything:2.0.1` |
| `TOOLBOX_PORT` / `STIRLING_PORT` / `LIBRESPEED_PORT` | Ports hôte exposés | `8000` / `8080` / `8081` |

### YouTube : Deno et authentification

L'image Docker contient déjà Deno et `yt-dlp-ejs`. Ils permettent à yt-dlp de
résoudre les challenges JavaScript de YouTube et ne demandent aucune
configuration.

Un fichier de cookies est uniquement nécessaire lorsque YouTube répond
« Sign in to confirm you're not a bot ». Le `.env` contient alors le chemin du
fichier, jamais les cookies eux-mêmes ni un token.

#### Préparer le fichier de cookies

1. Ouvrir une fenêtre privée, puis se connecter avec un compte YouTube dédié.
2. Dans le même onglet, ouvrir `https://www.youtube.com/robots.txt`.
3. Exporter les cookies `youtube.com` au format Netscape avec
**Get cookies.txt LOCALLY** (Chrome/Chromium) ou **cookies.txt** (Firefox).
4. Fermer la fenêtre privée et ne plus utiliser cette session dans le navigateur.

Les liens des extensions et les précautions à suivre sont maintenus dans la
[documentation officielle yt-dlp](https://github.com/yt-dlp/yt-dlp/wiki/Extractors#exporting-youtube-cookies).
Ne jamais utiliser l'ancienne extension Chrome **Get cookies.txt** sans
« LOCALLY », signalée comme malveillante par yt-dlp.

Déposer ensuite le fichier à la racine du projet :

```bash
install -d -m 700 secrets
install -m 644 /path/to/youtube-cookies.txt secrets/youtube-cookies.txt
```

Le dossier privé protège le fichier sur l'hôte. Le mode `644` permet au
processus non-root du conteneur de le lire ; le montage Docker reste en lecture
seule. Pour laisser yt-dlp actualiser son cookie jar sans modifier le secret,
l'application initialise une copie inscriptible dans le volume Docker
`toolbox_ytdlp_state`, puis conserve les mises à jour reçues de YouTube. Les
workers partagent ce jar sous verrou. Le remplacement du fichier source
réinitialise automatiquement la copie grâce à son empreinte SHA-256. Le dossier
`secrets/` est exclu de Git et du contexte de build.

#### Activer les cookies

Dans `.env` :

```dotenv
YTDLP_COOKIES_FILE=/run/secrets/youtube-cookies.txt
YTDLP_COOKIES_STATE_DIR=/var/lib/toolbox/yt-dlp
```

Dans le service `toolbox` de `compose.yml` :

```yaml
volumes:
- ./secrets/youtube-cookies.txt:/run/secrets/youtube-cookies.txt:ro
```

Recréer le service puis tester la session :

```bash
docker compose up -d --force-recreate toolbox
docker compose exec toolbox yt-dlp \
--cookies /run/secrets/youtube-cookies.txt \
--simulate --print title 'https://www.youtube.com/watch?v=BaW_jenozKc'
```

Si YouTube redemande une connexion, refaire l'export et remplacer le fichier.
Un PO Token est un mécanisme différent et n'est pas configuré par ce projet ;
le [guide yt-dlp](https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide) ne devient
pertinent que si l'erreur mentionne explicitement un PO Token.

---

## Versioning et images GHCR

La version de référence est `VERSION`. Pour la v2.0.0, elle alimente :
La version de référence est `VERSION`. Pour la v2.0.1, elle alimente :

- La version affichée dans le footer et `/health`.
- Les tags locaux générés par `make docker-build`.
Expand All @@ -111,9 +187,9 @@ La version de référence est `VERSION`. Pour la v2.0.0, elle alimente :
Images publiées :

```bash
ghcr.io/doalou/toolbox_everything:2.0.0
ghcr.io/doalou/toolbox_everything:2.0
ghcr.io/doalou/toolbox_everything:latest
ghcr.io/nerieum/toolboxeverything:2.0.1
ghcr.io/nerieum/toolboxeverything:2.0
ghcr.io/nerieum/toolboxeverything:latest
```

Règle de release :
Expand All @@ -122,16 +198,17 @@ Règle de release :
2. Reporter la version dans le badge README, les exemples GHCR et `CHANGELOG.md`.
3. Merger sur `main`.
4. Le workflow Docker détecte le bump de `VERSION` sur ce push et publie les tags
**immuables** `X.Y.Z` et `X.Y`, plus `latest`. Les commits sans bump (et le cron
nocturne) ne réécrivent que `latest`.
`X.Y.Z` (immuable), `X.Y` (dernière correction de la branche mineure) et
`latest`. Les commits sans bump, ainsi que le cron nocturne, ne réécrivent que
`latest`.
5. Le workflow `Release tag` crée en parallèle le tag Git `vX.Y.Z` (marqueur d'historique).

Exemple pour publier une nouvelle version :

```bash
echo 2.0.1 > VERSION
echo 2.0.2 > VERSION
git add VERSION CHANGELOG.md README.md
git commit -m "Release 2.0.1"
git commit -m "Release 2.0.2"
git push origin main
```

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.0
2.0.1
2 changes: 1 addition & 1 deletion app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def _read_version() -> str:
version_file = Path(__file__).resolve().parent.parent / "VERSION"
if version_file.exists():
return version_file.read_text(encoding="utf-8").strip()
return "2.0.0"
return "2.0.1"


__version__ = _read_version()
Expand Down
Loading