import * as Uebersicht from "uebersicht";
import * as DataWidget from "./data-widget.jsx";
import * as DataWidgetLoader from "./data-widget-loader.jsx";
import * as Icons from "../icons/icons.jsx";
import useWidgetRefresh from "../../hooks/use-widget-refresh";
import useServerSocket from "../../hooks/use-server-socket";
import { useSimpleBarContext } from "../simple-bar-context.jsx";
import * as Utils from "../../utils";
export { mpdStyles as styles } from "../../styles/components/data/mpd";
const { React } = Uebersicht;
const DEFAULT_REFRESH_FREQUENCY = 10000;
/**
* MPD Widget component
* @returns {JSX.Element|null} The MPD widget
*/
export const Widget = React.memo(() => {
const ref = React.useRef();
const { displayIndex, settings } = useSimpleBarContext();
const { widgets, mpdWidgetOptions } = settings;
const { mpdWidget } = widgets;
const {
refreshFrequency,
showSpecter,
mpdBinaryPath,
mpdHost,
mpdPort,
mpdFormatString,
showOnDisplay,
showIcon,
} = mpdWidgetOptions;
// Determine the refresh frequency for the widget
const refresh = React.useMemo(
() =>
Utils.getRefreshFrequency(refreshFrequency, DEFAULT_REFRESH_FREQUENCY),
[refreshFrequency],
);
// Determine if the widget should be visible on the current display
const visible =
Utils.isVisibleOnDisplay(displayIndex, showOnDisplay) && mpdWidget;
const [state, setState] = React.useState();
const [loading, setLoading] = React.useState(visible);
const [isMpdActive, setIsMpdActive] = React.useState(false);
const { volume: _volume } = state || {};
const defaultVolume = _volume && parseInt(_volume);
const [volume, setVolume] = React.useState(defaultVolume);
const [dragging, setDragging] = React.useState(false);
/**
* Reset the widget state
*/
const resetWidget = () => {
setState(undefined);
setLoading(false);
setIsMpdActive(false);
};
/**
* Fetch MPD data
*/
const getMpd = React.useCallback(async () => {
if (!visible) return;
try {
const mpdProcess = await Utils.cachedRun(
`pgrep -x mpd > /dev/null && echo "true" || echo "false"`,
refresh,
);
if (Utils.cleanupOutput(mpdProcess) === "false") {
setLoading(false);
setIsMpdActive(false);
return;
}
const [playerState, trackInfo, volumeState] = await Promise.all([
Utils.cachedRun(
`${mpdBinaryPath} --host ${mpdHost} --port ${mpdPort} | head -n 2 | tail -n 1 | awk '{print substr($1,2,length($1)-2)}' 2>/dev/null || echo "stopped"`,
refresh,
),
Utils.cachedRun(
`${mpdBinaryPath} --host ${mpdHost} --port ${mpdPort} --format "${mpdFormatString}" | head -n 1`,
refresh,
),
Utils.cachedRun(
`${mpdBinaryPath} --host ${mpdHost} --port ${mpdPort} volume | sed -e 's/volume:[ ]*//g' -e 's/%//g'`,
refresh,
),
]);
if (Utils.cleanupOutput(trackInfo) === "") {
setLoading(false);
setIsMpdActive(false);
return;
}
setState({
playerState: Utils.cleanupOutput(playerState),
trackInfo: Utils.cleanupOutput(trackInfo),
volume: Utils.cleanupOutput(volumeState),
});
setIsMpdActive(true);
setLoading(false);
} catch {
setLoading(false);
setIsMpdActive(false);
}
}, [visible, mpdBinaryPath, mpdHost, mpdPort, mpdFormatString, refresh]);
// Update the volume when dragging ends
React.useEffect(() => {
if (!dragging) setSound(mpdBinaryPath, mpdHost, mpdPort, volume);
}, [dragging, mpdBinaryPath, mpdHost, mpdPort, volume]);
// Set the volume state when dragging ends
React.useEffect(() => {
if (!dragging) setVolume(volume);
}, [dragging, volume]);
useServerSocket("mpd", visible, getMpd, resetWidget, setLoading);
useWidgetRefresh(visible, getMpd, refresh);
if (loading) return ;
if (!state || !isMpdActive) return null;
const { playerState, trackInfo } = state;
if (!trackInfo.length) return null;
const isPlaying = playerState === "playing";
const Icon = isPlaying ? Icons.Playing : Icons.Paused;
/**
* Handle click event to toggle play/pause
* @param {React.MouseEvent} e - The click event
*/
const onClick = async (e) => {
Utils.clickEffect(e);
await togglePlay(mpdBinaryPath, mpdHost, mpdPort);
await getMpd();
};
/**
* Handle volume change event
* @param {React.ChangeEvent} e - The change event
*/
const onChange = (e) => {
const value = parseInt(e.target.value);
setVolume(value);
};
const onMouseDown = () => setDragging(true);
const onMouseUp = () => setDragging(false);
const stopPropagation = (e) => e.stopPropagation();
const onMouseEnter = () =>
Utils.startSliding(ref.current, ".mpd__inner", ".mpd__info");
const onMouseLeave = () => Utils.stopSliding(ref.current, ".mpd__slider");
const classes = Utils.classNames("sound", "mpd", {
"mpd--playing": isPlaying,
"mpd--dragging": dragging,
});
return (
);
});
Widget.displayName = "Mpd";
/**
* Toggle play/pause state of MPD
* @param {string} path - The path to the MPD binary
* @param {string} host - The MPD host
* @param {string} port - The MPD port
* @returns {Promise}
*/
async function togglePlay(path, host, port) {
return Uebersicht.run(`${path} --host ${host} --port ${port} toggle`);
}
/**
* Set the volume of MPD
* @param {string} path - The path to the MPD binary
* @param {string} host - The MPD host
* @param {string} port - The MPD port
* @param {number} volume - The volume level to set
*/
function setSound(path, host, port, volume) {
if (!volume) return;
Uebersicht.run(`${path} --host ${host} --port ${port} volume ${volume}`);
}