diff --git a/bookmarks/api/routes.py b/bookmarks/api/routes.py index 78d9c58..aa49f4f 100644 --- a/bookmarks/api/routes.py +++ b/bookmarks/api/routes.py @@ -98,12 +98,13 @@ class BookmarkViewSet( @action(methods=["get"], detail=False) def check(self, request: HttpRequest): url = request.GET.get("url") + ignore_cache = request.GET.get("ignore_cache", False) in ["true"] bookmark = Bookmark.objects.filter(owner=request.user, url=url).first() existing_bookmark_data = ( self.get_serializer(bookmark).data if bookmark else None ) - metadata = website_loader.load_website_metadata(url) + metadata = website_loader.load_website_metadata(url, ignore_cache=ignore_cache) # Return tags that would be automatically applied to the bookmark profile = request.user.profile diff --git a/bookmarks/services/bookmarks.py b/bookmarks/services/bookmarks.py index c3e2f50..f2b8a81 100644 --- a/bookmarks/services/bookmarks.py +++ b/bookmarks/services/bookmarks.py @@ -197,6 +197,17 @@ def unshare_bookmarks(bookmark_ids: [Union[int, str]], current_user: User): ) +def refresh_bookmarks_metadata(bookmark_ids: [Union[int, str]], current_user: User): + sanitized_bookmark_ids = _sanitize_id_list(bookmark_ids) + owned_bookmarks = Bookmark.objects.filter( + owner=current_user, id__in=sanitized_bookmark_ids + ) + + for bookmark in owned_bookmarks: + tasks.refresh_metadata(bookmark) + tasks.load_preview_image(current_user, bookmark) + + def _merge_bookmark_data(from_bookmark: Bookmark, to_bookmark: Bookmark): to_bookmark.title = from_bookmark.title to_bookmark.description = from_bookmark.description diff --git a/bookmarks/services/tasks.py b/bookmarks/services/tasks.py index 3af0014..9c5ce61 100644 --- a/bookmarks/services/tasks.py +++ b/bookmarks/services/tasks.py @@ -6,6 +6,7 @@ import waybackpy from django.conf import settings from django.contrib.auth.models import User from django.db.models import Q +from django.utils import timezone from huey import crontab from huey.contrib.djhuey import HUEY as huey from huey.exceptions import TaskLockedException @@ -13,7 +14,7 @@ from waybackpy.exceptions import WaybackError, TooManyRequestsError from bookmarks.models import Bookmark, BookmarkAsset, UserProfile from bookmarks.services import assets, favicon_loader, preview_image_loader -from bookmarks.services.website_loader import DEFAULT_USER_AGENT +from bookmarks.services.website_loader import DEFAULT_USER_AGENT, load_website_metadata logger = logging.getLogger(__name__) @@ -225,6 +226,31 @@ def _schedule_bookmarks_without_previews_task(user_id: int): logging.exception(exc) +def refresh_metadata(bookmark: Bookmark): + if not settings.LD_DISABLE_BACKGROUND_TASKS: + _refresh_metadata_task(bookmark.id) + + +@task() +def _refresh_metadata_task(bookmark_id: int): + try: + bookmark = Bookmark.objects.get(id=bookmark_id) + except Bookmark.DoesNotExist: + return + + logger.info(f"Refresh metadata for bookmark. url={bookmark.url}") + + metadata = load_website_metadata(bookmark.url) + if metadata.title: + bookmark.title = metadata.title + if metadata.description: + bookmark.description = metadata.description + bookmark.date_modified = timezone.now() + + bookmark.save() + logger.info(f"Successfully refreshed metadata for bookmark. url={bookmark.url}") + + def is_html_snapshot_feature_active() -> bool: return settings.LD_ENABLE_SNAPSHOTS and not settings.LD_DISABLE_BACKGROUND_TASKS diff --git a/bookmarks/services/website_loader.py b/bookmarks/services/website_loader.py index 2134659..e396f57 100644 --- a/bookmarks/services/website_loader.py +++ b/bookmarks/services/website_loader.py @@ -27,10 +27,20 @@ class WebsiteMetadata: } +def load_website_metadata(url: str, ignore_cache: bool = False): + if ignore_cache: + return _load_website_metadata(url) + return _load_website_metadata_cached(url) + + # Caching metadata avoids scraping again when saving bookmarks, in case the # metadata was already scraped to show preview values in the bookmark form @lru_cache(maxsize=10) -def load_website_metadata(url: str): +def _load_website_metadata_cached(url: str): + return _load_website_metadata(url) + + +def _load_website_metadata(url: str): title = None description = None preview_image = None diff --git a/bookmarks/styles/bookmark-form.css b/bookmarks/styles/bookmark-form.css index 820fab8..6b52aa4 100644 --- a/bookmarks/styles/bookmark-form.css +++ b/bookmarks/styles/bookmark-form.css @@ -15,14 +15,23 @@ visibility: hidden; } - & .form-group .clear-button { - display: none; + & .form-group .suffix-button { padding: 0; border: none; height: auto; font-size: var(--font-size-sm); } + & .form-group .clear-button, + & .form-group #refresh-button { + display: none; + } + + & .form-group input.modified, + & .form-group textarea.modified { + background: var(--primary-color-shade); + } + & .form-input-hint.bookmark-exists { display: none; color: var(--warning-color); diff --git a/bookmarks/templates/bookmarks/bulk_edit/bar.html b/bookmarks/templates/bookmarks/bulk_edit/bar.html index 9f5d8a9..3648483 100644 --- a/bookmarks/templates/bookmarks/bulk_edit/bar.html +++ b/bookmarks/templates/bookmarks/bulk_edit/bar.html @@ -22,6 +22,7 @@ {% endif %} +