Ubuntu – How to set folder icons of multiple folders automatically

iconsnautilusnautilus-script

How to set the first picture of every folder as its folder icon?

The question linked above has an answer consisting a script which has been working for me. It just needs a little improvement.

What does it do?

It finds files with .jpg, .jpeg, .png, .gif, .icns, .ico extensions and sets them as the folder icon of the folder in which the file was found. It works on multiple folders, recursively. Basically it tries to find an image file inside the folder, and the first image that it finds is set as a folder icon. It works great for many scenarios, and setting up this script is usually the first thing I do after fresh install (because it's amazing).

What's the problem?

There might be a few directories which contain a of lot of image files and the first image file in that directory might not be well suited to being the folder icon.

What should it do?

Instead of being extension based, if it became filename based and targeted one (for example, folder.png) or multiple (eg albumart.png cover.png) filenames then this problem could be solved.

or better yet make both approaches work in a single script

  • Find predefined filenames
  • If found set it as folder icon and move to next folder
  • If NOT found then find predefined extension and set it as folder icon and move to next folder

Best Answer

I might still "elegant it up a bit" but below are the edited versions of the linked ones.

What is the difference?

I added a predefined list to the head section:

specs = ["folder.png", "cover.png", "monkey.png"]

and I replaced:

try:
    first = min(p for p in os.listdir(folder) 
                if p.split(".")[-1].lower() in ext)
except ValueError:
    pass

by:

fls = os.listdir(folder)
try:
    first = [p for p in fls if p in specs]
    first = first[0] if first else min(
        p for p in fls if p.split(".")[-1].lower() in ext
        )
except ValueError:
    pass

so that the script first tries to find (file) matches in the list specs, (only) if there are no, it jumps to searching for matching extension, and does the trick if it finds a suitable image.


1. The basic version

To be used with the targeted directory as argument:

#!/usr/bin/env python3
import subprocess
import os
import sys

# --- set the list of valid extensions below (lowercase)
# --- use quotes, *don't* include the dot!
ext = ["jpg", "jpeg", "png", "gif", "icns", "ico"]
# --- set the list of preferred filenames
# --- use quotes
specs = ["folder.png", "cover.png", "monkey.png"]
# ---

# retrieve the path of the targeted folder
dr = sys.argv[1]

for root, dirs, files in os.walk(dr):
    for directory in dirs:
        folder = os.path.join(root, directory)
        try:
            fls = os.listdir(folder)
            first = [p for p in fls if p in specs]
            first = first[0] if first else min(
                p for p in fls if p.split(".")[-1].lower() in ext
                )
        except (ValueError, PermissionError):
            pass

        else:
            subprocess.Popen([
                "gvfs-set-attribute", "-t", "string",
                os.path.abspath(folder), "metadata::custom-icon",
                "file://"+os.path.abspath(os.path.join(folder, first))
                ])

How to use

  1. Copy the script into an empty file, save it as change_icon.py
  2. In the head of the script, edit, if you like, the list of extensions to be used as valid icon images. Also set the preferred list of filenames.
  3. Run it with the targeted directory as an argument:

    python3 /path/to/change_icon.py <targeted_directory>
    

That's it!


2. The edited right-click option, to be used as a nautilus (right-click) script

#!/usr/bin/env python3
import subprocess
import os

# --- set the list of valid extensions below (lowercase)
# --- use quotes, *don't* include the dot!
ext = ["jpg", "jpeg", "png", "gif", "icns", "ico"]
# --- set the list of preferred filenames
# --- use quotes
specs = ["folder.png", "cover.png", "aap.png"]
# ---

def fix(path):
    for c in [("%23", "#"), ("%5D", "]"), ("%5E", "^"),
              ("file://", ""), ("%20", " ")]:
        path = path.replace(c[0], c[1])
    return path

# retrieve the path of the targeted folder
current = fix(os.getenv("NAUTILUS_SCRIPT_CURRENT_URI"))
dr = os.path.realpath(current)

for root, dirs, files in os.walk(dr):
    for directory in dirs:
        folder = os.path.join(root, directory)
        try:
            fls = os.listdir(folder)
            first = [p for p in fls if p in specs]
            first = first[0] if first else min(
                p for p in fls if p.split(".")[-1].lower() in ext
                )
        except (ValueError, PermissionError):
            pass

        else:
            subprocess.Popen([
                "gvfs-set-attribute", "-t", "string",
                os.path.abspath(folder), "metadata::custom-icon",
                "file://"+os.path.abspath(os.path.join(folder, first))
                ])

To use

  1. Create, if it doesn't exist yet, the directory

    ~/.local/share/nautilus/scripts
    
  2. Copy the script into an empty file, save it in ~/.local/share/nautilus/scripts as set_foldericons (no extension!), and make it executable.

  3. In the head of the script, edit, if you like, the list of extensions to be used as valid icon images. Also set the preferred list of filenames.
  4. Log out and back in, and it works.

If, for some reason you'd like to reset the icons inside a folder to their default icon(s), use the script here

Related Question