tuxgyver 6f5adc687e Cachette devient Noxely, et gagne une page À propos (1.6.0)
Le nom change, et pas seulement à l'écran : identifiant d'application, paquet
Kotlin, dépôt et dossier d'enregistrements suivent, pour qu'il ne subsiste pas
deux noms selon l'endroit où l'on regarde.

Android traite le nouvel identifiant comme une application distincte : une
installation en 1.5.0 ne se mettra pas à jour seule et devra être remplacée.
C'est le prix d'un renommage complet, assumé plutôt que contourné. Les
enregistrements déjà faits ne sont pas perdus, étant dans le stockage partagé,
et le scan continue d'écarter l'ancien dossier pour ne pas les compter deux fois.

L'icône fournie est détournée de son fond blanc avant d'être déclinée : posée
telle quelle, le masque du système aurait redécoupé un badge qui l'est déjà et
laissé du blanc sur les bords. La couleur de fond est échantillonnée dans
l'image, non choisie au jugé.

La page À propos dit enfin ce que l'application fait, ce qu'elle garantit, avec
quoi elle est construite et qui en répond. Sa version vient du manifeste et son
journal du CHANGELOG du dépôt : ni l'un ni l'autre ne peut dériver. La licence
n'étant pas déclarée, la pastille le signale en rouge au lieu d'en inventer une.
2026-09-13 13:30:39 +02:00

🎧 Noxely

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 Kotlin Compose Media3 Version Hors ligne

Noxely 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 Lecture Hors ligne Catalogue

✨ 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
🎧 Écoute avant téléchargement Chaque piste s'essaie en ligne d'un appui, sans rien mettre en cache
🧾 Choix piste par piste Un album ou un livre affiche ses pistes avec leur taille : on n'emporte que ce qu'on veut
🌍 Filtrage par langue Français, anglais, allemand, espagnol, italien, néerlandais
📋 Playlists Listes nommées, appui long pour y ajouter un titre, lecture enchaînée
🎼 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é
⏯️ Contrôles complets Lecture, pause, arrêt, piste précédente et suivante, ±15 s
🌙 Thème clair et sombre Material 3, palette de marque, contraste garanti
🔁 Reprise automatique Réseau coupé : mise en attente et reprise. Échec serveur : relance seule après 5 s, 15 s, 45 s
📻 Radio Les stations les plus écoutées, en direct — et enregistrables d'un appui
🏷️ Titrage automatique L'enregistrement prend le nom du morceau annoncé par la station
ℹ️ Page « À propos » Fonctions, garanties, pile technique, journal et auteur
👀 Titre en cours Ce que diffuse chaque station, lu dans son flux
⏰ Guet d'un titre L'enregistrement se déclenche quand le morceau voulu passe, et s'arrête à sa fin
🆕 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, filtrables par langue
🎼 Wikimedia Commons ✅ Enregistrements classiques et archives sonores libres
📻 Radio ✅ Annuaire Radio Browser, direct et enregistrement (flux HLS exclus)
🎧 Underground ✅ Netlabels et Free Music Archive : ~94 000 morceaux d'artistes indépendants

🔎 Catalogues évalués puis écartés

Candidat Pourquoi non
Openverse A cessé de servir l'accès anonyme : 401 sur huit requêtes sur huit, et jusqu'à 21 s de latence quand il répondait encore. Exige désormais une inscription.
ccMixter La recherche fonctionne, mais les fichiers ne sont servis qu'avec un en-tête Referer — que Media3 ne permet pas de poser par téléchargement.
Radio Browser Flux radio continus : rien à mettre en cache.

⚖️ Ce que Noxely 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

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 :

./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 :

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.


⚠️ Changement de nom en 1.6.0. L'application s'appelait Cachette et son identifiant était fr.tuxgyver.cachette. Android traite le nouvel identifiant fr.tuxgyver.noxely comme une application distincte : une installation en 1.5.0 ne se met pas à jour toute seule, il faut la désinstaller puis installer Noxely. Les enregistrements déjà rangés dans Music/Cachette survivent — ils sont dans le stockage partagé — et restent lisibles par tout lecteur.


📼 Où vont les enregistrements

À partir d'Android 10, dans Music/Noxely/, déclarés dans MediaStore : ils sont donc visibles de tous les lecteurs de l'appareil et survivent à la désinstallation. En deçà, ils restent dans le dossier privé de l'application, MediaStore y réclamant une permission de stockage dont Noxely n'a pas besoin par ailleurs.

Le nom du fichier porte le titre du morceau — et pas seulement la colonne TITLE : en publiant le fichier, MediaStore le relit et réécrit ce champ à partir du nom. Le scan local, lui, ignore Music/Noxely pour ne pas afficher deux fois ce que la bibliothèque connaît déjà.

Chaque enregistrement se supprime individuellement depuis l'écran Hors ligne : fichier, entrée MediaStore et référence en bibliothèque partent ensemble. « Tout libérer » ne les touche pas.


🆕 Mises à jour

Noxely é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 :

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/
├── NoxelyApp.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
├── radio/                  Annuaire des stations, service d'enregistrement du direct
├── update/                 Vérification des releases, téléchargement, notification
└── ui/                     Compose — thème, bibliothèque, hors ligne, playlists, sources,
                            parcours des catalogues, choix des pistes, 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.

S
Description
Lecteur audio et vidéo hors connexion pour Android : met des médias en cache pour les lire sans réseau.
Readme GPL-3.0
47 MiB
0 Stars 1 Watchers 0 Forks
Noxely 2.6.0
Latest
2026-09-20 21:21:16 +00:00
Languages
Kotlin 68.2%
Python 19.8%
HTML 8.9%
Makefile 1.9%
JavaScript 1.2%