The story

Not that long ago i started reading about ruby, in the hopes of finally finding a scripting language that i could learn, one that is simple, clean and with a beautiful syntax.

During the learning process I was looking for small projects, that could help solidify my understanding. Searching online was pretty frustrating since people were always recommending rails projects and that was not what i had in mind.

I started to brainstorm and so imaginy appeared.

Imaginy

I love wallpapers, i’m always on the favorites section of wallhaven and i change it daily.

Imaginy does exactly that but better and faster, by parsing the favorites section it downloads a new image daily and sets it as my background picture, super simple stuff.

Plan

My custom url (screen resolution, SFW) is constructed as so :

 https://alpha.wallhaven.cc/search?categories=111&purity=100&resolutions=1920x1080&sorting=favorites&order=desc&page=1 

with a default of 25 images per page.

  • We need a way of storing dynamic data like the image index to download, the image page index and also static information like the website url, the name of the folder where images are stored.
  • Obtain the url of the current image.
  • Download it to the image folder.
  • Set it as a background image.
  • Update our dynamic data.

Code

Config file

First of all we need a configuration class that will store all the data and is capable of exporting it to a file config.data for future use, We must assure that if the user deletes this file, our script won’t crash :)

For our export format we will use yaml, a simple language that describes data. By using the yaml format we can manipulate our config file really easily.

Internally we will use a hash table:

 # default configuration
  def default = {
     cmd: 'feh --bg-fill ',
     website: 'https://alpha.wallhaven.cc/search?categories=111&purity=100&resolutions=1920x1080&sorting=favorites&order=desc&page=',
     folder: 'walls/',
     page_size: 24,
     page: 1,
     image_index: 0,
   }
  end

All the fields are self-explanatory, except maybe the cmd one, we will explain it in the final section.

This is the config.data file, once the configuration class has been exported.

:cmd: 'feh --bg-fill '
:website: https://alpha.wallhaven.cc/search?categories=111&purity=100&resolutions=1920x1080&sorting=favorites&order=desc&page=
:folder: walls/
:page_size: 24
:page: 1
:image_index: 0

So now we have our default configuration, how do we export it? Simple, by using the yaml library.

 # store the current configuration
  def store
    File.open(@file_name, 'w') do |file|
      file.write YAML::dump(@data)
    end
  end

In order to read the config file we do as follows :

 def load
    # if the file exists
    if File.file?(@file_name)
     # load it and return the data
      File.open(@file_name, 'r') do |file|
        YAML::load(file)
      end
    else
    # the file doesn't exist, generate a default config
      default
    end
  end

If you want to understand how all of this fits toghether, check the MyConfig class.

Image Manager

The image manager is where all the action happens :), we get to play with nokogiri, a html parser. If you never used nokogiri or any other html parser don’t be worried, it’s super easy.

You can check the provided link to get familiar with the parser and you should also check the html code of the pages that we want to parse 1 | 2.

First we must obtain the urls of all the images on the page :

  # load the image url list for the current page
  def load_image_list

    # construct the web-page url with the current page
    website_url = @config.get_property(:website) + @config.get_property(:page).to_s

    # get the source code of the website page
    website_page = Nokogiri::HTML(open(website_url))

    # get the list of image url's
    website_page.css("a[class='preview']")
  end

For the first page we obtain this html code, containing all the 25 image urls on this page :

<a class="preview" href="https://alpha.wallhaven.cc/wallpaper/103929" target="_blank"></a>
<a class="preview" href="https://alpha.wallhaven.cc/wallpaper/64346" target="_blank"></a>
.
.
.
<a class="preview" href="https://alpha.wallhaven.cc/wallpaper/10694" target="_blank"></a>

The next step is to choose the current image index and download it locally like so :

 # download the current image
# return the image path
  def download_current_image

    image_index = @config.get_property(:image_index)

    # get the html block describing the current image
    image_block = @image_list[image_index]

    # get the image page url
    image_page_url = image_block['href']

    # get the source code of the specific image page
    image_page = Nokogiri::HTML(open(image_page_url))

    # get the image url block
    image_partial_url = image_page.css("img[id='wallpaper']")

    # get the image url and name
    image_url = 'https:' + image_partial_url[0]['src']
    image_name = Date.today.to_s + '-' + @config.get_property(:image_index).to_s

    # construct the image path
    image_path = @config.get_property(:folder) + image_name + File.extname(image_url)

    # download the image to disk
    save_image(image_path, image_url)

    image_path
  end

  # save the image at @image_url, locally as @image_name
  def save_image(image_name, image_url)
    File.open(image_name, 'wb') do |fo|
      fo.write open(image_url).read
    end
  end

Now that we have our image on the hdd, we must set it as a background. We could just execute a shell command like gsettings for Gnome3 or mateconftool for Mate, but those are desktop environment dependent. The easy way out is using feh which is shipped with many linux distributions including Arch Linux, Debian, FreeBSD, Gentoo, OpenBSD and Ubuntu.

Now that mystery line in our config file makes sense :

:cmd: 'feh --bg-fill '

this is a part of the command that is executed in order to change the background image, all that’s missing is the image path.

  # set the current image as background
  def change_background_image
    # get the current image path
    image_path = download_current_image

    # construct the system command
    set_background_cmd = @config.get_property(:cmd) + image_path

    # set the background image
    system(set_background_cmd)

    # update the existing config
    update_config
  end

After that the only thing left is to update our config file :

 # update the current config file
  def update_config
    image_index = @config.get_property(:image_index)

    if image_index == @config.get_property(:page_size) - 1
      # record a new page
      current_page = @config.get_property(:page)
      @config.set_property(:page, current_page + 1)
      @config.set_property(:image_index, 0)
    else
      # increase the image index
      @config.set_property(:image_index, image_index + 1)
    end
  end

That’s it! All we have to do is set this bad-boy running on system startup and enjoy our new wallpaper.

I hope this article was useful, if you want to check/fork/star the github repo you can do that here.

If you have any questions, suggestions leave a comment bellow.

Until next time :)