Skip to content

Commit 853dbf4

Browse files
committed
proof of concept for icon support
1 parent 89a6357 commit 853dbf4

File tree

5 files changed

+74
-12
lines changed

5 files changed

+74
-12
lines changed

default.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ with pythonPackages;
1111
doCheck = true;
1212
format = "pyproject";
1313
nativeBuildInputs = [pkgs.scdoc setuptools];
14-
propagatedBuildInputs = [pyxdg click];
14+
propagatedBuildInputs = [pyxdg click requests favicon pillow];
1515
checkInputs = [pytest];
1616
postInstall = ''
1717
mkdir -p $out/share/fish/vendor_completions.d

flake.nix

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,15 @@
2121
// {
2222
buildInputs = with pkgs; [
2323
ruff
24+
xdg-utils.out
2425
(python3.withPackages (ps:
2526
with ps; [
2627
pyxdg
28+
favicon
29+
pillow
30+
requests
2731
click
32+
2833
pytest
2934
mypy
3035
black

qbpm/main.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,18 @@ def list_(profile_dir: Path) -> None:
154154
print(profile.name)
155155

156156

157+
@main.command()
158+
@click.argument("profile_name")
159+
@click.pass_obj
160+
def icon(profile_dir: Path, profile_name: str) -> None:
161+
"""Edit a profile's config.py."""
162+
profile = Profile(profile_name, profile_dir)
163+
if not profile.exists():
164+
error(f"profile {profile.name} not found at {profile.root}")
165+
exit(1)
166+
profile.root / "config" / "config.py"
167+
168+
157169
def then_launch(
158170
operation: Callable[..., bool],
159171
profile: Profile,

qbpm/operations.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,17 @@ def choose(
8383
if not command:
8484
return False
8585

86-
selection_cmd = subprocess.Popen(
87-
command,
86+
selection_cmd = subprocess.run(
87+
command[1],
88+
input=command[0],
89+
text=True,
8890
shell=True,
8991
stdout=subprocess.PIPE,
9092
stderr=None,
93+
check=False,
9194
)
9295
out = selection_cmd.stdout
93-
selection = out and out.read().decode(errors="ignore").rstrip("\n")
96+
selection = out and out.rstrip("\n")
9497

9598
if selection:
9699
profile = Profile(selection, profile_dir)
@@ -102,13 +105,16 @@ def choose(
102105

103106
def menu_command(
104107
menu: str, profiles: list[str], qb_args: tuple[str, ...]
105-
) -> Optional[str]:
108+
) -> Optional[tuple[str | None, str]]:
106109
arg_string = " ".join(qb_args)
107110
if menu == "applescript":
108111
profile_list = '", "'.join(profiles)
109-
return f"""osascript -e \'set profiles to {{"{profile_list}"}}
112+
return (
113+
None,
114+
f"""osascript -e \'set profiles to {{"{profile_list}"}}
110115
set profile to choose from list profiles with prompt "qutebrowser: {arg_string}" default items {{item 1 of profiles}}
111-
item 1 of profile\'"""
116+
item 1 of profile\'""",
117+
)
112118

113119
prompt = "-p qutebrowser"
114120
command = menu
@@ -124,9 +130,11 @@ def menu_command(
124130
command = f"{menu} --prompt 'qutebrowser '"
125131
elif program == "fuzzel":
126132
command = f"{menu} -d"
133+
profiles = [f"{p}\0icon\x1fqbpm-{p}\n" for p in profiles]
134+
print(f"{profiles=}")
127135
exe = command.split(" ")[0]
128136
if not shutil.which(exe):
129137
error(f"command '{exe}' not found")
130138
return None
131-
profile_list = "\n".join(profiles)
132-
return f'echo "{profile_list}" | {command}'
139+
profile_list = "".join(profiles)
140+
return (profile_list, command)

qbpm/profiles.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
import subprocess
12
from functools import partial
23
from pathlib import Path
34
from sys import platform
5+
from tempfile import TemporaryDirectory
46
from typing import Optional
57

8+
import favicon
9+
import requests
10+
from PIL import Image
611
from xdg import BaseDirectory
712
from xdg.DesktopEntry import DesktopEntry
813

@@ -82,18 +87,48 @@ def create_config(
8287
application_dir = Path(BaseDirectory.xdg_data_home) / "applications" / "qbpm"
8388

8489

85-
def create_desktop_file(profile: Profile) -> None:
90+
def create_desktop_file(profile: Profile, icon: str | None) -> None:
8691
desktop = DesktopEntry(str(application_dir / f"{profile.name}.desktop"))
8792
desktop.set("Name", f"{profile.name} (qutebrowser profile)")
8893
# TODO allow passing in an icon value
89-
desktop.set("Icon", "qutebrowser")
94+
desktop.set("Icon", icon or "qutebrowser")
9095
desktop.set("Exec", " ".join(profile.cmdline()) + " %u")
9196
desktop.set("Categories", ["Network"])
9297
desktop.set("Terminal", False)
9398
desktop.set("StartupNotify", True)
9499
desktop.write()
95100

96101

102+
def download_icon(profile: Profile, home_page: str) -> str | None:
103+
icons = favicon.get(home_page)
104+
wanted = None
105+
print(icons)
106+
for icon in icons:
107+
if ".ico" in icon.url:
108+
wanted = icon
109+
print(f"chose {icon}")
110+
111+
tmp_dir = TemporaryDirectory()
112+
work_dir = Path(tmp_dir.name)
113+
fav = work_dir / "favicon.ico"
114+
if wanted:
115+
resp = requests.get(icon.url, timeout=10)
116+
with open(fav, "wb") as im:
117+
for chunk in resp.iter_content(1024):
118+
im.write(chunk)
119+
120+
name = f"qbpm-{profile.name}"
121+
image = Image.open(fav)
122+
png = work_dir / "icon.png"
123+
image.save(png)
124+
subprocess.run(
125+
f"xdg-icon-resource install --context apps {png} --size {image.size[0]} {name}",
126+
shell=True,
127+
)
128+
# image.save(Path(BaseDirectory.save_data_path("icons")) / "hicolor" / f"{image.size[0]}x{image.size[0]}" / "apps" / f"{name}.png")
129+
return name
130+
131+
97132
def ensure_profile_exists(profile: Profile, create: bool = True) -> bool:
98133
if profile.root.exists() and not profile.root.is_dir():
99134
error(f"{profile.root} is not a directory")
@@ -114,7 +149,9 @@ def new_profile(
114149
) -> bool:
115150
if create_profile(profile, overwrite):
116151
create_config(profile, home_page, overwrite)
152+
if home_page:
153+
icon = download_icon(profile, home_page)
117154
if desktop_file:
118-
create_desktop_file(profile)
155+
create_desktop_file(profile, icon)
119156
return True
120157
return False

0 commit comments

Comments
 (0)