mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-24 10:09:01 -06:00
LRC-LIB-KEYWORDS: File ▸ Import Keywords… and Export Keywords…
The File menu (beside the presets) imports a keyword list file (Lightroom Classic's, Capture One's, Photo Supreme's .utf8) and exports LightCraft's, with native file dialogs off the UI thread like the other file commands; the desktop app gives the synchronous ones too. Import says how many keywords it added and how many gained synonyms; export says how many it wrote and names the keywords Capture One won't import (; , < or > in a name). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3d75952562
commit
8160148a8b
@@ -213,6 +213,8 @@ fn services(originals: Originals, backend: Option<Backend>, files: Files, frozen
|
||||
// Face models need a folder to live in; the web has none.
|
||||
pick_model_file: None,
|
||||
save_preset_file: None,
|
||||
pick_keyword_list: None,
|
||||
save_keyword_list: None,
|
||||
pick_curve_preset_files: None,
|
||||
save_curve_preset_file: None,
|
||||
write: Some(Box::new(download)),
|
||||
|
||||
@@ -472,6 +472,22 @@ fn services(ctx: egui::Context, log_file: Option<&std::path::Path>) -> Services
|
||||
.save_file()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
})),
|
||||
pick_keyword_list: Some(Box::new(|| {
|
||||
rfd::FileDialog::new()
|
||||
.set_title(lightcraft_ui_egui::i18n::tr("Import Keywords"))
|
||||
.add_filter_nocase(lightcraft_ui_egui::i18n::tr("Keyword Lists"), &["txt", "utf8"])
|
||||
.pick_file()
|
||||
.map(|p| vec![p.to_string_lossy().to_string()])
|
||||
.unwrap_or_default()
|
||||
})),
|
||||
save_keyword_list: Some(Box::new(|name: &str| {
|
||||
rfd::FileDialog::new()
|
||||
.set_title(lightcraft_ui_egui::i18n::tr("Export Keywords"))
|
||||
.add_filter_nocase(lightcraft_ui_egui::i18n::tr("Keyword Lists"), &["txt"])
|
||||
.set_file_name(name)
|
||||
.save_file()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
})),
|
||||
pick_curve_preset_files: Some(Box::new(|| {
|
||||
rfd::FileDialog::new()
|
||||
.set_title(lightcraft_ui_egui::i18n::tr("Import Point Curve Presets"))
|
||||
|
||||
@@ -216,5 +216,9 @@
|
||||
"Added “{keyword}” to {n} photo{}": "„{keyword}“ zu {n} Foto{} hinzugefügt",
|
||||
"On {have} of {of} selected photos": "Auf {have} von {of} ausgewählten Fotos",
|
||||
"Remove “{name}”": "„{name}“ entfernen",
|
||||
"There is a keyword set “{name}” already": "Es gibt bereits einen Stichwortsatz „{name}“"
|
||||
"There is a keyword set “{name}” already": "Es gibt bereits einen Stichwortsatz „{name}“",
|
||||
"Added {n} keyword{}": "{n} Stichwort/Stichwörter hinzugefügt{:.0}",
|
||||
"; {n} gained synonyms": "; {n} erhielten Synonyme",
|
||||
"Exported {n} keyword{}": "{n} Stichwort/Stichwörter exportiert{:.0}",
|
||||
" — Capture One won't import a list with ; , < or > in a keyword: {names}": " — Capture One importiert keine Liste mit ; , < oder > in einem Stichwort: {names}"
|
||||
}
|
||||
|
||||
@@ -1921,5 +1921,10 @@
|
||||
"Keywords (⌥1–⌥9)": "Stichwörter (⌥1–⌥9)",
|
||||
"A keyword set needs a name": "Ein Stichwortsatz braucht einen Namen",
|
||||
"Save as a new set": "Als neuen Satz speichern",
|
||||
"Recent Keywords is built in: choose another name": "„Letzte Stichwörter“ ist vorgegeben: Wählen Sie einen anderen Namen"
|
||||
"Recent Keywords is built in: choose another name": "„Letzte Stichwörter“ ist vorgegeben: Wählen Sie einen anderen Namen",
|
||||
"Import Keywords…": "Stichwörter importieren…",
|
||||
"Export Keywords…": "Stichwörter exportieren…",
|
||||
"Import Keywords": "Stichwörter importieren",
|
||||
"Export Keywords": "Stichwörter exportieren",
|
||||
"Keyword Lists": "Stichwortlisten"
|
||||
}
|
||||
|
||||
@@ -216,5 +216,9 @@
|
||||
"Added “{keyword}” to {n} photo{}": "Se añadió “{keyword}” a {n} foto{}",
|
||||
"On {have} of {of} selected photos": "En {have} de {of} fotos seleccionadas",
|
||||
"Remove “{name}”": "Quitar “{name}”",
|
||||
"There is a keyword set “{name}” already": "Ya hay un conjunto de palabras clave “{name}”"
|
||||
"There is a keyword set “{name}” already": "Ya hay un conjunto de palabras clave “{name}”",
|
||||
"Added {n} keyword{}": "Se añadieron {n} palabra{} clave",
|
||||
"; {n} gained synonyms": "; {n} recibieron sinónimos",
|
||||
"Exported {n} keyword{}": "Se exportaron {n} palabra{} clave",
|
||||
" — Capture One won't import a list with ; , < or > in a keyword: {names}": " — Capture One no importa una lista con ; , < o > en una palabra clave: {names}"
|
||||
}
|
||||
|
||||
@@ -1766,5 +1766,10 @@
|
||||
"Keywords (⌥1–⌥9)": "Palabras clave (⌥1–⌥9)",
|
||||
"A keyword set needs a name": "Un conjunto de palabras clave necesita un nombre",
|
||||
"Save as a new set": "Guardar como conjunto nuevo",
|
||||
"Recent Keywords is built in: choose another name": "“Palabras clave recientes” es fijo: elige otro nombre"
|
||||
"Recent Keywords is built in: choose another name": "“Palabras clave recientes” es fijo: elige otro nombre",
|
||||
"Import Keywords…": "Importar palabras clave…",
|
||||
"Export Keywords…": "Exportar palabras clave…",
|
||||
"Import Keywords": "Importar palabras clave",
|
||||
"Export Keywords": "Exportar palabras clave",
|
||||
"Keyword Lists": "Listas de palabras clave"
|
||||
}
|
||||
|
||||
@@ -216,5 +216,9 @@
|
||||
"Added “{keyword}” to {n} photo{}": "写真{n}枚に「{keyword}」を追加しました{:.0}",
|
||||
"On {have} of {of} selected photos": "選択した写真{of}枚のうち{have}枚",
|
||||
"Remove “{name}”": "「{name}」を削除",
|
||||
"There is a keyword set “{name}” already": "キーワードセット「{name}」はすでにあります"
|
||||
"There is a keyword set “{name}” already": "キーワードセット「{name}」はすでにあります",
|
||||
"Added {n} keyword{}": "{n}件のキーワードを追加しました{:.0}",
|
||||
"; {n} gained synonyms": "。{n}件に同義語を追加",
|
||||
"Exported {n} keyword{}": "{n}件のキーワードを書き出しました{:.0}",
|
||||
" — Capture One won't import a list with ; , < or > in a keyword: {names}": " — Capture One は ; , < > を含むキーワードがあるリストを読み込みません: {names}"
|
||||
}
|
||||
|
||||
@@ -1766,5 +1766,10 @@
|
||||
"Keywords (⌥1–⌥9)": "キーワード (⌥1–⌥9)",
|
||||
"A keyword set needs a name": "キーワードセットには名前が必要です",
|
||||
"Save as a new set": "新しいセットとして保存",
|
||||
"Recent Keywords is built in: choose another name": "「最近使用したキーワード」は組み込みです。別の名前を選んでください"
|
||||
"Recent Keywords is built in: choose another name": "「最近使用したキーワード」は組み込みです。別の名前を選んでください",
|
||||
"Import Keywords…": "キーワードを読み込み…",
|
||||
"Export Keywords…": "キーワードを書き出し…",
|
||||
"Import Keywords": "キーワードを読み込み",
|
||||
"Export Keywords": "キーワードを書き出し",
|
||||
"Keyword Lists": "キーワードリスト"
|
||||
}
|
||||
|
||||
@@ -216,5 +216,9 @@
|
||||
"Added “{keyword}” to {n} photo{}": "“{keyword}” adicionada a {n} foto{}",
|
||||
"On {have} of {of} selected photos": "Em {have} de {of} fotos selecionadas",
|
||||
"Remove “{name}”": "Remover “{name}”",
|
||||
"There is a keyword set “{name}” already": "Já existe um conjunto de palavras-chave “{name}”"
|
||||
"There is a keyword set “{name}” already": "Já existe um conjunto de palavras-chave “{name}”",
|
||||
"Added {n} keyword{}": "Palavras-chave adicionadas: {n}{:.0}",
|
||||
"; {n} gained synonyms": "; {n} receberam sinônimos",
|
||||
"Exported {n} keyword{}": "Palavras-chave exportadas: {n}{:.0}",
|
||||
" — Capture One won't import a list with ; , < or > in a keyword: {names}": " — O Capture One não importa uma lista com ; , < ou > em uma palavra-chave: {names}"
|
||||
}
|
||||
|
||||
@@ -1766,5 +1766,10 @@
|
||||
"Keywords (⌥1–⌥9)": "Palavras-chave (⌥1–⌥9)",
|
||||
"A keyword set needs a name": "Um conjunto de palavras-chave precisa de um nome",
|
||||
"Save as a new set": "Salvar como novo conjunto",
|
||||
"Recent Keywords is built in: choose another name": "“Palavras-chave recentes” é fixo: escolha outro nome"
|
||||
"Recent Keywords is built in: choose another name": "“Palavras-chave recentes” é fixo: escolha outro nome",
|
||||
"Import Keywords…": "Importar palavras-chave…",
|
||||
"Export Keywords…": "Exportar palavras-chave…",
|
||||
"Import Keywords": "Importar palavras-chave",
|
||||
"Export Keywords": "Exportar palavras-chave",
|
||||
"Keyword Lists": "Listas de palavras-chave"
|
||||
}
|
||||
|
||||
@@ -216,5 +216,9 @@
|
||||
"Added “{keyword}” to {n} photo{}": "«{keyword}» добавлено к фото ({n}){:.0}",
|
||||
"On {have} of {of} selected photos": "На {have} из {of} выбранных фото",
|
||||
"Remove “{name}”": "Убрать «{name}»",
|
||||
"There is a keyword set “{name}” already": "Набор ключевых слов «{name}» уже есть"
|
||||
"There is a keyword set “{name}” already": "Набор ключевых слов «{name}» уже есть",
|
||||
"Added {n} keyword{}": "Добавлено ключевых слов: {n}{:.0}",
|
||||
"; {n} gained synonyms": "; синонимы добавлены к {n}",
|
||||
"Exported {n} keyword{}": "Экспортировано ключевых слов: {n}{:.0}",
|
||||
" — Capture One won't import a list with ; , < or > in a keyword: {names}": " — Capture One не импортирует список, где в ключевом слове есть ; , < или >: {names}"
|
||||
}
|
||||
|
||||
@@ -1442,5 +1442,10 @@
|
||||
"Set name": "Название набора",
|
||||
"Save as a new set": "Сохранить как новый набор",
|
||||
"Recent Keywords is built in: choose another name": "«Недавние ключевые слова» — встроенный набор: выберите другое название",
|
||||
"Recent Keywords": "Недавние ключевые слова"
|
||||
"Recent Keywords": "Недавние ключевые слова",
|
||||
"Import Keywords…": "Импорт ключевых слов…",
|
||||
"Export Keywords…": "Экспорт ключевых слов…",
|
||||
"Import Keywords": "Импорт ключевых слов",
|
||||
"Export Keywords": "Экспорт ключевых слов",
|
||||
"Keyword Lists": "Списки ключевых слов"
|
||||
}
|
||||
|
||||
@@ -216,5 +216,9 @@
|
||||
"Added “{keyword}” to {n} photo{}": "已将“{keyword}”添加到 {n} 张照片{:.0}",
|
||||
"On {have} of {of} selected photos": "所选 {of} 张照片中的 {have} 张",
|
||||
"Remove “{name}”": "移除“{name}”",
|
||||
"There is a keyword set “{name}” already": "已有关键字集“{name}”"
|
||||
"There is a keyword set “{name}” already": "已有关键字集“{name}”",
|
||||
"Added {n} keyword{}": "已添加 {n} 个关键字{:.0}",
|
||||
"; {n} gained synonyms": ";{n} 个获得了同义词",
|
||||
"Exported {n} keyword{}": "已导出 {n} 个关键字{:.0}",
|
||||
" — Capture One won't import a list with ; , < or > in a keyword: {names}": " — Capture One 不会导入关键字中含有 ; , < 或 > 的列表:{names}"
|
||||
}
|
||||
|
||||
@@ -1766,5 +1766,10 @@
|
||||
"Keywords (⌥1–⌥9)": "关键字 (⌥1–⌥9)",
|
||||
"A keyword set needs a name": "关键字集需要名称",
|
||||
"Save as a new set": "另存为新关键字集",
|
||||
"Recent Keywords is built in: choose another name": "“最近使用的关键字”为内置集:请另选名称"
|
||||
"Recent Keywords is built in: choose another name": "“最近使用的关键字”为内置集:请另选名称",
|
||||
"Import Keywords…": "导入关键字…",
|
||||
"Export Keywords…": "导出关键字…",
|
||||
"Import Keywords": "导入关键字",
|
||||
"Export Keywords": "导出关键字",
|
||||
"Keyword Lists": "关键字列表"
|
||||
}
|
||||
|
||||
@@ -216,5 +216,9 @@
|
||||
"Added “{keyword}” to {n} photo{}": "已將「{keyword}」加入 {n} 張照片{:.0}",
|
||||
"On {have} of {of} selected photos": "選取的 {of} 張照片中的 {have} 張",
|
||||
"Remove “{name}”": "移除「{name}」",
|
||||
"There is a keyword set “{name}” already": "已有關鍵字集「{name}」"
|
||||
"There is a keyword set “{name}” already": "已有關鍵字集「{name}」",
|
||||
"Added {n} keyword{}": "已加入 {n} 個關鍵字{:.0}",
|
||||
"; {n} gained synonyms": ";{n} 個獲得了同義詞",
|
||||
"Exported {n} keyword{}": "已匯出 {n} 個關鍵字{:.0}",
|
||||
" — Capture One won't import a list with ; , < or > in a keyword: {names}": " — Capture One 不會匯入關鍵字中含有 ; , < 或 > 的清單:{names}"
|
||||
}
|
||||
|
||||
@@ -1766,5 +1766,10 @@
|
||||
"Keywords (⌥1–⌥9)": "關鍵字 (⌥1–⌥9)",
|
||||
"A keyword set needs a name": "關鍵字集需要名稱",
|
||||
"Save as a new set": "另存為新關鍵字集",
|
||||
"Recent Keywords is built in: choose another name": "「最近使用的關鍵字」為內建集:請另選名稱"
|
||||
"Recent Keywords is built in: choose another name": "「最近使用的關鍵字」為內建集:請另選名稱",
|
||||
"Import Keywords…": "匯入關鍵字…",
|
||||
"Export Keywords…": "匯出關鍵字…",
|
||||
"Import Keywords": "匯入關鍵字",
|
||||
"Export Keywords": "匯出關鍵字",
|
||||
"Keyword Lists": "關鍵字清單"
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ mod tests_grid;
|
||||
#[cfg(test)]
|
||||
mod tests_keymap;
|
||||
#[cfg(test)]
|
||||
mod tests_keyword_files;
|
||||
#[cfg(test)]
|
||||
mod tests_keyword_list;
|
||||
#[cfg(test)]
|
||||
mod tests_keyword_set;
|
||||
@@ -124,6 +126,10 @@ pub struct Services {
|
||||
pub pick_tracklog: Option<PickFiles>,
|
||||
/// Save dialog for an exported `.lcpreset` file.
|
||||
pub save_preset_file: Option<SaveFile>,
|
||||
/// Open dialog for a keyword list file (`.txt`, Photo Supreme's `.utf8`; File ▸ Import Keywords…).
|
||||
pub pick_keyword_list: Option<PickFiles>,
|
||||
/// Save dialog for an exported keyword list (File ▸ Export Keywords…).
|
||||
pub save_keyword_list: Option<SaveFile>,
|
||||
/// Open dialog for point-curve preset files (`.lccurve`).
|
||||
pub pick_curve_preset_files: Option<PickFiles>,
|
||||
/// Save dialog for an exported `.lccurve` file.
|
||||
|
||||
@@ -77,6 +77,8 @@ const LAYOUT: &[(&str, &[&str])] = &[
|
||||
"---",
|
||||
"file.importPresets",
|
||||
"file.exportPresets",
|
||||
"file.importKeywords",
|
||||
"file.exportKeywords",
|
||||
"---",
|
||||
"dialog.export",
|
||||
"app.exportPrevious",
|
||||
|
||||
@@ -164,6 +164,8 @@ pub const UI_COMMANDS: &[UiCommand] = &[
|
||||
("app.quit", "Quit LightCraft", Some("Cmd+Q"), "File"),
|
||||
("file.importPresets", "Import Profiles & Presets…", None, "File"),
|
||||
("file.exportPresets", "Export Presets…", None, "File"),
|
||||
("file.importKeywords", "Import Keywords…", None, "File"),
|
||||
("file.exportKeywords", "Export Keywords…", None, "File"),
|
||||
// Edit panel ▸ Curve ▸ Point Curve dropdown
|
||||
("file.importCurvePresets", "Import Point Curve Presets…", None, ""),
|
||||
("file.exportCurvePresets", "Export Point Curve Presets…", None, ""),
|
||||
@@ -1346,6 +1348,68 @@ pub fn run_ui_command(app: &mut LightcraftApp, id: &str, p: &Value) -> Option<Re
|
||||
}
|
||||
return Some(r);
|
||||
}
|
||||
"file.importKeywords" => {
|
||||
// a keyword list file: Lightroom Classic's, Capture One's, Photo Supreme's (.utf8)
|
||||
let path = match p.get("path").and_then(Value::as_str) {
|
||||
Some(x) => x.to_string(),
|
||||
None => {
|
||||
let req = PickRequest::file(crate::i18n::tr("Import Keywords"), crate::i18n::tr("Keyword Lists"), &["txt", "utf8"]);
|
||||
match crate::pick::ask(app, id, p, "path", req, |s| s.pick_keyword_list.as_mut().map(|f| f())) {
|
||||
Picked::Now(v) => match v.into_iter().next() {
|
||||
Some(x) => x,
|
||||
None => return Some(Ok(Value::Null)),
|
||||
},
|
||||
Picked::Later => return Some(Ok(Value::Null)),
|
||||
Picked::Unavailable => return Some(Err("no file dialog on this platform".into())),
|
||||
}
|
||||
}
|
||||
};
|
||||
let r = app.session.execute("keyword.import", &json!({"path": path})).map_err(|e| e.to_string());
|
||||
match &r {
|
||||
Ok(v) => {
|
||||
let (added, updated) = (v["added"].as_u64().unwrap_or(0), v["updated"].as_u64().unwrap_or(0));
|
||||
let mut msg = crate::i18n::tr_format!("Added {n} keyword{}", if added == 1 { "" } else { "s" }, n = added);
|
||||
if updated > 0 {
|
||||
msg.push_str(&crate::i18n::tr_format!("; {n} gained synonyms", n = updated));
|
||||
}
|
||||
app.toast(&ctx, msg);
|
||||
}
|
||||
Err(e) => app.toast(&ctx, e.clone()),
|
||||
}
|
||||
return Some(r);
|
||||
}
|
||||
"file.exportKeywords" => {
|
||||
let path = match p.get("path").and_then(Value::as_str) {
|
||||
Some(x) => x.to_string(),
|
||||
None => {
|
||||
let name = "Keywords.txt";
|
||||
let req = PickRequest::save(crate::i18n::tr("Export Keywords"), crate::i18n::tr("Keyword Lists"), &["txt"], name);
|
||||
match crate::pick::ask(app, id, p, "path", req, |s| s.save_keyword_list.as_mut().map(|f| f(name).into_iter().collect())) {
|
||||
Picked::Now(v) => match v.into_iter().next() {
|
||||
Some(x) => x,
|
||||
None => return Some(Ok(Value::Null)),
|
||||
},
|
||||
Picked::Later => return Some(Ok(Value::Null)),
|
||||
Picked::Unavailable => return Some(Err("no file dialog on this platform".into())),
|
||||
}
|
||||
}
|
||||
};
|
||||
let r = app.session.execute("keyword.export", &json!({"path": path})).map_err(|e| e.to_string());
|
||||
if let Ok(v) = &r {
|
||||
let n = v["keywords"].as_u64().unwrap_or(0);
|
||||
let mut msg = crate::i18n::tr_format!("Exported {n} keyword{}", if n == 1 { "" } else { "s" }, n = n);
|
||||
// Capture One's importer refuses ; , < > in a list: say which to rename first
|
||||
let refuses: Vec<&str> = v["captureOneRefuses"].as_array().into_iter().flatten().filter_map(Value::as_str).collect();
|
||||
if !refuses.is_empty() {
|
||||
msg.push_str(&crate::i18n::tr_format!(
|
||||
" — Capture One won't import a list with ; , < or > in a keyword: {names}",
|
||||
names = refuses.iter().take(5).copied().collect::<Vec<_>>().join(" · ")
|
||||
));
|
||||
}
|
||||
app.toast(&ctx, msg);
|
||||
}
|
||||
return Some(r);
|
||||
}
|
||||
"file.exportPresets" => {
|
||||
let group = p.get("group").and_then(Value::as_str).map(str::to_string);
|
||||
let path = match p.get("path").and_then(Value::as_str) {
|
||||
|
||||
@@ -605,6 +605,8 @@ mod tests {
|
||||
"Recent Keywords",
|
||||
"Keywords (⌥1–⌥9)",
|
||||
"Save as a new set",
|
||||
"Import Keywords…",
|
||||
"Export Keywords…",
|
||||
];
|
||||
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("locales");
|
||||
for code in ["de", "es", "pt-br", "ru", "ja", "zh-hans", "zh-hant"] {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
//! File ▸ Import Keywords… / Export Keywords…: keyword list files (Lightroom Classic's format,
|
||||
//! which Capture One and Photo Supreme read and write too).
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
use crate::headless::Headless;
|
||||
use crate::{LightcraftApp, Services};
|
||||
|
||||
const T: Duration = Duration::from_secs(20);
|
||||
const SETTLE: Duration = Duration::from_secs(120);
|
||||
|
||||
fn app() -> Headless {
|
||||
let app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), Services { png: None, ..Default::default() });
|
||||
let mut h = Headless::new(app, [1200.0, 800.0], 1.0);
|
||||
h.settle(SETTLE);
|
||||
h
|
||||
}
|
||||
|
||||
fn temp_dir(tag: &str) -> std::path::PathBuf {
|
||||
let d = std::env::temp_dir().join(format!("lc-ui-keyword-files-{tag}-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&d);
|
||||
std::fs::create_dir_all(&d).unwrap();
|
||||
d
|
||||
}
|
||||
|
||||
fn run(h: &mut Headless, command: &str, params: serde_json::Value) -> serde_json::Value {
|
||||
let r = h.request("engine.execute", json!({"command": command, "params": params}), T);
|
||||
h.settle(SETTLE);
|
||||
r
|
||||
}
|
||||
|
||||
fn toast(h: &Headless) -> String {
|
||||
h.app.ui.toast.clone().map(|t| t.0).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// The File menu offers Import Keywords… and Export Keywords….
|
||||
#[test]
|
||||
fn the_file_menu_offers_keyword_files() {
|
||||
let mut h = app();
|
||||
let r = h.request("ui.menu.list", json!({}), T);
|
||||
let items = r["result"].to_string();
|
||||
for id in ["file.importKeywords", "file.exportKeywords"] {
|
||||
assert!(items.contains(id), "{id}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Import Keywords… reads a list and says how many keywords it added.
|
||||
#[test]
|
||||
fn importing_keywords_says_what_it_added() {
|
||||
let dir = temp_dir("import");
|
||||
let mut h = app();
|
||||
let path = dir.join("vocabulary.utf8");
|
||||
std::fs::write(&path, "[Places]\r\n\tPortugal\r\n\t\tLisbon\r\n").unwrap();
|
||||
let r = run(&mut h, "file.importKeywords", json!({"path": path.to_string_lossy()}));
|
||||
assert_eq!(r["ok"], true, "{r}");
|
||||
assert!(h.app.session.catalog.has_keyword("Places|Portugal|Lisbon"));
|
||||
assert!(toast(&h).contains("3"), "{}", toast(&h));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// Export Keywords… writes the list, and warns about the keywords Capture One won't import.
|
||||
#[test]
|
||||
fn exporting_keywords_warns_about_capture_one() {
|
||||
let dir = temp_dir("export");
|
||||
let mut h = app();
|
||||
let id = h.app.session.visible_cloned()[0].0;
|
||||
run(&mut h, "photo.setMeta", json!({"ids": [id], "addKeywords": ["fish, chips"]}));
|
||||
let path = dir.join("keywords.txt");
|
||||
let r = run(&mut h, "file.exportKeywords", json!({"path": path.to_string_lossy()}));
|
||||
assert_eq!(r["ok"], true, "{r}");
|
||||
assert!(std::fs::read_to_string(&path).unwrap().contains("fish, chips"));
|
||||
assert!(toast(&h).contains("Capture One"), "{}", toast(&h));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
Reference in New Issue
Block a user