diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 72ec7bcd8..7067db078 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -18,7 +18,7 @@ import warnings from collections.abc import Callable from enum import Enum from random import randrange -from typing import Any, Match, cast +from typing import Any, Iterable, Match, cast import bs4 import requests @@ -844,8 +844,12 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too # Media downloads ###################################################################### - def urlToLink(self, url: str) -> str: - fname = self.urlToFile(url) + def urlToLink(self, url: str, allowed_suffixes: Iterable[str] = ()) -> str: + fname = ( + self.urlToFile(url, allowed_suffixes) + if allowed_suffixes + else self.urlToFile(url) + ) if not fname: return '{}'.format( url, html.escape(urllib.parse.unquote(url)) @@ -861,9 +865,11 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too av_player.play_file_with_caller(fname, self.editorMode) return f"[sound:{html.escape(fname, quote=False)}]" - def urlToFile(self, url: str) -> str | None: + def urlToFile( + self, url: str, allowed_suffixes: Iterable[str] = pics + audio + ) -> str | None: l = url.lower() - for suffix in pics + audio: + for suffix in allowed_suffixes: if l.endswith(f".{suffix}"): return self._retrieveURL(url) # not a supported type @@ -1094,6 +1100,13 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too self.parentWindow.activateWindow() + def extract_img_path_from_html(self, html: str) -> str | None: + assert self.note is not None + # with allowed_suffixes=pics, all non-pics will be rendered as s and won't be included here + if not (images := self.mw.col.media.files_in_str(self.note.mid, html)): + return None + return os.path.join(self.mw.col.media.dir(), images[0]) + def select_image_from_clipboard_and_occlude(self) -> None: """Set up the mask editor for the image in the clipboard.""" @@ -1101,12 +1114,16 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too assert clipboard is not None mime = clipboard.mimeData() assert mime is not None - if not mime.hasImage(): + # try checking for urls first, fallback to image data + if ( + (html := self.web._processUrls(mime, allowed_suffixes=pics)) + and (path := self.extract_img_path_from_html(html)) + ) or (mime.hasImage() and (path := self._read_pasted_image(mime))): + self.setup_mask_editor(path) + self.parentWindow.activateWindow() + else: showWarning(tr.editing_no_image_found_on_clipboard()) return - path = self._read_pasted_image(mime) - self.setup_mask_editor(path) - self.parentWindow.activateWindow() def setup_mask_editor_for_new_note( self, @@ -1569,15 +1586,19 @@ class EditorWebView(AnkiWebView): html_content = mime.html()[11:] if internal else mime.html() return html_content, internal + # given _processUrls' extra allowed_suffixes kwarg, placate the typechecker + def process_url(mime: QMimeData, extended: bool = False) -> str | None: + return self._processUrls(mime, extended) + # favour url if it's a local link if ( mime.hasUrls() and (urls := mime.urls()) and urls[0].toString().startswith("file://") ): - types = (self._processUrls, self._processImage, self._processText) + types = (process_url, self._processImage, self._processText) else: - types = (self._processImage, self._processUrls, self._processText) + types = (self._processImage, process_url, self._processText) for fn in types: html = fn(mime, extended) @@ -1585,7 +1606,12 @@ class EditorWebView(AnkiWebView): return html, True return "", False - def _processUrls(self, mime: QMimeData, extended: bool = False) -> str | None: + def _processUrls( + self, + mime: QMimeData, + extended: bool = False, + allowed_suffixes: Iterable[str] = (), + ) -> str | None: if not mime.hasUrls(): return None @@ -1595,7 +1621,7 @@ class EditorWebView(AnkiWebView): # chrome likes to give us the URL twice with a \n if lines := url.splitlines(): url = lines[0] - buf += self.editor.urlToLink(url) + buf += self.editor.urlToLink(url, allowed_suffixes=allowed_suffixes) return buf