diff --git a/proto/anki/media.proto b/proto/anki/media.proto index 1455bdeb5..76d42931a 100644 --- a/proto/anki/media.proto +++ b/proto/anki/media.proto @@ -8,6 +8,7 @@ option java_multiple_files = true; package anki.media; import "anki/generic.proto"; +import "anki/notetypes.proto"; service MediaService { rpc CheckMedia(generic.Empty) returns (CheckMediaResponse); @@ -15,6 +16,8 @@ service MediaService { rpc TrashMediaFiles(TrashMediaFilesRequest) returns (generic.Empty); rpc EmptyTrash(generic.Empty) returns (generic.Empty); rpc RestoreTrash(generic.Empty) returns (generic.Empty); + rpc ExtractStaticMediaFiles(notetypes.NotetypeId) + returns (generic.StringList); } // Implicitly includes any of the above methods that are not listed in the diff --git a/pylib/anki/media.py b/pylib/anki/media.py index e09a1ba6f..f97c0ddb8 100644 --- a/pylib/anki/media.py +++ b/pylib/anki/media.py @@ -8,7 +8,7 @@ import pprint import re import sys import time -from typing import Callable +from typing import Callable, Sequence from anki import media_pb2 from anki._legacy import DeprecatedNamesMixin, deprecated_keywords @@ -149,6 +149,9 @@ class MediaManager(DeprecatedNamesMixin): files.append(fname) return files + def extract_static_media_files(self, mid: NotetypeId) -> Sequence[str]: + return self.col._backend.extract_static_media_files(mid) + def transform_names(self, txt: str, func: Callable) -> str: for reg in self.regexps: txt = re.sub(reg, func, txt) diff --git a/rslib/src/import_export/gather.rs b/rslib/src/import_export/gather.rs index ea6afd974..5b3c15304 100644 --- a/rslib/src/import_export/gather.rs +++ b/rslib/src/import_export/gather.rs @@ -16,8 +16,6 @@ use crate::revlog::RevlogEntry; use crate::search::CardTableGuard; use crate::search::NoteTableGuard; use crate::text::extract_media_refs; -use crate::text::extract_underscored_css_imports; -use crate::text::extract_underscored_references; #[derive(Debug, Default)] pub(super) struct ExchangeData { @@ -75,7 +73,7 @@ impl ExchangeData { gather_media_names_from_note(note, &mut inserter, &svg_getter); } for notetype in self.notetypes.iter() { - gather_media_names_from_notetype(notetype, &mut inserter); + notetype.gather_media_names(&mut inserter); } Ok(()) } @@ -158,19 +156,6 @@ fn gather_media_names_from_note( } } -fn gather_media_names_from_notetype(notetype: &Notetype, inserter: &mut impl FnMut(String)) { - for name in extract_underscored_css_imports(¬etype.config.css) { - inserter(name.to_string()); - } - for template in ¬etype.templates { - for template_side in [&template.config.q_format, &template.config.a_format] { - for name in extract_underscored_references(template_side) { - inserter(name.to_string()); - } - } - } -} - fn svg_getter(notetypes: &[Notetype]) -> impl Fn(NotetypeId) -> bool { let svg_map: HashMap = notetypes .iter() diff --git a/rslib/src/media/service.rs b/rslib/src/media/service.rs index 028a7f842..b2ec99d1d 100644 --- a/rslib/src/media/service.rs +++ b/rslib/src/media/service.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use anki_proto::generic; @@ -7,7 +9,9 @@ use anki_proto::media::TrashMediaFilesRequest; use crate::collection::Collection; use crate::error; +use crate::error::OrNotFound; use crate::notes::service::to_i64s; +use crate::notetype::NotetypeId; impl crate::services::MediaService for Collection { fn check_media(&mut self) -> error::Result { @@ -47,4 +51,19 @@ impl crate::services::MediaService for Collection { fn restore_trash(&mut self) -> error::Result<()> { self.media_checker()?.restore_trash() } + + fn extract_static_media_files( + &mut self, + ntid: anki_proto::notetypes::NotetypeId, + ) -> error::Result { + let ntid = NotetypeId::from(ntid); + let notetype = self.storage.get_notetype(ntid)?.or_not_found(ntid)?; + let mut files: HashSet = HashSet::new(); + let mut inserter = |name: String| { + files.insert(name); + }; + notetype.gather_media_names(&mut inserter); + + Ok(files.into_iter().collect::>().into()) + } } diff --git a/rslib/src/notetype/mod.rs b/rslib/src/notetype/mod.rs index 492b05b1c..c62a58e2e 100644 --- a/rslib/src/notetype/mod.rs +++ b/rslib/src/notetype/mod.rs @@ -58,6 +58,8 @@ use crate::storage::comma_separated_ids; use crate::template::FieldRequirements; use crate::template::ParsedTemplate; use crate::text::ensure_string_in_nfc; +use crate::text::extract_underscored_css_imports; +use crate::text::extract_underscored_references; define_newtype!(NotetypeId, i64); @@ -624,6 +626,19 @@ impl Notetype { HashSet::new() } } + + pub(crate) fn gather_media_names(&self, inserter: &mut impl FnMut(String)) { + for name in extract_underscored_css_imports(&self.config.css) { + inserter(name.to_string()); + } + for template in &self.templates { + for template_side in [&template.config.q_format, &template.config.a_format] { + for name in extract_underscored_references(template_side) { + inserter(name.to_string()); + } + } + } + } } /// True if the slice is empty or either template of the first tuple doesn't