Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28cb545685 | ||
|
|
51b60cda70 |
No files matched your search
@@ -5,6 +5,48 @@ 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.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é
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[](https://kotlinlang.org)
|
||||
[](https://developer.android.com/jetpack/compose)
|
||||
[](https://developer.android.com/media/media3)
|
||||
[](VERSION)
|
||||
[](VERSION)
|
||||
[](LICENSE)
|
||||
[](#-comment-ça-marche)
|
||||
|
||||
@@ -59,6 +59,8 @@ synchronisations : c'est là qu'est l'économie.
|
||||
| | |
|
||||
|---|---|
|
||||
| 🔄 **Synchronisation sobre** | Toutes les 6 h, en Wi-Fi non facturé et batterie suffisante |
|
||||
| 📱 **Appairage** | Un bouton côté serveur, un bouton côté téléphone : aucun jeton à recopier |
|
||||
| 🔑 **Un jeton par appareil** | Chacun le sien, révocable seul depuis l'onglet Appareils |
|
||||
| 🎵 **Lecture** | Audio et vidéo, arrière-plan, casque, écran verrouillé, ±15 s, stop |
|
||||
| 📚 **Bibliothèque** | Médias reçus du serveur et contenu déjà présent sur l'appareil |
|
||||
| 📋 **Playlists** | Listes nommées, appui long pour y ajouter un titre |
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely
|
||||
|
||||
import android.Manifest
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely
|
||||
|
||||
import android.app.Application
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.library
|
||||
|
||||
import fr.tuxgyver.noxely.model.MediaEntry
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.library
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.library
|
||||
|
||||
import fr.tuxgyver.noxely.model.MediaEntry
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.library
|
||||
|
||||
import android.content.ContentUris
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.library
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.offline
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.offline
|
||||
|
||||
import android.app.Notification
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.offline
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.playback
|
||||
|
||||
import androidx.core.net.toUri
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.playback
|
||||
|
||||
import android.app.PendingIntent
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.playback
|
||||
|
||||
import android.content.ComponentName
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.radio
|
||||
|
||||
import android.content.ContentValues
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.sources
|
||||
|
||||
import java.net.HttpURLConnection
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.sync
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.sync
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import fr.tuxgyver.noxely.library.ImportedStore
|
||||
import fr.tuxgyver.noxely.model.MediaEntry
|
||||
import fr.tuxgyver.noxely.model.MediaKind
|
||||
@@ -44,6 +57,44 @@ class SyncClient(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le jeton auprès du serveur, pendant sa fenêtre d'appairage.
|
||||
*
|
||||
* Le téléphone ne peut pas deviner un jeton — c'est ce qu'il protège. Il peut en revanche le
|
||||
* recevoir, à condition que quelqu'un ait ouvert la fenêtre depuis l'interface du serveur, et
|
||||
* que la demande vienne du réseau local.
|
||||
*/
|
||||
suspend fun pair(baseUrl: String): Result<String> = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
val base = baseUrl.trim().trimEnd('/')
|
||||
val connection = openConnection("$base/api/pair", readTimeoutMs = 20_000).apply {
|
||||
requestMethod = "POST"
|
||||
doOutput = true
|
||||
setRequestProperty("Content-Type", "application/json")
|
||||
// Le nom du modèle rend l'appareil reconnaissable dans la liste du serveur.
|
||||
val name = (Build.MANUFACTURER + " " + Build.MODEL).trim()
|
||||
outputStream.use { it.write(("{" + "\"name\":\"" + name + "\"}").toByteArray()) }
|
||||
}
|
||||
val body = try {
|
||||
when (connection.responseCode) {
|
||||
403 -> throw IllegalStateException(
|
||||
connection.errorStream?.bufferedReader()?.readText()
|
||||
?.let { json.parseToJsonElement(it).jsonObject.text("detail") }
|
||||
?: "Aucun appairage en cours sur le serveur"
|
||||
)
|
||||
in 200..299 -> connection.inputStream.bufferedReader().readText()
|
||||
else -> throw IllegalStateException("Le serveur a répondu ${connection.responseCode}")
|
||||
}
|
||||
} finally {
|
||||
connection.disconnect()
|
||||
}
|
||||
val payload = json.parseToJsonElement(body).jsonObject
|
||||
val token = payload.text("token") ?: throw IllegalStateException("Réponse sans jeton")
|
||||
settings.update { it.copy(baseUrl = base, token = token) }
|
||||
"${payload.text("name")} ${payload.text("version")}"
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sync(): SyncResult = withContext(Dispatchers.IO) {
|
||||
val config = settings.config.value
|
||||
if (!config.isConfigured) return@withContext SyncResult(0, 0, "Serveur non configuré")
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.sync
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.about
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.about
|
||||
|
||||
import fr.tuxgyver.noxely.BuildConfig
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.about
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.components
|
||||
|
||||
import android.graphics.Bitmap
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.components
|
||||
|
||||
import java.util.Locale
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.components
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.library
|
||||
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.offline
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.player
|
||||
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.player
|
||||
|
||||
import androidx.annotation.OptIn
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.playlists
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.playlists
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.playlists
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.server
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -127,7 +139,7 @@ fun ServerScreen(
|
||||
OutlinedTextField(
|
||||
value = token,
|
||||
onValueChange = { token = it; probe = null },
|
||||
label = { Text("Jeton") },
|
||||
label = { Text("Jeton (rempli par l'appairage)") },
|
||||
singleLine = true,
|
||||
visualTransformation = PasswordVisualTransformation(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -135,6 +147,26 @@ fun ServerScreen(
|
||||
Spacer(Modifier.size(14.dp))
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Button(
|
||||
enabled = !busy && url.isNotBlank(),
|
||||
onClick = {
|
||||
busy = true
|
||||
probe = null
|
||||
scope.launch {
|
||||
container.sync.pair(url).fold(
|
||||
onSuccess = { name ->
|
||||
token = container.serverSettings.config.value.token
|
||||
probe = "Connecté à $name — jeton reçu"
|
||||
},
|
||||
onFailure = { probe = it.message ?: "Appairage impossible" },
|
||||
)
|
||||
busy = false
|
||||
}
|
||||
},
|
||||
) { Text("Appairer") }
|
||||
|
||||
Spacer(Modifier.width(10.dp))
|
||||
|
||||
TextButton(
|
||||
enabled = !busy && url.isNotBlank() && token.isNotBlank(),
|
||||
onClick = {
|
||||
busy = true
|
||||
@@ -152,7 +184,7 @@ fun ServerScreen(
|
||||
busy = false
|
||||
}
|
||||
},
|
||||
) { Text("Tester et enregistrer") }
|
||||
) { Text("Tester") }
|
||||
|
||||
Spacer(Modifier.width(10.dp))
|
||||
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.sources
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.ui.theme
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.update
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.update
|
||||
|
||||
import android.Manifest
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/*
|
||||
* Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
* Copyright © 2026 Johnny (tuxgyver)
|
||||
*
|
||||
* Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
* selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
* telle que publiée par la Free Software Foundation.
|
||||
*
|
||||
* Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
* Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
*/
|
||||
|
||||
package fr.tuxgyver.noxely.update
|
||||
|
||||
import android.content.Context
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
# Copyright © 2026 Johnny (tuxgyver)
|
||||
#
|
||||
# Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
# selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
# telle que publiée par la Free Software Foundation.
|
||||
#
|
||||
# Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
# Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
# Copyright © 2026 Johnny (tuxgyver)
|
||||
#
|
||||
# Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
# selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
# telle que publiée par la Free Software Foundation.
|
||||
#
|
||||
# Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
# Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
|
||||
"""Point d'entrée : `python -m noxely_server`."""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
# Copyright © 2026 Johnny (tuxgyver)
|
||||
#
|
||||
# Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
# selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
# telle que publiée par la Free Software Foundation.
|
||||
#
|
||||
# Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
# Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
|
||||
"""API HTTP du serveur Noxely.
|
||||
|
||||
Le client Android ne consomme que `/api/health`, `/api/library` et `/api/media/{id}/file` :
|
||||
@@ -7,6 +17,9 @@ mobile doit pouvoir se contenter d'un sondage bref, c'est là qu'est le gain de
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import ipaddress
|
||||
import secrets
|
||||
import time
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
|
||||
@@ -19,6 +32,7 @@ from .storage import Storage
|
||||
from .workers import Downloader, Watcher, safe_name
|
||||
|
||||
VERSION = "1.0.0"
|
||||
PAIRING_WINDOW_SECONDS = 120
|
||||
WEB_DIR = Path(__file__).parent / "web"
|
||||
|
||||
config = config_module.load()
|
||||
@@ -28,9 +42,18 @@ watcher = Watcher(config, storage)
|
||||
|
||||
|
||||
def authorised(x_noxely_token: str = Header(default="")) -> None:
|
||||
"""Sans jeton valide, le serveur ne répond rien : il expose des fichiers."""
|
||||
if x_noxely_token != config.token:
|
||||
"""Sans jeton valide, le serveur ne répond rien : il expose des fichiers.
|
||||
|
||||
Deux formes valent : le jeton maître, qui pilote le serveur depuis le navigateur, et celui
|
||||
d'un appareil appairé. Les distinguer est ce qui permet de révoquer un téléphone perdu sans
|
||||
déranger les autres.
|
||||
"""
|
||||
if x_noxely_token and x_noxely_token == config.token:
|
||||
return
|
||||
device = storage.device_by_token(x_noxely_token) if x_noxely_token else None
|
||||
if not device:
|
||||
raise HTTPException(status_code=401, detail="Jeton invalide")
|
||||
storage.touch_device(device["id"])
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@@ -180,6 +203,68 @@ def queue_state() -> dict:
|
||||
return {"items": [m.public() for m in pending if m.state != "ready"][:50]}
|
||||
|
||||
|
||||
#: Fenêtre d'appairage : instant d'expiration, ou None si elle est fermée.
|
||||
_pairing_until: float | None = None
|
||||
|
||||
|
||||
def _is_private(host: str) -> bool:
|
||||
"""Le réseau local, et lui seul."""
|
||||
try:
|
||||
address = ipaddress.ip_address(host)
|
||||
return address.is_private or address.is_loopback
|
||||
except ValueError:
|
||||
return host in ("localhost", "")
|
||||
|
||||
|
||||
@app.post("/api/pair/open", dependencies=[Depends(authorised)])
|
||||
def open_pairing() -> dict:
|
||||
"""Ouvre une fenêtre de deux minutes pendant laquelle un appareil du réseau local peut
|
||||
réclamer le jeton. Fermée le reste du temps : sinon le jeton ne protégerait plus rien."""
|
||||
global _pairing_until
|
||||
_pairing_until = time.time() + PAIRING_WINDOW_SECONDS
|
||||
return {"open": True, "seconds": PAIRING_WINDOW_SECONDS}
|
||||
|
||||
|
||||
@app.get("/api/pair/state", dependencies=[Depends(authorised)])
|
||||
def pairing_state() -> dict:
|
||||
remaining = int(_pairing_until - time.time()) if _pairing_until else 0
|
||||
return {"open": remaining > 0, "seconds": max(0, remaining)}
|
||||
|
||||
|
||||
@app.get("/api/devices", dependencies=[Depends(authorised)])
|
||||
def devices() -> dict:
|
||||
return {"devices": storage.devices()}
|
||||
|
||||
|
||||
@app.delete("/api/devices/{device_id}", dependencies=[Depends(authorised)])
|
||||
def revoke_device(device_id: str) -> dict:
|
||||
"""Révoque un appareil : son jeton cesse immédiatement d'être accepté."""
|
||||
return {"revoked": storage.delete_device(device_id)}
|
||||
|
||||
|
||||
@app.post("/api/pair")
|
||||
def pair(request: Request, payload: dict = Body(default={})) -> dict:
|
||||
"""Livre le jeton à un appareil du réseau local, pendant la fenêtre d'appairage.
|
||||
|
||||
La fenêtre se referme dès qu'un appareil s'est servi : une ouverture, un appairage.
|
||||
"""
|
||||
global _pairing_until
|
||||
client = request.client.host if request.client else ""
|
||||
if not _pairing_until or time.time() > _pairing_until:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Aucun appairage en cours. Ouvrez-en un depuis l'interface du serveur.",
|
||||
)
|
||||
if not _is_private(client):
|
||||
raise HTTPException(status_code=403, detail="Appairage réservé au réseau local")
|
||||
_pairing_until = None
|
||||
# Chaque appairage reçoit son propre jeton : révoquer l'un n'atteint pas les autres.
|
||||
token = secrets.token_urlsafe(24)
|
||||
name = str(payload.get("name") or "Appareil").strip()[:60]
|
||||
storage.add_device(name, token)
|
||||
return {"token": token, "name": "Noxely", "version": VERSION, "device": name}
|
||||
|
||||
|
||||
@app.get("/api/bootstrap")
|
||||
def bootstrap(request: Request) -> dict:
|
||||
"""Livre le jeton — uniquement à une requête venue de la machine elle-même.
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
# Copyright © 2026 Johnny (tuxgyver)
|
||||
#
|
||||
# Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
# selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
# telle que publiée par la Free Software Foundation.
|
||||
#
|
||||
# Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
# Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
|
||||
"""Configuration du serveur Noxely, lue de l'environnement.
|
||||
|
||||
Aucune valeur sensible n'est écrite dans le dépôt : le jeton et les chemins viennent d'un
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
# Copyright © 2026 Johnny (tuxgyver)
|
||||
#
|
||||
# Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
# selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
# telle que publiée par la Free Software Foundation.
|
||||
#
|
||||
# Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
# Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
|
||||
"""Catalogues libres et annuaire radio, portés depuis le client Android.
|
||||
|
||||
Les pièges vérifiés côté mobile restent valables ici et sont conservés en commentaire :
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
# Copyright © 2026 Johnny (tuxgyver)
|
||||
#
|
||||
# Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
# selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
# telle que publiée par la Free Software Foundation.
|
||||
#
|
||||
# Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
# Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
|
||||
"""Recherche dans les catalogues libres de droits."""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
# Copyright © 2026 Johnny (tuxgyver)
|
||||
#
|
||||
# Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
# selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
# telle que publiée par la Free Software Foundation.
|
||||
#
|
||||
# Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
# Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
|
||||
"""Annuaire des stations et lecture des métadonnées ICY."""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
# Copyright © 2026 Johnny (tuxgyver)
|
||||
#
|
||||
# Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
# selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
# telle que publiée par la Free Software Foundation.
|
||||
#
|
||||
# Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
# Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
|
||||
"""Index des médias et des guets, en SQLite.
|
||||
|
||||
Les fichiers vivent sur le disque ; la base ne porte que leurs métadonnées et l'état de la
|
||||
@@ -32,6 +42,15 @@ CREATE TABLE IF NOT EXISTS media (
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS media_updated ON media(updated_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS devices (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
token TEXT NOT NULL UNIQUE,
|
||||
created_at INTEGER NOT NULL,
|
||||
last_seen INTEGER
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS devices_token ON devices(token);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS watches (
|
||||
id TEXT PRIMARY KEY,
|
||||
query TEXT NOT NULL,
|
||||
@@ -140,6 +159,38 @@ class Storage:
|
||||
self._db.commit()
|
||||
return item
|
||||
|
||||
# --- Appareils ------------------------------------------------------
|
||||
|
||||
def add_device(self, name: str, token: str) -> str:
|
||||
device_id = uuid.uuid4().hex
|
||||
self._db.execute(
|
||||
"INSERT INTO devices (id,name,token,created_at) VALUES (?,?,?,?)",
|
||||
(device_id, name.strip() or "Appareil", token, now()),
|
||||
)
|
||||
self._db.commit()
|
||||
return device_id
|
||||
|
||||
def device_by_token(self, token: str) -> dict | None:
|
||||
row = self._db.execute("SELECT * FROM devices WHERE token = ?", (token,)).fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
def touch_device(self, device_id: str) -> None:
|
||||
"""Dernière utilisation : c'est ce qui permet de reconnaître un appareil oublié."""
|
||||
self._db.execute("UPDATE devices SET last_seen = ? WHERE id = ?", (now(), device_id))
|
||||
self._db.commit()
|
||||
|
||||
def devices(self) -> list[dict]:
|
||||
return [
|
||||
# Le jeton n'est jamais renvoyé : le lister reviendrait à le distribuer.
|
||||
{k: v for k, v in dict(row).items() if k != "token"}
|
||||
for row in self._db.execute("SELECT * FROM devices ORDER BY created_at DESC")
|
||||
]
|
||||
|
||||
def delete_device(self, device_id: str) -> bool:
|
||||
cursor = self._db.execute("DELETE FROM devices WHERE id = ?", (device_id,))
|
||||
self._db.commit()
|
||||
return cursor.rowcount > 0
|
||||
|
||||
# --- Guets ----------------------------------------------------------
|
||||
|
||||
def add_watch(self, query: str) -> str:
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
Copyright © 2026 Johnny (tuxgyver)
|
||||
|
||||
Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
telle que publiée par la Free Software Foundation.
|
||||
|
||||
Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
-->
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
@@ -46,6 +57,7 @@
|
||||
<span class="grow" style="flex:1"></span>
|
||||
<input type="password" id="token" placeholder="Jeton" style="width:190px">
|
||||
<button class="ghost" onclick="saveToken()">Enregistrer</button>
|
||||
<button class="go" onclick="openPairing()">📱 Appairer</button>
|
||||
</header>
|
||||
<main>
|
||||
<div class="tabs">
|
||||
@@ -53,6 +65,7 @@
|
||||
<button onclick="show('watch')" aria-selected="false" id="t-watch">📡 Guets</button>
|
||||
<button onclick="show('queue')" aria-selected="false" id="t-queue">⏳ File</button>
|
||||
<button onclick="show('library')" aria-selected="false" id="t-library">📚 Bibliothèque</button>
|
||||
<button onclick="show('devices')" aria-selected="false" id="t-devices">📱 Appareils</button>
|
||||
</div>
|
||||
|
||||
<section id="p-search">
|
||||
@@ -76,6 +89,10 @@
|
||||
|
||||
<section id="p-queue" hidden><div id="queue"></div></section>
|
||||
<section id="p-library" hidden><div id="library"></div></section>
|
||||
<section id="p-devices" hidden>
|
||||
<div class="row"><button class="go" onclick="openPairing()">Appairer un appareil</button></div>
|
||||
<div id="devices"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
@@ -108,13 +125,14 @@ async function api(path, options = {}) {
|
||||
}
|
||||
|
||||
function show(name) {
|
||||
for (const tab of ['search', 'watch', 'queue', 'library']) {
|
||||
for (const tab of ['search', 'watch', 'queue', 'library', 'devices']) {
|
||||
$('p-' + tab).hidden = tab !== name;
|
||||
$('t-' + tab).setAttribute('aria-selected', String(tab === name));
|
||||
}
|
||||
if (name === 'queue') loadQueue();
|
||||
if (name === 'library') loadLibrary();
|
||||
if (name === 'watch') loadWatches();
|
||||
if (name === 'devices') loadDevices();
|
||||
}
|
||||
|
||||
async function refreshAll() {
|
||||
@@ -245,6 +263,34 @@ async function sweep() {
|
||||
loadWatches();
|
||||
}
|
||||
|
||||
async function loadDevices() {
|
||||
try {
|
||||
const { devices } = await api('/devices');
|
||||
$('devices').innerHTML = devices.length ? devices.map((d) => `
|
||||
<div class="card"><div class="grow"><b>${esc(d.name)}</b><small>appairé le ${
|
||||
new Date(d.created_at).toLocaleString('fr-FR')}${
|
||||
d.last_seen ? ' · vu le ' + new Date(d.last_seen).toLocaleString('fr-FR')
|
||||
: ' · jamais synchronisé'}</small></div>
|
||||
<button class="ghost" onclick="revokeDevice('${d.id}','${esc(d.name)}')">Révoquer</button></div>`).join('')
|
||||
: '<div class="empty">Aucun appareil appairé</div>';
|
||||
} catch (e) { $('devices').innerHTML = `<div class="empty">${esc(String(e))}</div>`; }
|
||||
}
|
||||
|
||||
async function revokeDevice(id, name) {
|
||||
if (!confirm(`Révoquer « ${name} » ? Il ne pourra plus se synchroniser tant qu'il n'est pas réappairé.`)) return;
|
||||
await api('/devices/' + id, { method: 'DELETE' });
|
||||
loadDevices();
|
||||
}
|
||||
|
||||
async function openPairing() {
|
||||
try {
|
||||
const { seconds } = await api('/pair/open', { method: 'POST' });
|
||||
alert(`Appairage ouvert pour ${seconds} secondes.\n\n` +
|
||||
`Sur le téléphone : Réglages ▸ Connexion ▸ Appairer.\n` +
|
||||
`La fenêtre se referme dès qu'un appareil s'est servi.`);
|
||||
} catch (e) { alert('Appairage impossible : ' + e); }
|
||||
}
|
||||
|
||||
const esc = (s) => String(s).replace(/[&<>"']/g, (c) =>
|
||||
({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# Noxely — collecte côté serveur, écoute hors connexion côté mobile.
|
||||
# Copyright © 2026 Johnny (tuxgyver)
|
||||
#
|
||||
# Ce programme est un logiciel libre : vous pouvez le redistribuer et le modifier
|
||||
# selon les termes de la GNU General Public License, version 3 ou ultérieure,
|
||||
# telle que publiée par la Free Software Foundation.
|
||||
#
|
||||
# Il est distribué dans l'espoir d'être utile, mais SANS AUCUNE GARANTIE.
|
||||
# Voir le fichier LICENSE à la racine du dépôt pour le texte complet.
|
||||
|
||||
"""Tâches de fond : file de téléchargement, guet radio et enregistrement.
|
||||
|
||||
C'est ici que se déplace la consommation qui pesait sur le téléphone : le serveur balaie les
|
||||
|
||||
Reference in new issue
Block a user