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 { musicStyles as styles } from "../../styles/components/data/music";
const { React } = Uebersicht;
const DEFAULT_REFRESH_FREQUENCY = 10000;
/**
* Music widget component.
* @returns {JSX.Element|null} The music widget.
*/
export const Widget = React.memo(() => {
const { displayIndex, settings } = useSimpleBarContext();
const { widgets, musicWidgetOptions } = settings;
const { musicWidget } = widgets;
const { refreshFrequency, showSpecter, showOnDisplay, showIcon } =
musicWidgetOptions;
// 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) && musicWidget;
const [state, setState] = React.useState();
const [loading, setLoading] = React.useState(visible);
const [isMusicActive, setIsMusicActive] = React.useState(false);
/**
* Resets the widget state.
*/
const resetWidget = () => {
setState(undefined);
setLoading(false);
setIsMusicActive(false);
};
/**
* Fetches the current music information.
*/
const getMusic = React.useCallback(async () => {
if (!visible) return;
const osVersion = await Utils.cachedRun(`sw_vers -productVersion`, refresh);
const processName =
Utils.cleanupOutput(osVersion) === "10.15" ? "iTunes" : "Music";
const isRunning = await Utils.cachedRun(
`pgrep -xq "${processName}" && echo "true" || echo "false"`,
refresh,
);
if (Utils.cleanupOutput(isRunning) === "false") {
setLoading(false);
setIsMusicActive(false);
return;
}
const output = await Utils.cachedRun(
`osascript -e 'tell application "${processName}"' -e 'set output to (player state as string) & "|" & (name of current track as string) & "|" & (artist of current track as string)' -e 'end tell' 2>/dev/null || echo "stopped|unknown track|unknown artist"`,
refresh,
);
const [playerState, trackName, artistName] =
Utils.cleanupOutput(output).split("|");
setState({
playerState,
trackName,
artistName,
processName: Utils.cleanupOutput(processName),
});
setIsMusicActive(true);
setLoading(false);
}, [visible, refresh]);
// Use server socket to listen for music events
useServerSocket("music", visible, getMusic, resetWidget, setLoading);
// Refresh the widget at the specified interval
useWidgetRefresh(visible, getMusic, refresh);
if (loading) return ;
if (!state || !isMusicActive) return null;
const { processName, playerState, trackName, artistName } = state;
if (!trackName.length) return null;
const isPlaying = playerState === "playing";
const Icon = isPlaying ? Icons.Playing : Icons.Paused;
/**
* Handles click event to toggle play/pause.
* @param {React.MouseEvent} e - The click event.
*/
const onClick = (e) => {
Utils.clickEffect(e);
togglePlay(!isPlaying, processName);
getMusic();
};
/**
* Handles right-click event to skip to the next track.
* @param {React.MouseEvent} e - The right-click event.
*/
const onRightClick = (e) => {
Utils.clickEffect(e);
Uebersicht.run(
`osascript -e 'tell application "${processName}" to Next Track'`,
);
getMusic();
};
/**
* Handles middle-click event to open the music application.
* @param {React.MouseEvent} e - The middle-click event.
*/
const onMiddleClick = (e) => {
Utils.clickEffect(e);
Uebersicht.run(`open -a '${processName}'`);
getMusic();
};
const classes = Utils.classNames("music", {
"music--playing": isPlaying,
});
return (
{trackName} - {artistName}
);
});
Widget.displayName = "Music";
/**
* Toggles play/pause state of the music application.
* @param {boolean} isPaused - Whether the music is paused.
* @param {string} processName - The name of the music application process.
*/
function togglePlay(isPaused, processName) {
if (isPaused) {
Uebersicht.run(`osascript -e 'tell application "${processName}" to play'`);
} else {
Uebersicht.run(`osascript -e 'tell application "${processName}" to pause'`);
}
}