Cachette met des médias de côté pendant qu'il y a du réseau, pour les lire quand il n'y en a plus. - Lecture audio et vidéo en arrière-plan (Media3 MediaSessionService) - Mise en cache explicite, reprise automatique après coupure (WorkManager) - Sources : appareil (MediaStore), lien direct, flux RSS de podcast - Catalogues libres de droits : Internet Archive et LibriVox - Parcours d'une source avec sélection multiple et téléchargement en lot - Interface Jetpack Compose Material 3, thèmes clair et sombre - Makefile catégorisé, publication Gitea déléguée à BorisSkill Le fichier VERSION est la source unique de la version : versionCode en est déduit et la publication s'y réfère.
207 lines
8.9 KiB
Markdown
207 lines
8.9 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.
|
|
|
|
[](https://developer.android.com)
|
|
[](https://kotlinlang.org)
|
|
[](https://developer.android.com/jetpack/compose)
|
|
[](https://developer.android.com/media/media3)
|
|
[](VERSION)
|
|
[](#-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 |
|
|
|:---:|:---:|:---:|:---:|
|
|
|  |  |  |  |
|
|
|
|
---
|
|
|
|
## ✨ 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 |
|
|
|
|
---
|
|
|
|
## 📦 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 |
|
|
|
|
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.
|
|
|
|
---
|
|
|
|
## 🗂️ 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
|
|
└── 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.
|