Compare commits

...

3 Commits

2 changed files with 65 additions and 9 deletions

View File

@ -2,16 +2,21 @@
"Creates a firefox web app" "Creates a firefox web app"
import argparse import argparse
import io
import shlex
import stat
import sys import sys
import os
import os.path as pt import os.path as pt
from urllib.parse import urlparse, urlunparse from urllib.parse import urlparse, urlunparse
from collections import Counter from collections import Counter
import requests import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from PIL import Image
REPO_DIR = pt.dirname(sys.argv[0]) REPO_DIR = pt.abspath(pt.dirname(sys.argv[0]))
BIN_DIR = f"{REPO_DIR}/bin" BIN_DIR = f"{REPO_DIR}/bin"
ICON_DIR = f"{REPO_DIR}/icons" ICON_DIR = f"{REPO_DIR}/icons"
@ -37,6 +42,40 @@ def absolute_url(base_url, relative_url):
return relative_url return relative_url
# pylint: disable=W0613
def create_webapp(name, url, exec_name, logo, profile):
"Creates the necessary files for the webapp."
local_vars = locals()
# Create a dictionary with shell-quoted version of arguments
quoted = {i: shlex.quote(local_vars[i]) for i in ("name", "url", "exec_name",
"logo", "profile")}
# Download and convert the logo
logo_pt = f"{ICON_DIR}/{exec_name}.png"
res = requests.get(logo, allow_redirects=True)
with Image.open(io.BytesIO(res.content)) as img:
img.save(logo_pt, "PNG")
# Create the binary
script_pt = f"{BIN_DIR}/{exec_name}"
with open(script_pt, "w") as script:
script.write("#!/usr/bin/env sh\n")
script.write(f"firefox --profile {quoted['profile']} {quoted['url']}\n")
os.chmod(script_pt, os.stat(script_pt).st_mode | stat.S_IXUSR | stat.S_IXGRP)
# Create the desktop file
desk_pt = pt.expanduser(f"~/.local/share/applications/{exec_name}.desktop")
with open(desk_pt, "w") as desktop:
desktop.write(f"""
[Desktop Entry]
Name={name} (Web App)
Exec={script_pt}
Terminal=false
Type=Application
Icon={logo_pt}
Categories=Network;X-WebApps
""".strip() + "\n")
def extract_metadata(url): def extract_metadata(url):
"Extract metadata using bs4" "Extract metadata using bs4"
# Get and parse the page # Get and parse the page
@ -45,7 +84,9 @@ def extract_metadata(url):
metadata = {} metadata = {}
# Find the title # Find the title
titles = [soup.title.string] titles = []
if soup.title:
titles = [soup.title.string]
for tag in soup.find_all("meta"): for tag in soup.find_all("meta"):
title_props = ["title", "og:title", "twitter:title"] title_props = ["title", "og:title", "twitter:title"]
if tag.get("property", None) in title_props \ if tag.get("property", None) in title_props \
@ -53,11 +94,16 @@ def extract_metadata(url):
titles.append(tag["content"]) titles.append(tag["content"])
# Set title to the most common if it occurs more than once, else prefer # Set title to the most common if it occurs more than once, else prefer
# title tag # title tag
most_common = Counter(titles).most_common(1)[0] most_common = Counter(titles).most_common(1)
if most_common[1] > 1: if not most_common:
metadata["title"] = most_common[0].strip() metadata["title"] = None
elif most_common[0][1] > 1:
metadata["title"] = most_common[0][0].strip()
else: else:
metadata["title"] = soup.title.string.strip() if soup.title:
metadata["title"] = soup.title.string.strip()
else:
metadata["title"] = most_common[0][0].strip()
# Find the image. # Find the image.
# Try link first, followed by /favicon.{png,ico}, followed by og:, twitter: # Try link first, followed by /favicon.{png,ico}, followed by og:, twitter:
@ -121,6 +167,7 @@ def main():
eprint(f"Maybe you meant https://{args.url} ?") eprint(f"Maybe you meant https://{args.url} ?")
sys.exit(1) sys.exit(1)
print("Fetching details ...")
metadata = extract_metadata(args.url) metadata = extract_metadata(args.url)
if not args.name: if not args.name:
args.name = metadata["title"] args.name = metadata["title"]
@ -146,6 +193,13 @@ def main():
print(f"Logo URL:\t\t{args.logo}") print(f"Logo URL:\t\t{args.logo}")
print(f"Executable Name:\t{args.exec_name}") print(f"Executable Name:\t{args.exec_name}")
print(f"Firefox Profile:\t{args.firefox_profile}") print(f"Firefox Profile:\t{args.firefox_profile}")
print()
print("Do you want to create the app with the above details (Y/n): ",
end=' ')
inp = input()
if not inp or inp[0].upper() != "N":
create_webapp(args.name, args.url, args.exec_name, args.logo,
args.firefox_profile)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -75,7 +75,7 @@ if [[ $FIREFOX_PROFILE == "" ]] || (( NEW == 1 )); then
echo -n "Use an existing profile for apps? (y/N): " echo -n "Use an existing profile for apps? (y/N): "
read -r input read -r input
if [[ ${input^^} == "Y" ]]; then if [[ ${input^^} == "Y" ]]; then
echo "Enter path to existing profile (or run the script with --firefox_profile): " echo -n "Enter path to existing profile (or run the script with --firefox_profile): "
read -r FIREFOX_PROFILE read -r FIREFOX_PROFILE
else else
NEW=1 NEW=1
@ -113,13 +113,13 @@ read -r
mkdir "$FIREFOX_PROFILE/chrome" &> /dev/null || true mkdir "$FIREFOX_PROFILE/chrome" &> /dev/null || true
HIDDEN_SELECTORS=() HIDDEN_SELECTORS=()
echo -e "Do you want to hide tabs? (y/N)" echo -n "Do you want to hide tabs? (y/N) "
read -r input read -r input
if [[ ${input^^} == "Y" ]]; then if [[ ${input^^} == "Y" ]]; then
HIDDEN_SELECTORS=("${HIDDEN_SELECTORS[@]}" "#tabbrowser-tabs") HIDDEN_SELECTORS=("${HIDDEN_SELECTORS[@]}" "#tabbrowser-tabs")
fi fi
echo -e "Do you want to hide main toolbar (address bar, back, forward, etc)? (y/N)" echo -n "Do you want to hide main toolbar (address bar, back, forward, etc)? (y/N) "
read -r input read -r input
if [[ ${input^^} == "Y" ]]; then if [[ ${input^^} == "Y" ]]; then
HIDDEN_SELECTORS=("${HIDDEN_SELECTORS[@]}" "#nav-bar") HIDDEN_SELECTORS=("${HIDDEN_SELECTORS[@]}" "#nav-bar")
@ -137,3 +137,5 @@ if (( ${#HIDDEN_SELECTORS[@]} > 0 )); then
visibility: collapse !important; visibility: collapse !important;
}" >> "$FIREFOX_PROFILE/chrome/userChrome.css" }" >> "$FIREFOX_PROFILE/chrome/userChrome.css"
fi fi
echo "Optional: Add $(cd "$ICON_DIR"; pwd) to your PATH to allowing launching the app from command line"