From aace131a0e73a04c18ee3d6baf37c6ec710cb3c7 Mon Sep 17 00:00:00 2001 From: soraefir Date: Sat, 13 Jun 2026 19:21:33 +0200 Subject: [PATCH] Radio --- .../wayland/apps/eww/bar/css/_colors.scss | 4 +- .../home/wayland/apps/eww/bar/css/_radio.scss | 122 +++++---- .../home/wayland/apps/eww/bar/css/_sys.scss | 4 +- .../home/wayland/apps/eww/bar/scripts/net/net | 2 +- .../wayland/apps/eww/bar/scripts/panel-toggle | 26 +- .../home/wayland/apps/eww/bar/scripts/radio | 248 +++++++++++++----- .../wayland/apps/eww/bar/windows/clock.yuck | 26 +- .../wayland/apps/eww/bar/windows/popup.yuck | 4 +- .../wayland/apps/eww/bar/windows/radio.yuck | 173 +++++------- .../wayland/apps/eww/bar/windows/sys.yuck | 6 +- modules/home/wayland/apps/eww/default.nix | 2 + modules/home/wayland/base/default.nix | 1 + 12 files changed, 374 insertions(+), 244 deletions(-) diff --git a/modules/home/wayland/apps/eww/bar/css/_colors.scss b/modules/home/wayland/apps/eww/bar/css/_colors.scss index b780c9c..781021e 100644 --- a/modules/home/wayland/apps/eww/bar/css/_colors.scss +++ b/modules/home/wayland/apps/eww/bar/css/_colors.scss @@ -26,4 +26,6 @@ $border-radius: 8px; $border-width: 2px; $gaps-screen: 8px; -$gaps-window: 4px; \ No newline at end of file +$gaps-window: 4px; + +$panel-font-size: 10pt; \ No newline at end of file diff --git a/modules/home/wayland/apps/eww/bar/css/_radio.scss b/modules/home/wayland/apps/eww/bar/css/_radio.scss index c0642d7..9424597 100644 --- a/modules/home/wayland/apps/eww/bar/css/_radio.scss +++ b/modules/home/wayland/apps/eww/bar/css/_radio.scss @@ -1,66 +1,90 @@ -.radio-win { - //margin: $gaps-screen; - //padding: .5em; + +.radio-accent { background-color: $base0D; } + +// Now playing +.radio-now-playing { + margin-bottom: 8pt; } -.album_art, .station_art { +.radio-art { background-repeat: no-repeat; @include border-radius; + @include background-base2; + min-width: 72px; + min-height: 72px; } -.album_art { - background-size: 240px; - min-height: 240px; - min-width: 240px; - margin: $gaps-screen; -} - -.song { - @include color-accent; - font-size: 24px; - font-weight: bold; - margin: 20px 0 0; -} - -.artist { - color: $base0E; - font-size: 16px; - font-weight: normal; - margin: 0 0 $gaps-screen; -} - -.btn_bar { +.radio-art-icon { + font-size: 2em; @include color-body; - font-size: 20px; +} + +.radio-song { + font-size: 0.88em; font-weight: bold; - margin: $gaps-screen 0; + @include color-base; + margin-bottom: 2pt; } -.btn_play { - font-size: 48px; - font-weight: bold; - margin: 0 12px; - - &:hover { @include color-base; } +.radio-artist { + font-size: 0.74em; + @include color-body; } -.station_list { - border-right: $border-width solid $base03; - margin-right: $gaps-screen; +// Controls +.radio-controls { + margin-top: 6pt; } -.station_art { - background-size: 50px; - min-height: 50px; - min-width: 50px; - margin: $gaps-window; - margin-right: $gaps-screen; - @include background-base; - border: $border-width solid $base00; - - &:hover { border-color: $base04; } +.radio-ctrl-btn { + font-size: 2em; + padding: 4pt 20pt; + @include border-radius; + @include color-body; + &:hover { @include background-active; @include color-base; } } -.station_sel { - border-color: $base03; +// Station list +.station-list {} + +scrollbar { + background-color: transparent; + border: none; + min-width: 6px; +} +scrollbar trough { + @include background-base2; + @include border-radius; + min-width: 6px; +} +scrollbar slider { + background-color: $base04; + @include border-radius; + min-width: 6px; + min-height: 20px; + &:hover { background-color: $base05; } +} + +.station-row { + padding: 4pt 3pt; + @include border-radius; + &:hover { @include background-base2; } +} + +.station-row-active { + .station-name { @include color-accent; } +} + +.station-icon { + background-repeat: no-repeat; + @include border-radius; + @include background-base2; + min-width: 28px; + min-height: 28px; + margin-right: 8pt; +} + +.station-name { + font-size: 0.76em; + @include color-body; } diff --git a/modules/home/wayland/apps/eww/bar/css/_sys.scss b/modules/home/wayland/apps/eww/bar/css/_sys.scss index 5df3709..1da0dde 100644 --- a/modules/home/wayland/apps/eww/bar/css/_sys.scss +++ b/modules/home/wayland/apps/eww/bar/css/_sys.scss @@ -7,7 +7,7 @@ .cpubar, .gpubar, .membar, .batbar { @include background-base2; margin: $gaps-window 0; } // Window chrome -.sys-win { padding: 10pt; } +.sys-win { padding: 10pt; font-size: $panel-font-size; } .sys-section { margin-bottom: 0; } .sys-section-header { margin-bottom: 10pt; } @@ -48,7 +48,7 @@ .ram-ring { color: $base08; } .swap-ring { color: $base09; } -// Ring margins — freq rings use larger margins for concentric overlay effect +// Ring margins - freq rings use larger margins for concentric overlay effect .cpu-usage-ring, .gpu-ring { margin: 3pt; } .ram-ring, .bat-ring { margin: 4pt; } .swap-ring { margin: 3.5pt; } diff --git a/modules/home/wayland/apps/eww/bar/scripts/net/net b/modules/home/wayland/apps/eww/bar/scripts/net/net index bc27df3..bda2c44 100755 --- a/modules/home/wayland/apps/eww/bar/scripts/net/net +++ b/modules/home/wayland/apps/eww/bar/scripts/net/net @@ -41,7 +41,7 @@ make_content() { usb_connected=true fi - # WiFi — use IP presence as connection indicator (more reliable than wpa_cli) + # WiFi - use IP presence as connection indicator (more reliable than wpa_cli) local wifi_connected=false wifi_enabled=false wifi_icon="󰤮" wifi_ssid="" if ! rfkill list wlan 2>/dev/null | grep -q "Soft blocked: yes"; then wifi_enabled=true diff --git a/modules/home/wayland/apps/eww/bar/scripts/panel-toggle b/modules/home/wayland/apps/eww/bar/scripts/panel-toggle index f4bbcad..ea43ce0 100755 --- a/modules/home/wayland/apps/eww/bar/scripts/panel-toggle +++ b/modules/home/wayland/apps/eww/bar/scripts/panel-toggle @@ -1,12 +1,30 @@ #!/usr/bin/env bash PANEL="$1" -CURRENT=$(eww state 2>/dev/null | grep '^active-panel:' | sed 's/^active-panel: //' | tr -d '"') + +# Drop concurrent invocations, but recover from stale locks (dead processes) +LOCK="/tmp/eww_panel_toggle.lock" +if [ -f "$LOCK" ] && kill -0 "$(cat "$LOCK" 2>/dev/null)" 2>/dev/null; then + exit 0 +fi +echo $$ > "$LOCK" +trap 'rm -f "$LOCK"' EXIT + +CURRENT=$(eww get active-panel 2>/dev/null | tr -d '"') + +open_popup() { + local screen + screen=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.focused == true) | .name' | head -n1) + if [ -n "$screen" ]; then + eww open popup --screen "$screen" + else + eww open popup + fi +} if [ "$CURRENT" = "$PANEL" ]; then - eww close popup eww update active-panel="" + eww close popup 2>/dev/null else eww update active-panel="$PANEL" - SCREEN=$(hyprctl monitors -j 2>/dev/null | jq -r '.[] | select(.focused == true) | .name' | head -n1) - [ -n "$SCREEN" ] && eww open popup --screen "$SCREEN" || eww open popup + open_popup fi diff --git a/modules/home/wayland/apps/eww/bar/scripts/radio b/modules/home/wayland/apps/eww/bar/scripts/radio index 812599a..99ab68a 100755 --- a/modules/home/wayland/apps/eww/bar/scripts/radio +++ b/modules/home/wayland/apps/eww/bar/scripts/radio @@ -1,106 +1,224 @@ #!/usr/bin/env bash -get_time_ms() { - date -u +%s%3N -} - URL_BASE="https://www.radiorecord.ru/api" - MPV_PID_FILE="/tmp/mpv_radio_pid" RADIO_ID_FILE="/tmp/radio_id" -STATIONS="[]" +STATION_IDS='[507,522,523,536,537,42532,42602]' -PID=$( [ -e "$MPV_PID_FILE" ] && cat "$MPV_PID_FILE" || echo 0 ) -RADIO_ID=$( [ -e "$RADIO_ID_FILE" ] && cat "$RADIO_ID_FILE" || echo 0 ) -RADIO_URL="" -INFO='{"id":null,"artist":null,"song":null,"image600":null}' -PAUSED=$(( $PID == 0 || $RADIO_ID == 0 ? 1 : 0 )) +# Custom (non-radiorecord) stations +# id must be a unique integer >= 1000000 to avoid collision with radiorecord IDs. +# icon_fill_white: URL to station icon image, or "" for none. +CUSTOM_STATIONS='[ +]' + +STATIONS="[]" +DEFAULT_INFO='{"artist":"","song":"","image600":""}' +DEFAULT_MEDIA='{"player":"","status":"Stopped","artist":"","title":"","art":""}' + +PID=$([ -e "$MPV_PID_FILE" ] && cat "$MPV_PID_FILE" || echo 0) +RADIO_ID=$([ -e "$RADIO_ID_FILE" ] && cat "$RADIO_ID_FILE" || echo 0) +PAUSED=$(( PID == 0 ? 1 : 0 )) +INFO="$DEFAULT_INFO" +MEDIA="$DEFAULT_MEDIA" STATUS="{}" -get_radio() { - echo "$STATIONS" | jq -r --argjson sel_id "$RADIO_ID" 'map(select(.id == $sel_id)).[0]' +get_stations() { + local rr + rr=$(curl -s --compressed "$URL_BASE/stations/" 2>/dev/null \ + | jq --argjson ids "$STATION_IDS" \ + '.result.stations | map(select(.id | IN($ids[]))) | map({id, title, stream_hls, icon_fill_white, "radiorecord": true})' 2>/dev/null) + jq -n \ + --argjson rr "${rr:-[]}" \ + --argjson custom "$CUSTOM_STATIONS" \ + '$rr + ($custom | map(. + {"radiorecord": false}))' } get_song() { - echo $(curl -s "$URL_BASE/station/history/?id=$RADIO_ID" | jq '.result.history[0] | ({id, artist, song, image600})') + curl -s --compressed "$URL_BASE/station/history/?id=$RADIO_ID" \ + | jq '.result.history[0] | {artist, song, image600}' } - -update() { - PID=$( [ -e "$MPV_PID_FILE" ] && cat "$MPV_PID_FILE" || echo 0 ) - RADIO_ID=$( [ -e "$RADIO_ID_FILE" ] && cat "$RADIO_ID_FILE" || echo 0 ) - PAUSED=$(( $PID == 0 && $RADIO_ID > 0 ? 1 : 0 )) +get_stream_url() { + echo "$STATIONS" | jq -r --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].stream_hls // empty' } -start() { - STATIONS=$(curl -s "$URL_BASE/stations/" | jq --argjson ids '[507,522,523,536,537,42532,42602]' '.result.stations | map(select(.id | IN($ids[]))) | map({id, title, stream_hls, icon_fill_white})') - RADIO_URL=$(get_radio | jq -r '.stream_hls') - if [ "$PAUSED" = 0 ]; then - kill -9 $PID +get_player_info() { + local player status artist title art + # exclude mpv (radio) so it never appears as "external player" + player=$(playerctl -l 2>/dev/null | grep -Ev '^mpv' | head -1) + if [ -z "$player" ]; then + echo '{"player":"","status":"Stopped","artist":"","title":"","art":""}' + return fi - mpv "$RADIO_URL" & echo $! > "$MPV_PID_FILE" - echo $RADIO_ID > "$RADIO_ID_FILE" + status=$(playerctl -p "$player" status 2>/dev/null || echo "Stopped") + artist=$(playerctl -p "$player" metadata artist 2>/dev/null || echo "") + title=$(playerctl -p "$player" metadata title 2>/dev/null || echo "") + art=$(playerctl -p "$player" metadata mpris:artUrl 2>/dev/null || echo "") + jq -cnr --arg player "$player" --arg status "$status" \ + --arg artist "$artist" --arg title "$title" --arg art "$art" \ + '{player:$player, status:$status, artist:$artist, title:$title, art:$art}' } +update_state() { + PID=$([ -e "$MPV_PID_FILE" ] && cat "$MPV_PID_FILE" || echo 0) + RADIO_ID=$([ -e "$RADIO_ID_FILE" ] && cat "$RADIO_ID_FILE" || echo 0) + if [ "$PID" -gt 0 ] && ! kill -0 "$PID" 2>/dev/null; then + PID=0 + rm -f "$MPV_PID_FILE" + fi + PAUSED=$(( PID == 0 ? 1 : 0 )) +} -toggle() { - if [ "$PAUSED" = 1 ]; then - echo $RADIO_ID $PID - start +emit_status() { + jq -cnr \ + --argjson stations "${STATIONS:-[]}" \ + --argjson radio_id "${RADIO_ID:-0}" \ + --argjson is_paused "${PAUSED:-1}" \ + --argjson info "${INFO:-$DEFAULT_INFO}" \ + --argjson media "${MEDIA:-$DEFAULT_MEDIA}" \ + '{"is_paused": $is_paused, "song": $info, "radio": $radio_id, "stations": $stations, "media": $media}' +} + +get_mpv_node() { + wpctl status 2>/dev/null | awk ' + /Streams:/ { in_s = 1 } + /Sinks:|Sources:|Clients:/ { in_s = 0 } + in_s && /[0-9]+\..*mpv/ { match($0, /[0-9]+/); print substr($0, RSTART, RLENGTH); exit } + ' +} + +get_volume() { + if [ "$PID" -gt 0 ] && kill -0 "$PID" 2>/dev/null; then + local node_id; node_id=$(get_mpv_node) + [ -n "$node_id" ] && wpctl get-volume "$node_id" 2>/dev/null \ + | awk '{printf "%d", $2 * 100}' else - kill -9 "$PID" - rm -f $MPV_PID_FILE + local player; player=$(playerctl -l 2>/dev/null | grep -Ev '^mpv' | head -1) + [ -n "$player" ] && playerctl -p "$player" volume 2>/dev/null \ + | awk '{printf "%d", $1 * 100}' fi } -status() { - echo $(jq -cnr --argjson pid "$PID" --argjson stations "$STATIONS" --argjson radio_id "$RADIO_ID" --argjson is_paused "$PAUSED" --argjson info "$INFO" '{"is_paused": $is_paused, "song": $info, "radio": $radio_id, "stations": $stations}') +do_vol() { + local vol="${1%.*}" + if [ "$PID" -gt 0 ] && kill -0 "$PID" 2>/dev/null; then + local node_id; node_id=$(get_mpv_node) + [ -n "$node_id" ] && wpctl set-volume "$node_id" "${vol}%" + else + playerctl volume "$(awk -v v="$vol" 'BEGIN{printf "%.2f", v/100}')" 2>/dev/null + fi + eww update radio-vol="$vol" } +do_mute() { + if [ "$PID" -gt 0 ] && kill -0 "$PID" 2>/dev/null; then + local node_id; node_id=$(get_mpv_node) + if [ -n "$node_id" ]; then + wpctl set-mute "$node_id" toggle + if wpctl get-volume "$node_id" 2>/dev/null | grep -q MUTED; then + eww update radio-muted=true + else + eww update radio-muted=false + fi + fi + else + wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle + if wpctl get-volume @DEFAULT_AUDIO_SINK@ 2>/dev/null | grep -q MUTED; then + eww update radio-muted=true + else + eww update radio-muted=false + fi + fi +} + +do_start() { + [ "$RADIO_ID" -le 0 ] && return + STATIONS=$(get_stations) + RADIO_URL=$(get_stream_url) + [ -z "$RADIO_URL" ] || [ "$RADIO_URL" = "null" ] && return + [ "$PID" -gt 0 ] && kill "$PID" 2>/dev/null + nohup mpv --no-video --quiet "$RADIO_URL" >/dev/null 2>&1 & + echo $! > "$MPV_PID_FILE" + echo "$RADIO_ID" > "$RADIO_ID_FILE" +} status_loop() { - STATUS=$(status) - echo $STATUS - STATIONS=$(curl -s "$URL_BASE/stations/" | jq --argjson ids '[507,522,523,536,537,42532,42602]' '.result.stations | map(select(.id | IN($ids[]))) | map({id, title, stream_hls, icon_fill_white})') - last_time_info=0 - last_time=0 + STATIONS=$(get_stations) + echo "$(emit_status)" + + last_pid_check=0 + last_song_fetch=0 + last_media_fetch=0 + while true; do - current_time=$(get_time_ms) - delta=$((current_time - last_time)) - delta_i=$((current_time - last_time_info)) - if [[ $delta -gt 1000 ]]; then - PID=$( [ -e "$MPV_PID_FILE" ] && cat "$MPV_PID_FILE" || echo 0 ) - RADIO_ID=$( [ -e "$RADIO_ID_FILE" ] && cat "$RADIO_ID_FILE" || echo 0 ) - PAUSED=$(( $PID == 0 && $RADIO_ID > 0 ? 1 : 0 )) - NEW_STATUS=$(status) - if [[ "$NEW_STATUS" != "$STATUS" ]]; then - STATUS=$NEW_STATUS - echo $STATUS - fi - last_time=$current_time + now=$(date -u +%s%3N) + + if (( now - last_pid_check > 1000 )); then + update_state + NEW_STATUS=$(emit_status) + if [ "$NEW_STATUS" != "$STATUS" ]; then STATUS=$NEW_STATUS; echo "$STATUS"; fi + last_pid_check=$now fi - if [[ $delta_i -gt 15000 ]]; then - if [ "$PAUSED" = 1 ]; then - INFO="{\"id\":null,\"artist\":null,\"song\":null,\"image600\": \"$(get_radio | jq -r '.icon_fill_white')\"}" + + if (( now - last_song_fetch > 15000 )); then + if [ "$PAUSED" = 0 ]; then + IS_RR=$(echo "$STATIONS" | jq --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].radiorecord // true') + if [ "$IS_RR" = "true" ]; then + FETCHED=$(get_song) + INFO=$(echo "$INFO" "$FETCHED" | jq -s ' + reduce .[] as $x ({}; . + ($x | with_entries(select(.value != null))))') + else + STATION_TITLE=$(echo "$STATIONS" | jq -r --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].title // ""') + INFO=$(jq -cnr --arg title "$STATION_TITLE" '{artist:"",song:$title,image600:""}') + fi else - INFO=$(echo "$INFO" "$(get_song)" | jq -s ' reduce .[] as $item ( {}; . + ( - reduce ($item | to_entries[]) as $entry ( {}; if $entry.value != null then .[$entry.key] = $entry.value else . end - )) )') + STATION_IMG=$(echo "$STATIONS" \ + | jq -r --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].icon_fill_white // ""') + STATION_TITLE=$(echo "$STATIONS" \ + | jq -r --argjson id "$RADIO_ID" 'map(select(.id == $id))[0].title // ""') + INFO=$(jq -cnr --arg img "$STATION_IMG" --arg title "$STATION_TITLE" \ + '{artist:"",song:$title,image600:$img}') fi - last_time_info=$current_time + NEW_STATUS=$(emit_status) + if [ "$NEW_STATUS" != "$STATUS" ]; then STATUS=$NEW_STATUS; echo "$STATUS"; fi + last_song_fetch=$now fi + + if (( now - last_media_fetch > 3000 )); then + MEDIA=$(get_player_info) + NEW_STATUS=$(emit_status) + if [ "$NEW_STATUS" != "$STATUS" ]; then STATUS=$NEW_STATUS; echo "$STATUS"; fi + VOL=$(get_volume) + [ -n "$VOL" ] && eww update radio-vol="$VOL" 2>/dev/null + last_media_fetch=$now + fi + + sleep 0.5 done } -# Main script case "$1" in - "start") - RADIO_ID=$2 - start + start) + RADIO_ID="$2" + echo "$RADIO_ID" > "$RADIO_ID_FILE" + do_start ;; - "toggle") - toggle + vol) + do_vol "$2" + ;; + mute) + update_state + do_mute + ;; + toggle) + update_state + if [ "$PAUSED" = 1 ]; then + [ "$RADIO_ID" -gt 0 ] && do_start + else + [ "$PID" -gt 0 ] && kill "$PID" 2>/dev/null + rm -f "$MPV_PID_FILE" + fi ;; *) status_loop diff --git a/modules/home/wayland/apps/eww/bar/windows/clock.yuck b/modules/home/wayland/apps/eww/bar/windows/clock.yuck index 649894f..c15045b 100644 --- a/modules/home/wayland/apps/eww/bar/windows/clock.yuck +++ b/modules/home/wayland/apps/eww/bar/windows/clock.yuck @@ -82,23 +82,15 @@ (defwidget quick-section [] (box :orientation "v" :space-evenly false :class "sys-section" (section-header :title "Quick Actions" :accent "quick-accent") - (box :orientation "h" :homogeneous true :class "quick-grid" - (quick-btn - :icon "󰸉" :label "Wallpaper" - :onclick "scripts/wallpaper" - :active false) - (quick-btn - :icon "󱐋" :label "Power Save" - :onclick "eww update power-save=$(scripts/power-save)" - :active {power-save}) - (quick-btn - :icon "󰌵" :label "Night Light" - :onclick "eww update night-light=$(scripts/nightlight)" - :active {night-light}) - (quick-btn - :icon "󰹑" :label "Screenshot" - :onclick "scripts/screenshot" - :active false)))) + (box :orientation "v" :space-evenly false :class "quick-grid" :spacing 4 + (box :orientation "h" :space-evenly true + (quick-btn :icon "󰸉" :label "Wallpaper" :onclick "scripts/wallpaper" :active false) + (quick-btn :icon "󱐋" :label "Power Save" :onclick "eww update power-save=$(scripts/power-save)" :active {power-save}) + (quick-btn :icon "󰌵" :label "Night Light" :onclick "eww update night-light=$(scripts/nightlight)" :active {night-light})) + (box :orientation "h" :space-evenly true + (quick-btn :icon "󰹑" :label "Screenshot" :onclick "scripts/screenshot" :active false) + (quick-btn :icon "󰌾" :label "Lock" :onclick "swaylock" :active false) + (quick-btn :icon "󱉨" :label "Color Pick" :onclick "hyprpicker -a" :active false))))) ; --- Brightness --- diff --git a/modules/home/wayland/apps/eww/bar/windows/popup.yuck b/modules/home/wayland/apps/eww/bar/windows/popup.yuck index 32f6e42..54e3685 100644 --- a/modules/home/wayland/apps/eww/bar/windows/popup.yuck +++ b/modules/home/wayland/apps/eww/bar/windows/popup.yuck @@ -6,7 +6,9 @@ (revealer :reveal {active-panel == "net"} :transition "slidedown" :duration 120 (net-win)) (revealer :reveal {active-panel == "clock"} :transition "slidedown" :duration 120 - (clock-win)))) + (clock-win)) + (revealer :reveal {active-panel == "radio"} :transition "slidedown" :duration 120 + (radio-win)))) (defwindow popup :monitor 0 diff --git a/modules/home/wayland/apps/eww/bar/windows/radio.yuck b/modules/home/wayland/apps/eww/bar/windows/radio.yuck index 1bd471f..590fba3 100644 --- a/modules/home/wayland/apps/eww/bar/windows/radio.yuck +++ b/modules/home/wayland/apps/eww/bar/windows/radio.yuck @@ -1,111 +1,82 @@ -(deflisten radio :initial '{"is_paused":1,"song":{"artist":"","song":"","image600":"https://www.radiorecord.ru/upload/stations_images/record_image600_white_fill.png"},"stations":[]}' "scripts/radio") -(defvar radio_rev false) -(defwindow radio - :monitor 0 - :geometry (geometry - :x "0%" - :y "0%" - :anchor "bottom right" - :width "0px" - :height "0px") - (window (radio-win))) +(deflisten radio + :initial '{"is_paused":1,"song":{"artist":"","song":"","image600":""},"radio":0,"stations":[],"media":{"player":"","status":"Stopped","artist":"","title":"","art":""}}' + "scripts/radio") +(defvar radio-vol 80) +(defvar radio-muted false) +(defwidget radio-win [] + (box :class "sys-win" :orientation "v" :space-evenly false -(defwidget radio-win [] - (box - :space-evenly false - :orientation "h" - (revealer - :visible radio_rev - :reveal radio_rev - :transition "slideleft" - (radio-selector)) - (box - :space-evenly false - :orientation "v" + ; --- Now Playing --- + (box :orientation "v" :space-evenly false :class "sys-section" + (section-header :title "Media" :accent "radio-accent") + (box :orientation "h" :space-evenly false :valign "center" :spacing 10 :class "radio-now-playing" + (box :class "radio-art" + :style "background-image: url('${radio.is_paused == 0 ? radio.song.image600 : (radio.media.status != 'Stopped' ? radio.media.art : radio.song.image600)}'); background-size: cover; background-position: center;" + (label :class "radio-art-icon" :halign "center" :valign "center" + :visible {radio.is_paused == 0 + ? radio.song.image600 == "" + : (radio.media.status != "Stopped" + ? radio.media.art == "" + : radio.song.image600 == "")} + :text "󰝚")) + (box :orientation "v" :space-evenly false :hexpand true :valign "center" + (label :class "radio-song" :halign "start" :wrap true :limit-width 20 + :text {radio.is_paused == 0 + ? (radio.song.song != "" ? radio.song.song : "Tuning in...") + : (radio.media.status != "Stopped" + ? (radio.media.title != "" ? radio.media.title : "Playing") + : (radio.song.song != "" ? radio.song.song : "Nothing playing"))}) + (label :class "radio-artist" :halign "start" :wrap true :limit-width 22 + :text {radio.is_paused == 0 + ? radio.song.artist + : (radio.media.status != "Stopped" + ? (radio.media.artist != "" ? radio.media.artist : radio.media.player) + : "")}))) - (box - :class "album_art" - :style "background-size:cover; background-image: url('${radio.song.image600?:'https://www.radiorecord.ru/upload/stations_images/record_image600_white_fill.png'}');") - (box - :space-evenly false - :orientation "v" - (label - :halign "center" - :class "song" - :wrap "true" - :limit-width 18 - :text "${radio.song.song?:'...'}") - (label - :halign "center" - :class "artist" - :wrap "true" - :limit-width 22 - :text "${radio.song.artist?:'...'}") - (box - :space-evenly true - :orientation "h" - :halign "center" - :class "btn_bar" + ; Controls - hidden only in placeholder state + (box :visible {radio.is_paused == 0 || radio.radio > 0 || radio.media.status != "Stopped"} + :orientation "v" :space-evenly false :class "radio-controls" + (box :orientation "h" :space-evenly true :halign "center" + (button :visible {radio.is_paused == 1 && radio.media.status != "Stopped"} + :class "radio-ctrl-btn" :timeout "2s" :onclick "playerctl previous" + (label :text "󰒮")) + (button :class "radio-ctrl-btn" :timeout "2s" + :onclick {radio.is_paused == 1 && radio.media.status != "Stopped" ? "playerctl play-pause" : "scripts/radio toggle"} + (label :text {radio.is_paused == 0 ? "󰏥" : (radio.media.status == "Playing" ? "󰏥" : "󰐌")})) + (button :visible {radio.is_paused == 1 && radio.media.status != "Stopped"} + :class "radio-ctrl-btn" :timeout "2s" :onclick "playerctl next" + (label :text "󰒭"))) + (vol-row + :icon "󰕾" + :value {radio-vol} + :onchange "scripts/radio vol {}" + :onclick "scripts/radio mute" + :muted {radio-muted}))) - (button - :class "btn_left" - :onclick "${EWW_CMD} update radio_rev=${!radio_rev}" - (box "󰷐")) + (box :class "section-sep") - (button - :class "btn_play" - :timeout "2s" - :onclick "scripts/radio toggle" - "${radio.is_paused==1 ? "󰐌" : "󰏥"}") - - (button - :class "btn_right" - :onclick "" - (box :visible false "󰔶")) - ) - ) - ) - ) -) - - -(defwidget radio-selector [] - (scroll - :active true - :vscroll true - :hscroll false - - (box - :class "station_list" - :space-evenly false - :orientation "v" - - (for station in {radio.stations} + ; --- Stations --- + (box :orientation "v" :space-evenly false :class "sys-section" + (section-header :title "Radio Stations" :accent "radio-accent") + (scroll :vscroll true :hscroll false :height 130 + (box :class "station-list" :space-evenly false :orientation "v" + (for station in {radio.stations} (button - :class "station_art ${(radio.radio == station.id)?'station_sel':''}" - :timeout "2s" - :onclick "scripts/radio start ${station.id}" - :tooltip "${station.title}" - :style "background-size:cover; background-image: url('${station.icon_fill_white}');" - ) - ) - ) - ) -) + :class "station-row ${radio.radio == station.id ? 'station-row-active' : ''}" + :timeout "2s" + :onclick "scripts/radio start ${station.id}" + :tooltip {station.title} + (box :orientation "h" :space-evenly false :valign "center" + (box :class "station-icon" + :style "background-image: url('${station.icon_fill_white}'); background-size: contain; background-position: center;") + (label :class "station-name" :text {station.title} + :hexpand true :halign "start" :limit-width 16))))))))) (defwidget radio-mod [] (module - (box - :orientation "v" - (button - :onclick "(sleep 0.1 && eww-open-on-current-screen radio --toggle --no-daemonize)" - (label - :show-truncated false - :class "icon-text" - :text "󰝚") - ) - ) - ) -) + (eventbox + :onclick "(sleep 0.1 && scripts/panel-toggle radio)" + (box :orientation "v" + (label :show-truncated false :class "icon-text" :text "󰝚"))))) diff --git a/modules/home/wayland/apps/eww/bar/windows/sys.yuck b/modules/home/wayland/apps/eww/bar/windows/sys.yuck index 39dccd6..8a440ac 100644 --- a/modules/home/wayland/apps/eww/bar/windows/sys.yuck +++ b/modules/home/wayland/apps/eww/bar/windows/sys.yuck @@ -69,7 +69,7 @@ (box :orientation "v" :space-evenly false :class "sys-section" (section-header :title "GPU" :accent "gpu-accent") (box :orientation "h" :space-evenly true - ; GFX — outer: activity%, inner: clock% of range + ; GFX - outer: activity%, inner: clock% of range (box :orientation "v" :space-evenly false :halign "center" (overlay (circular-progress :width 68 :height 68 @@ -84,7 +84,7 @@ (box :halign "center" :valign "center" (label :class "gpu-ring-value" :text "${round(gpu.gfx_pct, 0)}%"))) (label :class "gpu-ring-label" :text "GFX" :halign "center")) - ; Memory — outer: activity%, inner: memory clock% of range + ; Memory - outer: activity%, inner: memory clock% of range (box :orientation "v" :space-evenly false :halign "center" (overlay (circular-progress :width 68 :height 68 @@ -99,7 +99,7 @@ (box :halign "center" :valign "center" (label :class "gpu-ring-value" :text "${round(gpu.mem_pct, 0)}%"))) (label :class "gpu-ring-label" :text "Mem" :halign "center")) - ; Media — outer: activity%, inner: video clock% of GPU clock max + ; Media - outer: activity%, inner: video clock% of GPU clock max (box :orientation "v" :space-evenly false :halign "center" (overlay (circular-progress :width 68 :height 68 diff --git a/modules/home/wayland/apps/eww/default.nix b/modules/home/wayland/apps/eww/default.nix index 6ab479f..c24bf78 100755 --- a/modules/home/wayland/apps/eww/default.nix +++ b/modules/home/wayland/apps/eww/default.nix @@ -59,6 +59,8 @@ in { $gaps-screen: ${config.colorScheme.palette.gaps-screen}px; $gaps-window: ${config.colorScheme.palette.gaps-window}px; + + $panel-font-size: 10pt; ''; }; } diff --git a/modules/home/wayland/base/default.nix b/modules/home/wayland/base/default.nix index c288d99..6e04399 100644 --- a/modules/home/wayland/base/default.nix +++ b/modules/home/wayland/base/default.nix @@ -35,6 +35,7 @@ in { brightnessctl awww + playerctl ]; xdg.mimeApps = {