elkowar / eww

ElKowars wacky widgets
https://elkowar.github.io/eww
MIT License
8.8k stars 364 forks source link

[BUG] background-image property does not work with remote images #742

Open Madeleaan opened 1 year ago

Madeleaan commented 1 year ago

Checklist before submitting an issue

Description of the bug

When using the background-image property on a box, it can render local files, but it does not render remote files. Does not work with either http or https. #719 has already reported this, however it got closed as "not planned" and it looks like it does not work for me as well. I noticed that when i select any remote file, the gtk+ style property is just url("data:image/png;base64,").

Reproducing the issue

(defwidget testwidget []
    (box :class "test"))
.test { 
    background-image: url("https://i.scdn.co/image/ab67616d0000b273015c484a7aca592df1a77828"); 
    background-size: cover;
    min-height: 24px;
    min-width: 24px;
}

Expected behaviour

Should render the image like it does with local files

rshchekotov commented 1 year ago

While this is being fixed, I thought I'd share my (probably obvious) hack to get this to work.

Note: I use the fish-shell and fish-scripts, but it should be trivial to translate them to bash and the likes:

~/config/scripts/audio-control.fish

set -l command $argv[1]
# other 'subcommands', etc.
if [ "$command" = "art" ]
  set -l art_thumb_cache "$HOME/.cache/music-thumbs"
  if not [ -d "$art_thumb_cache" ]
    mkdir -p "$art_thumb_cache"
  end
  set -l art_url (playerctl metadata mpris:artUrl)
  if string match -q "https://*" "$art_url"
    set -l art_name (basename "$art_url")
    set -l art_path "$art_thumb_cache/$art_name"
    if not [ -f "$art_path" ]
      curl -fLo "$art_path" "$art_url"
    end
    echo "$art_path"
  else
    echo "$art_url"
  end
end

and then: ~/config/eww/eww.yuck

(defpoll music_cover_art :interval "1s" "~/config/scripts/audio-control.fish art")

;; within the widget, where you want to use it:
(box :class "music-song-cover-art"
  :style "background-image: url(\"${music_cover_art}\");")

As I said this is a hack and it'd be great if this were supported out-of-the-box, but this hack comes with the additional benefit that you have manual control over the cache.

Edit: the cleanup script is UNTESTED: run at your own peril A smart scripter may be able to implement an LFU-cache )), I guess something along the lines of:

function cleanup
  set dir_path "$HOME/.cache/music-thumbs/"
  set file_list (find $dir_path -type f -printf "%A@ %p\n" | sort -rn | cut -d " " -f2-)
  set num_files_to_remove (math (count $file_list) - 64)

  if test $num_files_to_remove -gt 0
    for file in (seq $num_files_to_remove)
      set file_to_remove (echo $file_list[$file])
      rm $file_to_remove
    end
  end
end
Wunderharke commented 8 months ago

Is this won't fix or are there any news?

houdini-21 commented 7 months ago

Hi! I have the same problem. To solve it, I programmed this script in bash. In my case, I just want to get the image from Spotify, but you can modify it according to your needs.

What the script does is to get the URL of the image and cache it to display it as a local image. Also, to make sure that the image is always up to date, every time the script runs, it will delete all cached images, ensuring that no unnecessary images are saved.

#!/bin/bash

# Get the album art URL using playerctl
art_url=$(playerctl metadata --format '{{ (mpris:artUrl) }}' --player=spotify)

# Check if the art URL exists
if [ -n "$art_url" ]; then
    # Get the cache directory location
    cache_dir="$HOME/.album_art_cache"

    # Create the cache directory if it doesn't exist
    mkdir -p "$cache_dir"

    # Delete all images in the cache
    rm -f "$cache_dir"/*

    # Get the file name from the URL (last part of the URL)
    filename=$(basename "$art_url")

    # Create the full file path in the cache
    cache_path="$cache_dir/$filename"

    # Download the image
    wget -O "$cache_path" "$art_url" > /dev/null 2>&1

    # Display the path to the cached image
    echo "$cache_path"
else
    echo "Failed to get the album art URL."
fi

eww variable

(defpoll imgSong :interval "1s" "widgets/scripts/getImage.sh")
TheStachelfisch commented 4 weeks ago

I reworked the scripts above a bit and created a better one. This uses XDG_RUNTIME_DIR to save on disk-writes, doesn't have any dependency on wget, checks if an image is actually a link and returns an already-downloaded image instead of downloading it again.

But most importantly it uses listening now instead of interval-based polling.

To add this, add the deflisten variable to your window and create a file containing the script. You can change the initial image to whatever you want, I left it blank.

(deflisten music-image :initial "" "bash ./location-to/your-script")
#!/usr/bin/env bash

playerctl metadata --format "{{mpris:artUrl}}" --follow | while IFS= read -r line; do
    if [[ $line == *"http"* ]]; then
        cache_dir="$XDG_RUNTIME_DIR/album_art_cache"
        mkdir -p "$cache_dir"
        file_name=$(basename "$line")
        file_path="$cache_dir/$file_name"

        # Check if file exists
        if [ -e "$file_path" ]; then
            # File exists, return path immediately
            echo "file://$file_path"
        else 
            curl --output "$file_path" "$line" > /dev/null 2>&1
            echo "file://$file_path"
        fi 
    else
        # artUrl doesn't have a link, nothing to do
        echo "$line"
    fi
done