Replies: 4 comments 3 replies
-
Afaik, Though, I suspect the websocket way might be a better deal since it's the server pushing updates, rather than polling rapidly |
Beta Was this translation helpful? Give feedback.
-
Yeah, you should be able to connect to any running As a side note, the original, very old version of musikcube used to have an option to write the currently playing metadata to a text file at the location of your choosing... some people would just do that, then setup some sort of script to poll the file every few seconds to pick up changes. I wonder if that'd be a useful feature for future versions? |
Beta Was this translation helpful? Give feedback.
-
I just remembered that a while back I was working on a little Javascript WebSocket demo that never saw the light of day. Here's an example websocket client written with node with just a few dependencies: import _ from 'lodash';
import ReconnectingWebSocket from 'reconnecting-websocket';
import WebSocket from 'ws';
import { EventEmitter } from 'node:events';
const DEFAULT_TIMEOUT_MS = 7500;
let socket;
let messageId = 0;
const deviceId = String(Math.random());
let reconnect = false;
const pending = {};
export const events = new EventEmitter();
export const eventType = {
connect: 'connect',
disconnect: 'disconnect',
message: 'message',
error: 'error',
};
export const messageType = {
request: 'request',
response: 'response',
broadcast: 'broadcast',
timeout: 'timeout',
};
const connected = () => socket && socket.readyState === socket.OPEN;
const disconnected = () =>
socket &&
(socket.readyState === socket.CLOSED || socket.readyState === socket.CLOSING);
const onConnected = () => {
events.emit(eventType.connect);
};
const onDisconnected = (event) => {
if (!reconnect && socket) {
const oldSocket = socket;
_.defer(() => close(oldSocket));
}
if (socket) {
reconnect = false;
events.emit(eventType.disconnect, event);
}
socket = null;
};
const onError = (event) => {
events.emit(eventType.error, event);
};
const onMessageReceived = (event) => {
const message = Object.freeze(JSON.parse(event.data));
const context = pending[message.id];
if (
message.type === messageType.response &&
message.name === 'authenticate'
) {
reconnect = true;
onConnected();
} else {
if (message.type === messageType.broadcast) {
notify(message);
} else if (message.type === messageType.response) {
const callback = context && context.callback;
notify(message, callback);
}
}
if (context) {
forget(context);
}
};
const onTimeout = (messageId) => {
const context = pending[messageId];
if (context) {
fail(context);
forget(context);
}
};
const notify = (message, callback) => {
callback && callback(message);
events.emit(eventType.message, message);
};
const fail = (context, errorType) => {
const message = _.cloneDeep(context.message);
message.type = errorType || messageType.timeout;
notify(Object.freeze(message), context.callback);
};
const forget = (context) => {
clearTimeout(context.timeoutId);
delete pending[context.message.id];
};
const close = (instance) => {
instance = instance || socket;
if (socket) {
instance.close(1000, '', { keepClosed: true });
}
};
const connect = (host, port, password) => {
disconnect();
socket = new ReconnectingWebSocket(`ws://${host}:${port}`, [], { WebSocket });
socket.onopen = () => {
request('authenticate', { password });
};
socket.onclose = onDisconnected;
socket.onmessage = onMessageReceived;
socket.onerror = onError;
};
const disconnect = () => {
close(socket);
socket = null;
};
const request = (name, options, callback, timeout) => {
if (connected()) {
if (_.isFunction(options)) {
/* if no options are specified, shift arguments left one */
timeout = callback;
callback = options;
options = {};
}
const id = `musikcube-react-${messageId++}`;
const message = Object.freeze({
name,
type: messageType.request,
id,
device_id: deviceId,
options: options || {},
});
const context = { callback, message };
pending[id] = context;
timeout = _.isNumber(timeout) ? timeout : DEFAULT_TIMEOUT_MS;
if (timeout > 0) {
context.timeoutId = setTimeout(() => {
onTimeout(id);
}, timeout);
}
socket.send(JSON.stringify(message));
return id;
}
};
const broadcast = (name, options) => {
if (connected()) {
socket.send(
JSON.stringify({
type: messageType.broadcast,
id: `musikcube-react-${messageId++}`,
device_id: deviceId,
options: options || {},
})
);
}
};
const respond = (message, options) => {
if (connected()) {
const response = _.deepClone(message);
response.options = options;
socket.send(JSON.stringify(response));
}
};
const cancel = (messageId) => {
if (pending[messageId]) {
forget(pending[messageId]);
}
};
export default {
connect,
connected,
disconnected,
disconnect,
request,
cancel,
respond,
broadcast,
events,
}; You can then use it as follows: import mcws from './musikcube-websocket.js';
/* upon connecting, query current playback information */
mcws.events.addListener('connect', () => {
mcws.request('get_playback_overview', {}, (overview) => {
console.log(overview);
});
});
/* as music plays and tracks and state changes, overview updated
messages will be sent automatically to connected clients. you can
catch them all like this... */
mcws.events.addListener('message', (message) => {
if (message.name === 'playback_overview_changed') {
console.log(message);
}
});
mcws.connect('192.168.1.221', 7905, 'yourpasswordhere'); Using the example above, upon connecting to the server you should see the following output:
|
Beta Was this translation helpful? Give feedback.
-
I've just written this Node program, boiled down to what I need it to do and display and no more. It's designed to be ran at the same time as Musikcube, via an alias that runs it in the background (after a short delay) when Musikcube is called. It exits when the socket is closed. It's specifically designed for use with i3blocks. Sorry if there's no syntax highlighting; it wouldn't let me upload a file with a .js extension. It depends only on the It may be a useful starting point for anybody else looking to do a similar thing in as simple a way as possible.
This is the i3blocks block config that is called when the Node program fires
This is the alias I have set up in my
|
Beta Was this translation helpful? Give feedback.
-
I would attempt this myself and submit it, but my C++ knowledge only extends as far as Arduino and the Arduino libraries. I'd like to be able to grab this info using a shell command so I could put it in my status bar (i3 Blocks). It could be as simple as writing the info to some file in /var that can then be read with built-in shell commands. Much like how the Linux kernel puts all kinds of system info into files that can be read like any text file.
Beta Was this translation helpful? Give feedback.
All reactions