L'interface du serveur affichait un champ de jeton dans son en-tête, visible en permanence alors qu'il ne sert qu'une fois : il descend dans l'onglet Réglages, avec le nom du serveur, sa version et le nombre d'appareils appairés. Elle gagne surtout une page À propos sur le modèle des autres projets : cinq sections, contenu séparé du rendu, version lue de /api/health et journal servi depuis le CHANGELOG du dépôt — rien de recopié, donc rien qui puisse diverger. Au passage, le serveur annonçait 1.0.0 depuis six publications ; il lit maintenant le fichier VERSION comme le client.
333 lines
18 KiB
Markdown
333 lines
18 KiB
Markdown
# Changelog
|
||
|
||
Toutes les évolutions notables de Noxely sont documentées ici.
|
||
|
||
Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/)
|
||
et le versionnage [SemVer](https://semver.org/lang/fr/).
|
||
|
||
## [2.3.0] — 2026-09-13
|
||
|
||
### Ajouté
|
||
|
||
- **Page « À propos » du serveur.** Un onglet `ℹ️ À propos` avec en-tête d'identité et cinq
|
||
sections : ce que le serveur fait, ce qu'il garantit, avec quoi il est construit, ce qui a
|
||
changé, et qui en répond. Le contenu vit dans un module de constantes séparé du rendu.
|
||
- Le journal est **lu** et non recopié : une route `/api/changelog` sert le `CHANGELOG.md` du
|
||
dépôt, que la page interprète pour ce qu'il emploie réellement — titres, puces et gras.
|
||
|
||
### Modifié
|
||
|
||
- **Le champ « jeton » quitte l'en-tête du serveur**, où il n'avait rien à faire, pour l'onglet
|
||
`⚙️ Réglages`. Une carte y donne aussi le nom du serveur, sa version et le nombre d'appareils
|
||
appairés. Sans jeton connu, la pastille d'état indique où le saisir.
|
||
- **Le serveur annonce la version du dépôt.** Il lisait une constante figée à `1.0.0`, restée en
|
||
arrière de six publications ; il lit maintenant le fichier `VERSION`, comme le client.
|
||
|
||
### Corrigé
|
||
|
||
- `saveToken()` relançait l'amorçage du jeton au lieu de rafraîchir la page : un bloc y avait été
|
||
déplacé par erreur lors d'une édition antérieure.
|
||
- Une garantie annoncée à tort. La page affirmait que toute route hors `/api/health` exigeait un
|
||
jeton ; la page elle-même, ses ressources et le journal répondent sans. Les sept garanties ont
|
||
été confrontées au code une à une, celle-ci reformulée.
|
||
|
||
## [2.2.0] — 2026-09-13
|
||
|
||
### Ajouté
|
||
|
||
- **Un jeton par appareil.** Chaque appairage émet désormais sa propre valeur, au lieu de
|
||
distribuer le jeton maître. Révoquer un téléphone perdu n'atteint plus les autres : vérifié en
|
||
appairant deux appareils, en révoquant le premier, et en constatant que le second continue de
|
||
se synchroniser (401 pour l'un, 200 pour l'autre, jeton maître intact).
|
||
- **Onglet « Appareils »** dans l'interface du serveur : nom du modèle, date d'appairage,
|
||
dernière synchronisation — ou « jamais synchronisé » —, et révocation d'un clic.
|
||
- Le téléphone s'annonce par son modèle lors de l'appairage : un appareil qu'on ne sait pas
|
||
nommer ne se révoque pas.
|
||
|
||
### Sécurité
|
||
|
||
- Les jetons d'appareil ne sont **jamais renvoyés** par la route qui les liste : les afficher
|
||
reviendrait à les distribuer.
|
||
- La date de dernière utilisation est enregistrée à chaque appel authentifié, ce qui permet de
|
||
repérer un appareil oublié.
|
||
|
||
### Compatibilité
|
||
|
||
- La table s'ajoute sans toucher aux données existantes, et le jeton maître reste accepté : un
|
||
appareil appairé avant cette version garde l'accès. Le réappairer lui donnera un jeton propre,
|
||
et donc une révocation individuelle.
|
||
|
||
## [2.1.0] — 2026-09-13
|
||
|
||
### Ajouté
|
||
|
||
- **Appairage du téléphone.** Plus de jeton à recopier : on ouvre une fenêtre depuis l'interface
|
||
du serveur, on appuie sur « Appairer » dans Réglages ▸ Connexion, et l'application reçoit le
|
||
jeton. Le champ de saisie reste disponible pour qui préfère le coller à la main.
|
||
|
||
Le téléphone ne peut pas deviner un jeton — c'est ce qu'il protège — mais il peut le recevoir,
|
||
sous trois conditions cumulées : ouvrir la fenêtre exige déjà le jeton, elle expire au bout de
|
||
deux minutes, et elle se referme dès qu'un appareil s'est servi. La demande doit en outre venir
|
||
d'une adresse privée : un appairage n'est jamais joignable depuis Internet.
|
||
|
||
- En-têtes de licence GPL v3 dans les cinquante fichiers de code (Kotlin, Python, HTML), rédigés
|
||
en français comme le reste du projet, le texte faisant foi restant celui de `LICENSE`.
|
||
|
||
## [2.0.1] — 2026-09-13
|
||
|
||
### Ajouté
|
||
|
||
- **Licence GNU GPL v3.** Le texte intégral est dans `LICENSE` — copié de la référence système,
|
||
empreinte `1ebbd3e34237af26da5dc08a4e440464`, et non retranscrit. Le README expose la formule
|
||
complète, y compris le point le plus souvent passé sous silence : toute version modifiée
|
||
redistribuée doit l'être sous la même licence, code source compris.
|
||
- L'onglet Auteur de la page « À propos » porte la licence et le copyright.
|
||
|
||
### Corrigé
|
||
|
||
- La pastille de licence de la page « À propos » ne s'affiche plus en rouge : elle signalait
|
||
qu'aucune licence n'était déclarée, ce qui n'est plus le cas.
|
||
|
||
## [2.0.0] — 2026-09-13
|
||
|
||
Noxely se sépare en deux : un **serveur** qui collecte, un **mobile** qui écoute. Tout ce qui
|
||
tenait le téléphone éveillé a migré ailleurs — c'est la raison d'être de cette version.
|
||
|
||
### Ajouté
|
||
|
||
- **Serveur** (`server/`, Python + FastAPI + SQLite). Il porte désormais la recherche dans les
|
||
catalogues, le guet des morceaux sur les radios, l'enregistrement des directs et la file de
|
||
téléchargement. Une interface web permet de le piloter depuis un navigateur : recherche, guets,
|
||
file et bibliothèque.
|
||
- **Recherche simultanée sur toutes les sources.** Les quatre catalogues sont interrogés en
|
||
parallèle et les résultats fusionnés, chacun étiqueté de son origine, les plus légers en tête.
|
||
Un catalogue en panne n'empêche pas les autres de répondre.
|
||
- **Balayage radio continu.** Un tour enchaîne sur le suivant au lieu d'attendre trois minutes :
|
||
un morceau guetté a été capté en moins de vingt-cinq secondes à la mesure. Une sonde ne lit que
|
||
le premier bloc de métadonnées puis referme, ce qui rend la permanence tenable là où garder
|
||
quarante flux ouverts coûterait plusieurs mégabits par seconde.
|
||
- **Synchronisation client/serveur.** Le mobile demande ce qui a changé depuis son curseur,
|
||
télécharge les fichiers prêts et se rendort. Toutes les six heures, en Wi-Fi non facturé et
|
||
batterie suffisante.
|
||
- **Réglages en sous-onglets** sur le mobile : Connexion, Synchronisation, Application.
|
||
- Cibles `make server-*` : environnement, lancement, service systemd, jeton, journal.
|
||
|
||
### Changé
|
||
|
||
- **L'application Android devient un client de synchronisation.** Recherche, catalogues, radio,
|
||
guets et enregistrement quittent le téléphone : vingt fichiers supprimés. Restent la
|
||
bibliothèque, la lecture, les playlists, le hors-ligne et les réglages.
|
||
- L'onglet Sources devient Réglages.
|
||
|
||
### Corrigé
|
||
|
||
- Le jeton du serveur était régénéré à chaque démarrage : il aurait fallu reconfigurer le mobile
|
||
après le moindre redémarrage. Il est désormais conservé dans `data/token`, en lecture seule pour
|
||
son propriétaire.
|
||
- Wikimedia Commons répondait 403 : sa politique refuse les agents génériques. L'agent annonce
|
||
maintenant le projet et son dépôt — 28 résultats là où il n'y en avait aucun. L'erreur était
|
||
masquée par la fusion des sources, et n'est apparue qu'en interrogeant la source seule.
|
||
- `NOXELY_DATA` pointait par défaut sur `/var/lib/noxely`, inaccessible au compte qui lance
|
||
`make server-run` : le défaut devient `./data`, et l'unité systemd crée elle-même son dossier
|
||
d'état. Un dossier inutilisable donne désormais une phrase, plus une trace Python.
|
||
- La lecture des métadonnées ICY n'itérait rien côté serveur : `iter_bytes` au lieu de
|
||
`aiter_bytes` sur un client asynchrone. Trente-six stations écoutées, zéro titre lu.
|
||
- Une coupure de l'annuaire Radio Browser faisait échouer tout le balayage : une seconde tentative
|
||
est faite, et l'erreur renvoyée est lisible.
|
||
|
||
### Migration
|
||
|
||
Le mobile seul ne sert plus à rien : il faut installer le serveur (`make server-venv`, puis
|
||
`make server-install`), relever son jeton (`make server-token`) et le saisir dans Réglages ▸
|
||
Connexion. Consultée depuis la machine du serveur, l'interface web se remplit seule.
|
||
|
||
## [1.7.0] — 2026-09-13
|
||
|
||
### Ajouté
|
||
|
||
- **Guet sur l'ensemble des stations.** On nomme un morceau ou un artiste, sans désigner de
|
||
station : Noxely balaie les quarante stations les plus écoutées, quatre à la fois, et déclenche
|
||
l'enregistrement dès qu'une le diffuse. Un écran dédié sépare ce qui est attendu de ce qui a été
|
||
capté, avec le nombre de balayages et l'heure du dernier.
|
||
- **Veille en arrière-plan.** Deux mécanismes, parce qu'un seul ne suffit pas : une tâche
|
||
périodique tourne en permanence au quart d'heure — le minimum qu'autorise Android — et une
|
||
veille active, à enclencher, balaie toutes les trois minutes, écran éteint compris. Un morceau
|
||
durant trois à quatre minutes, la tâche périodique en manque forcément ; l'écran le dit au lieu
|
||
de le taire. La veille s'arrête d'elle-même quand plus rien n'est guetté.
|
||
- **Pochettes automatiques.** Les titres sans image en reçoivent une, cherchée sur leur nom via la
|
||
recherche publique d'iTunes, sans clé. Le nom est nettoyé au préalable : un enregistrement radio
|
||
traîne celui de sa station, qui brouillerait la requête.
|
||
- **Bouton d'actualisation** de l'écran Hors ligne : relance le scan de l'appareil et complète les
|
||
pochettes manquantes, par lots bornés pour ne pas marteler le service.
|
||
- L'écran de lecture affiche la provenance du morceau — nom de la playlist s'il en vient —, sa
|
||
source, sa taille et sa date.
|
||
|
||
### Corrigé
|
||
|
||
- Un morceau guetté n'était pas enregistré : le champ de saisie était vidé avant que la coroutine
|
||
n'en lise la valeur. Elle est désormais capturée avant l'effacement.
|
||
|
||
## [1.6.0] — 2026-09-13
|
||
|
||
### Changé
|
||
|
||
- **L'application s'appelle désormais Noxely.** Le nom affiché, l'identifiant d'application
|
||
(`fr.tuxgyver.noxely`), le paquet Kotlin, le dépôt et le dossier d'enregistrements
|
||
(`Music/Noxely`) suivent tous.
|
||
|
||
⚠️ **Android considère le nouvel identifiant comme une application distincte.** Une
|
||
installation en 1.5.0 ne se met donc pas à jour toute seule : il faut la désinstaller, puis
|
||
installer Noxely. Les enregistrements déjà rangés dans `Music/Cachette` ne sont pas perdus —
|
||
ils sont dans le stockage partagé, restent lisibles par tout lecteur, et le scan continue de
|
||
les écarter pour ne pas les afficher en double.
|
||
|
||
- Nouvelle icône. Le visuel fourni est détouré de son fond, décliné aux cinq densités et posé
|
||
sur une couleur échantillonnée dans l'image : sans cela, le masque du système aurait découpé
|
||
une seconde fois un badge qui l'est déjà, en laissant du blanc sur les bords.
|
||
|
||
### Ajouté
|
||
|
||
- Page « À propos », accessible depuis l'écran Sources : ce que fait l'application, ce qu'elle
|
||
garantit, avec quoi elle est construite, ce qui a changé et qui en répond. La version est lue
|
||
du manifeste et le journal du CHANGELOG du dépôt, recopié à la compilation — l'un et l'autre
|
||
ne peuvent donc pas diverger de la réalité.
|
||
- La pastille de licence signale en rouge qu'aucune licence n'est déclarée dans le dépôt, plutôt
|
||
que d'en afficher une par défaut.
|
||
|
||
## [1.5.0] — 2026-09-13
|
||
|
||
### Ajouté
|
||
|
||
- **Titre en cours par station.** Aucun annuaire ne l'expose : le morceau diffusé est lu dans le
|
||
flux lui-même. Les douze premières stations affichées sont sondées quatre à la fois, et le
|
||
résultat conservé — au-delà, le nombre de connexions ne se justifierait plus. Les stations qui
|
||
n'annoncent rien gardent leur description.
|
||
- **Guet d'un titre.** On indique un morceau ou un artiste, et l'enregistrement se déclenche dès
|
||
que la station l'annonce, puis s'arrête à la fin du morceau. Le flux est lu sans être écrit
|
||
pendant l'attente. Une station sans métadonnées refuse la programmation plutôt que d'attendre
|
||
indéfiniment.
|
||
- **Catalogue Underground.** Restreint aux collections `netlabels` et `freemusicarchive`, où tout
|
||
est publié sous licence libre par les artistes : environ 94 000 morceaux indépendants, que la
|
||
recherche généraliste noyait.
|
||
- **Suppression individuelle d'un enregistrement** depuis l'écran Hors ligne : fichier, entrée
|
||
MediaStore et référence en bibliothèque sont retirés ensemble.
|
||
|
||
### Modifié
|
||
|
||
- Les enregistrements sont désormais écrits dans `Music/Noxely` et déclarés dans MediaStore
|
||
(Android 10 et plus) : ils deviennent visibles des autres lecteurs et survivent à la
|
||
désinstallation. Le scan local ignore ce dossier, faute de quoi chaque enregistrement
|
||
apparaîtrait deux fois.
|
||
- Le nom de fichier porte le titre du morceau. En publiant le fichier, MediaStore le relit et
|
||
réécrit le champ `TITLE` depuis ce nom : l'y inscrire directement ne survivait pas.
|
||
|
||
## [1.4.0] — 2026-09-13
|
||
|
||
### Ajouté
|
||
|
||
- Titrage automatique des enregistrements radio. Le service demande désormais les métadonnées
|
||
ICY, ce qui fait annoncer par la station le morceau diffusé : l'enregistrement prend son nom,
|
||
la station passe en sous-titre, et le titre en cours s'affiche pendant la capture comme dans
|
||
la notification. Ces octets de métadonnées sont retirés avant écriture — laissés dans le
|
||
fichier, ils le rendraient illisible. Les stations qui n'en diffusent pas gardent le nom
|
||
« Station — date ».
|
||
|
||
### Corrigé
|
||
|
||
- Les enregistrements radio n'apparaissaient pas dans l'onglet « Hors ligne », qui ne listait que
|
||
ce qui porte un état de téléchargement. L'écran annonçait donc un nombre d'éléments disponibles
|
||
sans réseau en omettant précisément ceux qui l'étaient déjà. La taille affichée additionne
|
||
maintenant le cache et les enregistrements, et « Tout libérer » précise qu'il ne les touche pas.
|
||
- Les titres comportant une apostrophe étaient tronqués à la première rencontrée : « Ain't That a
|
||
Kick in the Head » devenait « Ain ». La valeur est lue jusqu'à la fin du champ ICY.
|
||
|
||
## [1.3.0] — 2026-09-13
|
||
|
||
### Ajouté
|
||
|
||
- **Radio.** Les stations les plus écoutées via l'annuaire communautaire Radio Browser, avec
|
||
filtres par genre et recherche par nom. Chaque station s'écoute en direct, ou s'enregistre :
|
||
un service de premier plan copie le flux dans un fichier jusqu'à l'arrêt demandé, et le verse
|
||
dans la bibliothèque — déjà hors connexion, sans rien à télécharger ensuite.
|
||
- Relance automatique des téléchargements en échec, après une pause croissante de 5, 15 puis
|
||
45 secondes. Une relance manuelle repart d'un compteur neuf.
|
||
|
||
### Notes
|
||
|
||
- Les flux HLS sont écartés de la liste des stations : ils se lisent, mais ne s'enregistrent pas
|
||
par copie d'octets, et un bouton qui échouerait vaudrait moins que pas de bouton.
|
||
- La relance et la reprise sur coupure réseau sont deux mécanismes distincts, et c'est voulu :
|
||
sans réseau, Media3 met le téléchargement en attente et le reprend de lui-même ; la relance,
|
||
elle, ne traite que les vrais échecs serveur.
|
||
- Le repli automatique vers une autre source n'a pas été retenu : rien ne garantit qu'un même
|
||
titre trouvé sur deux catalogues soit le même enregistrement, et l'application téléchargerait
|
||
silencieusement autre chose que ce qui a été choisi.
|
||
|
||
## [1.2.0] — 2026-09-13
|
||
|
||
### Ajouté
|
||
|
||
- Écran de choix des pistes. Un album ou un livre n'est plus une boîte noire : ses pistes sont
|
||
listées avec leur durée et leur taille, le total est annoncé, et le bouton dit exactement ce
|
||
qu'on s'apprête à télécharger (« Garder 4,2 Mo »). Les variantes d'un même morceau sont
|
||
regroupées — autant de lignes que d'œuvres, pas que de formats.
|
||
- Écoute en ligne avant téléchargement : un appui sur une piste la joue en streaming sans rien
|
||
mettre en cache.
|
||
- Playlists : listes nommées, ajout par appui long sur un média ou depuis l'écran de lecture,
|
||
lecture enchaînée, suppression piste par piste.
|
||
- Contrôles de lecture complétés : arrêt, saut de 15 secondes en avant et en arrière.
|
||
- Filtrage par langue des catalogues (français, anglais, allemand, espagnol, italien, néerlandais).
|
||
- Nouvelle source libre : **Wikimedia Commons**, pour les enregistrements classiques et les
|
||
archives sonores du domaine public.
|
||
|
||
### Corrigé
|
||
|
||
- Le filtre de langue de LibriVox ne pouvait pas fonctionner côté serveur : l'API accepte le
|
||
paramètre `language` puis l'ignore (`language=French` seul renvoie des livres anglais). Le tri
|
||
se fait désormais sur le champ `language` de chaque livre.
|
||
- Délais réseau ajustés d'après la latence mesurée des services : LibriVox répond en 14 à
|
||
16 secondes quelle que soit la taille demandée, ce qui faisait expirer les requêtes parallèles.
|
||
|
||
### Notes
|
||
|
||
- Deux catalogues ont été évalués puis écartés sur mesure plutôt que sur principe : **Openverse**
|
||
refuse désormais l'accès anonyme (401 sur huit requêtes sur huit) et **ccMixter** exige un
|
||
en-tête `Referer` que Media3 ne permet pas de poser par téléchargement.
|
||
|
||
## [1.1.0] — 2026-09-11
|
||
|
||
### Ajouté
|
||
|
||
- Mise à jour intégrée : Noxely surveille les releases du dépôt, télécharge l'APK en Wi-Fi
|
||
et propose l'installation. Vérification quotidienne en tâche de fond, et à la demande depuis
|
||
l'écran Sources, qui affiche aussi la version installée.
|
||
- La taille approximative de chaque résultat Internet Archive est affichée avant sélection : un
|
||
élément de 500 Mo ne se laisse plus confondre avec une chanson.
|
||
|
||
### Corrigé
|
||
|
||
- Recherche LibriVox qui ne renvoyait rien. L'API cherche les titres en correspondance exacte
|
||
sauf si la requête est préfixée par `^`, et les auteurs sur le seul nom de famille : « Jules
|
||
Verne » n'atteignait donc ni l'un ni l'autre. Les trois critères sont désormais interrogés en
|
||
parallèle et dédoublonnés, et un « aucun résultat » (HTTP 404) n'est plus traité comme une panne.
|
||
- Téléchargements Internet Archive qui échouaient ou n'aboutissaient pas. Trois causes distinctes :
|
||
les éléments en accès restreint répondaient 401 et sont maintenant écartés dès la recherche ;
|
||
seuls les fichiers `.mp3` et `.mp4` étaient acceptés, ce qui excluait des éléments ne proposant
|
||
que du `.m4a` ou de l'`.ogg` ; et le fichier retenu était le premier trouvé, souvent l'original
|
||
non compressé — c'est désormais le plus petit dérivé, soit 9 Mo au lieu de 30 sur un cas mesuré.
|
||
|
||
## [1.0.0] — 2026-09-11
|
||
|
||
### Ajouté
|
||
|
||
- Lecture audio et vidéo en arrière-plan (Media3 `MediaSessionService`), avec notification
|
||
système, commandes casque et écran verrouillé.
|
||
- Mise en cache hors connexion explicite : téléchargement à la demande, reprise automatique
|
||
après coupure réseau ou redémarrage via WorkManager.
|
||
- Bibliothèque unifiée mêlant le contenu de l'appareil (MediaStore) et les médias importés.
|
||
- Sources : appareil, lien direct `http(s)`, flux RSS de podcast.
|
||
- Catalogues libres de droits intégrés : Internet Archive et LibriVox, sans compte ni clé d'API.
|
||
- Parcours d'une source avec sélection multiple et téléchargement en lot.
|
||
- Écran « Hors ligne » : taille du cache, progression, libération élément par élément ou en bloc.
|
||
- Interface Jetpack Compose Material 3, thème clair et sombre, palette de marque.
|