rox

Un lecteur sur lequel bâtir

rox a ce que les autres lecteurs embarquent un serveur web pour obtenir : un socket JSON-RPC local, des événements poussés à mesure que les choses changent, un proxy MCP et un flux sortant vers Icecast.

Le serveur que rox refuse d'être

La manière habituelle pour un lecteur de bureau de s'ouvrir, c'est d'embarquer un serveur web. Cela achète le contrôle à distance et coûte tout le reste : un port sur ta machine, une histoire d'authentification, une interface web qui abandonne l'apparence du lecteur, et une surface d'attaque qu'un lecteur de musique n'a aucune raison d'avoir. Tous les utilisateurs paient pour que les rares qui scriptent leur lecteur le puissent.

rox garde la capacité et refuse le véhicule. Son interface machine est un socket local, un socket de domaine Unix sous Linux et macOS et un tube nommé sous Windows, rattaché au dossier de données pour que deux instances portables ne se croisent jamais. L'authentification, ce sont les permissions du système de fichiers : tout ce qui peut lire ta musique peut déjà faire ce que le socket permet. Rien n'écoute sur un port, et aucun serveur HTTP n'est embarqué dans le binaire.

Un socket, tout le lecteur

Le protocole est JSON-RPC 2.0, un objet par ligne, ouvert par une poignée de main de version. transport.* pilote la platine : bascule, position, volume, la section A-B. queue.* édite l'ordre de lecture par identifiant d'entrée stable, la ligne que tu as déplacée reste donc la ligne que tu visais. library.* cherche, renvoie les tags complets de la piste en cours et remet la pochette. library.rescan et tasks.* lancent, suivent et arrêtent les réanalyses de la bibliothèque et les passes d'analyse, et la réponse au lancement porte le nombre de pistes et l'estimation de durée que la fenêtre t'aurait montrés. Chaque méthode lit et pilote le même lecteur et la même bibliothèque que les panneaux, le socket ne peut donc jamais dire quelque chose que l'interface ne dirait pas.

Les précédents sont l'IPC JSON de mpv et le protocole de mpd, et leur coût aussi : un consommateur a besoin d'un client socket là où un serveur aurait offert curl. roxctl, le client de référence dans l'arbre des sources, couvre le cas du shell, un appel par invocation, --json quand c'est un script qui lit, si bien que roxctl next et roxctl search miles davis marchent avant que tu aies écrit une ligne de quoi que ce soit.

Poussé, pour ne jamais sonder

Un front-end qui ne peut pas s'abonner finira par sonder, les événements font donc partie du contrat depuis la version un. Appelle subscribe et le socket pousse des trames à mesure que les choses bougent : event.track au changement de piste, event.playback quand l'état de lecture, le volume ou la sourdine changent, event.queue quand l'ordre change.

Un consommateur qui prend du retard est coupé plutôt qu'attendu, un client à l'arrêt ne peut donc jamais refouler jusque dans la lecture. roxctl watch imprime le flux à mesure qu'il arrive, soit un affichage de lecture en cours pour une barre d'état, résolu en une boucle shell.

MCP, qu'aucun autre lecteur n'a

À côté de l'application se tient rox-mcp, un binaire mince qui parle MCP sur stdio d'un côté et au socket de l'autre. Pointe Claude ou n'importe quel client MCP dessus et le lecteur devient des outils : ce qui joue, chercher dans la bibliothèque, lire la file d'attente, piloter le transport, lancer une nouvelle analyse ou une passe d'analyse. « Mets-moi quelque chose de calme des années 70 » cesse d'être une fonctionnalité que rox devrait développer et devient une phrase que ton assistant peut exécuter.

Chaque outil relaie une méthode du socket, la surface MCP est donc par construction un sous-ensemble de ce que sert le socket et ne peut pas le devancer. Et c'est doublement opt-in : un interrupteur de fonctions IA, désactivé par défaut, révèle une page de réglages MCP avec son propre interrupteur, et chaque appel d'outil revérifie les deux dans l'application en marche avant de répondre. Désactivé veut dire un refus net, pas un blocage.

La moitié audio

Le contrôle est la moitié d'un front-end ; l'autre moitié, c'est entendre quelque chose. rox se connecte en sortie à un serveur Icecast comme client source et pousse le flux traité, encodé en MP3, vers le point de montage que tu nommes. La prise vient après la chaîne de traitement, la diffusion est donc exactement ce que reçoivent les enceintes, égaliseur compris.

La direction est tout le propos. rox se connecte en sortie, il ne sert jamais : le point de montage, les auditeurs et la face réseau appartiennent tous à Icecast. Face à un serveur injoignable, les blocs tombent par terre plutôt que de toucher à la lecture locale, et le client se reconnecte de lui-même, à son propre rythme, tant que la config reste en place.

Ce que cela ouvre

Chaque pièce est utile seule. Un démon de touches multimédias ou un widget de barre d'état, c'est le socket et quelques lignes. Un contrôleur matériel, un stream deck ou un bouton sur un microcontrôleur, c'est transport.* depuis n'importe quoi qui atteint un socket. Le mode kiosque d'une salle d'écoute, ce sont les méthodes de file d'attente et event.track qui pilotent un affichage.

Mets-les ensemble et rox est le backend d'une installation de streaming personnelle : transport et file d'attente par le socket, l'audio pris sur le point de montage Icecast, et un front-end écrit dans ce que tu veux, avec ta bibliothèque jouant sur la machine qui la stocke.

Apporte un client socket

Le socket est actif partout où rox tourne, rox-mcp est livré à côté de l'application, et la page MCP des réglages a la ligne de config pour ton client. roxctl se compile depuis les sources en une commande cargo.