Jean-Tinland / simple-bar

A yabai status bar widget for Übersicht
https://www.jeantinland.com/toolbox/simple-bar
MIT License
1.19k stars 127 forks source link

MPD now playing integration? #159

Closed hrwtech closed 3 years ago

hrwtech commented 3 years ago

I don't have much to offer by way of how but could you offer some some assistance

Jean-Tinland commented 3 years ago

Hi, I'll look into it soon and see if I can integrate something similar to the Spotify or Music/iTunes widget or link the more basic Browser Track widget!

kvndrsslr commented 3 years ago

Here is how I did that:

Could be improved by offering to customize the current-song format in simple-bar settings.

Jean-Tinland commented 3 years ago

As suggested in #176 this could be handled in a future extensible custom widget system! I'll keep you posted here.

Thank you @kvndrsslr for your research.

kvndrsslr commented 3 years ago

That (#176) is a great idea indeed!

In case @hrwtech wants to use my approach in the meantime. In the end I opted for mpc (installable through brew install mpc) instead of ncmpcpp for obtaining track info because it also allows to toggle play/pause among other stuff and ncmpcpp is more of a ncurses client than a command line swiss knife. Instead of creating a new widget I just hacked the music/iTunes widget as I do not use Apple Music and if you're using MPD the chance is you do neither. Here are the two files you need to replace:

lib/components/Music.jsx

import { React, run } from 'uebersicht'
import Specter from './Specter.jsx'
import { PlayingIcon, PausedIcon } from './Icons.jsx'

import { refreshData, clickEffect, classnames } from '../utils'

import { getSettings } from '../settings.js'

const { useRef } = React

const Music = ({ output }) => {
  const ref = useRef()
  const settings = getSettings()
  const { widgets, musicWidgetOptions } = settings
  const { musicWidget } = widgets
  if (!musicWidget || !output) return null
  const { playerState, trackInfo } = output
  const { showSpecter } = musicWidgetOptions
  if (trackInfo === '') return null

  const isPlaying = playerState === 'playing'
  const Icon = isPlaying ? PlayingIcon : PausedIcon

  const onClick = (e) => {
    clickEffect(e)
    run(`mpc toggle`).then(refreshData)
  }
  const onMouseEnter = () => {
    const target = ref.current
    if (!target) return
    const inner = target.querySelector('.music__inner')
    const slider = target.querySelector('.music__slider')
    const delta = inner.clientWidth - slider.clientWidth
    if (delta > 0) return
    const timing = Math.round((Math.abs(delta) * 100) / 4)
    Object.assign(slider.style, {
      transform: `translateX(${delta}px)`,
      transition: `transform ${timing}ms linear`
    })
  }
  const onMouseLeave = () => {
    const target = ref.current
    target && target.querySelector('.music__slider').removeAttribute('style')
  }

  const classes = classnames('music', {
    'music--playing': isPlaying
  })

  return (
    <div ref={ref} className={classes} onClick={onClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
      <Icon className="music__icon" />
      {showSpecter && isPlaying && <Specter />}
      <div className="music__inner">
        <div className="music__slider">
          {trackInfo}
        </div>
      </div>
    </div>
  )
}

export default Music

lib/scripts/get_data.sh

ACTIVE_WIDGETS="$1"
NETWORK_DEVICE="$2"
WEATHER_LOCATION="$3"
VPN_CONNECTION_NAME="$4"

contains() {
  if printf '%s\n' "$1" | grep -Fqe "$2"; then
    return 0
  else
    return 1
  fi
}

ZOOM_MIC=""
ZOOM_VIDEO=""
if contains "$ACTIVE_WIDGETS" "zoomWidget"; then
  ZOOM_MIC=$(osascript ./simple-bar/lib/scripts/zoom-mute-status.scpt)
  ZOOM_VIDEO=$(osascript ./simple-bar/lib/scripts/zoom-video-status.scpt)
fi

WEATHER="{}"
if contains "$ACTIVE_WIDGETS" "weatherWidget"; then
  if [ "$WEATHER_LOCATION" != "" ]; then
    WEATHER=$(curl -s "wttr.in/$WEATHER_LOCATION?format=j1" 2>/dev/null || echo "{}")
    if contains "$WEATHER" "Unknown"; then 
      WEATHER="{}"
    fi
  fi
fi

if contains "$ACTIVE_WIDGETS" "batteryWidget"; then
  BATTERY_PERCENTAGE=$(pmset -g batt | egrep '([0-9]+\%).*' -o --colour=auto | cut -f1 -d'%')
  BATTERY_STATUS=$(pmset -g batt | grep "'.*'" | sed "s/'//g" | cut -c 18-19)

  CAFFEINATE=caffeinate
  CAFFEINATE_PID=""
  if pgrep $CAFFEINATE 2>&1 >/dev/null; then
    CAFFEINATE_PID=$(pgrep $CAFFEINATE)
  fi

  BATTERY_CHARGING=""
  if [ "$BATTERY_STATUS" = "Ba" ]; then
    BATTERY_CHARGING="false"
  elif [ "$BATTERY_STATUS" = "AC" ]; then
    BATTERY_CHARGING="true"
  fi
fi

if contains "$ACTIVE_WIDGETS" "vpnWidget"; then
  VPN_IS_RUNNING=$(osascript -e 'tell application "System Events" to (name of processes) contains "Viscosity"' 2>&1)
  if [ "$VPN_IS_RUNNING" = true ]; then
    VPN_STATUS=$(osascript -e "tell application \"Viscosity\" to return state of the first connection where name is equal to \"$VPN_CONNECTION_NAME\"" 2>/dev/null)
  fi
fi

if contains "$ACTIVE_WIDGETS" "wifiWidget"; then
  WIFI_STATUS=$(ifconfig $NETWORK_DEVICE | grep status | cut -c 10-)
  WIFI_SSID=$(networksetup -getairportnetwork $NETWORK_DEVICE | cut -c 24-)
fi

if contains "$ACTIVE_WIDGETS" "soundWidget"; then
  VOLUME=$(osascript -e 'set ovol to output volume of (get volume settings)')
  MUTED=$(osascript -e 'set ovol to output muted of (get volume settings)')
fi

if contains "$ACTIVE_WIDGETS" "micWidget"; then
  MIC=$(osascript -e 'set ovol to input volume of (get volume settings)')
fi

if contains "$ACTIVE_WIDGETS" "keyboardWidget"; then
  KEYBOARD=$(defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleSelectedInputSources | egrep -w 'KeyboardLayout Name' | sed 's/"//g' | sed 's/KeyboardLayout Name = //g')
fi

if contains "$ACTIVE_WIDGETS" "spotifyWidget"; then
  SPOTIFY_IS_RUNNING=$(osascript -e 'tell application "System Events" to (name of processes) contains "Spotify"' 2>&1)

  if [ "$SPOTIFY_IS_RUNNING" = true ]; then
    SPOTIFY_PLAYER_STATE=$(osascript -e 'tell application "Spotify" to player state as string' 2>/dev/null || echo "stopped")
    SPOTIFY_TRACK_NAME=$(osascript -e 'tell application "Spotify" to name of current track as string' 2>/dev/null | tr \" \' || echo "unknown track")
    SPOTIFY_ARTIST_NAME=$(osascript -e 'tell application "Spotify" to artist of current track as string' 2>/dev/null | tr \" \' || echo "unknown artist")
  fi
fi

if contains "$ACTIVE_WIDGETS" "musicWidget"; then
    MUSIC_TRACK_INFO=$(mpc --format "%title%[ - %artist%]|[%file%]" | head -n 1)
    MUSIC_PLAYER_STATE=$(mpc | head -n 2 | tail -n 1 | awk '{print substr($1,2,length($1)-2)}')
fi

BROWSER_TRACK="{}"
if contains "$ACTIVE_WIDGETS" "browserTrackWidget"; then
  if GET_BROWSER_TRACK=$(osascript ./simple-bar/lib/scripts/browser_audio.applescript 2>&1); then
    BROWSER_TRACK=$GET_BROWSER_TRACK
  fi
fi

echo $(cat <<-EOF
  {
    "weather": {
      "data": $WEATHER
    },
    "battery": {
      "percentage": "$BATTERY_PERCENTAGE",
      "charging": "$BATTERY_CHARGING",
      "caffeinate": "$CAFFEINATE_PID"
    },
    "zoom": {
      "mic": "$ZOOM_MIC",
      "video": "$ZOOM_VIDEO"
    },
    "vpn": {
      "status": "$VPN_STATUS"
    },
    "wifi": {
      "status": "$WIFI_STATUS",
      "ssid": "$WIFI_SSID"
    },
    "sound": {
      "volume": "$VOLUME",
      "muted": "$MUTED"
    },
    "mic": {
      "volume": "$MIC"
    },
    "keyboard" : {
      "layout": "$KEYBOARD"
    },
    "spotify": {
      "spotifyIsRunning": "$SPOTIFY_IS_RUNNING",
      "playerState": "$SPOTIFY_PLAYER_STATE",
      "trackName": "$SPOTIFY_TRACK_NAME",
      "artistName": "$SPOTIFY_ARTIST_NAME"
    },
    "music": {
      "playerState": "$MUSIC_PLAYER_STATE",
      "trackInfo": "$MUSIC_TRACK_INFO"
    },
    "browserTrack": $BROWSER_TRACK
  }
EOF
)

In case you are running your mpd server on another host, just search/replace "mpc" with "mpc --host YOUR_HOSTNAME_OR_IP".

hrwtech commented 3 years ago

Thanks to everyone for their feedback! Unfortunately, this does not work whatsoever.

  1. This modification is not built on the most up to date version of Simple-bar
  2. There is no "lib/components/Music.jsx" should I assume that you mean "lib/components/data/music.jsx"?
  3. lib/scripts/get_data.sh is not a part of the Simple-bar stack.... so how does this tie in to making the mod work? I don't see anywhere where the script is even called so how is it supposed to work?

Do you actually have this working with the newest simple-bar?

kvndrsslr commented 3 years ago

Hey @hrwtech, sorry I did only update to the latest version just now! I did however do things right this time and created a new Widget for mpc complete with a section in the simple-bar settings to set the host/port of your MPD instance and the format string you like to display. You can try out PR #182 to see it in action.