linkding/bookmarks/services/favicon_loader.py
Sascha Ißbrücker 814401be2e
Add option for showing bookmark favicons (#390)
* Implement favicon loader

* Implement load favicon task

* Show favicons in bookmark list

* Add missing migration

* Load missing favicons on import

* Automatically refresh favicons

* Add enable favicon setting

* Update uwsgi config to host favicons

* Improve settings wording

* Fix favicon loader test setup

* Document LD_FAVICON_PROVIDER setting

* Add refresh favicons button
2023-01-21 16:36:10 +01:00

57 lines
1.5 KiB
Python

import os.path
import re
import shutil
import time
from pathlib import Path
from urllib.parse import urlparse
import requests
from django.conf import settings
max_file_age = 60 * 60 * 24 # 1 day
def _ensure_favicon_folder():
Path(settings.LD_FAVICON_FOLDER).mkdir(parents=True, exist_ok=True)
def _url_to_filename(url: str) -> str:
name = re.sub(r'\W+', '_', url)
return f'{name}.png'
def _get_base_url(url: str) -> str:
parsed_uri = urlparse(url)
return f'{parsed_uri.scheme}://{parsed_uri.hostname}'
def _get_favicon_path(favicon_file: str) -> Path:
return Path(os.path.join(settings.LD_FAVICON_FOLDER, favicon_file))
def _is_stale(path: Path) -> bool:
stat = path.stat()
file_age = time.time() - stat.st_mtime
return file_age >= max_file_age
def load_favicon(url: str) -> str:
# Get base URL so that we can reuse favicons for multiple bookmarks with the same host
base_url = _get_base_url(url)
favicon_name = _url_to_filename(base_url)
favicon_path = _get_favicon_path(favicon_name)
# Load icon if it doesn't exist yet or has become stale
if not favicon_path.exists() or _is_stale(favicon_path):
# Create favicon folder if not exists
_ensure_favicon_folder()
# Load favicon from provider, save to file
favicon_url = settings.LD_FAVICON_PROVIDER.format(url=base_url)
response = requests.get(favicon_url, stream=True)
with open(favicon_path, 'wb') as file:
shutil.copyfileobj(response.raw, file)
del response
return favicon_name