mirror of
https://github.com/sissbruecker/linkding
synced 2024-11-10 06:04:15 +00:00
814401be2e
* 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
57 lines
1.5 KiB
Python
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
|