Files
Noxely/README.md
T
tuxgyver c1912c2056 Recherche LibriVox réparée, téléchargements Archive fiabilisés, mises à jour intégrées (1.1.0)
Deux bugs signalés à l'usage, et la distribution hors magasin qui manquait.

LibriVox ne renvoyait jamais rien : l'API cherche les titres en correspondance
exacte sauf préfixe « ^ », et les auteurs sur le seul nom de famille. Les trois
critères sont interrogés en parallèle et dédoublonnés, et un 404 « aucun
résultat » n'est plus confondu avec une panne.

Les téléchargements Internet Archive échouaient pour trois raisons distinctes :
éléments en accès restreint répondant 401, filtre limité aux seuls .mp3/.mp4, et
choix du premier fichier plutôt que du plus petit dérivé. La taille de chaque
résultat est désormais affichée avant sélection, ce qui distingue une chanson
d'une émission de 500 Mo.

L'application étant distribuée hors magasin, elle surveille elle-même les
releases du dépôt, télécharge l'APK en Wi-Fi et propose l'installation, qui
reste un geste de l'utilisateur comme Android l'impose.
2026-09-11 20:57:58 +02:00

241 lines
11 KiB
Markdown

# 🎧 Cachette
> Mettez la musique et les vidéos **de côté** pendant que vous avez du réseau. Écoutez-les quand vous n'en avez plus.
[![Platform](https://img.shields.io/badge/Plateforme-Android%208.0%2B-3DDC84?logo=android&logoColor=white)](https://developer.android.com)
[![Kotlin](https://img.shields.io/badge/Kotlin-2.1.0-7F52FF?logo=kotlin&logoColor=white)](https://kotlinlang.org)
[![Compose](https://img.shields.io/badge/Jetpack%20Compose-Material%203-4285F4?logo=jetpackcompose&logoColor=white)](https://developer.android.com/jetpack/compose)
[![Media3](https://img.shields.io/badge/Media3-1.11.1-FF6F00?logo=googleplay&logoColor=white)](https://developer.android.com/media/media3)
[![Version](https://img.shields.io/badge/Version-1.1.0-6B4EFF)](VERSION)
[![Hors ligne](https://img.shields.io/badge/Hors%20ligne-100%25-3FD8C0)](#-comment-ça-marche)
Cachette est un lecteur audio et vidéo pensé pour les trajets, les tunnels, les avions et les
forfaits épuisés. On choisit ce qu'on emporte, l'application le télécharge, et tout reste lisible
sans la moindre connexion.
---
## 📸 Aperçu
| Bibliothèque | Lecture | Hors ligne | Catalogues libres |
|:---:|:---:|:---:|:---:|
| ![Bibliothèque](docs/screenshots/bibliotheque.png) | ![Lecture](docs/screenshots/lecture.png) | ![Hors ligne](docs/screenshots/hors-ligne.png) | ![Catalogue](docs/screenshots/catalogue.png) |
---
## ✨ Fonctionnalités
| | |
|---|---|
| 🎵 **Audio et vidéo** | Un seul lecteur, une seule file d'attente, la vidéo bascule sur une surface dédiée |
| 📥 **Mise en cache explicite** | Rien n'est téléchargé sans un geste : vous décidez quoi garder |
| 🔍 **Parcours des sources** | On entre dans une source, on coche ce qu'on veut, on télécharge en lot |
| 🎼 **Catalogues libres de droits** | Recherche intégrée dans Internet Archive et LibriVox, sans compte ni clé d'API |
| 📻 **Podcasts RSS** | Un flux, ses épisodes, et le choix de ceux qu'on emporte |
| 🔗 **Liens directs** | N'importe quelle URL audio ou vidéo, y compris un serveur média auto-hébergé |
| 📱 **Contenu de l'appareil** | Lecture de la bibliothèque MediaStore, déjà disponible hors connexion par nature |
| 🎛️ **Lecture en arrière-plan** | Notification système, casque, Bluetooth, écran verrouillé |
| 🌙 **Thème clair et sombre** | Material 3, palette de marque, contraste garanti |
| 🔁 **Reprise automatique** | Un téléchargement coupé repart tout seul via WorkManager |
| 🆕 **Mise à jour intégrée** | Surveille les releases du dépôt, télécharge l'APK en Wi-Fi et propose l'installation |
---
## 📦 Sources disponibles
| Source | État | Détail |
|---|:---:|---|
| 📱 Appareil | ✅ | Musique et vidéos MediaStore |
| 🔗 Lien direct | ✅ | URL `http(s)` vers un fichier audio ou vidéo |
| 📻 Podcast RSS | ✅ | Flux standard avec `<enclosure>` |
| 🏛️ Internet Archive | ✅ | Domaine public / Creative Commons |
| 📚 LibriVox | ✅ | Livres audio du domaine public |
### ⚖️ Ce que Cachette ne fait pas — et pourquoi
| Service | Pourquoi |
|---|---|
| 🎵 **Spotify** | Le SDK ne donne aucun accès au flux audio. Le hors-ligne n'existe que dans l'application Spotify. Un podcast également diffusé en RSS public reste ajoutable. |
| ▶️ **YouTube** | Les conditions d'utilisation interdisent d'extraire ou de stocker les flux. Le hors-ligne passe par YouTube Premium. |
| ☁️ **Google Drive / Photos** | Techniquement faisable pour vos propres fichiers, mais cela exige un identifiant OAuth Google lié à votre compte développeur, volontairement non embarqué ici. |
C'est un choix assumé : l'application ne contourne rien, elle travaille avec les sources qui
autorisent la copie locale.
---
## 🧭 Comment ça marche
```
Source ──► Bibliothèque ──► [ Garder ] ──► Cache Media3 ──► Lecture hors ligne
│
reprise auto (WorkManager)
```
Trois propriétaires de données, aucun doublon :
- **MediaStore** possède le contenu local — on le relit, on ne le recopie pas ;
- **`imported.json`** possède les seules références distantes ajoutées à la main ;
- **l'index Media3** possède l'état du cache — taille, progression, complétude.
Le cache utilise un `NoOpCacheEvictor` : il contient **exactement** ce que vous avez demandé à
garder, et rien n'en est effacé sans votre geste. La lecture lit ce cache mais n'y écrit jamais,
pour qu'une écoute en ligne ne le fasse pas gonfler à votre insu.
---
## 🚀 Démarrage
### Prérequis
- ☕ JDK 17
- 🤖 Android SDK avec la plateforme **36** et les build-tools 36
- 📱 Un appareil ou un émulateur en **Android 8.0 (API 26)** ou plus
### Construire
```bash
make build # APK de debug
make install # installe sur l'appareil connecté
make run # installe et lance
make lint # analyse Android Lint
make release # APK de release (signé si keystore.properties existe)
make publish # release Gitea : tag + notes + APK signé
make publish-dry # simulation, sans rien envoyer
make deploy # release puis publish
```
`make help` liste toutes les cibles, groupées par catégorie.
Sans `make`, les commandes Gradle équivalentes fonctionnent :
```bash
./gradlew assembleDebug
./gradlew installDebug
```
L'APK se trouve dans `app/build/outputs/apk/debug/app-debug.apk`.
### Signer une version de publication
Créez un `keystore.properties` à la racine — il est ignoré par git :
```properties
storeFile=cachette.jks
storePassword=...
keyAlias=cachette
keyPassword=...
```
### 📤 Publier une version
`make publish` crée le tag, la release Gitea et y attache l'APK **signé**. La cible est
idempotente : relancée sur une version déjà publiée, elle réutilise la release et remplace l'asset.
`make publish-dry` répète l'opération à blanc.
La publication délègue à `gitea_release.sh` de **BorisSkill**, qui déduit l'hôte, le propriétaire
et le dépôt du remote `origin` — il n'y a donc aucun nom de dépôt à configurer ici. Le chemin du
script est surchargeable : `make publish GITEA_RELEASE=/chemin/gitea_release.sh`.
Prérequis :
| Élément | Rôle |
|---|---|
| `VERSION` | Source unique de la version — `versionCode` en est déduit (`1.0.0` → `10000`) |
| `CHANGELOG.md` | La section `## [x.y.z]` devient le corps des notes de release |
| `keystore.properties` | Signature — sans lui l'APK est `-unsigned` et refusé à la publication |
| Dépôt git | Un `origin` configuré : le tag `vx.y.z` y est poussé |
Le jeton Gitea est résolu dans l'ordre : `$GITEA_TOKEN`, `.gitea-token` à la racine du dépôt,
`~/.config/gitea/token`, puis repli sur `~/.git-credentials`. Aucun secret n'est stocké dans ce
dépôt.
Pour monter de version, éditez `VERSION` et ajoutez la section correspondante au `CHANGELOG.md` :
Gradle, le tag git et les notes de release suivront seuls.
---
## 🔐 Permissions et vie privée
| Permission | Pourquoi |
|---|---|
| `READ_MEDIA_AUDIO` / `READ_MEDIA_VIDEO` | Lister la musique et les vidéos de l'appareil |
| `INTERNET` | Télécharger ce que vous avez choisi de garder |
| `POST_NOTIFICATIONS` | Commandes de lecture et progression des téléchargements |
| `FOREGROUND_SERVICE_*` | Continuer la lecture et les téléchargements écran éteint |
| `REQUEST_INSTALL_PACKAGES` | Proposer l'installation d'une mise à jour téléchargée |
Aucune donnée ne quitte l'appareil : pas de compte, pas de télémétrie, pas de sauvegarde cloud
(`dataExtractionRules` exclut explicitement le contenu mis en cache). Les seules connexions
sortantes sont celles des URL que vous ajoutez et des catalogues que vous interrogez.
> ⚠️ `usesCleartextTraffic` est activé pour permettre l'usage d'un serveur média auto-hébergé en
> `http://` sur le réseau local. C'est un compromis assumé ; préférez `https` dès que possible.
---
## 🆕 Mises à jour
Cachette étant distribuée hors magasin, elle surveille elle-même les releases du dépôt.
```
Release publiée ──► vérification quotidienne (WorkManager, Wi-Fi)
│
├─ version plus récente ? ──► téléchargement de l'APK
│ │
└─ sinon : rien ▼
notification « prête à installer »
│
installation = geste utilisateur
```
L'installation reste un geste explicite : Android l'impose à toute application hors magasin.
L'écran **Sources** porte la même vérification à la demande, et affiche la version installée.
Le dépôt étant privé, la lecture des releases demande un jeton. Il est lu depuis
`update-token.properties` (ignoré par git) et injecté dans `BuildConfig` à la compilation :
```properties
giteaUpdateToken=…
```
> ⚠️ Un jeton embarqué dans un APK est extractible. Celui-ci est volontairement limité à la portée
> `read:repository` et révocable depuis Gitea. Il donne un accès en lecture aux dépôts du compte —
> ne pas lui accorder davantage.
---
## 🗂️ Structure
```
app/src/main/java/fr/tuxgyver/cachette/
├── CachetteApp.kt Application + conteneur de dépendances (assemblé à la main)
├── MainActivity.kt
├── model/ MediaEntry, MediaKind, SourceKind
├── library/ MediaStore, persistance des imports, bibliothèque unifiée
├── sources/ Import URL et RSS, catalogues Internet Archive et LibriVox
├── offline/ Cache Media3, gestionnaire et service de téléchargement
├── playback/ MediaSessionService, ExoPlayer, contrôleur d'interface
├── update/ Vérification des releases, téléchargement, notification
└── ui/ Compose — thème, bibliothèque, hors ligne, sources, parcours, lecteur
```
---
## 🧪 État de vérification
Validé sur émulateur Pixel (Android 17, API 37) :
- ✅ Scan MediaStore audio et vidéo, vignettes comprises
- ✅ Lecture avec session média système et position qui progresse
- ✅ Import d'un flux RSS et de ses épisodes
- ✅ Téléchargement d'un épisode jusqu'à l'état « disponible hors connexion »
- ✅ Recherche Internet Archive, résolution du fichier et mise en cache
- ✅ Android Lint sans erreur
---
## 📄 Licence
À définir par l'auteur du dépôt.