mirror of
https://github.com/ankitects/anki.git
synced 2025-09-23 08:22:24 -04:00
include report in MediaCheckOutput
This commit is contained in:
parent
dc9362d4ed
commit
5c8e3df612
6 changed files with 152 additions and 71 deletions
|
@ -271,9 +271,7 @@ message SyncMediaIn {
|
||||||
message MediaCheckOut {
|
message MediaCheckOut {
|
||||||
repeated string unused = 1;
|
repeated string unused = 1;
|
||||||
repeated string missing = 2;
|
repeated string missing = 2;
|
||||||
repeated string dirs = 3;
|
string report = 3;
|
||||||
repeated string oversize = 4;
|
|
||||||
map<string,string> renamed = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message TrashMediaFilesIn {
|
message TrashMediaFilesIn {
|
||||||
|
|
|
@ -60,7 +60,7 @@ class MediaChecker:
|
||||||
"Run the check on a background thread."
|
"Run the check on a background thread."
|
||||||
return self.mw.col.media.check()
|
return self.mw.col.media.check()
|
||||||
|
|
||||||
def _on_finished(self, future: Future):
|
def _on_finished(self, future: Future) -> None:
|
||||||
hooks.bg_thread_progress_callback.remove(self._on_progress)
|
hooks.bg_thread_progress_callback.remove(self._on_progress)
|
||||||
self.mw.progress.finish()
|
self.mw.progress.finish()
|
||||||
self.progress_dialog = None
|
self.progress_dialog = None
|
||||||
|
@ -70,8 +70,8 @@ class MediaChecker:
|
||||||
if isinstance(exc, Interrupted):
|
if isinstance(exc, Interrupted):
|
||||||
return
|
return
|
||||||
|
|
||||||
output = future.result()
|
output: MediaCheckOutput = future.result()
|
||||||
report = describe_output(output)
|
report = output.report
|
||||||
|
|
||||||
# show report and offer to delete
|
# show report and offer to delete
|
||||||
diag = QDialog(self.mw)
|
diag = QDialog(self.mw)
|
||||||
|
@ -156,56 +156,3 @@ class MediaChecker:
|
||||||
self.progress_dialog = None
|
self.progress_dialog = None
|
||||||
|
|
||||||
tooltip(_("Files moved to trash."))
|
tooltip(_("Files moved to trash."))
|
||||||
|
|
||||||
|
|
||||||
def describe_output(output: MediaCheckOutput) -> str:
|
|
||||||
buf = []
|
|
||||||
|
|
||||||
buf.append(_("Missing files: {}").format(len(output.missing)))
|
|
||||||
buf.append(_("Unused files: {}").format(len(output.unused)))
|
|
||||||
if output.renamed:
|
|
||||||
buf.append(_("Renamed files: {}").format(len(output.renamed)))
|
|
||||||
if output.oversize:
|
|
||||||
buf.append(_("Over 100MB: {}".format(output.oversize)))
|
|
||||||
if output.dirs:
|
|
||||||
buf.append(_("Subfolders: {}".format(output.dirs)))
|
|
||||||
|
|
||||||
buf.append("")
|
|
||||||
|
|
||||||
if output.renamed:
|
|
||||||
buf.append(_("Some files have been renamed for compatibility:"))
|
|
||||||
buf.extend(
|
|
||||||
_("Renamed: %(old)s -> %(new)s") % dict(old=k, new=v)
|
|
||||||
for (k, v) in sorted(output.renamed.items())
|
|
||||||
)
|
|
||||||
buf.append("")
|
|
||||||
|
|
||||||
if output.oversize:
|
|
||||||
buf.append(_("Files over 100MB can not be synced with AnkiWeb."))
|
|
||||||
buf.extend(_("Over 100MB: {}").format(f) for f in sorted(output.oversize))
|
|
||||||
buf.append("")
|
|
||||||
|
|
||||||
if output.dirs:
|
|
||||||
buf.append(_("Folders inside the media folder are not supported."))
|
|
||||||
buf.extend(_("Folder: {}").format(f) for f in sorted(output.dirs))
|
|
||||||
buf.append("")
|
|
||||||
|
|
||||||
if output.missing:
|
|
||||||
buf.append(
|
|
||||||
_(
|
|
||||||
"The following files are referenced by cards, but were not found in the media folder:"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
buf.extend(_("Missing: {}").format(f) for f in sorted(output.missing))
|
|
||||||
buf.append("")
|
|
||||||
|
|
||||||
if output.unused:
|
|
||||||
buf.append(
|
|
||||||
_(
|
|
||||||
"The following files were found in the media folder, but do not appear to be used on any cards:"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
buf.extend(_("Unused: {}").format(f) for f in sorted(output.unused))
|
|
||||||
buf.append("")
|
|
||||||
|
|
||||||
return "\n".join(buf)
|
|
||||||
|
|
|
@ -361,14 +361,14 @@ impl Backend {
|
||||||
|
|
||||||
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
|
let mgr = MediaManager::new(&self.media_folder, &self.media_db)?;
|
||||||
let mut checker = MediaChecker::new(&mgr, &self.col_path, callback, &self.i18n);
|
let mut checker = MediaChecker::new(&mgr, &self.col_path, callback, &self.i18n);
|
||||||
let output = checker.check()?;
|
let mut output = checker.check()?;
|
||||||
|
|
||||||
|
let report = checker.summarize_output(&mut output);
|
||||||
|
|
||||||
Ok(pb::MediaCheckOut {
|
Ok(pb::MediaCheckOut {
|
||||||
unused: output.unused,
|
unused: output.unused,
|
||||||
missing: output.missing,
|
missing: output.missing,
|
||||||
renamed: output.renamed,
|
report,
|
||||||
dirs: output.dirs,
|
|
||||||
oversize: output.oversize,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
rslib/src/i18n/media-check.ftl
Normal file
19
rslib/src/i18n/media-check.ftl
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
missing-count = Missing files: {$count}
|
||||||
|
unused-count = Unused files: {$count}
|
||||||
|
renamed-count = Renamed files: {$count}
|
||||||
|
oversize-count = Over 100MB: {$count}
|
||||||
|
subfolder-count = Subfolders: {$count}
|
||||||
|
|
||||||
|
renamed-header = Some files have been renamed for compatibility:
|
||||||
|
oversize-header = Files over 100MB can not be synced with AnkiWeb.
|
||||||
|
subfolder-header = Folders inside the media folder are not supported.
|
||||||
|
missing-header =
|
||||||
|
The following files are referenced by cards, but were not found in the media folder:
|
||||||
|
unused-header =
|
||||||
|
The following files were found in the media folder, but do not appear to be used on any cards:
|
||||||
|
|
||||||
|
renamed-file = Renamed: {$old} -> {$new}
|
||||||
|
oversize-file = Over 100MB: {$filename}
|
||||||
|
subfolder-file = Folder: {$filename}
|
||||||
|
missing-file = Missing: {$filename}
|
||||||
|
unused-file = Unused: {$filename}
|
|
@ -10,6 +10,21 @@ use unic_langid::LanguageIdentifier;
|
||||||
|
|
||||||
pub use fluent::fluent_args as tr_args;
|
pub use fluent::fluent_args as tr_args;
|
||||||
|
|
||||||
|
/// Helper for creating args with &strs
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! tr_strs {
|
||||||
|
( $($key:expr => $value:expr),* ) => {
|
||||||
|
{
|
||||||
|
let mut args: fluent::FluentArgs = fluent::FluentArgs::new();
|
||||||
|
$(
|
||||||
|
args.insert($key, $value.to_string().into());
|
||||||
|
)*
|
||||||
|
args
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub use tr_strs;
|
||||||
|
|
||||||
/// All languages we (currently) support, excluding the fallback
|
/// All languages we (currently) support, excluding the fallback
|
||||||
/// English.
|
/// English.
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
@ -169,8 +184,8 @@ impl I18nCategory {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get translation with one or more arguments.
|
/// Get translation with one or more arguments.
|
||||||
pub fn trn(&self, key: &str, args: FluentArgs) -> Cow<str> {
|
pub fn trn(&self, key: &str, args: FluentArgs) -> String {
|
||||||
self.tr_(key, Some(args))
|
self.tr_(key, Some(args)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tr_<'a>(&'a self, key: &str, args: Option<FluentArgs>) -> Cow<'a, str> {
|
fn tr_<'a>(&'a self, key: &str, args: Option<FluentArgs>) -> Cow<'a, str> {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use crate::cloze::expand_clozes_to_reveal_latex;
|
use crate::cloze::expand_clozes_to_reveal_latex;
|
||||||
use crate::err::{AnkiError, Result};
|
use crate::err::{AnkiError, Result};
|
||||||
use crate::i18n::I18n;
|
use crate::i18n::{tr_args, tr_strs, I18n, TranslationFile};
|
||||||
use crate::latex::extract_latex;
|
use crate::latex::extract_latex;
|
||||||
use crate::media::col::{
|
use crate::media::col::{
|
||||||
for_every_note, get_note_types, mark_collection_modified, open_or_create_collection_db,
|
for_every_note, get_note_types, mark_collection_modified, open_or_create_collection_db,
|
||||||
|
@ -78,9 +78,6 @@ where
|
||||||
let folder_check = self.check_media_folder(&mut ctx)?;
|
let folder_check = self.check_media_folder(&mut ctx)?;
|
||||||
let referenced_files = self.check_media_references(&folder_check.renamed)?;
|
let referenced_files = self.check_media_references(&folder_check.renamed)?;
|
||||||
let (unused, missing) = find_unused_and_missing(folder_check.files, referenced_files);
|
let (unused, missing) = find_unused_and_missing(folder_check.files, referenced_files);
|
||||||
|
|
||||||
let _ = self.i18n;
|
|
||||||
|
|
||||||
Ok(MediaCheckOutput {
|
Ok(MediaCheckOutput {
|
||||||
unused,
|
unused,
|
||||||
missing,
|
missing,
|
||||||
|
@ -90,6 +87,88 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn summarize_output(&self, output: &mut MediaCheckOutput) -> String {
|
||||||
|
let mut buf = String::new();
|
||||||
|
let cat = self.i18n.get(TranslationFile::MediaCheck);
|
||||||
|
|
||||||
|
// top summary area
|
||||||
|
buf += &cat.trn("missing-count", tr_args!["count"=>output.missing.len()]);
|
||||||
|
buf.push('\n');
|
||||||
|
|
||||||
|
buf += &cat.trn("unused-count", tr_args!["count"=>output.unused.len()]);
|
||||||
|
buf.push('\n');
|
||||||
|
|
||||||
|
if !output.renamed.is_empty() {
|
||||||
|
buf += &cat.trn("renamed-count", tr_args!["count"=>output.renamed.len()]);
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
if !output.oversize.is_empty() {
|
||||||
|
buf += &cat.trn("oversize-count", tr_args!["count"=>output.oversize.len()]);
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
if !output.dirs.is_empty() {
|
||||||
|
buf += &cat.trn("subfolder-count", tr_args!["count"=>output.dirs.len()]);
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push('\n');
|
||||||
|
|
||||||
|
if !output.renamed.is_empty() {
|
||||||
|
buf += &cat.tr("renamed-header");
|
||||||
|
buf.push('\n');
|
||||||
|
for (old, new) in &output.renamed {
|
||||||
|
buf += &cat.trn("renamed-file", tr_strs!["old"=>old,"new"=>new]);
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
buf.push('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if !output.oversize.is_empty() {
|
||||||
|
output.oversize.sort();
|
||||||
|
buf += &cat.tr("oversize-header");
|
||||||
|
buf.push('\n');
|
||||||
|
for fname in &output.oversize {
|
||||||
|
buf += &cat.trn("oversize-file", tr_strs!["filename"=>fname]);
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
buf.push('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if !output.dirs.is_empty() {
|
||||||
|
output.dirs.sort();
|
||||||
|
buf += &cat.tr("subfolder-header");
|
||||||
|
buf.push('\n');
|
||||||
|
for fname in &output.dirs {
|
||||||
|
buf += &cat.trn("subfolder-file", tr_strs!["filename"=>fname]);
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
buf.push('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if !output.missing.is_empty() {
|
||||||
|
output.missing.sort();
|
||||||
|
buf += &cat.tr("missing-header");
|
||||||
|
buf.push('\n');
|
||||||
|
for fname in &output.missing {
|
||||||
|
buf += &cat.trn("missing-file", tr_strs!["filename"=>fname]);
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
buf.push('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if !output.unused.is_empty() {
|
||||||
|
output.unused.sort();
|
||||||
|
buf += &cat.tr("unused-header");
|
||||||
|
buf.push('\n');
|
||||||
|
for fname in &output.unused {
|
||||||
|
buf += &cat.trn("unused-file", tr_strs!["filename"=>fname]);
|
||||||
|
buf.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
/// Check all the files in the media folder.
|
/// Check all the files in the media folder.
|
||||||
///
|
///
|
||||||
/// - Renames files with invalid names
|
/// - Renames files with invalid names
|
||||||
|
@ -377,17 +456,18 @@ mod test {
|
||||||
fs::write(&mgr.media_folder.join("normal.jpg"), "normal")?;
|
fs::write(&mgr.media_folder.join("normal.jpg"), "normal")?;
|
||||||
fs::write(&mgr.media_folder.join("foo[.jpg"), "foo")?;
|
fs::write(&mgr.media_folder.join("foo[.jpg"), "foo")?;
|
||||||
fs::write(&mgr.media_folder.join("_under.jpg"), "foo")?;
|
fs::write(&mgr.media_folder.join("_under.jpg"), "foo")?;
|
||||||
|
fs::write(&mgr.media_folder.join("unused.jpg"), "foo")?;
|
||||||
|
|
||||||
let i18n = I18n::new(&["zz"], "dummy");
|
let i18n = I18n::new(&["zz"], "dummy");
|
||||||
|
|
||||||
let progress = |_n| true;
|
let progress = |_n| true;
|
||||||
let mut checker = MediaChecker::new(&mgr, &col_path, progress, &i18n);
|
let mut checker = MediaChecker::new(&mgr, &col_path, progress, &i18n);
|
||||||
let output = checker.check()?;
|
let mut output = checker.check()?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
output,
|
output,
|
||||||
MediaCheckOutput {
|
MediaCheckOutput {
|
||||||
unused: vec![],
|
unused: vec!["unused.jpg".into()],
|
||||||
missing: vec!["ぱぱ.jpg".into()],
|
missing: vec!["ぱぱ.jpg".into()],
|
||||||
renamed: vec![("foo[.jpg".into(), "foo.jpg".into())]
|
renamed: vec![("foo[.jpg".into(), "foo.jpg".into())]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -400,6 +480,28 @@ mod test {
|
||||||
assert!(fs::metadata(&mgr.media_folder.join("foo[.jpg")).is_err());
|
assert!(fs::metadata(&mgr.media_folder.join("foo[.jpg")).is_err());
|
||||||
assert!(fs::metadata(&mgr.media_folder.join("foo.jpg")).is_ok());
|
assert!(fs::metadata(&mgr.media_folder.join("foo.jpg")).is_ok());
|
||||||
|
|
||||||
|
let report = checker.summarize_output(&mut output);
|
||||||
|
assert_eq!(
|
||||||
|
report,
|
||||||
|
"Missing files: 1
|
||||||
|
Unused files: 1
|
||||||
|
Renamed files: 1
|
||||||
|
Subfolders: 1
|
||||||
|
|
||||||
|
Some files have been renamed for compatibility:
|
||||||
|
Renamed: foo[.jpg -> foo.jpg
|
||||||
|
|
||||||
|
Folders inside the media folder are not supported.
|
||||||
|
Folder: folder
|
||||||
|
|
||||||
|
The following files are referenced by cards, but were not found in the media folder:
|
||||||
|
Missing: ぱぱ.jpg
|
||||||
|
|
||||||
|
The following files were found in the media folder, but do not appear to be used on any cards:
|
||||||
|
Unused: unused.jpg
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue