From d06784958d73159b5e4abafe5114804662114ed7 Mon Sep 17 00:00:00 2001 From: Ryan Schanzenbacher Date: Thu, 9 Jul 2026 22:15:53 -0400 Subject: move ubersicht modules in tree --- .../lib/components/data/spotify.jsx | 189 +++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100755 users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/spotify.jsx (limited to 'users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/spotify.jsx') diff --git a/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/spotify.jsx b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/spotify.jsx new file mode 100755 index 0000000..bc9f6e6 --- /dev/null +++ b/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/spotify.jsx @@ -0,0 +1,189 @@ +import * as Uebersicht from "uebersicht"; +import * as DataWidget from "./data-widget.jsx"; +import * as DataWidgetLoader from "./data-widget-loader.jsx"; +import useWidgetRefresh from "../../hooks/use-widget-refresh"; +import useServerSocket from "../../hooks/use-server-socket"; +import { useSimpleBarContext } from "../simple-bar-context.jsx"; +import * as Icons from "../icons/icons.jsx"; +import * as Utils from "../../utils"; + +export { spotifyStyles as styles } from "../../styles/components/data/spotify"; + +const { React } = Uebersicht; + +const DEFAULT_REFRESH_FREQUENCY = 10000; + +/** + * Spotify widget component. + * @returns {JSX.Element|null} The Spotify widget. + */ +export const Widget = React.memo(() => { + const { displayIndex, settings } = useSimpleBarContext(); + const { widgets, spotifyWidgetOptions } = settings; + const { spotifyWidget } = widgets; + const { + refreshFrequency, + showSpecter, + showOnDisplay, + showIcon, + showSpotifyMetadata, + } = spotifyWidgetOptions; + + // 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 + const visible = + Utils.isVisibleOnDisplay(displayIndex, showOnDisplay) && spotifyWidget; + + const [state, setState] = React.useState(); + const [loading, setLoading] = React.useState(visible); + const [isSpotifyActive, setIsSpotifyActive] = React.useState(false); + + /** + * Resets the widget state. + */ + const resetWidget = () => { + setState(undefined); + setLoading(false); + setIsSpotifyActive(false); + }; + + /** + * Fetches the current Spotify state. + */ + const getSpotify = React.useCallback(async () => { + if (!visible) return; + const isRunning = await Utils.cachedRun( + `pgrep -fq '[S]potify Helper' && echo "true" || echo "false"`, + refresh, + ); + if (Utils.cleanupOutput(isRunning) === "false") { + setLoading(false); + setIsSpotifyActive(false); + setState({ + playerState: "", + trackName: "", + artistName: "", + }); + return; + } + // if showSpotifyMetadata is enabled, retrieves all information + if (showSpotifyMetadata) { + const output = await Utils.cachedRun( + `osascript -e 'tell application "Spotify"' -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||"`, + refresh, + ); + const [playerState, trackName, artistName] = + Utils.cleanupOutput(output).split("|"); + setState({ + playerState, + trackName, + artistName, + }); + // else, only playerState + } else { + const playerState = await Utils.cachedRun( + `osascript -e 'tell application "Spotify" to player state as string' 2>/dev/null || echo "stopped"`, + refresh, + ); + setState({ + playerState: Utils.cleanupOutput(playerState), + trackName: "", + artistName: "", + }); + } + + setIsSpotifyActive(true); + setLoading(false); + }, [visible, showSpotifyMetadata, refresh]); + + // Set up server socket and widget refresh hooks + useServerSocket("spotify", visible, getSpotify, resetWidget, setLoading); + useWidgetRefresh(visible, getSpotify, refresh); + + if (loading) return ; + if (!state || !isSpotifyActive) return null; + const { playerState, trackName, artistName } = state; + + if (!trackName.length && showSpotifyMetadata) return null; + + const label = artistName.length ? `${trackName} - ${artistName}` : trackName; + const isPlaying = playerState === "playing"; + const Icon = getIcon(playerState); + + /** + * Handles click event to toggle play/pause. + * @param {React.MouseEvent} e - The click event. + */ + const onClick = (e) => { + Utils.clickEffect(e); + togglePlay(!isPlaying); + getSpotify(); + }; + + /** + * 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 "Spotify" to Next Track'`); + getSpotify(); + }; + + /** + * Handles middle-click event to open Spotify. + * @param {React.MouseEvent} e - The middle-click event. + */ + const onMiddleClick = (e) => { + Utils.clickEffect(e); + Uebersicht.run(`open -a 'Spotify'`); + getSpotify(); + }; + + const classes = Utils.classNames("spotify", { + "spotify--hidden-metadata": !showSpotifyMetadata, + "spotify--playing": isPlaying, + }); + + return ( + + {showSpotifyMetadata && label} + + ); +}); + +Widget.displayName = "Spotify"; + +/** + * Toggles play/pause state of Spotify. + * @param {boolean} isPaused - Whether the player is paused. + */ +function togglePlay(isPaused) { + const state = isPaused ? "play" : "pause"; + Uebersicht.run(`osascript -e 'tell application "Spotify" to ${state}'`); +} + +/** + * Gets the appropriate icon based on the player state. + * @param {string} playerState - The current state of the player. + * @returns {JSX.Element} The icon component. + */ +function getIcon(playerState) { + if (playerState === "stopped") return Icons.Stopped; + if (playerState === "playing") return Icons.Playing; + return Icons.Paused; +} -- cgit v1.3