Snippets

Denis Nelubin Adds any URL as the Chrome application to the main menu

Created by Denis Nelubin last modified
#!/usr/bin/env python3

import argparse
import tempfile
import os.path
import subprocess
import urllib.request
import urllib.parse
from PIL import Image


VENDOR = 'chrome'
CHROME = '/opt/google/chrome/google-chrome'
CHROME_PROFILE = 'Default'


def main():
    parser = argparse.ArgumentParser(description='Adds some site as the application to the main menu.')
    parser.add_argument('url', help='url of the site to be used as the app')
    parser.add_argument('--title', default='Chrome App', help='menu item title (default: Chrome App)')
    parser.add_argument('--icon', default=None, help='url to the PNG icon for the app (default: None)')
    parser.add_argument('--wmclass', default=None, help='WM_CLASS for the app (default: same as url domain)')

    args = parser.parse_args()

    with tempfile.TemporaryDirectory() as tempdir:
        wmclass = args.wmclass if args.wmclass else find_class(args.url)
        icon = download_icon(tempdir, args.icon, wmclass)
        icon_name = install_icon(icon, wmclass)
        desktop_file = create_desktop_file(tempdir, args.url, args.title, icon_name, wmclass)
        install_desktop_file(desktop_file)
    

def find_class(url):
    parts = urllib.parse.urlsplit(url)
    return parts.hostname


def download_icon(tempdir, icon_url, wmclass):
    if icon_url is None:
        return None
    try:
        print('Retrieving', icon_url)
        filename = os.path.join(tempdir, '%s-%s.png' % (VENDOR, wmclass))
        filename, _ = urllib.request.urlretrieve(icon_url, filename=filename)
        return filename
    except Exception as e:
        print('Failed:', e)
        return None


def install_icon(icon_file, wmclass):
    if icon_file is None:
        return None
    try:
        icon_name = '%s-%s' % (VENDOR, wmclass)
        size = resize_icon(icon_file)

        print('Installing', icon_file, 'as', icon_name)
        subprocess.run(
            ['xdg-icon-resource', 'install', '--size', str(size), icon_file, icon_name],
            stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True
        )
        return icon_name
    except Exception as e:
        print('Failed:', e)
        return None


def resize_icon(icon_file):
    image = Image.open(icon_file)
    actual_size, _ = image.size
    best_size = find_icon_size(actual_size)
    if best_size != actual_size:
        print('Resizing', icon_file, 'to', best_size)
        resized = image.resize((best_size, best_size), Image.LANCZOS)
        resized.save(icon_file)
    return best_size


def find_icon_size(actual_size):
    if actual_size >= 256:
        return 256
    elif actual_size >= 128:
        return 128
    elif actual_size >= 64:
        return 64
    elif actual_size >= 32:
        return 32
    else:
        return 16


def create_desktop_file(tempdir, url, title, icon_name, wmclass):
    desktop_file = os.path.join(tempdir, '%s-%s.desktop' % (VENDOR, wmclass))
    try:
        print('Creating', desktop_file)
        with open(desktop_file, 'wt') as f:
            print('[Desktop Entry]', file=f)
            print('Type=Application', file=f)
            print('Name=%s' % title, file=f)
            if icon_name:
                print('Icon=%s' % icon_name, file=f)
            print('Exec=%s --profile-directory=%s --app=%s' % (CHROME, CHROME_PROFILE, url), file=f)
            print('StartupWMClass=%s' % wmclass, file=f)
            print('Terminal=false', file=f)
            print('Categories=Network', file=f)
        return desktop_file
    except Exception as e:
        print('Failed:', e)
        return None


def install_desktop_file(desktop_file):
    if desktop_file is None:
        return
    try:
        print('Installing', desktop_file)
        subprocess.run(
            ['xdg-desktop-menu', 'install', desktop_file],
            stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True
        )
    except Exception as e:
        print('Failed:', e)


if __name__ == '__main__':
    main()

Comments (0)