rox

Un lettore su cui costruire

rox ha quello per cui gli altri lettori incorporano un web server: un socket JSON-RPC locale, eventi spinti man mano che le cose cambiano, un proxy MCP e uno stream in uscita verso Icecast.

Il server che rox si rifiuta di essere

Il modo consueto in cui un lettore desktop si apre all'esterno è incorporare un web server. Compra il controllo remoto e paga tutto il resto: una porta sulla tua macchina, un problema di autenticazione, una web UI che abbandona il look del lettore, e una superficie d'attacco che un lettore musicale non ha motivo di avere. La pagano tutti gli utenti perché i pochi che scriptano il loro lettore possano farlo.

rox tiene la capacità e rifiuta il mezzo. La sua interfaccia macchina è un socket locale, un socket di dominio Unix su Linux e macOS e una named pipe su Windows, legato alla cartella dati così due istanze portatili non si incrociano mai. L'auth sono i permessi del filesystem: qualsiasi cosa possa leggere la tua musica può già fare ciò che il socket consente. Niente è in ascolto su una porta, e nessun server HTTP viaggia nel binario.

Un socket, l'intero lettore

Il protocollo è JSON-RPC 2.0, un oggetto per riga, aperto con un handshake di versione. transport.* comanda il deck: toggle, seek, volume, la sezione A-B. queue.* modifica l'ordine di riproduzione per id di voce stabile, così la riga che hai spostato è ancora la riga che intendevi. library.* cerca, restituisce i tag completi della traccia in riproduzione e consegna le copertine. library.rescan e tasks.* avviano, seguono e fermano le riscansioni e i passaggi di analisi, e la risposta all'avvio porta il numero di tracce e la stima di durata che la finestra ti avrebbe mostrato. Ogni metodo legge e comanda lo stesso lettore e la stessa libreria dei pannelli, quindi il socket non può mai dire qualcosa che l'interfaccia non direbbe.

I precedenti sono l'IPC JSON di mpv e il protocollo di mpd, e il costo è lo stesso: a un consumatore serve un client socket dove un server avrebbe offerto curl. roxctl, il client di riferimento nell'albero dei sorgenti, copre il caso shell, una chiamata per invocazione, --json quando a leggere è uno script, così roxctl next e roxctl search miles davis funzionano prima che tu abbia scritto una riga di qualsiasi cosa.

Push, così non fai mai polling

Un front end che non può sottoscrivere finirà per fare polling, quindi gli eventi sono nel contratto dalla prima versione. Chiama subscribe e il socket spinge frame man mano che le cose si muovono: event.track al cambio di traccia, event.playback quando cambiano stato di riproduzione, volume o muto, event.queue quando cambia l'ordine.

Un consumatore che resta indietro viene scollegato invece che atteso, così un client in stallo non può mai frenare la riproduzione. roxctl watch stampa il flusso man mano che arriva, che è un display del brano in riproduzione per una barra di stato risolto in un loop di shell.

MCP, che nessun altro lettore ha

Accanto all'app c'è rox-mcp, un binario sottile che parla MCP su stdio da un lato e col socket dall'altro. Puntaci Claude o qualsiasi client MCP e il lettore diventa strumenti: cosa suona, cerca nella libreria, leggi la coda, comanda il trasporto, avvia una riscansione o un passaggio di analisi. "Metti in coda qualcosa di tranquillo dagli anni '70" smette di essere una funzione che rox dovrebbe farsi crescere e diventa una frase su cui il tuo assistente può agire.

Ogni strumento fa da proxy a un metodo del socket, quindi la superficie MCP è per costruzione un sottoinsieme di quello che il socket serve e non può correre più avanti. Ed è opt-in due volte: un interruttore delle funzioni AI, spento di default, rivela una pagina di impostazioni MCP con il suo interruttore, e ogni chiamata di strumento ricontrolla gli interruttori nell'app in esecuzione prima di rispondere. Spento significa un rifiuto chiaro, non un blocco.

La metà audio

Il controllo è metà di un front end; l'altra metà è sentire qualcosa. rox si connette in uscita a un server Icecast come client sorgente e spinge il flusso elaborato, codificato in MP3, sul mount che indichi. La presa viene dopo la catena di elaborazione, quindi la trasmissione è esattamente quello che arriva alle casse, equalizzatore compreso.

La direzione è il punto. rox si connette in uscita, non serve mai: il mount, gli ascoltatori e il lato rete appartengono tutti a Icecast. Un server irraggiungibile fa cadere i chunk a terra invece di toccare la riproduzione locale, e il sink si riconnette per conto suo finché la configurazione è al suo posto.

Cosa apre tutto questo

Ogni pezzo è utile da solo. Un demone per i tasti multimediali o un widget per la barra di stato sono il socket e poche righe. Un controller hardware, uno stream deck o una manopola su un microcontrollore, è transport.* da qualsiasi cosa raggiunga un socket. Il servizio da chiosco per una sala d'ascolto sono i metodi della coda e event.track che muove un display.

Mettili insieme e rox è il backend di un impianto di streaming personale: trasporto e coda sul socket, audio incorporato dal mount Icecast, e un front end scritto in quello che preferisci, con la tua libreria che suona sulla macchina che la conserva.

Porta un client socket

Il socket è acceso ovunque giri rox, rox-mcp viaggia accanto all'app, e la pagina MCP nelle impostazioni ha la riga di configurazione per il tuo client. roxctl si compila con un solo comando cargo dal codice sorgente.